Merge autoland to mozilla-central. a=merge
authorBogdan Tara <btara@mozilla.com>
Mon, 02 Sep 2019 12:48:14 +0300
changeset 491076 db860d4b10078af59956a4279d1bb6f299e3935c
parent 491044 c80c2571005ecd66acd905370509318d05d31e47 (current diff)
parent 491075 6e9f2e7ef81929eca589179b5bd9d0b70dba0d24 (diff)
child 491219 0fe8d7dd713ddda2c9bb1670895bc6b57de55550
child 491220 62310b6831f29df1650a0bbb90b9af2bac7097ca
push id36521
push userbtara@mozilla.com
push dateMon, 02 Sep 2019 09:48:57 +0000
treeherdermozilla-central@db860d4b1007 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone71.0a1
first release with
nightly linux32
db860d4b1007 / 71.0a1 / 20190902094857 / files
nightly linux64
db860d4b1007 / 71.0a1 / 20190902094857 / files
nightly mac
db860d4b1007 / 71.0a1 / 20190902094857 / files
nightly win32
db860d4b1007 / 71.0a1 / 20190902094857 / files
nightly win64
db860d4b1007 / 71.0a1 / 20190902094857 / 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 autoland to mozilla-central. a=merge
--- a/browser/components/aboutconfig/content/aboutconfig.css
+++ b/browser/components/aboutconfig/content/aboutconfig.css
@@ -175,8 +175,12 @@ td.cell-value > form > input[type="numbe
 
 .button-delete {
   background-image: url("chrome://global/skin/icons/delete.svg");
 }
 
 .button-reset {
   background-image: url("chrome://browser/skin/undo.svg");
 }
+
+.button-reset:dir(rtl) {
+  transform: scaleX(-1);
+}
--- a/devtools/client/inspector/rules/new-rules.js
+++ b/devtools/client/inspector/rules/new-rules.js
@@ -623,16 +623,17 @@ class RulesView {
       maxWidth: () => {
         // Return the width of the closest declaration container element.
         const containerElement = element.closest(".ruleview-propertycontainer");
         return containerElement.getBoundingClientRect().width;
       },
       multiline: true,
       popup: this.autocompletePopup,
       property: declaration,
+      showSuggestCompletionOnEmpty: true,
     });
   }
 
   /**
    * Shows the new inplace editor for a new declaration.
    *
    * @param  {DOMNode} element
    *         A new declaration span element to be edited.
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -95,16 +95,18 @@ skip-if = (verify && debug && os == 'win
 [browser_rules_completion-existing-property_01.js]
 [browser_rules_completion-existing-property_02.js]
 [browser_rules_completion-new-property_01.js]
 [browser_rules_completion-new-property_02.js]
 skip-if = (verify && !debug && os == 'win')
 [browser_rules_completion-new-property_03.js]
 [browser_rules_completion-new-property_04.js]
 [browser_rules_completion-new-property_multiline.js]
+[browser_rules_completion-on-empty.js]
+[browser_rules_completion-shortcut.js]
 [browser_rules_computed-lists_01.js]
 [browser_rules_computed-lists_02.js]
 [browser_rules_computed-lists_03.js]
 [browser_rules_completion-popup-hidden-after-navigation.js]
 [browser_rules_content_01.js]
 [browser_rules_content_02.js]
 [browser_rules_variables-in-pseudo-element_01.js]
 [browser_rules_variables-in-pseudo-element_02.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-on-empty.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the suggest completion popup behavior of CSS property field.
+
+const TEST_URI = "<h1 style='color: lime'>Header</h1>";
+
+add_task(async function() {
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector, view } = await openRuleView();
+
+  info("Selecting the test node");
+  await selectNode("h1", inspector);
+
+  const rule = getRuleViewRuleEditor(view, 0).rule;
+  const prop = rule.textProps[0];
+
+  info("Test with css property value field");
+  await testCompletion(view, prop.editor.valueSpan, true);
+
+  info("Test with css property name field");
+  await testCompletion(view, prop.editor.nameSpan, false);
+});
+
+async function testCompletion(view, target, isExpectedOpenPopup) {
+  const editor = await focusEditableField(view, target);
+
+  info(
+    "Check the suggest completion popup visibility after clearing the field"
+  );
+
+  const onChanged = view.once("ruleview-changed");
+  const popupEvent = isExpectedOpenPopup ? "popup-opened" : "popup-closed";
+  const onPopupEvent =
+    editor.popup.isOpen === isExpectedOpenPopup
+      ? Promise.resolve()
+      : once(view.popup, popupEvent);
+  EventUtils.synthesizeKey("VK_BACK_SPACE", {}, view.styleWindow);
+
+  // Flush the debounce to update the preview text.
+  view.debounce.flush();
+
+  await Promise.all([onChanged, onPopupEvent]);
+  is(
+    editor.popup.isOpen,
+    isExpectedOpenPopup,
+    "The popup visibility is correct"
+  );
+
+  if (editor.popup.isOpen) {
+    info("Close the suggest completion popup");
+    const closingEvents = [
+      view.once("ruleview-changed"),
+      once(view.popup, "popup-closed"),
+    ];
+    EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
+    await Promise.all(closingEvents);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-shortcut.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the shortcut key for the suggest completion popup.
+
+const TEST_URI = "<h1 style='colo: lim'>Header</h1>";
+const TEST_SHORTCUTS = [
+  {
+    key: " ",
+    modifiers: { ctrlKey: true },
+  },
+  {
+    key: "VK_DOWN",
+    modifiers: {},
+  },
+];
+
+add_task(async function() {
+  for (const shortcut of TEST_SHORTCUTS) {
+    info(
+      "Start to test for the shortcut " +
+        `key: "${shortcut.key}" modifiers: ${Object.keys(shortcut.modifiers)}`
+    );
+
+    const tab = await addTab(
+      "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)
+    );
+    const { inspector, view } = await openRuleView();
+
+    info("Selecting the test node");
+    await selectNode("h1", inspector);
+
+    const rule = getRuleViewRuleEditor(view, 0).rule;
+    const prop = rule.textProps[0];
+
+    info("Test with css property name field");
+    const nameEditor = await focusEditableField(view, prop.editor.nameSpan);
+    await testCompletion(shortcut, view, nameEditor, "color");
+
+    info("Test with css property value field");
+    const valueEditor = inplaceEditor(view.styleDocument.activeElement);
+    await testCompletion(shortcut, view, valueEditor, "lime");
+
+    await removeTab(tab);
+  }
+});
+
+async function testCompletion(shortcut, view, editor, expectedValue) {
+  const spanEl = editor.elt;
+
+  info("Move cursor to the end");
+  EventUtils.synthesizeKey("VK_RIGHT", {}, view.styleWindow);
+  await waitUntil(
+    () => editor.input.selectionStart === editor.input.selectionEnd
+  );
+
+  info("Check whether the popup opens after sending the shortcut key");
+  const onPopupOpened = once(view.popup, "popup-opened");
+  EventUtils.synthesizeKey(shortcut.key, shortcut.modifiers, view.styleWindow);
+  await onPopupOpened;
+  ok(view.popup.isOpen, "The popup opened correctly");
+
+  info("Commit the suggestion");
+  const onChanged = view.once("ruleview-changed");
+  const onPopupClosed = once(view.popup, "popup-closed");
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
+  await Promise.all([onChanged, onPopupClosed]);
+  is(spanEl.textContent, expectedValue, "The value is set correctly");
+}
--- a/devtools/client/inspector/rules/test/browser_rules_gridline-names-autocomplete.js
+++ b/devtools/client/inspector/rules/test/browser_rules_gridline-names-autocomplete.js
@@ -32,17 +32,17 @@ const changeTestData = [
 const newAreaTestData = [
   ["g", {}, "gap", OPEN, SELECTED, !CHANGE],
   ["VK_DOWN", {}, "grid", OPEN, SELECTED, !CHANGE],
   ["VK_DOWN", {}, "grid-area", OPEN, SELECTED, !CHANGE],
   ["VK_TAB", {}, "", !OPEN, !SELECTED, !CHANGE],
   "grid-line-names-updated",
   ["c", {}, "col1-start", OPEN, SELECTED, CHANGE],
   ["VK_BACK_SPACE", {}, "c", !OPEN, !SELECTED, CHANGE],
-  ["VK_BACK_SPACE", {}, "", !OPEN, !SELECTED, CHANGE],
+  ["VK_BACK_SPACE", {}, "", OPEN, !SELECTED, CHANGE],
   ["r", {}, "revert", OPEN, SELECTED, CHANGE],
   ["VK_DOWN", {}, "row1-start", OPEN, SELECTED, CHANGE],
   ["r", {}, "rr", !OPEN, !SELECTED, CHANGE],
   ["VK_BACK_SPACE", {}, "r", !OPEN, !SELECTED, CHANGE],
   ["o", {}, "row1-start", OPEN, SELECTED, CHANGE],
   ["VK_RETURN", {}, "", !OPEN, !SELECTED, CHANGE],
 ];
 
@@ -53,17 +53,17 @@ const newRowTestData = [
   ["r", {}, "grid", OPEN, SELECTED, !CHANGE],
   ["i", {}, "grid", OPEN, SELECTED, !CHANGE],
   ["d", {}, "grid", OPEN, SELECTED, !CHANGE],
   ["-", {}, "grid-area", OPEN, SELECTED, !CHANGE],
   ["r", {}, "grid-row", OPEN, SELECTED, !CHANGE],
   ["VK_RETURN", {}, "", !OPEN, !SELECTED, !CHANGE],
   "grid-line-names-updated",
   ["c", {}, "c", !OPEN, !SELECTED, CHANGE],
-  ["VK_BACK_SPACE", {}, "", !OPEN, !SELECTED, CHANGE],
+  ["VK_BACK_SPACE", {}, "", OPEN, !SELECTED, CHANGE],
   ["r", {}, "revert", OPEN, SELECTED, CHANGE],
   ["VK_DOWN", {}, "row1-start", OPEN, SELECTED, CHANGE],
   ["VK_TAB", {}, "", !OPEN, !SELECTED, CHANGE],
 ];
 
 const TEST_URL = URL_ROOT + "doc_grid_names.html";
 
 add_task(async function() {
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -381,17 +381,19 @@ var setProperty = async function(
   textProp,
   value,
   blurNewProperty = true
 ) {
   await focusEditableField(view, textProp.editor.valueSpan);
 
   const onPreview = view.once("ruleview-changed");
   if (value === null) {
+    const onPopupOpened = once(view.popup, "popup-opened");
     EventUtils.synthesizeKey("VK_DELETE", {}, view.styleWindow);
+    await onPopupOpened;
   } else {
     EventUtils.sendString(value, view.styleWindow);
   }
   view.debounce.flush();
   await onPreview;
 
   const onValueDone = view.once("ruleview-changed");
   EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -339,16 +339,17 @@ TextPropertyEditor.prototype = {
         popup: this.popup,
         multiline: true,
         maxWidth: () => this.container.getBoundingClientRect().width,
         cssProperties: this.cssProperties,
         cssVariables:
           this.rule.elementStyle.variablesMap.get(this.rule.pseudoElement) ||
           [],
         getGridLineNames: this.getGridlineNames,
+        showSuggestCompletionOnEmpty: true,
       });
     }
   },
 
   /**
    * Get the grid line names of the grid that the currently selected element is
    * contained in.
    *
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -154,16 +154,19 @@ function isKeyIn(key, ...keys) {
  *      defaults to false
  *    {Object} cssProperties: An instance of CSSProperties.
  *    {Object} cssVariables: A Map object containing all CSS variables.
  *    {Number} defaultIncrement: The value by which the input is incremented
  *      or decremented by default (0.1 for properties like opacity and 1 by default)
  *    {Function} getGridLineNames:
  *       Will be called before offering autocomplete sugestions, if the property is
  *       a member of GRID_PROPERTY_NAMES.
+ *    {Boolean} showSuggestCompletionOnEmpty:
+ *       If true, show the suggestions in case that the current text becomes empty.
+ *       Defaults to false.
  */
 function editableField(options) {
   return editableItem(options, function(element, event) {
     if (!options.element.inplaceEditor) {
       new InplaceEditor(options, event);
     }
   });
 }
@@ -287,16 +290,17 @@ function InplaceEditor(options, event) {
   this.stopOnReturn = !!options.stopOnReturn;
   this.contentType = options.contentType || CONTENT_TYPES.PLAIN_TEXT;
   this.property = options.property;
   this.popup = options.popup;
   this.preserveTextStyles =
     options.preserveTextStyles === undefined
       ? false
       : !!options.preserveTextStyles;
+  this.showSuggestCompletionOnEmpty = !!options.showSuggestCompletionOnEmpty;
 
   this._onBlur = this._onBlur.bind(this);
   this._onWindowBlur = this._onWindowBlur.bind(this);
   this._onKeyPress = this._onKeyPress.bind(this);
   this._onInput = this._onInput.bind(this);
   this._onKeyup = this._onKeyup.bind(this);
   this._onAutocompletePopupClick = this._onAutocompletePopupClick.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
@@ -1191,25 +1195,30 @@ InplaceEditor.prototype = {
     if (isPopupOpen && isKeyIn(key, "UP", "DOWN", "PAGE_UP", "PAGE_DOWN")) {
       prevent = true;
       cycling = true;
       this._cycleCSSSuggestion(isKeyIn(key, "UP", "PAGE_UP"));
       this._doValidation();
     }
 
     if (isKeyIn(key, "BACK_SPACE", "DELETE", "LEFT", "RIGHT", "HOME", "END")) {
-      if (isPopupOpen) {
+      if (isPopupOpen && this.currentInputValue !== "") {
         this._hideAutocompletePopup();
       }
     } else if (
-      !cycling &&
-      !multilineNavigation &&
-      !event.metaKey &&
-      !event.altKey &&
-      !event.ctrlKey
+      // We may show the suggestion completion if Ctrl+space is pressed, or if an
+      // otherwise unhandled key is pressed and the user is not cycling through the
+      // options in the pop-up menu, it is not an expanded shorthand property, and no
+      // modifier key is pressed.
+      (event.key === " " && event.ctrlKey) ||
+      (!cycling &&
+        !multilineNavigation &&
+        !event.metaKey &&
+        !event.altKey &&
+        !event.ctrlKey)
     ) {
       this._maybeSuggestCompletion(true);
     }
 
     if (this.multiline && event.shiftKey && isKeyIn(key, "RETURN")) {
       prevent = false;
     } else if (
       this._advanceChars(event.charCode, input.value, input.selectionStart) ||
@@ -1390,16 +1399,21 @@ InplaceEditor.prototype = {
     if (this._measurement) {
       this._updateSize();
     }
 
     // Call the user's change handler if available.
     if (this.change) {
       this.change(this.currentInputValue);
     }
+
+    // In case that the current value becomes empty, show the suggestions if needed.
+    if (this.currentInputValue === "" && this.showSuggestCompletionOnEmpty) {
+      this._maybeSuggestCompletion(false);
+    }
   },
 
   /**
    * Stop propagation on the provided event
    */
   _stopEventPropagation: function(e) {
     e.stopPropagation();
   },
--- a/devtools/client/webconsole/middleware/event-telemetry.js
+++ b/devtools/client/webconsole/middleware/event-telemetry.js
@@ -51,16 +51,17 @@ function eventTelemetryMiddleware(teleme
           session_id: sessionId,
         }
       );
     } else if (action.type === EVALUATE_EXPRESSION) {
       // Send telemetry event. If we are in the browser toolbox we send -1 as the
       // toolbox session id.
       telemetry.recordEvent("execute_js", "webconsole", null, {
         lines: action.expression.split(/\n/).length,
+        input: state.ui.editor ? "multiline" : "inline",
         session_id: sessionId,
       });
     }
 
     return res;
   };
 }
 
--- a/devtools/client/webconsole/test/browser/browser.ini
+++ b/devtools/client/webconsole/test/browser/browser.ini
@@ -466,16 +466,17 @@ skip-if = true # Bug 1572667
 [browser_webconsole_stubs_css_message.js]
 skip-if = true # Bug 1572667
 [browser_webconsole_stubs_evaluation_result.js]
 skip-if = true # Bug 1572667
 [browser_webconsole_stubs_network_event.js]
 skip-if = true # Bug 1572667
 [browser_webconsole_stubs_page_error.js]
 skip-if = true # Bug 1572667
+[browser_webconsole_telemetry_execute_js.js]
 [browser_webconsole_telemetry_js_errors.js]
 [browser_webconsole_telemetry_filters_changed.js]
 [browser_webconsole_telemetry_persist_toggle_changed.js]
 [browser_webconsole_telemetry_jump_to_definition.js]
 [browser_webconsole_telemetry_object_expanded.js]
 [browser_webconsole_time_methods.js]
 [browser_webconsole_timestamps.js]
 [browser_webconsole_trackingprotection_errors.js]
--- a/devtools/client/webconsole/test/browser/browser_jsterm_multiline.js
+++ b/devtools/client/webconsole/test/browser/browser_jsterm_multiline.js
@@ -4,19 +4,17 @@
 
 // Tests that the console waits for more input instead of evaluating
 // when valid, but incomplete, statements are present upon pressing enter
 // -or- when the user ends a line with shift + enter.
 
 "use strict";
 
 const TEST_URI =
-  "http://example.com/browser/devtools/client/webconsole/" +
-  "test/browser/test-console.html";
-const ALL_CHANNELS = Ci.nsITelemetry.DATASET_ALL_CHANNELS;
+  "http://example.com/browser/devtools/client/webconsole/test/browser/test-console.html";
 
 const SHOULD_ENTER_MULTILINE = [
   { input: "function foo() {" },
   { input: "var a = 1," },
   { input: "var a = 1;", shiftKey: true },
   { input: "function foo() { }", shiftKey: true },
   { input: "function" },
   { input: "(x) =>" },
@@ -37,103 +35,31 @@ const SHOULD_EXECUTE = [
   { input: "99 + 3" },
   { input: "1, 2, 3" },
   // errors
   { input: "function f(x) { let y = 1, }" },
   { input: "function f(x=,) {" },
   { input: "{2,}" },
 ];
 
-const SINGLE_LINE_DATA = {
-  timestamp: null,
-  category: "devtools.main",
-  method: "execute_js",
-  object: "webconsole",
-  value: null,
-  extra: {
-    lines: "1",
-  },
-};
-
-const DATA = [
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  {
-    timestamp: null,
-    category: "devtools.main",
-    method: "execute_js",
-    object: "webconsole",
-    value: null,
-    extra: {
-      lines: "3",
-    },
-  },
-];
-
 add_task(async function() {
-  // Let's reset the counts.
-  Services.telemetry.clearEvents();
-
-  // Ensure no events have been logged
-  const snapshot = Services.telemetry.snapshotEvents(ALL_CHANNELS, true);
-  ok(!snapshot.parent, "No events have been logged for the main process");
-
   const hud = await openNewTabAndConsole(TEST_URI);
 
   for (const { input, shiftKey } of SHOULD_ENTER_MULTILINE) {
     setInputValue(hud, input);
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
 
     // We need to remove the spaces at the end of the input since code mirror do some
     // automatic indent in some case.
     const newValue = getInputValue(hud).replace(/ +$/g, "");
     is(newValue, input + "\n", "A new line was added");
   }
 
   for (const { input, shiftKey } of SHOULD_EXECUTE) {
     setInputValue(hud, input);
+    const onMessage = waitForMessage(hud, "", ".result");
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
+    await onMessage;
 
     await waitFor(() => !getInputValue(hud));
     is(getInputValue(hud), "", "Input is cleared");
   }
-
-  await executeAndWaitForMessage(
-    hud,
-    "document.\nlocation.\nhref",
-    TEST_URI,
-    ".result"
-  );
-
-  checkEventTelemetry();
 });
-
-function checkEventTelemetry() {
-  const snapshot = Services.telemetry.snapshotEvents(ALL_CHANNELS, true);
-  const events = snapshot.parent.filter(
-    event =>
-      event[1] === "devtools.main" &&
-      event[2] === "execute_js" &&
-      event[3] === "webconsole" &&
-      event[4] === null
-  );
-
-  for (const i in DATA) {
-    const [timestamp, category, method, object, value, extra] = events[i];
-    const expected = DATA[i];
-
-    // ignore timestamp
-    ok(timestamp > 0, "timestamp is greater than 0");
-    is(category, expected.category, "category is correct");
-    is(method, expected.method, "method is correct");
-    is(object, expected.object, "object is correct");
-    is(value, expected.value, "value is correct");
-
-    is(extra.lines, expected.extra.lines, "lines is correct");
-  }
-}
copy from devtools/client/webconsole/test/browser/browser_jsterm_multiline.js
copy to devtools/client/webconsole/test/browser/browser_webconsole_telemetry_execute_js.js
--- a/devtools/client/webconsole/test/browser/browser_jsterm_multiline.js
+++ b/devtools/client/webconsole/test/browser/browser_webconsole_telemetry_execute_js.js
@@ -1,139 +1,97 @@
 /* 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/. */
 
-// Tests that the console waits for more input instead of evaluating
-// when valid, but incomplete, statements are present upon pressing enter
-// -or- when the user ends a line with shift + enter.
+// Tests that the console record the execute_js telemetry event with expected data
+// when evaluating expressions.
 
 "use strict";
 
-const TEST_URI =
-  "http://example.com/browser/devtools/client/webconsole/" +
-  "test/browser/test-console.html";
+const TEST_URI = `data:text/html,<meta charset=utf8>Test execute_js telemetry event`;
 const ALL_CHANNELS = Ci.nsITelemetry.DATASET_ALL_CHANNELS;
 
-const SHOULD_ENTER_MULTILINE = [
-  { input: "function foo() {" },
-  { input: "var a = 1," },
-  { input: "var a = 1;", shiftKey: true },
-  { input: "function foo() { }", shiftKey: true },
-  { input: "function" },
-  { input: "(x) =>" },
-  { input: "let b = {" },
-  { input: "let a = [" },
-  { input: "{" },
-  { input: "{ bob: 3343," },
-  { input: "function x(y=" },
-  { input: "Array.from(" },
-  // shift + enter creates a new line despite parse errors
-  { input: "{2,}", shiftKey: true },
-];
-const SHOULD_EXECUTE = [
-  { input: "function foo() { }" },
-  { input: "var a = 1;" },
-  { input: "function foo() { var a = 1; }" },
-  { input: '"asdf"' },
-  { input: "99 + 3" },
-  { input: "1, 2, 3" },
-  // errors
-  { input: "function f(x) { let y = 1, }" },
-  { input: "function f(x=,) {" },
-  { input: "{2,}" },
-];
+add_task(async function() {
+  await pushPref("devtools.webconsole.features.editor", true);
 
-const SINGLE_LINE_DATA = {
-  timestamp: null,
-  category: "devtools.main",
-  method: "execute_js",
-  object: "webconsole",
-  value: null,
-  extra: {
-    lines: "1",
-  },
-};
-
-const DATA = [
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  SINGLE_LINE_DATA,
-  {
-    timestamp: null,
-    category: "devtools.main",
-    method: "execute_js",
-    object: "webconsole",
-    value: null,
-    extra: {
-      lines: "3",
-    },
-  },
-];
-
-add_task(async function() {
   // Let's reset the counts.
   Services.telemetry.clearEvents();
 
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(ALL_CHANNELS, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
   const hud = await openNewTabAndConsole(TEST_URI);
 
-  for (const { input, shiftKey } of SHOULD_ENTER_MULTILINE) {
-    setInputValue(hud, input);
-    EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
+  info("Evaluate a single line");
+  await keyboardExecuteAndWaitForMessage(hud, `"single line"`, "", ".result");
 
-    // We need to remove the spaces at the end of the input since code mirror do some
-    // automatic indent in some case.
-    const newValue = getInputValue(hud).replace(/ +$/g, "");
-    is(newValue, input + "\n", "A new line was added");
-  }
+  info("Evaluate another single line");
+  await keyboardExecuteAndWaitForMessage(hud, `"single line 2"`, "", ".result");
+
+  info("Evaluate multiple lines");
+  await keyboardExecuteAndWaitForMessage(hud, `"n"\n.trim()`, "", ".result");
 
-  for (const { input, shiftKey } of SHOULD_EXECUTE) {
-    setInputValue(hud, input);
-    EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
+  info("Switch to editor mode");
+  await toggleLayout(hud);
+
+  info("Evaluate a single line in editor mode");
+  await keyboardExecuteAndWaitForMessage(hud, `"single line 3"`, "", ".result");
 
-    await waitFor(() => !getInputValue(hud));
-    is(getInputValue(hud), "", "Input is cleared");
-  }
-
-  await executeAndWaitForMessage(
+  info("Evaluate multiple lines in editor mode");
+  await keyboardExecuteAndWaitForMessage(
     hud,
-    "document.\nlocation.\nhref",
-    TEST_URI,
+    `"y"\n.trim()\n.trim()`,
+    "",
     ".result"
   );
 
-  checkEventTelemetry();
+  info("Evaluate multiple lines again in editor mode");
+  await keyboardExecuteAndWaitForMessage(hud, `"x"\n.trim()`, "", ".result");
+
+  checkEventTelemetry([
+    getTelemetryEventData({ lines: 1, input: "inline" }),
+    getTelemetryEventData({ lines: 1, input: "inline" }),
+    getTelemetryEventData({ lines: 2, input: "inline" }),
+    getTelemetryEventData({ lines: 1, input: "multiline" }),
+    getTelemetryEventData({ lines: 3, input: "multiline" }),
+    getTelemetryEventData({ lines: 2, input: "multiline" }),
+  ]);
+
+  info("Switch back to inline mode");
+  await toggleLayout(hud);
 });
 
-function checkEventTelemetry() {
+function checkEventTelemetry(expectedData) {
   const snapshot = Services.telemetry.snapshotEvents(ALL_CHANNELS, true);
   const events = snapshot.parent.filter(
     event =>
       event[1] === "devtools.main" &&
       event[2] === "execute_js" &&
       event[3] === "webconsole" &&
       event[4] === null
   );
 
-  for (const i in DATA) {
+  for (const [i, expected] of expectedData.entries()) {
     const [timestamp, category, method, object, value, extra] = events[i];
-    const expected = DATA[i];
 
     // ignore timestamp
     ok(timestamp > 0, "timestamp is greater than 0");
-    is(category, expected.category, "category is correct");
-    is(method, expected.method, "method is correct");
-    is(object, expected.object, "object is correct");
-    is(value, expected.value, "value is correct");
-
-    is(extra.lines, expected.extra.lines, "lines is correct");
+    is(category, expected.category, "'category' is correct");
+    is(method, expected.method, "'method' is correct");
+    is(object, expected.object, "'object' is correct");
+    is(value, expected.value, "'value' is correct");
+    is(extra.lines, expected.extra.lines, "'lines' is correct");
+    is(extra.input, expected.extra.input, "'input' is correct");
   }
 }
+
+function getTelemetryEventData(extra) {
+  return {
+    timestamp: null,
+    category: "devtools.main",
+    method: "execute_js",
+    object: "webconsole",
+    value: null,
+    extra,
+  };
+}
--- a/devtools/client/webconsole/test/browser/head.js
+++ b/devtools/client/webconsole/test/browser/head.js
@@ -246,16 +246,44 @@ function executeAndWaitForMessage(
   selector = ".message"
 ) {
   const onMessage = waitForMessage(hud, matchingText, selector);
   execute(hud, input);
   return onMessage;
 }
 
 /**
+ * Set the input value, simulates the right keyboard event to evaluate it, depending on
+ * if the console is in editor mode or not, and wait for a message with the expected text
+ * (and an optional selector) to be displayed in the output.
+ *
+ * @param {Object} hud : The webconsole.
+ * @param {String} input : The input expression to execute.
+ * @param {String} matchingText : A string that should match the message body content.
+ * @param {String} selector : A selector that should match the message node.
+ */
+function keyboardExecuteAndWaitForMessage(
+  hud,
+  input,
+  matchingText,
+  selector = ".message"
+) {
+  setInputValue(hud, input);
+  const onMessage = waitForMessage(hud, matchingText, selector);
+  if (isEditorModeEnabled(hud)) {
+    EventUtils.synthesizeKey("KEY_Enter", {
+      [Services.appinfo.OS === "Darwin" ? "metaKey" : "ctrlKey"]: true,
+    });
+  } else {
+    EventUtils.synthesizeKey("VK_RETURN");
+  }
+  return onMessage;
+}
+
+/**
  * Wait for a predicate to return a result.
  *
  * @param function condition
  *        Invoked once in a while until it returns a truthy value. This should be an
  *        idempotent function, since we have to run it a second time after it returns
  *        true in order to return the value.
  * @param string message [optional]
  *        A message to output if the condition fails.
--- a/devtools/server/actors/replay/replay.js
+++ b/devtools/server/actors/replay/replay.js
@@ -217,17 +217,17 @@ function addScriptSource(source) {
 function considerScript(script) {
   // The set of scripts which is exposed to the debugger server is the same as
   // the scripts for which the progress counter is updated.
   return RecordReplayControl.shouldUpdateProgressCounter(script.url);
 }
 
 function setEmptyInstrumentationId(script) {
   script.setInstrumentationId(0);
-  script.getChildScripts().foreach(setEmptyInstrumentationId);
+  script.getChildScripts().forEach(setEmptyInstrumentationId);
 }
 
 dbg.onNewScript = function(script) {
   if (RecordReplayControl.areThreadEventsDisallowed()) {
     // This script is part of an eval on behalf of the debugger.
     return;
   }
 
@@ -253,16 +253,17 @@ dbg.onNewScript = function(script) {
 const gHtmlContent = new Map();
 
 getWindow().docShell.watchedByDevtools = true;
 
 Services.obs.addObserver(
   {
     observe(subject, topic, data) {
       assert(topic == "webnavigation-create");
+      subject.QueryInterface(Ci.nsIDocShell);
       subject.watchedByDevtools = true;
     },
   },
   "webnavigation-create"
 );
 
 Services.obs.addObserver(
   {
--- a/gfx/gl/GLContextCGL.h
+++ b/gfx/gl/GLContextCGL.h
@@ -5,54 +5,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GLCONTEXTCGL_H_
 #define GLCONTEXTCGL_H_
 
 #include "GLContext.h"
 
 #include "OpenGL/OpenGL.h"
-#include <IOSurface/IOSurface.h>
 
 #ifdef __OBJC__
 #  include <AppKit/NSOpenGL.h>
 #else
 typedef void NSOpenGLContext;
 #endif
 
-#include <unordered_map>
-
-#include "mozilla/HashFunctions.h"
-#include "mozilla/UniquePtr.h"
-
 class nsIWidget;
 
 namespace mozilla {
 namespace gl {
 
-class MozFramebuffer;
-
 class GLContextCGL : public GLContext {
   friend class GLContextProviderCGL;
 
   NSOpenGLContext* mContext;
-  GLuint mDefaultFramebuffer = 0;
-
-  struct IOSurfaceRefHasher {
-    std::size_t operator()(const IOSurfaceRef& aSurface) const {
-      return HashGeneric(reinterpret_cast<uintptr_t>(aSurface));
-    }
-  };
-
-  std::unordered_map<IOSurfaceRef, UniquePtr<MozFramebuffer>,
-                     IOSurfaceRefHasher>
-      mRegisteredIOSurfaceFramebuffers;
-
- protected:
-  virtual void OnMarkDestroyed() override;
 
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextCGL, override)
   GLContextCGL(CreateContextFlags flags, const SurfaceCaps& caps,
                NSOpenGLContext* context, bool isOffscreen);
 
   ~GLContextCGL();
 
@@ -76,20 +54,14 @@ class GLContextCGL : public GLContext {
 
   virtual bool IsDoubleBuffered() const override;
 
   virtual bool SwapBuffers() override;
 
   virtual void GetWSIInfo(nsCString* const out) const override;
 
   Maybe<SymbolLoader> GetSymbolLoader() const override;
-
-  virtual GLuint GetDefaultFramebuffer() override;
-
-  void RegisterIOSurface(IOSurfaceRef aSurface);
-  void UnregisterIOSurface(IOSurfaceRef aSurface);
-  void UseRegisteredIOSurfaceForDefaultFramebuffer(IOSurfaceRef aSurface);
 };
 
 }  // namespace gl
 }  // namespace mozilla
 
 #endif  // GLCONTEXTCGL_H_
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -10,17 +10,16 @@
 #include <OpenGL/gl.h>
 #include "gfxFailure.h"
 #include "mozilla/StaticPrefs_gfx.h"
 #include "mozilla/StaticPrefs_gl.h"
 #include "mozilla/StaticPrefs_layout.h"
 #include "prenv.h"
 #include "GeckoProfiler.h"
 #include "MozFramebuffer.h"
-#include "mozilla/gfx/MacIOSurface.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "ScopedGLHelpers.h"
 
 #include <OpenGL/OpenGL.h>
 
 // When running inside a VM, creating an accelerated OpenGL context usually
 // fails. Uncomment this line to emulate that behavior.
@@ -83,22 +82,16 @@ GLContextCGL::~GLContextCGL() {
       // this, the next time we call [NSOpenGLContext currentContext],
       // "invalid context" will be printed to the console.
       [NSOpenGLContext clearCurrentContext];
     }
     [mContext release];
   }
 }
 
-void GLContextCGL::OnMarkDestroyed() {
-  mRegisteredIOSurfaceFramebuffers.clear();
-
-  mDefaultFramebuffer = 0;
-}
-
 CGLContextObj GLContextCGL::GetCGLContext() const {
   return static_cast<CGLContextObj>([mContext CGLContextObj]);
 }
 
 bool GLContextCGL::MakeCurrentImpl() const {
   if (mContext) {
     [mContext makeCurrentContext];
     MOZ_ASSERT(IsCurrentImpl());
@@ -137,56 +130,16 @@ bool GLContextCGL::SwapBuffers() {
 
 void GLContextCGL::GetWSIInfo(nsCString* const out) const { out->AppendLiteral("CGL"); }
 
 Maybe<SymbolLoader> GLContextCGL::GetSymbolLoader() const {
   const auto& lib = sCGLLibrary.Library();
   return Some(SymbolLoader(*lib));
 }
 
-GLuint GLContextCGL::GetDefaultFramebuffer() { return mDefaultFramebuffer; }
-
-void GLContextCGL::UseRegisteredIOSurfaceForDefaultFramebuffer(IOSurfaceRef aSurface) {
-  MakeCurrent();
-
-  auto fb = mRegisteredIOSurfaceFramebuffers.find(aSurface);
-  MOZ_RELEASE_ASSERT(fb != mRegisteredIOSurfaceFramebuffers.end(),
-                     "IOSurface has not been registered with this GLContext");
-
-  mDefaultFramebuffer = fb->second->mFB;
-}
-
-void GLContextCGL::RegisterIOSurface(IOSurfaceRef aSurface) {
-  MOZ_RELEASE_ASSERT(
-      mRegisteredIOSurfaceFramebuffers.find(aSurface) == mRegisteredIOSurfaceFramebuffers.end(),
-      "double-registering IOSurface");
-
-  uint32_t width = IOSurfaceGetWidth(aSurface);
-  uint32_t height = IOSurfaceGetHeight(aSurface);
-
-  MakeCurrent();
-  GLuint tex = CreateTexture();
-
-  {
-    const ScopedBindTexture bindTex(this, tex, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
-    CGLTexImageIOSurface2D([mContext CGLContextObj], LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_RGBA,
-                           width, height, LOCAL_GL_BGRA, LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV,
-                           aSurface, 0);
-  }
-
-  auto fb = MozFramebuffer::CreateWith(this, IntSize(width, height), 0, mCaps.depth,
-                                       LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex);
-  mRegisteredIOSurfaceFramebuffers.insert({aSurface, std::move(fb)});
-}
-
-void GLContextCGL::UnregisterIOSurface(IOSurfaceRef aSurface) {
-  size_t removeCount = mRegisteredIOSurfaceFramebuffers.erase(aSurface);
-  MOZ_RELEASE_ASSERT(removeCount == 1, "Unregistering IOSurface that's not registered");
-}
-
 already_AddRefed<GLContext> GLContextProviderCGL::CreateWrappingExisting(void*, void*) {
   return nullptr;
 }
 
 static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = {
     NSOpenGLPFAAllowOfflineRenderers, 0};
 
 static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered_accel[] = {
--- a/gfx/gl/moz.build
+++ b/gfx/gl/moz.build
@@ -41,16 +41,17 @@ EXPORTS += [
     'GLLibraryEGL.h',
     'GLLibraryLoader.h',
     'GLReadTexImageHelper.h',
     'GLScreenBuffer.h',
     'GLTextureImage.h',
     'GLTypes.h',
     'GLUploadHelpers.h',
     'HeapCopyOfStackArray.h',
+    'MozFramebuffer.h',
     'ScopedGLHelpers.h',
     'SharedSurface.h',
     'SharedSurfaceEGL.h',
     'SharedSurfaceGL.h',
     'SurfaceTypes.h',
 ]
 
 if CONFIG['MOZ_X11']:
--- a/gfx/layers/NativeLayer.h
+++ b/gfx/layers/NativeLayer.h
@@ -1,20 +1,28 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_NativeLayer_h
 #define mozilla_layers_NativeLayer_h
 
+#include "mozilla/Maybe.h"
+
+#include "GLTypes.h"
 #include "nsISupportsImpl.h"
 #include "nsRegion.h"
 
 namespace mozilla {
+
+namespace gl {
+class GLContext;
+}  // namespace gl
+
 namespace layers {
 
 class NativeLayer;
 class NativeLayerCA;
 
 // NativeLayerRoot and NativeLayer allow building up a flat layer "tree" of
 // sibling layers. These layers provide a cross-platform abstraction for the
 // platform's native layers, such as CoreAnimation layers on macOS.
@@ -62,16 +70,81 @@ class NativeLayer {
   // call to NextSurface.
   virtual void SetRect(const gfx::IntRect& aRect) = 0;
   virtual gfx::IntRect GetRect() = 0;
 
   // Define which parts of the layer are opaque..
   virtual void SetOpaqueRegion(const gfx::IntRegion& aRegion) = 0;
   virtual gfx::IntRegion OpaqueRegion() = 0;
 
+  // Whether the surface contents are flipped vertically compared to this
+  // layer's coordinate system. Can be set on any thread at any time.
+  virtual void SetSurfaceIsFlipped(bool aIsFlipped) = 0;
+  virtual bool SurfaceIsFlipped() = 0;
+
+  // Invalidates the specified region in all surfaces that are tracked by this
+  // layer.
+  virtual void InvalidateRegionThroughoutSwapchain(
+      const gfx::IntRegion& aRegion) = 0;
+
+  // Returns a DrawTarget. The size of the DrawTarget will be the size of the
+  // rect that has been passed to SetRect. The caller should draw to that
+  // DrawTarget, then drop its reference to the DrawTarget, and then call
+  // NotifySurfaceReady(). It can limit its drawing to
+  // CurrentSurfaceInvalidRegion() (which is in the DrawTarget's device space).
+  // After a call to NextSurface*, NextSurface* must not be called again until
+  // after NotifySurfaceReady has been called. Can be called on any thread. When
+  // used from multiple threads, callers need to make sure that they still only
+  // call NextSurface and NotifySurfaceReady alternatingly and not in any other
+  // order.
+  virtual RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
+      gfx::BackendType aBackendType) = 0;
+
+  // Set the GLContext to use for the MozFramebuffer that are returned from
+  // NextSurfaceAsFramebuffer. If changed to a different value, all
+  // MozFramebuffers tracked by this layer will be discarded.
+  // It's a good idea to call SetGLContext(nullptr) before destroying this
+  // layer so that GL resource destruction happens at a good time and on the
+  // right thread.
+  virtual void SetGLContext(gl::GLContext* aGLContext) = 0;
+  virtual gl::GLContext* GetGLContext() = 0;
+
+  // Must only be called if a non-null GLContext is set on this layer.
+  // Returns a GLuint for a framebuffer that can be used for drawing to the
+  // surface. The size of the framebuffer will be the size of the rect that has
+  // been passed to SetRect. If aNeedsDepth is true, the framebuffer is created
+  // with a depth buffer. The caller should draw to the framebuffer, unbind
+  // it, and then call NotifySurfaceReady(). It can limit its drawing to
+  // CurrentSurfaceInvalidRegion() (which is in the framebuffer's device space,
+  // possibly "upside down" if SurfaceIsFlipped()). The framebuffer will be
+  // created using the GLContext that was set on this layer with a call to
+  // SetGLContext. The NativeLayer will keep a reference to the MozFramebuffer
+  // so that it can reuse the same MozFramebuffer whenever it uses the same
+  // underlying surface. Calling SetGLContext with a different context will
+  // release that reference. After a call to NextSurface*, NextSurface* must not
+  // be called again until after NotifySurfaceReady has been called. Can be
+  // called on any thread. When used from multiple threads, callers need to make
+  // sure that they still only call NextSurface and NotifySurfaceReady
+  // alternatingly and not in any other order.
+  virtual Maybe<GLuint> NextSurfaceAsFramebuffer(bool aNeedsDepth) = 0;
+
+  // The invalid region of the surface that has been returned from the most
+  // recent call to NextSurface*. Newly-created surfaces are entirely invalid.
+  // For surfaces that have been used before, the invalid region is the union of
+  // all invalid regions that have been passed to
+  // InvalidateRegionThroughoutSwapchain since the last time that
+  // NotifySurfaceReady was called for this surface. Can only be called between
+  // calls to NextSurface* and NotifySurfaceReady. Can be called on any thread.
+  virtual gfx::IntRegion CurrentSurfaceInvalidRegion() = 0;
+
+  // Indicates that the surface which has been returned from the most recent
+  // call to NextSurface* is now finished being drawn to and can be displayed on
+  // the screen. Resets the invalid region on the surface to the empty region.
+  virtual void NotifySurfaceReady() = 0;
+
  protected:
   virtual ~NativeLayer() {}
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif  // mozilla_layers_NativeLayer_h
--- a/gfx/layers/NativeLayerCA.h
+++ b/gfx/layers/NativeLayerCA.h
@@ -4,32 +4,39 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_NativeLayerCA_h
 #define mozilla_layers_NativeLayerCA_h
 
 #include <IOSurface/IOSurface.h>
 
 #include <deque>
+#include <unordered_map>
 
-#include "mozilla/Maybe.h"
 #include "mozilla/Mutex.h"
 
+#include "mozilla/gfx/MacIOSurface.h"
 #include "mozilla/layers/NativeLayer.h"
 #include "CFTypeRefPtr.h"
 #include "nsRegion.h"
 #include "nsISupportsImpl.h"
 
 #ifdef __OBJC__
 @class CALayer;
 #else
 typedef void CALayer;
 #endif
 
 namespace mozilla {
+
+namespace gl {
+class GLContextCGL;
+class MozFramebuffer;
+}  // namespace gl
+
 namespace layers {
 
 class IOSurfaceRegistry {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IOSurfaceRegistry)
 
   virtual void RegisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) = 0;
   virtual void UnregisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) = 0;
@@ -91,46 +98,38 @@ class NativeLayerRootCA : public NativeL
 // surfaces in its swap chain.
 class NativeLayerCA : public NativeLayer {
  public:
   virtual NativeLayerCA* AsNativeLayerCA() override { return this; }
 
   // Overridden methods
   void SetRect(const gfx::IntRect& aRect) override;
   gfx::IntRect GetRect() override;
-
-  // The invalid region of the surface that has been returned from the most
-  // recent call to NextSurface. Newly-created surfaces are entirely invalid.
-  // For surfaces that have been used before, the invalid region is the union of
-  // all invalid regions that have been passed to
-  // invalidateRegionThroughoutSwapchain since the last time that
-  // NotifySurfaceReady was called for this surface. Can only be called between
-  // calls to NextSurface and NotifySurfaceReady. Can be called on any thread.
-  gfx::IntRegion CurrentSurfaceInvalidRegion();
-
-  // Invalidates the specified region in all surfaces that are tracked by this
-  // layer.
-  void InvalidateRegionThroughoutSwapchain(const gfx::IntRegion& aRegion);
+  void InvalidateRegionThroughoutSwapchain(
+      const gfx::IntRegion& aRegion) override;
+  RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
+      gfx::BackendType aBackendType) override;
+  void SetGLContext(gl::GLContext* aGLContext) override;
+  gl::GLContext* GetGLContext() override;
+  Maybe<GLuint> NextSurfaceAsFramebuffer(bool aNeedsDepth) override;
+  gfx::IntRegion CurrentSurfaceInvalidRegion() override;
+  void NotifySurfaceReady() override;
+  void SetSurfaceIsFlipped(bool aIsFlipped) override;
+  bool SurfaceIsFlipped() override;
 
   // Returns an IOSurface that can be drawn to. The size of the IOSurface will
   // be the size of the rect that has been passed to SetRect.
   // The returned surface is guaranteed to be not in use by the window server.
   // After a call to NextSurface, NextSurface must not be called again until
   // after NotifySurfaceReady has been called. Can be called on any thread. When
   // used from multiple threads, callers need to make sure that they still only
   // call NextSurface and NotifySurfaceReady alternatingly and not in any other
   // order.
   CFTypeRefPtr<IOSurfaceRef> NextSurface();
-
-  // Indicates that the surface which has been returned from the most recent
-  // call to NextSurface is now finished being drawn to and can be displayed on
-  // the screen. The surface will be used during the next call to the layer's
-  // ApplyChanges method. Resets the invalid region on the surface to the empty
-  // region.
-  void NotifySurfaceReady();
+  CFTypeRefPtr<IOSurfaceRef> NextSurfaceLocked(const MutexAutoLock&);
 
   // Consumers may provide an object that implements the IOSurfaceRegistry
   // interface.
   // The registry's methods, Register/UnregisterSurface, will be called
   // synchronously during calls to NextSurface(), SetSurfaceRegistry(), and the
   // NativeLayer destructor, on the thread that those things happen to run on.
   // If this layer already owns surfaces when SetSurfaceRegistry gets called
   // with a non-null surface registry, those surfaces will immediately
@@ -140,21 +139,16 @@ class NativeLayerCA : public NativeLayer
   // will immediately be unregistered.
   // Since NativeLayer objects are reference counted and can be used from
   // different threads, it is recommended to call SetSurfaceRegistry(nullptr)
   // before destroying the NativeLayer so that the UnregisterSurface calls
   // happen at a deterministic time and on the right thread.
   void SetSurfaceRegistry(RefPtr<IOSurfaceRegistry> aSurfaceRegistry);
   RefPtr<IOSurfaceRegistry> GetSurfaceRegistry();
 
-  // Whether the surface contents are flipped vertically compared to this
-  // layer's coordinate system. Can be set on any thread at any time.
-  void SetSurfaceIsFlipped(bool aIsFlipped);
-  bool SurfaceIsFlipped();
-
   // Set an opaque region on the layer. Internally, this causes the creation
   // of opaque and transparent sublayers to cover the regions.
   // The coordinates in aRegion are relative to mPosition.
   void SetOpaqueRegion(const gfx::IntRegion& aRegion) override;
   gfx::IntRegion OpaqueRegion() override;
 
  protected:
   friend class NativeLayerRootCA;
@@ -162,16 +156,20 @@ class NativeLayerCA : public NativeLayer
   NativeLayerCA();
   ~NativeLayerCA() override;
 
   // To be called by NativeLayerRootCA:
   CALayer* UnderlyingCALayer() { return mWrappingCALayer; }
   void ApplyChanges();
   void SetBackingScale(float aBackingScale);
 
+  GLuint GetOrCreateFramebufferForSurface(const MutexAutoLock&,
+                                          CFTypeRefPtr<IOSurfaceRef> aSurface,
+                                          bool aNeedsDepth);
+
   struct SurfaceWithInvalidRegion {
     CFTypeRefPtr<IOSurfaceRef> mSurface;
     gfx::IntRegion mInvalidRegion;
     gfx::IntSize mSize;
   };
 
   std::vector<SurfaceWithInvalidRegion> RemoveExcessUnusedSurfaces(
       const MutexAutoLock&);
@@ -243,16 +241,24 @@ class NativeLayerCA : public NativeLayer
   // Both mInProgressSurface and mReadySurface can be Some() at the same time.
   Maybe<SurfaceWithInvalidRegion> mReadySurface;
 
   // The queue of surfaces which make up our "swap chain".
   // mSurfaces.front() is the next surface we'll attempt to use.
   // mSurfaces.back() is the one we submitted most recently.
   std::deque<SurfaceWithInvalidRegion> mSurfaces;
 
+  // Non-null between calls to NextSurfaceAsDrawTarget and NotifySurfaceReady.
+  RefPtr<MacIOSurface> mInProgressLockedIOSurface;
+
+  RefPtr<gl::GLContextCGL> mGLContext;
+
+  std::unordered_map<CFTypeRefPtr<IOSurfaceRef>, UniquePtr<gl::MozFramebuffer>>
+      mFramebuffers;
+
   gfx::IntPoint mPosition;
   gfx::IntSize mSize;
   gfx::IntRegion mOpaqueRegion;  // coordinates relative to mPosition
 
   // Lazily initialized by first call to ApplyChanges.
   CALayer* mWrappingCALayer = nullptr;    // strong
   std::deque<CALayer*> mContentCALayers;  // strong
 
--- a/gfx/layers/NativeLayerCA.mm
+++ b/gfx/layers/NativeLayerCA.mm
@@ -6,16 +6,20 @@
 #import "mozilla/layers/NativeLayerCA.h"
 
 #import <QuartzCore/QuartzCore.h>
 #import <CoreVideo/CVPixelBuffer.h>
 
 #include <utility>
 #include <algorithm>
 
+#include "GLContextCGL.h"
+#include "MozFramebuffer.h"
+#include "ScopedGLHelpers.h"
+
 @interface CALayer (PrivateSetContentsOpaque)
 - (void)setContentsOpaque:(BOOL)opaque;
 @end
 
 namespace mozilla {
 namespace layers {
 
 using gfx::IntPoint;
@@ -108,16 +112,20 @@ void NativeLayerRootCA::SetBackingScale(
   }
 }
 
 NativeLayerCA::NativeLayerCA() : mMutex("NativeLayerCA") {}
 
 NativeLayerCA::~NativeLayerCA() {
   SetSurfaceRegistry(nullptr);  // or maybe MOZ_RELEASE_ASSERT(!mSurfaceRegistry) would be better?
 
+  if (mInProgressLockedIOSurface) {
+    mInProgressLockedIOSurface->Unlock(false);
+    mInProgressLockedIOSurface = nullptr;
+  }
   if (mInProgressSurface) {
     IOSurfaceDecrementUseCount(mInProgressSurface->mSurface.get());
   }
   if (mReadySurface) {
     IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
   }
 
   for (CALayer* contentLayer : mContentCALayers) {
@@ -239,32 +247,35 @@ void NativeLayerCA::InvalidateRegionThro
   }
   for (auto& surf : mSurfaces) {
     surf.mInvalidRegion.OrWith(r);
   }
 }
 
 CFTypeRefPtr<IOSurfaceRef> NativeLayerCA::NextSurface() {
   MutexAutoLock lock(mMutex);
+  return NextSurfaceLocked(lock);
+}
 
+CFTypeRefPtr<IOSurfaceRef> NativeLayerCA::NextSurfaceLocked(const MutexAutoLock& aLock) {
   IntSize surfaceSize = mSize;
   if (surfaceSize.IsEmpty()) {
     NSLog(@"NextSurface returning nullptr because of invalid surfaceSize (%d, %d).",
           surfaceSize.width, surfaceSize.height);
     return nullptr;
   }
 
   MOZ_RELEASE_ASSERT(
       !mInProgressSurface,
       "ERROR: Do not call NextSurface twice in sequence. Call NotifySurfaceReady before the "
       "next call to NextSurface.");
 
   // Find the last surface in unusedSurfaces which has the right size. If such
   // a surface exists, it is the surface we will recycle.
-  std::vector<SurfaceWithInvalidRegion> unusedSurfaces = RemoveExcessUnusedSurfaces(lock);
+  std::vector<SurfaceWithInvalidRegion> unusedSurfaces = RemoveExcessUnusedSurfaces(aLock);
   auto surfIter = std::find_if(
       unusedSurfaces.rbegin(), unusedSurfaces.rend(),
       [surfaceSize](const SurfaceWithInvalidRegion& s) { return s.mSize == surfaceSize; });
 
   Maybe<SurfaceWithInvalidRegion> surf;
   if (surfIter != unusedSurfaces.rend()) {
     // We found the surface we want to recycle.
     surf = Some(*surfIter);
@@ -290,36 +301,109 @@ CFTypeRefPtr<IOSurfaceRef> NativeLayerCA
     surf =
         Some(SurfaceWithInvalidRegion{newSurf, IntRect(IntPoint(0, 0), surfaceSize), surfaceSize});
   }
 
   // Delete all other unused surfaces.
   if (mSurfaceRegistry) {
     for (auto unusedSurf : unusedSurfaces) {
       mSurfaceRegistry->UnregisterSurface(unusedSurf.mSurface);
+      mFramebuffers.erase(unusedSurf.mSurface);
     }
   }
   unusedSurfaces.clear();
 
   MOZ_RELEASE_ASSERT(surf);
   mInProgressSurface = std::move(surf);
   IOSurfaceIncrementUseCount(mInProgressSurface->mSurface.get());
   return mInProgressSurface->mSurface;
 }
 
+RefPtr<gfx::DrawTarget> NativeLayerCA::NextSurfaceAsDrawTarget(gfx::BackendType aBackendType) {
+  MutexAutoLock lock(mMutex);
+  CFTypeRefPtr<IOSurfaceRef> surface = NextSurfaceLocked(lock);
+  if (!surface) {
+    return nullptr;
+  }
+
+  mInProgressLockedIOSurface = new MacIOSurface(std::move(surface));
+  mInProgressLockedIOSurface->Lock(false);
+  return mInProgressLockedIOSurface->GetAsDrawTargetLocked(aBackendType);
+}
+
+void NativeLayerCA::SetGLContext(gl::GLContext* aContext) {
+  MutexAutoLock lock(mMutex);
+
+  RefPtr<gl::GLContextCGL> glContextCGL = gl::GLContextCGL::Cast(aContext);
+  MOZ_RELEASE_ASSERT(glContextCGL, "Unexpected GLContext type");
+
+  if (glContextCGL != mGLContext) {
+    mFramebuffers.clear();
+    mGLContext = glContextCGL;
+  }
+}
+
+gl::GLContext* NativeLayerCA::GetGLContext() {
+  MutexAutoLock lock(mMutex);
+  return mGLContext;
+}
+
+Maybe<GLuint> NativeLayerCA::NextSurfaceAsFramebuffer(bool aNeedsDepth) {
+  MutexAutoLock lock(mMutex);
+  CFTypeRefPtr<IOSurfaceRef> surface = NextSurfaceLocked(lock);
+  if (!surface) {
+    return Nothing();
+  }
+
+  return Some(GetOrCreateFramebufferForSurface(lock, std::move(surface), aNeedsDepth));
+}
+
+GLuint NativeLayerCA::GetOrCreateFramebufferForSurface(const MutexAutoLock&,
+                                                       CFTypeRefPtr<IOSurfaceRef> aSurface,
+                                                       bool aNeedsDepth) {
+  auto fbCursor = mFramebuffers.find(aSurface);
+  if (fbCursor != mFramebuffers.end()) {
+    return fbCursor->second->mFB;
+  }
+
+  MOZ_RELEASE_ASSERT(
+      mGLContext, "Only call NextSurfaceAsFramebuffer when a GLContext is set on this NativeLayer");
+  mGLContext->MakeCurrent();
+  GLuint tex = mGLContext->CreateTexture();
+  {
+    const gl::ScopedBindTexture bindTex(mGLContext, tex, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+    CGLTexImageIOSurface2D(mGLContext->GetCGLContext(), LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+                           LOCAL_GL_RGBA, mSize.width, mSize.height, LOCAL_GL_BGRA,
+                           LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, aSurface.get(), 0);
+  }
+
+  auto fb = gl::MozFramebuffer::CreateWith(mGLContext, mSize, 0, aNeedsDepth,
+                                           LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex);
+  GLuint fbo = fb->mFB;
+  mFramebuffers.insert({aSurface, std::move(fb)});
+
+  return fbo;
+}
+
 void NativeLayerCA::NotifySurfaceReady() {
   MutexAutoLock lock(mMutex);
 
   MOZ_RELEASE_ASSERT(mInProgressSurface,
                      "NotifySurfaceReady called without preceding call to NextSurface");
   if (mReadySurface) {
     IOSurfaceDecrementUseCount(mReadySurface->mSurface.get());
     mSurfaces.push_back(*mReadySurface);
     mReadySurface = Nothing();
   }
+
+  if (mInProgressLockedIOSurface) {
+    mInProgressLockedIOSurface->Unlock(false);
+    mInProgressLockedIOSurface = nullptr;
+  }
+
   mReadySurface = std::move(mInProgressSurface);
   mReadySurface->mInvalidRegion = IntRect();
 }
 
 void NativeLayerCA::ApplyChanges() {
   MutexAutoLock lock(mMutex);
 
   if (!mWrappingCALayer) {
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -22,21 +22,16 @@
 #include "mozilla/StaticPrefs_layers.h"
 #include "mozilla/StaticPrefs_nglayout.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "YCbCrUtils.h"
 #include <algorithm>
 #include "ImageContainer.h"
 
-#ifdef XP_MACOSX
-#  include "mozilla/gfx/MacIOSurface.h"
-#  include "mozilla/layers/NativeLayerCA.h"
-#endif
-
 namespace mozilla {
 using namespace mozilla::gfx;
 
 namespace layers {
 
 class DataTextureSourceBasic : public DataTextureSource,
                                public TextureSourceBasic {
  public:
@@ -1032,41 +1027,32 @@ Maybe<gfx::IntRect> BasicCompositor::Beg
     mInvalidRegion.And(aInvalidRegion, rect);
   }
 
   if (mInvalidRegion.IsEmpty()) {
     return Nothing();
   }
 
   RefPtr<CompositingRenderTarget> target;
-#ifdef XP_MACOSX
-  NativeLayerCA* nativeLayer = aNativeLayer->AsNativeLayerCA();
-  MOZ_RELEASE_ASSERT(nativeLayer, "Unexpected native layer type");
-  nativeLayer->SetSurfaceIsFlipped(false);
-  CFTypeRefPtr<IOSurfaceRef> surf = nativeLayer->NextSurface();
-  if (!surf) {
+  aNativeLayer->SetSurfaceIsFlipped(false);
+  IntRegion invalidRelativeToLayer = mInvalidRegion.MovedBy(-rect.TopLeft());
+  aNativeLayer->InvalidateRegionThroughoutSwapchain(invalidRelativeToLayer);
+  RefPtr<DrawTarget> dt =
+      aNativeLayer->NextSurfaceAsDrawTarget(BackendType::SKIA);
+  if (!dt) {
     return Nothing();
   }
-  IntRegion invalidRelativeToLayer = mInvalidRegion.MovedBy(-rect.TopLeft());
-  nativeLayer->InvalidateRegionThroughoutSwapchain(invalidRelativeToLayer);
-  invalidRelativeToLayer = nativeLayer->CurrentSurfaceInvalidRegion();
+  invalidRelativeToLayer = aNativeLayer->CurrentSurfaceInvalidRegion();
   mInvalidRegion = invalidRelativeToLayer.MovedBy(rect.TopLeft());
   MOZ_RELEASE_ASSERT(!mInvalidRegion.IsEmpty());
   mCurrentNativeLayer = aNativeLayer;
-  mCurrentIOSurface = new MacIOSurface(std::move(surf));
-  mCurrentIOSurface->Lock(false);
-  RefPtr<DrawTarget> dt =
-      mCurrentIOSurface->GetAsDrawTargetLocked(BackendType::SKIA);
   IntRegion clearRegion;
   clearRegion.Sub(mInvalidRegion, aOpaqueRegion);
   // Set up a render target for drawing directly to dt.
   target = CreateRootRenderTarget(dt, rect, clearRegion);
-#else
-  MOZ_CRASH("Unexpected native layer on this platform");
-#endif
 
   MOZ_RELEASE_ASSERT(target);
   SetRenderTarget(target);
 
   gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, mInvalidRegion);
 
   mRenderTarget->mDrawTarget->PushClipRect(Rect(aClipRect.valueOr(rect)));
 
@@ -1075,30 +1061,21 @@ Maybe<gfx::IntRect> BasicCompositor::Beg
 
 void BasicCompositor::EndRenderingToNativeLayer() {
   // Pop aClipRect/bounds rect
   mRenderTarget->mDrawTarget->PopClip();
 
   // Pop mInvalidRegion
   mRenderTarget->mDrawTarget->PopClip();
 
-  MOZ_RELEASE_ASSERT(mCurrentNativeLayer);
-
   SetRenderTarget(mNativeLayersReferenceRT);
 
-#ifdef XP_MACOSX
-  NativeLayerCA* nativeLayer = mCurrentNativeLayer->AsNativeLayerCA();
-  MOZ_RELEASE_ASSERT(nativeLayer, "Unexpected native layer type");
-  mCurrentIOSurface->Unlock(false);
-  mCurrentIOSurface = nullptr;
-  nativeLayer->NotifySurfaceReady();
+  MOZ_RELEASE_ASSERT(mCurrentNativeLayer);
+  mCurrentNativeLayer->NotifySurfaceReady();
   mCurrentNativeLayer = nullptr;
-#else
-  MOZ_CRASH("Unexpected native layer on this platform");
-#endif
 }
 
 void BasicCompositor::EndFrame() {
   Compositor::EndFrame();
 
   if (mCurrentFrameDest != FrameDestination::NATIVE_LAYERS) {
     // Pop aClipRect/bounds rect
     mRenderTarget->mDrawTarget->PopClip();
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -8,20 +8,16 @@
 #define MOZILLA_GFX_BASICCOMPOSITOR_H
 
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Triangle.h"
 #include "mozilla/gfx/Polygon.h"
 
-#ifdef XP_MACOSX
-class MacIOSurface;
-#endif
-
 namespace mozilla {
 namespace layers {
 
 class BasicCompositingRenderTarget : public CompositingRenderTarget {
  public:
   BasicCompositingRenderTarget(gfx::DrawTarget* aDrawTarget,
                                const gfx::IntRect& aRect,
                                const gfx::IntPoint& aClipSpaceOrigin)
@@ -197,22 +193,16 @@ class BasicCompositor : public Composito
   // The current render target for drawing
   RefPtr<BasicCompositingRenderTarget> mRenderTarget;
 
   // The native layer that we're currently rendering to, if any.
   // Non-null only between BeginFrameForWindow and EndFrame if
   // BeginFrameForWindow has been called with a non-null aNativeLayer.
   RefPtr<NativeLayer> mCurrentNativeLayer;
 
-#ifdef XP_MACOSX
-  // The MacIOSurface that we're currently rendering to, if any.
-  // Non-null in the same cases as mCurrentNativeLayer.
-  RefPtr<MacIOSurface> mCurrentIOSurface;
-#endif
-
   gfx::IntRegion mInvalidRegion;
 
   uint32_t mMaxTextureSize;
   bool mIsPendingEndRemoteDrawing;
   bool mShouldInvalidateWindow = false;
 
   // Where the current frame is being rendered to.
   enum class FrameDestination : uint8_t {
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -604,17 +604,21 @@ void LayerManagerComposite::UpdateAndRen
     // their latency.
     mPayload.Clear();
 
     return;
   }
 
   // We don't want our debug overlay to cause more frames to happen
   // so we will invalidate after we've decided if something changed.
-  InvalidateDebugOverlay(invalid, mRenderBounds);
+  // Only invalidate if we're not using native layers. When using native layers,
+  // UpdateDebugOverlayNativeLayers will repaint the appropriate layer areas.
+  if (!mNativeLayerRoot) {
+    InvalidateDebugOverlay(invalid, mRenderBounds);
+  }
 
   bool rendered = Render(invalid, opaque);
 #if defined(MOZ_WIDGET_ANDROID)
   RenderToPresentationSurface();
 #endif
 
   if (!mTarget && rendered) {
     mInvalidRegion.SetEmpty();
@@ -666,88 +670,88 @@ void LayerManagerComposite::DrawPaintTim
     mPaintCounter = new PaintCounter();
   }
 
   TimeDuration compositeTime = TimeStamp::Now() - mRenderStartTime;
   mPaintCounter->Draw(aCompositor, mLastPaintTime, compositeTime);
 }
 #endif
 
+static Rect RectWithEdges(int32_t aTop, int32_t aRight, int32_t aBottom,
+                          int32_t aLeft) {
+  return Rect(aLeft, aTop, aRight - aLeft, aBottom - aTop);
+}
+
+void LayerManagerComposite::DrawBorder(const IntRect& aOuter,
+                                       int32_t aBorderWidth,
+                                       const Color& aColor,
+                                       const Matrix4x4& aTransform) {
+  EffectChain effects;
+  effects.mPrimaryEffect = new EffectSolidColor(aColor);
+
+  IntRect inner(aOuter);
+  inner.Deflate(aBorderWidth);
+  // Top and bottom border sides
+  mCompositor->DrawQuad(
+      RectWithEdges(aOuter.Y(), aOuter.XMost(), inner.Y(), aOuter.X()), aOuter,
+      effects, 1, aTransform);
+  mCompositor->DrawQuad(
+      RectWithEdges(inner.YMost(), aOuter.XMost(), aOuter.YMost(), aOuter.X()),
+      aOuter, effects, 1, aTransform);
+  // Left and right border sides
+  mCompositor->DrawQuad(
+      RectWithEdges(inner.Y(), inner.X(), inner.YMost(), aOuter.X()), aOuter,
+      effects, 1, aTransform);
+  mCompositor->DrawQuad(
+      RectWithEdges(inner.Y(), aOuter.XMost(), inner.YMost(), inner.XMost()),
+      aOuter, effects, 1, aTransform);
+}
+
+void LayerManagerComposite::DrawTranslationWarningOverlay(
+    const IntRect& aBounds) {
+  // Black blorder
+  IntRect blackBorderBounds(aBounds);
+  blackBorderBounds.Deflate(4);
+  DrawBorder(blackBorderBounds, 6, Color(0, 0, 0, 1), Matrix4x4());
+
+  // Warning border, yellow to red
+  IntRect warnBorder(aBounds);
+  warnBorder.Deflate(5);
+  DrawBorder(warnBorder, 4, Color(1, 1.f - mWarningLevel, 0, 1), Matrix4x4());
+}
+
 static uint16_t sFrameCount = 0;
 void LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds) {
   bool drawFps = StaticPrefs::layers_acceleration_draw_fps();
   bool drawFrameColorBars = StaticPrefs::gfx_draw_color_bars();
 
   // Don't draw diagnostic overlays if we want to snapshot the output.
   if (mTarget) {
     return;
   }
 
   if (drawFps) {
-    float alpha = 1;
 #ifdef ANDROID
     // Draw a translation delay warning overlay
-    int width;
-    int border;
-
-    TimeStamp now = TimeStamp::Now();
-    if (!mWarnTime.IsNull() &&
-        (now - mWarnTime).ToMilliseconds() < kVisualWarningDuration) {
-      EffectChain effects;
-
-      // Black blorder
-      border = 4;
-      width = 6;
-      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1));
-      mCompositor->DrawQuad(
-          gfx::Rect(border, border, aBounds.Width() - 2 * border, width),
-          aBounds, effects, alpha, gfx::Matrix4x4());
-      mCompositor->DrawQuad(gfx::Rect(border, aBounds.Height() - border - width,
-                                      aBounds.Width() - 2 * border, width),
-                            aBounds, effects, alpha, gfx::Matrix4x4());
-      mCompositor->DrawQuad(
-          gfx::Rect(border, border + width, width,
-                    aBounds.Height() - 2 * border - width * 2),
-          aBounds, effects, alpha, gfx::Matrix4x4());
-      mCompositor->DrawQuad(
-          gfx::Rect(aBounds.Width() - border - width, border + width, width,
-                    aBounds.Height() - 2 * border - 2 * width),
-          aBounds, effects, alpha, gfx::Matrix4x4());
-
-      // Content
-      border = 5;
-      width = 4;
-      effects.mPrimaryEffect =
-          new EffectSolidColor(gfx::Color(1, 1.f - mWarningLevel, 0, 1));
-      mCompositor->DrawQuad(
-          gfx::Rect(border, border, aBounds.Width() - 2 * border, width),
-          aBounds, effects, alpha, gfx::Matrix4x4());
-      mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width,
-                                      aBounds.Width() - 2 * border, width),
-                            aBounds, effects, alpha, gfx::Matrix4x4());
-      mCompositor->DrawQuad(
-          gfx::Rect(border, border + width, width,
-                    aBounds.Height() - 2 * border - width * 2),
-          aBounds, effects, alpha, gfx::Matrix4x4());
-      mCompositor->DrawQuad(
-          gfx::Rect(aBounds.Width() - border - width, border + width, width,
-                    aBounds.Height() - 2 * border - 2 * width),
-          aBounds, effects, alpha, gfx::Matrix4x4());
+    if (!mWarnTime.IsNull() && (TimeStamp::Now() - mWarnTime).ToMilliseconds() <
+                                   kVisualWarningDuration) {
+      DrawTranslationWarningOverlay(aBounds);
       SetDebugOverlayWantsNextFrame(true);
     }
 #endif
 
     GPUStats stats;
     stats.mScreenPixels = mRenderBounds.Width() * mRenderBounds.Height();
     mCompositor->GetFrameStats(&stats);
 
     std::string text = mDiagnostics->GetFrameOverlayString(stats);
     mTextRenderer->RenderText(mCompositor, text, IntPoint(2, 5), Matrix4x4(),
                               24, 600, TextRenderer::FontType::FixedWidth);
 
+    float alpha = 1;
     if (mUnusedApzTransformWarning) {
       // If we have an unused APZ transform on this composite, draw a 20x20 red
       // box in the top-right corner
       EffectChain effects;
       effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 0, 0, 1));
       mCompositor->DrawQuad(gfx::Rect(aBounds.Width() - 20, 0, 20, 20), aBounds,
                             effects, alpha, gfx::Matrix4x4());
 
@@ -771,31 +775,119 @@ void LayerManagerComposite::RenderDebugO
   if (drawFrameColorBars) {
     gfx::IntRect sideRect(0, 0, 10, aBounds.Height());
 
     EffectChain effects;
     effects.mPrimaryEffect =
         new EffectSolidColor(gfxUtils::GetColorForFrameNumber(sFrameCount));
     mCompositor->DrawQuad(Rect(sideRect), sideRect, effects, 1.0,
                           gfx::Matrix4x4());
-  }
 
-  if (drawFrameColorBars) {
     // We intentionally overflow at 2^16.
     sFrameCount++;
   }
 
 #ifdef USE_SKIA
   bool drawPaintTimes = StaticPrefs::gfx_content_always_paint();
   if (drawPaintTimes) {
     DrawPaintTimes(mCompositor);
   }
 #endif
 }
 
+void LayerManagerComposite::UpdateDebugOverlayNativeLayers() {
+  // Remove all debug layers first because PlaceNativeLayers might have changed
+  // the z-order. By removing and re-adding, we keep the debug overlay layers
+  // on top.
+  if (mGPUStatsLayer) {
+    mNativeLayerRoot->RemoveLayer(mGPUStatsLayer);
+  }
+  if (mUnusedTransformWarningLayer) {
+    mNativeLayerRoot->RemoveLayer(mUnusedTransformWarningLayer);
+  }
+  if (mDisabledApzWarningLayer) {
+    mNativeLayerRoot->RemoveLayer(mDisabledApzWarningLayer);
+  }
+
+  bool drawFps = StaticPrefs::layers_acceleration_draw_fps();
+
+  if (drawFps) {
+    if (!mGPUStatsLayer) {
+      mGPUStatsLayer = mNativeLayerRoot->CreateLayer();
+    }
+
+    GPUStats stats;
+    stats.mScreenPixels = mRenderBounds.Area();
+    mCompositor->GetFrameStats(&stats);
+
+    std::string text = mDiagnostics->GetFrameOverlayString(stats);
+    IntSize size = mTextRenderer->ComputeSurfaceSize(
+        text, 600, TextRenderer::FontType::FixedWidth);
+
+    mGPUStatsLayer->SetRect(IntRect(IntPoint(2, 5), size));
+    RefPtr<DrawTarget> dt =
+        mGPUStatsLayer->NextSurfaceAsDrawTarget(BackendType::SKIA);
+    mTextRenderer->RenderTextToDrawTarget(dt, text, 600,
+                                          TextRenderer::FontType::FixedWidth);
+    mGPUStatsLayer->NotifySurfaceReady();
+    mNativeLayerRoot->AppendLayer(mGPUStatsLayer);
+
+    // The two warning layers are created on demand and their content is only
+    // drawn once. After that, they only get moved (if the window size changes)
+    // and conditionally shown.
+    // The drawing would be unnecessary if we had native "color layers".
+    if (mUnusedApzTransformWarning) {
+      // If we have an unused APZ transform on this composite, draw a 20x20 red
+      // box in the top-right corner.
+      if (!mUnusedTransformWarningLayer) {
+        mUnusedTransformWarningLayer = mNativeLayerRoot->CreateLayer();
+        mUnusedTransformWarningLayer->SetRect(IntRect(0, 0, 20, 20));
+        mUnusedTransformWarningLayer->SetOpaqueRegion(IntRect(0, 0, 20, 20));
+        RefPtr<DrawTarget> dt =
+            mUnusedTransformWarningLayer->NextSurfaceAsDrawTarget(
+                BackendType::SKIA);
+        dt->FillRect(Rect(0, 0, 20, 20), ColorPattern(Color(1, 0, 0, 1)));
+        mUnusedTransformWarningLayer->NotifySurfaceReady();
+      }
+      mUnusedTransformWarningLayer->SetRect(
+          IntRect(mRenderBounds.XMost() - 20, mRenderBounds.Y(), 20, 20));
+      mNativeLayerRoot->AppendLayer(mUnusedTransformWarningLayer);
+
+      mUnusedApzTransformWarning = false;
+      SetDebugOverlayWantsNextFrame(true);
+    }
+
+    if (mDisabledApzWarning) {
+      // If we have a disabled APZ on this composite, draw a 20x20 yellow box
+      // in the top-right corner, to the left of the unused-apz-transform
+      // warning box.
+      if (!mDisabledApzWarningLayer) {
+        mDisabledApzWarningLayer = mNativeLayerRoot->CreateLayer();
+        mDisabledApzWarningLayer->SetRect(IntRect(0, 0, 20, 20));
+        mDisabledApzWarningLayer->SetOpaqueRegion(IntRect(0, 0, 20, 20));
+        RefPtr<DrawTarget> dt =
+            mDisabledApzWarningLayer->NextSurfaceAsDrawTarget(
+                BackendType::SKIA);
+        dt->FillRect(Rect(0, 0, 20, 20), ColorPattern(Color(1, 1, 0, 1)));
+        mDisabledApzWarningLayer->NotifySurfaceReady();
+      }
+      mDisabledApzWarningLayer->SetRect(
+          IntRect(mRenderBounds.XMost() - 40, mRenderBounds.Y(), 20, 20));
+      mNativeLayerRoot->AppendLayer(mDisabledApzWarningLayer);
+
+      mDisabledApzWarning = false;
+      SetDebugOverlayWantsNextFrame(true);
+    }
+  } else {
+    mGPUStatsLayer = nullptr;
+    mUnusedTransformWarningLayer = nullptr;
+    mDisabledApzWarningLayer = nullptr;
+  }
+}
+
 RefPtr<CompositingRenderTarget>
 LayerManagerComposite::PushGroupForLayerEffects() {
   // This is currently true, so just making sure that any new use of this
   // method is flagged for investigation
   MOZ_ASSERT(StaticPrefs::layers_effect_invert() ||
              StaticPrefs::layers_effect_grayscale() ||
              StaticPrefs::layers_effect_contrast() != 0.0);
 
@@ -1154,38 +1246,33 @@ bool LayerManagerComposite::Render(const
     if (hasContentPaint) {
       if (RefPtr<RecordedFrame> frame =
               mCompositor->RecordFrame(TimeStamp::Now())) {
         mCompositionRecorder->RecordFrame(frame);
       }
     }
   }
 
-  if (!usingNativeLayers) {
+  if (usingNativeLayers) {
+    UpdateDebugOverlayNativeLayers();
+  } else {
     // Allow widget to render a custom foreground.
     mCompositor->GetWidget()->DrawWindowOverlay(
         &widgetContext, LayoutDeviceIntRect::FromUnknownRect(bounds));
 
 #if defined(MOZ_WIDGET_ANDROID)
     // Depending on the content shift the toolbar may be rendered on top of
     // some of the content so it must be rendered after the content.
     if (jni::IsFennec()) {
       RenderToolbar();
     }
     HandlePixelsTarget();
 #endif  // defined(MOZ_WIDGET_ANDROID)
 
     // Debugging
-    // FIXME: We should render the debug overlay when using native layers, too.
-    // But we can't split the debug overlay rendering into multiple tiles
-    // because of a cyclic dependency: We want to display stats about the
-    // rendering of the entire window, but at the time when we render into the
-    // native layers, we do not know all the information about this frame yet.
-    // So we need to render the debug layer into an additional native layer on
-    // top, probably.
     RenderDebugOverlay(bounds);
   }
 
   {
     AUTO_PROFILER_LABEL("LayerManagerComposite::Render:EndFrame", GRAPHICS);
 
     mCompositor->EndFrame();
   }
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -436,16 +436,22 @@ class LayerManagerComposite final : publ
   void InvalidateDebugOverlay(nsIntRegion& aInvalidRegion,
                               const gfx::IntRect& aBounds);
 
   /**
    * Render debug overlays such as the FPS/FrameCounter above the frame.
    */
   void RenderDebugOverlay(const gfx::IntRect& aBounds);
 
+  void DrawBorder(const gfx::IntRect& aOuter, int32_t aBorderWidth,
+                  const gfx::Color& aColor, const gfx::Matrix4x4& aTransform);
+  void DrawTranslationWarningOverlay(const gfx::IntRect& aBounds);
+
+  void UpdateDebugOverlayNativeLayers();
+
   RefPtr<CompositingRenderTarget> PushGroupForLayerEffects();
   void PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
                                gfx::IntRect aClipRect, bool aGrayscaleEffect,
                                bool aInvertEffect, float aContrastEffect);
 
   /**
    * Create or recycle native layers to cover aRegion or aRect.
    * This method takes existing layers from the front of aLayersToRecycle (or
@@ -480,16 +486,19 @@ class LayerManagerComposite final : publ
   bool mInTransaction;
   bool mIsCompositorReady;
 
   RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
   CompositorScreenshotGrabber mProfilerScreenshotGrabber;
   RefPtr<TextRenderer> mTextRenderer;
   RefPtr<NativeLayerRoot> mNativeLayerRoot;
   std::deque<RefPtr<NativeLayer>> mNativeLayers;
+  RefPtr<NativeLayer> mGPUStatsLayer;
+  RefPtr<NativeLayer> mUnusedTransformWarningLayer;
+  RefPtr<NativeLayer> mDisabledApzWarningLayer;
 
 #ifdef USE_SKIA
   /**
    * Render paint and composite times above the frame.
    */
   void DrawPaintTimes(Compositor* aCompositor);
   RefPtr<PaintCounter> mPaintCounter;
 #endif
--- a/gfx/layers/composite/TextRenderer.cpp
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -58,40 +58,38 @@ void TextRenderer::RenderText(Compositor
   const FontBitmapInfo* info = GetFontInfo(aFontType);
 
   // For now we only have a bitmap font with a 24px cell size, so we just
   // scale it up if the user wants larger text.
   Float scaleFactor = Float(aTextSize) / Float(info->mCellHeight);
   aTargetPixelWidth /= scaleFactor;
 
   RefPtr<TextureSource> src =
-      RenderText(aCompositor, aText, aTextSize, aTargetPixelWidth, aFontType);
+      RenderText(aCompositor, aText, aTargetPixelWidth, aFontType);
   if (!src) {
     return;
   }
 
   RefPtr<EffectRGB> effect = new EffectRGB(src, true, SamplingFilter::LINEAR);
   EffectChain chain;
   chain.mPrimaryEffect = effect;
 
   Matrix4x4 transform = aTransform;
   transform.PreScale(scaleFactor, scaleFactor, 1.0f);
 
   IntRect drawRect(aOrigin, src->GetSize());
   IntRect clip(-10000, -10000, 20000, 20000);
   aCompositor->DrawQuad(Rect(drawRect), clip, chain, 1.0f, transform);
 }
 
-RefPtr<TextureSource> TextRenderer::RenderText(TextureSourceProvider* aProvider,
-                                               const string& aText,
-                                               uint32_t aTextSize,
-                                               uint32_t aTargetPixelWidth,
-                                               FontType aFontType) {
+IntSize TextRenderer::ComputeSurfaceSize(const string& aText,
+                                         uint32_t aTargetPixelWidth,
+                                         FontType aFontType) {
   if (!EnsureInitialized(aFontType)) {
-    return nullptr;
+    return IntSize();
   }
 
   FontCache* cache = mFonts[aFontType].get();
   const FontBitmapInfo* info = cache->mInfo;
 
   uint32_t numLines = 1;
   uint32_t maxWidth = 0;
   uint32_t lineWidth = 0;
@@ -106,79 +104,90 @@ RefPtr<TextureSource> TextRenderer::Rend
       lineWidth = 0;
       continue;
     }
 
     lineWidth += info->GetGlyphWidth(aText[i]);
     maxWidth = std::max(lineWidth, maxWidth);
   }
 
-  // Create a surface to draw our glyphs to.
-  RefPtr<DataSourceSurface> textSurf = Factory::CreateDataSourceSurface(
-      IntSize(maxWidth, numLines * info->mCellHeight), sTextureFormat);
-  if (NS_WARN_IF(!textSurf)) {
-    return nullptr;
-  }
+  return IntSize(maxWidth, numLines * info->mCellHeight);
+}
 
-  DataSourceSurface::MappedSurface map;
-  if (NS_WARN_IF(
-          !textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map))) {
+RefPtr<TextureSource> TextRenderer::RenderText(TextureSourceProvider* aProvider,
+                                               const string& aText,
+                                               uint32_t aTargetPixelWidth,
+                                               FontType aFontType) {
+  if (!EnsureInitialized(aFontType)) {
     return nullptr;
   }
 
-  // Initialize the surface to transparent white.
-  memset(map.mData, uint8_t(sBackgroundOpacity * 255.0f),
-         numLines * info->mCellHeight * map.mStride);
-
-  uint32_t currentXPos = 0;
-  uint32_t currentYPos = 0;
-
-  const unsigned int kGlyphsPerLine = info->mTextureWidth / info->mCellWidth;
-
-  // Copy our glyphs onto the surface.
-  for (uint32_t i = 0; i < aText.length(); i++) {
-    if (aText[i] == '\n' ||
-        (aText[i] == ' ' && currentXPos > aTargetPixelWidth)) {
-      currentYPos += info->mCellHeight;
-      currentXPos = 0;
-      continue;
-    }
+  IntSize size = ComputeSurfaceSize(aText, aTargetPixelWidth, aFontType);
 
-    uint32_t index = aText[i] - info->mFirstChar;
-    uint32_t glyphXOffset = (index % kGlyphsPerLine) * info->mCellWidth *
-                            BytesPerPixel(sTextureFormat);
-    uint32_t truncatedLine = index / kGlyphsPerLine;
-    uint32_t glyphYOffset =
-        truncatedLine * info->mCellHeight * cache->mMap.mStride;
-
-    uint32_t glyphWidth = info->GetGlyphWidth(aText[i]);
+  // Create a DrawTarget to draw our glyphs to.
+  RefPtr<DrawTarget> dt =
+      Factory::CreateDrawTarget(BackendType::SKIA, size, sTextureFormat);
 
-    for (uint32_t y = 0; y < info->mCellHeight; y++) {
-      memcpy(map.mData + (y + currentYPos) * map.mStride +
-                 currentXPos * BytesPerPixel(sTextureFormat),
-             cache->mMap.mData + glyphYOffset + y * cache->mMap.mStride +
-                 glyphXOffset,
-             glyphWidth * BytesPerPixel(sTextureFormat));
-    }
-
-    currentXPos += glyphWidth;
-  }
-
-  textSurf->Unmap();
-
+  RenderTextToDrawTarget(dt, aText, aTargetPixelWidth, aFontType);
+  RefPtr<SourceSurface> surf = dt->Snapshot();
+  RefPtr<DataSourceSurface> dataSurf = surf->GetDataSurface();
   RefPtr<DataTextureSource> src = aProvider->CreateDataTextureSource();
 
-  if (!src->Update(textSurf)) {
+  if (!src->Update(dataSurf)) {
     // Upload failed.
     return nullptr;
   }
 
   return src;
 }
 
+void TextRenderer::RenderTextToDrawTarget(DrawTarget* aDrawTarget,
+                                          const string& aText,
+                                          uint32_t aTargetPixelWidth,
+                                          FontType aFontType) {
+  if (!EnsureInitialized(aFontType)) {
+    return;
+  }
+
+  // Initialize the DrawTarget to transparent white.
+  IntSize size = aDrawTarget->GetSize();
+  aDrawTarget->FillRect(Rect(0, 0, size.width, size.height),
+                        ColorPattern(Color(1.0, 1.0, 1.0, sBackgroundOpacity)),
+                        DrawOptions(1.0, CompositionOp::OP_SOURCE));
+
+  IntPoint currentPos;
+
+  FontCache* cache = mFonts[aFontType].get();
+  const FontBitmapInfo* info = cache->mInfo;
+
+  const unsigned int kGlyphsPerLine = info->mTextureWidth / info->mCellWidth;
+
+  // Copy our glyphs onto the DrawTarget.
+  for (uint32_t i = 0; i < aText.length(); i++) {
+    if (aText[i] == '\n' ||
+        (aText[i] == ' ' && currentPos.x > int32_t(aTargetPixelWidth))) {
+      currentPos.y += info->mCellHeight;
+      currentPos.x = 0;
+      continue;
+    }
+
+    uint32_t index = aText[i] - info->mFirstChar;
+    uint32_t cellIndexY = index / kGlyphsPerLine;
+    uint32_t cellIndexX = index - (cellIndexY * kGlyphsPerLine);
+    uint32_t glyphWidth = info->GetGlyphWidth(aText[i]);
+    IntRect srcRect(cellIndexX * info->mCellWidth,
+                    cellIndexY * info->mCellHeight, glyphWidth,
+                    info->mCellHeight);
+
+    aDrawTarget->CopySurface(cache->mGlyphBitmaps, srcRect, currentPos);
+
+    currentPos.x += glyphWidth;
+  }
+}
+
 /* static */ const FontBitmapInfo* TextRenderer::GetFontInfo(FontType aType) {
   switch (aType) {
     case FontType::Default:
       return &sDefaultCompositorFont;
     case FontType::FixedWidth:
       return &sFixedWidthCompositorFont;
     default:
       MOZ_ASSERT_UNREACHABLE("unknown font type");
--- a/gfx/layers/composite/TextRenderer.h
+++ b/gfx/layers/composite/TextRenderer.h
@@ -27,26 +27,35 @@ class TextRenderer final {
  public:
   NS_INLINE_DECL_REFCOUNTING(TextRenderer)
 
   enum class FontType { Default, FixedWidth, NumTypes };
 
   TextRenderer() = default;
 
   RefPtr<TextureSource> RenderText(TextureSourceProvider* aProvider,
-                                   const std::string& aText, uint32_t aTextSize,
+                                   const std::string& aText,
                                    uint32_t aTargetPixelWidth,
                                    FontType aFontType);
 
   void RenderText(Compositor* aCompositor, const std::string& aText,
                   const gfx::IntPoint& aOrigin,
                   const gfx::Matrix4x4& aTransform, uint32_t aTextSize,
                   uint32_t aTargetPixelWidth,
                   FontType aFontType = FontType::Default);
 
+  gfx::IntSize ComputeSurfaceSize(const std::string& aText,
+                                  uint32_t aTargetPixelWidth,
+                                  FontType aFontType = FontType::Default);
+
+  void RenderTextToDrawTarget(gfx::DrawTarget* aDrawTarget,
+                              const std::string& aText,
+                              uint32_t aTargetPixelWidth,
+                              FontType aFontType = FontType::Default);
+
   struct FontCache {
     ~FontCache();
     RefPtr<gfx::DataSourceSurface> mGlyphBitmaps;
     gfx::DataSourceSurface::MappedSurface mMap;
     const FontBitmapInfo* mInfo;
   };
 
  protected:
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -954,17 +954,16 @@ class CompositingRenderTarget : public T
 #endif
 
   /**
    * Perform a clear when recycling a non opaque surface.
    * The clear is deferred to when the render target is bound.
    */
   void ClearOnBind() { mClearOnBind = true; }
 
-  void SetOrigin(const gfx::IntPoint& aOrigin) { mOrigin = aOrigin; }
   const gfx::IntPoint& GetOrigin() const { return mOrigin; }
   gfx::IntRect GetRect() { return gfx::IntRect(GetOrigin(), GetSize()); }
 
   /**
    * If a Projection matrix is set, then it is used for rendering to
    * this render target instead of generating one.  If no explicit
    * projection is set, Compositors are expected to generate an
    * orthogonal maaping that maps 0..1 to the full size of the render
--- a/gfx/layers/mlgpu/LayerManagerMLGPU.cpp
+++ b/gfx/layers/mlgpu/LayerManagerMLGPU.cpp
@@ -432,19 +432,18 @@ void LayerManagerMLGPU::RenderLayers() {
 void LayerManagerMLGPU::DrawDebugOverlay() {
   IntSize windowSize = mSwapChain->GetSize();
 
   GPUStats stats;
   mDevice->GetDiagnostics(&stats);
   stats.mScreenPixels = windowSize.width * windowSize.height;
 
   std::string text = mDiagnostics->GetFrameOverlayString(stats);
-  RefPtr<TextureSource> texture =
-      mTextRenderer->RenderText(mTextureSourceProvider, text, 30, 600,
-                                TextRenderer::FontType::FixedWidth);
+  RefPtr<TextureSource> texture = mTextRenderer->RenderText(
+      mTextureSourceProvider, text, 600, TextRenderer::FontType::FixedWidth);
   if (!texture) {
     return;
   }
 
   if (mUsingInvalidation &&
       (texture->GetSize().width > kDebugOverlayMaxWidth ||
        texture->GetSize().height > kDebugOverlayMaxHeight)) {
     gfxCriticalNote << "Diagnostic overlay exceeds invalidation area: %s"
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
@@ -12,111 +12,106 @@
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
 CompositingRenderTargetOGL::~CompositingRenderTargetOGL() {
-  if (mGL && mGL->MakeCurrent()) {
+  if (mGLResourceOwnership == GLResourceOwnership::OWNED_BY_RENDER_TARGET &&
+      mGL && mGL->MakeCurrent()) {
     mGL->fDeleteTextures(1, &mTextureHandle);
     mGL->fDeleteFramebuffers(1, &mFBO);
   }
 }
 
 void CompositingRenderTargetOGL::BindTexture(GLenum aTextureUnit,
                                              GLenum aTextureTarget) {
-  MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+  MOZ_ASSERT(!mNeedInitialization);
   MOZ_ASSERT(mTextureHandle != 0);
   mGL->fActiveTexture(aTextureUnit);
   mGL->fBindTexture(aTextureTarget, mTextureHandle);
 }
 
 void CompositingRenderTargetOGL::BindRenderTarget() {
   bool needsClear = false;
 
-  if (mInitParams.mStatus != InitParams::INITIALIZED) {
-    InitializeImpl();
-    if (mInitParams.mInit == INIT_MODE_CLEAR) {
+  if (mNeedInitialization) {
+    Initialize(mNeedInitialization->mFBOTextureTarget);
+    if (mNeedInitialization->mInitMode == INIT_MODE_CLEAR) {
       needsClear = true;
       mClearOnBind = false;
     }
+    mNeedInitialization = Nothing();
   } else {
-    MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
     GLuint fbo = GetFBO();
     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
     GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
       // The main framebuffer (0) of non-offscreen contexts
       // might be backed by a EGLSurface that needs to be renewed.
       if (mFBO == 0 && !mGL->IsOffscreen()) {
         mGL->RenewSurface(mCompositor->GetWidget());
         result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
       }
       if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
         nsAutoCString msg;
         msg.AppendPrintf(
             "Framebuffer not complete -- CheckFramebufferStatus returned 0x%x, "
-            "GLContext=%p, IsOffscreen()=%d, mFBO=%d, aFBOTextureTarget=0x%x, "
+            "GLContext=%p, IsOffscreen()=%d, mFBO=%d, "
             "aRect.width=%d, aRect.height=%d",
-            result, mGL.get(), mGL->IsOffscreen(), mFBO,
-            mInitParams.mFBOTextureTarget, mInitParams.mSize.width,
-            mInitParams.mSize.height);
+            result, mGL.get(), mGL->IsOffscreen(), mFBO, mSize.width,
+            mSize.height);
         NS_WARNING(msg.get());
       }
     }
 
     needsClear = mClearOnBind;
   }
 
   if (needsClear) {
     ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, true);
-    ScopedScissorRect autoScissorRect(mGL, 0, 0, mInitParams.mSize.width,
-                                      mInitParams.mSize.height);
+    ScopedScissorRect autoScissorRect(mGL, 0, 0, mSize.width, mSize.height);
     mGL->fClearColor(0.0, 0.0, 0.0, 0.0);
     mGL->fClearDepth(0.0);
     mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
   }
 }
 
 GLuint CompositingRenderTargetOGL::GetFBO() const {
-  MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+  MOZ_ASSERT(!mNeedInitialization);
   return mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
 }
 
 #ifdef MOZ_DUMP_PAINTING
 already_AddRefed<DataSourceSurface> CompositingRenderTargetOGL::Dump(
     Compositor* aCompositor) {
-  MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+  MOZ_ASSERT(!mNeedInitialization);
   CompositorOGL* compositorOGL = aCompositor->AsCompositorOGL();
   return ReadBackSurface(mGL, mTextureHandle, true,
                          compositorOGL->GetFBOFormat());
 }
 #endif
 
-void CompositingRenderTargetOGL::InitializeImpl() {
-  MOZ_ASSERT(mInitParams.mStatus == InitParams::READY);
-
+void CompositingRenderTargetOGL::Initialize(GLenum aFBOTextureTarget) {
   // TODO: call mGL->GetBackbufferFB(), use that
   GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
   mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
   mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
-                             mInitParams.mFBOTextureTarget, mTextureHandle, 0);
+                             aFBOTextureTarget, mTextureHandle, 0);
 
   // Making this call to fCheckFramebufferStatus prevents a crash on
   // PowerVR. See bug 695246.
   GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
   if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
     nsAutoCString msg;
     msg.AppendPrintf(
         "Framebuffer not complete -- error 0x%x, aFBOTextureTarget 0x%x, mFBO "
         "%d, mTextureHandle %d, aRect.width %d, aRect.height %d",
-        result, mInitParams.mFBOTextureTarget, mFBO, mTextureHandle,
-        mInitParams.mSize.width, mInitParams.mSize.height);
+        result, aFBOTextureTarget, mFBO, mTextureHandle, mSize.width,
+        mSize.height);
     NS_ERROR(msg.get());
   }
-
-  mInitParams.mStatus = InitParams::INITIALIZED;
 }
 
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.h
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.h
@@ -35,113 +35,97 @@ namespace layers {
 
 class TextureSource;
 
 class CompositingRenderTargetOGL : public CompositingRenderTarget {
   typedef mozilla::gl::GLContext GLContext;
 
   friend class CompositorOGL;
 
-  // For lazy initialisation of the GL stuff
-  struct InitParams {
-    InitParams()
-        : mStatus(NO_PARAMS), mFBOTextureTarget(0), mInit(INIT_MODE_NONE) {}
-    InitParams(const gfx::IntSize& aSize, const gfx::IntSize& aPhySize,
-               GLenum aFBOTextureTarget, SurfaceInitMode aInit)
-        : mStatus(READY),
-          mSize(aSize),
-          mPhySize(aPhySize),
-          mFBOTextureTarget(aFBOTextureTarget),
-          mInit(aInit) {}
+  enum class GLResourceOwnership : uint8_t {
+    // Framebuffer and texture will be deleted when the RenderTarget is
+    // destroyed.
+    OWNED_BY_RENDER_TARGET,
 
-    enum { NO_PARAMS, READY, INITIALIZED } mStatus;
-    /*
-     * Users of render target would draw in logical size, but it is
-     * actually drawn to a surface in physical size.  GL surfaces have
-     * a limitation on their size, a smaller surface would be
-     * allocated for the render target if the caller requests in a
-     * size too big.
-     */
-    gfx::IntSize mSize;     // Logical size, the expected by callers.
-    gfx::IntSize mPhySize;  // Physical size, the real size of the surface.
+    // Framebuffer and texture are only used by the RenderTarget, but never
+    // deleted.
+    EXTERNALLY_OWNED
+  };
+
+  struct InitParams {
     GLenum mFBOTextureTarget;
-    SurfaceInitMode mInit;
+    SurfaceInitMode mInitMode;
   };
 
  public:
-  CompositingRenderTargetOGL(CompositorOGL* aCompositor,
-                             const gfx::IntPoint& aOrigin,
-                             const gfx::IntPoint& aClipSpaceOrigin,
-                             GLuint aTexure, GLuint aFBO)
-      : CompositingRenderTarget(aOrigin),
-        mInitParams(),
-        mCompositor(aCompositor),
-        mGL(aCompositor->gl()),
-        mClipSpaceOrigin(aClipSpaceOrigin),
-        mTextureHandle(aTexure),
-        mFBO(aFBO) {
-    MOZ_ASSERT(mGL);
-  }
-
   ~CompositingRenderTargetOGL();
 
   const char* Name() const override { return "CompositingRenderTargetOGL"; }
 
   /**
    * Create a render target around the default FBO, for rendering straight to
    * the window.
    */
-  static already_AddRefed<CompositingRenderTargetOGL> RenderTargetForWindow(
+  static already_AddRefed<CompositingRenderTargetOGL> CreateForWindow(
       CompositorOGL* aCompositor, const gfx::IntSize& aSize) {
     RefPtr<CompositingRenderTargetOGL> result = new CompositingRenderTargetOGL(
-        aCompositor, gfx::IntPoint(), gfx::IntPoint(), 0, 0);
-    result->mInitParams = InitParams(aSize, aSize, 0, INIT_MODE_NONE);
-    result->mInitParams.mStatus = InitParams::INITIALIZED;
+        aCompositor, gfx::IntRect(gfx::IntPoint(), aSize), gfx::IntPoint(),
+        aSize, GLResourceOwnership::EXTERNALLY_OWNED, 0, 0, Nothing());
     return result.forget();
   }
 
-  /**
-   * Some initialisation work on the backing FBO and texture.
-   * We do this lazily so that when we first set this render target on the
-   * compositor we do not have to re-bind the FBO after unbinding it, or
-   * alternatively leave the FBO bound after creation.
-   */
-  void Initialize(const gfx::IntSize& aSize, const gfx::IntSize& aPhySize,
-                  GLenum aFBOTextureTarget, SurfaceInitMode aInit) {
-    MOZ_ASSERT(mInitParams.mStatus == InitParams::NO_PARAMS,
-               "Initialized twice?");
-    // postpone initialization until we actually want to use this render target
-    mInitParams = InitParams(aSize, aPhySize, aFBOTextureTarget, aInit);
+  static already_AddRefed<CompositingRenderTargetOGL>
+  CreateForNewFBOAndTakeOwnership(CompositorOGL* aCompositor, GLuint aTexture,
+                                  GLuint aFBO, const gfx::IntRect& aRect,
+                                  const gfx::IntPoint& aClipSpaceOrigin,
+                                  const gfx::IntSize& aPhySize,
+                                  GLenum aFBOTextureTarget,
+                                  SurfaceInitMode aInit) {
+    RefPtr<CompositingRenderTargetOGL> result = new CompositingRenderTargetOGL(
+        aCompositor, aRect, aClipSpaceOrigin, aPhySize,
+        GLResourceOwnership::OWNED_BY_RENDER_TARGET, aTexture, aFBO,
+        Some(InitParams{aFBOTextureTarget, aInit}));
+    return result.forget();
+  }
+
+  static already_AddRefed<CompositingRenderTargetOGL>
+  CreateForExternallyOwnedFBO(CompositorOGL* aCompositor, GLuint aFBO,
+                              const gfx::IntRect& aRect,
+                              const gfx::IntPoint& aClipSpaceOrigin) {
+    RefPtr<CompositingRenderTargetOGL> result = new CompositingRenderTargetOGL(
+        aCompositor, aRect, aClipSpaceOrigin, aRect.Size(),
+        GLResourceOwnership::EXTERNALLY_OWNED, 0, aFBO, Nothing());
+    return result.forget();
   }
 
   void BindTexture(GLenum aTextureUnit, GLenum aTextureTarget);
 
   /**
    * Call when we want to draw into our FBO
    */
   void BindRenderTarget();
 
   bool IsWindow() { return mFBO == 0; }
 
   GLuint GetFBO() const;
 
   GLuint GetTextureHandle() const {
-    MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+    MOZ_ASSERT(!mNeedInitialization);
     return mTextureHandle;
   }
 
   // TextureSourceOGL
   TextureSourceOGL* AsSourceOGL() override {
     // XXX - Bug 900770
     MOZ_ASSERT(
         false,
         "CompositingRenderTargetOGL should not be used as a TextureSource");
     return nullptr;
   }
-  gfx::IntSize GetSize() const override { return mInitParams.mSize; }
+  gfx::IntSize GetSize() const override { return mSize; }
 
   // The point that DrawGeometry's aClipRect is relative to. Will be (0, 0) for
   // root render targets and equal to GetOrigin() for non-root render targets.
   gfx::IntPoint GetClipSpaceOrigin() const { return mClipSpaceOrigin; }
 
   gfx::SurfaceFormat GetFormat() const override {
     // XXX - Should it be implemented ? is the above assert true ?
     MOZ_ASSERT(false, "Not implemented");
@@ -153,35 +137,76 @@ class CompositingRenderTargetOGL : publi
   void SetClipRect(const Maybe<gfx::IntRect>& aRect) { mClipRect = aRect; }
   const Maybe<gfx::IntRect>& GetClipRect() const { return mClipRect; }
 
 #ifdef MOZ_DUMP_PAINTING
   already_AddRefed<gfx::DataSourceSurface> Dump(
       Compositor* aCompositor) override;
 #endif
 
-  const gfx::IntSize& GetInitSize() const { return mInitParams.mSize; }
+  const gfx::IntSize& GetInitSize() const { return mSize; }
+  const gfx::IntSize& GetPhysicalSize() const { return mPhySize; }
 
- private:
+ protected:
+  CompositingRenderTargetOGL(CompositorOGL* aCompositor,
+                             const gfx::IntRect& aRect,
+                             const gfx::IntPoint& aClipSpaceOrigin,
+                             const gfx::IntSize& aPhySize,
+                             GLResourceOwnership aGLResourceOwnership,
+                             GLuint aTexure, GLuint aFBO,
+                             const Maybe<InitParams>& aNeedInitialization)
+      : CompositingRenderTarget(aRect.TopLeft()),
+        mNeedInitialization(aNeedInitialization),
+        mSize(aRect.Size()),
+        mPhySize(aPhySize),
+        mCompositor(aCompositor),
+        mGL(aCompositor->gl()),
+        mClipSpaceOrigin(aClipSpaceOrigin),
+        mGLResourceOwnership(aGLResourceOwnership),
+        mTextureHandle(aTexure),
+        mFBO(aFBO) {
+    MOZ_ASSERT(mGL);
+  }
+
   /**
-   * Actually do the initialisation. Note that we leave our FBO bound, and so
-   * calling this method is only suitable when about to use this render target.
+   * Actually do the initialisation.
+   * We do this lazily so that when we first set this render target on the
+   * compositor we do not have to re-bind the FBO after unbinding it, or
+   * alternatively leave the FBO bound after creation. Note that we leave our
+   * FBO bound, and so calling this method is only suitable when about to use
+   * this render target.
+   */
+  void Initialize(GLenum aFBOTextureTarget);
+
+  /**
+   * Some() between construction and Initialize, if initialization was
+   * requested.
    */
-  void InitializeImpl();
+  Maybe<InitParams> mNeedInitialization;
 
-  InitParams mInitParams;
+  /*
+   * Users of render target would draw in logical size, but it is
+   * actually drawn to a surface in physical size.  GL surfaces have
+   * a limitation on their size, a smaller surface would be
+   * allocated for the render target if the caller requests in a
+   * size too big.
+   */
+  gfx::IntSize mSize;     // Logical size, the expected by callers.
+  gfx::IntSize mPhySize;  // Physical size, the real size of the surface.
+
   /**
    * There is temporary a cycle between the compositor and the render target,
    * each having a strong ref to the other. The compositor's reference to
    * the target is always cleared at the end of a frame.
    */
   RefPtr<CompositorOGL> mCompositor;
   RefPtr<GLContext> mGL;
   Maybe<gfx::IntRect> mClipRect;
   gfx::IntPoint mClipSpaceOrigin;
+  GLResourceOwnership mGLResourceOwnership;
   GLuint mTextureHandle;
   GLuint mFBO;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif /* MOZILLA_GFX_SURFACEOGL_H */
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -53,21 +53,16 @@
 #include "HeapCopyOfStackArray.h"
 #include "GLBlitHelper.h"
 #include "mozilla/gfx/Swizzle.h"
 
 #if MOZ_WIDGET_ANDROID
 #  include "GeneratedJNIWrappers.h"
 #endif
 
-#ifdef XP_MACOSX
-#  include "GLContextCGL.h"
-#  include "mozilla/layers/NativeLayerCA.h"
-#endif
-
 #include "GeckoProfiler.h"
 
 namespace mozilla {
 
 using namespace std;
 using namespace gfx;
 
 namespace layers {
@@ -568,19 +563,19 @@ gfx::Rect CompositorOGL::GetTextureCoord
   }
 
   return textureRect;
 }
 
 void CompositorOGL::PrepareViewport(CompositingRenderTargetOGL* aRenderTarget) {
   MOZ_ASSERT(aRenderTarget);
   // Logical surface size.
-  const gfx::IntSize& size = aRenderTarget->mInitParams.mSize;
+  const gfx::IntSize& size = aRenderTarget->GetSize();
   // Physical surface size.
-  const gfx::IntSize& phySize = aRenderTarget->mInitParams.mPhySize;
+  const gfx::IntSize& phySize = aRenderTarget->GetPhysicalSize();
 
   // Set the viewport correctly.
   mGLContext->fViewport(mSurfaceOrigin.x, mSurfaceOrigin.y, phySize.width,
                         phySize.height);
 
   mViewportSize = size;
 
   if (!aRenderTarget->HasComplexProjection()) {
@@ -635,22 +630,21 @@ already_AddRefed<CompositingRenderTarget
   if (!gl()) {
     // CompositingRenderTargetOGL does not work without a gl context.
     return nullptr;
   }
 
   GLuint tex = 0;
   GLuint fbo = 0;
   IntRect rect = aRect;
-  IntSize FBOSize;
-  CreateFBOWithTexture(rect, false, 0, &fbo, &tex, &FBOSize);
-  RefPtr<CompositingRenderTargetOGL> surface = new CompositingRenderTargetOGL(
-      this, aRect.TopLeft(), aRect.TopLeft(), tex, fbo);
-  surface->Initialize(aRect.Size(), FBOSize, mFBOTextureTarget, aInit);
-  return surface.forget();
+  IntSize fboSize;
+  CreateFBOWithTexture(rect, false, 0, &fbo, &tex, &fboSize);
+  return CompositingRenderTargetOGL::CreateForNewFBOAndTakeOwnership(
+      this, tex, fbo, aRect, aRect.TopLeft(), aRect.Size(), mFBOTextureTarget,
+      aInit);
 }
 
 already_AddRefed<CompositingRenderTarget>
 CompositorOGL::CreateRenderTargetFromSource(
     const IntRect& aRect, const CompositingRenderTarget* aSource,
     const IntPoint& aSourcePoint) {
   MOZ_ASSERT(!aRect.IsZeroArea(),
              "Trying to create a render target of invalid size");
@@ -666,21 +660,19 @@ CompositorOGL::CreateRenderTargetFromSou
 
   GLuint tex = 0;
   GLuint fbo = 0;
   const CompositingRenderTargetOGL* sourceSurface =
       static_cast<const CompositingRenderTargetOGL*>(aSource);
   IntRect sourceRect(aSourcePoint, aRect.Size());
   CreateFBOWithTexture(sourceRect, true, sourceSurface->GetFBO(), &fbo, &tex);
 
-  RefPtr<CompositingRenderTargetOGL> surface = new CompositingRenderTargetOGL(
-      this, aRect.TopLeft(), aRect.TopLeft(), tex, fbo);
-  surface->Initialize(aRect.Size(), sourceRect.Size(), mFBOTextureTarget,
-                      INIT_MODE_NONE);
-  return surface.forget();
+  return CompositingRenderTargetOGL::CreateForNewFBOAndTakeOwnership(
+      this, tex, fbo, aRect, aRect.TopLeft(), sourceRect.Size(),
+      mFBOTextureTarget, INIT_MODE_NONE);
 }
 
 void CompositorOGL::SetRenderTarget(CompositingRenderTarget* aSurface) {
   MOZ_ASSERT(aSurface);
   CompositingRenderTargetOGL* surface =
       static_cast<CompositingRenderTargetOGL*>(aSurface);
   if (mCurrentRenderTarget != surface) {
     mCurrentRenderTarget = surface;
@@ -758,125 +750,48 @@ void CompositorOGL::ClearRect(const gfx:
 
   ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true);
   ScopedScissorRect autoScissorRect(mGLContext, aRect.X(), y, aRect.Width(),
                                     aRect.Height());
   mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
   mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
 }
 
-#ifdef XP_MACOSX
-class SurfaceRegistryWrapperAroundCompositorOGL
-    : public layers::IOSurfaceRegistry {
- public:
-  explicit SurfaceRegistryWrapperAroundCompositorOGL(CompositorOGL* aCompositor)
-      : mCompositor(aCompositor) {}
-  void RegisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) override {
-    mCompositor->RegisterIOSurface((IOSurfacePtr)aSurface.get());
-  }
-  void UnregisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) override {
-    mCompositor->UnregisterIOSurface((IOSurfacePtr)aSurface.get());
-  }
-  RefPtr<CompositorOGL> mCompositor;
-};
-
-void CompositorOGL::RegisterIOSurface(IOSurfacePtr aSurface) {
-  MOZ_RELEASE_ASSERT(mRegisteredIOSurfaceRenderTargets.find(aSurface) ==
-                         mRegisteredIOSurfaceRenderTargets.end(),
-                     "double-registering IOSurface");
-
-  IOSurfaceRef surface = (IOSurfaceRef)aSurface;
-  auto glContextCGL = GLContextCGL::Cast(mGLContext);
-  MOZ_RELEASE_ASSERT(glContextCGL, "Unexpected GLContext type");
-
-  IntSize size(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
-
-  mGLContext->MakeCurrent();
-  GLuint tex = mGLContext->CreateTexture();
-  {
-    const ScopedBindTexture bindTex(mGLContext, tex,
-                                    LOCAL_GL_TEXTURE_RECTANGLE_ARB);
-    CGLTexImageIOSurface2D(glContextCGL->GetCGLContext(),
-                           LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_RGBA,
-                           size.width, size.height, LOCAL_GL_BGRA,
-                           LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, surface, 0);
-  }
-
-  GLuint fbo = mGLContext->CreateFramebuffer();
-  {
-    const ScopedBindFramebuffer bindFB(mGLContext, fbo);
-    mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
-                                      LOCAL_GL_COLOR_ATTACHMENT0,
-                                      LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex, 0);
-  }
-
-  RefPtr<CompositingRenderTargetOGL> rt = new CompositingRenderTargetOGL(
-      this, gfx::IntPoint(), gfx::IntPoint(), tex, fbo);
-  rt->Initialize(size, size, LOCAL_GL_TEXTURE_RECTANGLE_ARB, INIT_MODE_NONE);
-
-  mRegisteredIOSurfaceRenderTargets.insert({aSurface, rt});
-}
-
-void CompositorOGL::UnregisterIOSurface(IOSurfacePtr aSurface) {
-  size_t removeCount = mRegisteredIOSurfaceRenderTargets.erase(aSurface);
-  MOZ_RELEASE_ASSERT(removeCount == 1,
-                     "Unregistering IOSurface that's not registered");
-}
-#endif
-
 already_AddRefed<CompositingRenderTargetOGL>
 CompositorOGL::RenderTargetForNativeLayer(NativeLayer* aNativeLayer,
                                           IntRegion& aInvalidRegion) {
   if (aInvalidRegion.IsEmpty()) {
     return nullptr;
   }
 
-#ifdef XP_MACOSX
-  NativeLayerCA* nativeLayer = aNativeLayer->AsNativeLayerCA();
-  MOZ_RELEASE_ASSERT(nativeLayer, "Unexpected native layer type");
-  nativeLayer->SetSurfaceIsFlipped(true);
-  auto glContextCGL = GLContextCGL::Cast(mGLContext);
-  MOZ_RELEASE_ASSERT(glContextCGL, "Unexpected GLContext type");
-  RefPtr<layers::IOSurfaceRegistry> currentRegistry =
-      nativeLayer->GetSurfaceRegistry();
-  if (!currentRegistry) {
-    nativeLayer->SetSurfaceRegistry(
-        MakeAndAddRef<SurfaceRegistryWrapperAroundCompositorOGL>(this));
-  } else {
-    MOZ_RELEASE_ASSERT(static_cast<SurfaceRegistryWrapperAroundCompositorOGL*>(
-                           currentRegistry.get())
-                           ->mCompositor == this);
-  }
-  CFTypeRefPtr<IOSurfaceRef> surf = nativeLayer->NextSurface();
-  if (!surf) {
+  aNativeLayer->SetSurfaceIsFlipped(true);
+  aNativeLayer->SetGLContext(mGLContext);
+
+  IntRect layerRect = aNativeLayer->GetRect();
+  IntRegion invalidRelativeToLayer =
+      aInvalidRegion.MovedBy(-layerRect.TopLeft());
+  aNativeLayer->InvalidateRegionThroughoutSwapchain(invalidRelativeToLayer);
+  Maybe<GLuint> fbo = aNativeLayer->NextSurfaceAsFramebuffer(false);
+  if (!fbo) {
     return nullptr;
   }
 
-  IntRect layerRect = nativeLayer->GetRect();
-  IntRegion invalidRelativeToLayer =
-      aInvalidRegion.MovedBy(-layerRect.TopLeft());
-  nativeLayer->InvalidateRegionThroughoutSwapchain(invalidRelativeToLayer);
-  invalidRelativeToLayer = nativeLayer->CurrentSurfaceInvalidRegion();
+  invalidRelativeToLayer = aNativeLayer->CurrentSurfaceInvalidRegion();
   aInvalidRegion = invalidRelativeToLayer.MovedBy(layerRect.TopLeft());
 
-  auto match = mRegisteredIOSurfaceRenderTargets.find((IOSurfacePtr)surf.get());
-  MOZ_RELEASE_ASSERT(match != mRegisteredIOSurfaceRenderTargets.end(),
-                     "IOSurface has not been registered with this Compositor");
-  RefPtr<CompositingRenderTargetOGL> rt = match->second;
-  rt->SetOrigin(layerRect.TopLeft());
+  RefPtr<CompositingRenderTargetOGL> rt =
+      CompositingRenderTargetOGL::CreateForExternallyOwnedFBO(
+          this, *fbo, layerRect, IntPoint());
 
   // Clip the render target to the invalid rect. This conserves memory bandwidth
   // and power.
   IntRect invalidRect = aInvalidRegion.GetBounds();
   rt->SetClipRect(invalidRect == layerRect ? Nothing() : Some(invalidRect));
 
   return rt.forget();
-#else
-  MOZ_CRASH("Unexpected native layer on this platform");
-#endif
 }
 
 Maybe<IntRect> CompositorOGL::BeginFrameForWindow(
     const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect,
     const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion) {
   MOZ_RELEASE_ASSERT(!mTarget, "mTarget not cleared properly");
   return BeginFrame(aInvalidRegion, aClipRect, aRenderBounds, aOpaqueRegion);
 }
@@ -940,17 +855,16 @@ Maybe<gfx::IntRect> CompositorOGL::Begin
 
   RefPtr<CompositingRenderTarget> rt =
       RenderTargetForNativeLayer(aNativeLayer, layerInvalid);
   if (!rt) {
     return Nothing();
   }
   SetRenderTarget(rt);
   mCurrentNativeLayer = aNativeLayer;
-  mPixelsPerFrame += rect.Area();
 
   mGLContext->fClearColor(mClearColor.r, mClearColor.g, mClearColor.b,
                           mClearColor.a);
   if (const Maybe<IntRect>& rtClip = mCurrentRenderTarget->GetClipRect()) {
     // We need to apply a scissor rect during the clear. And since clears with
     // scissor rects are usually treated differently by the GPU than regular
     // clears, let's try to clear as little as possible in order to conserve
     // memory bandwidth.
@@ -961,18 +875,20 @@ Maybe<gfx::IntRect> CompositorOGL::Begin
           clearRegion.GetBounds() - mCurrentRenderTarget->GetOrigin();
       ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST,
                                            true);
       ScopedScissorRect autoScissorRect(mGLContext, clearRect.x,
                                         FlipY(clearRect.YMost()),
                                         clearRect.Width(), clearRect.Height());
       mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
     }
+    mPixelsPerFrame += rtClip->Area();
   } else {
     mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
+    mPixelsPerFrame += rect.Area();
   }
 
   return Some(rect);
 }
 
 void CompositorOGL::NormalDrawingDone() {
   // Now is a good time to update mFullWindowRenderTarget.
   if (!mCurrentNativeLayer) {
@@ -1038,24 +954,18 @@ void CompositorOGL::EndRenderingToNative
     IntRect rect = mCurrentRenderTarget->GetRect();
     DrawQuad(Rect(rect), rect - rect.TopLeft(), effectChain, 1.0, Matrix4x4(),
              Rect(rect));
   }
 
   mCurrentRenderTarget->SetClipRect(Nothing());
   SetRenderTarget(mNativeLayersReferenceRT);
 
-#ifdef XP_MACOSX
-  NativeLayerCA* nativeLayer = mCurrentNativeLayer->AsNativeLayerCA();
-  MOZ_RELEASE_ASSERT(nativeLayer, "Unexpected native layer type");
-  nativeLayer->NotifySurfaceReady();
+  mCurrentNativeLayer->NotifySurfaceReady();
   mCurrentNativeLayer = nullptr;
-#else
-  MOZ_CRASH("Unexpected native layer on this platform");
-#endif
 }
 
 Maybe<IntRect> CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
                                          const Maybe<IntRect>& aClipRect,
                                          const IntRect& aRenderBounds,
                                          const nsIntRegion& aOpaqueRegion) {
   AUTO_PROFILER_LABEL("CompositorOGL::BeginFrame", GRAPHICS);
 
@@ -1095,17 +1005,17 @@ Maybe<IntRect> CompositorOGL::BeginFrame
 
   // Default blend function implements "OVER"
   mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                  LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
   mGLContext->fEnable(LOCAL_GL_BLEND);
 
   RefPtr<CompositingRenderTarget> rt;
   if (mCanRenderToDefaultFramebuffer) {
-    rt = CompositingRenderTargetOGL::RenderTargetForWindow(this, rect.Size());
+    rt = CompositingRenderTargetOGL::CreateForWindow(this, rect.Size());
   } else if (mTarget) {
     rt = CreateRenderTarget(rect, INIT_MODE_CLEAR);
   } else {
     MOZ_CRASH("Unexpected call");
   }
 
   if (!rt) {
     return Nothing();
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -32,24 +32,16 @@
 #include "nsCOMPtr.h"         // for already_AddRefed
 #include "nsDebug.h"          // for NS_ASSERTION, NS_WARNING
 #include "nsISupportsImpl.h"  // for MOZ_COUNT_CTOR, etc
 #include "nsTArray.h"         // for AutoTArray, nsTArray, etc
 #include "nsThreadUtils.h"    // for nsRunnable
 #include "nsXULAppAPI.h"      // for XRE_GetProcessType
 #include "nscore.h"           // for NS_IMETHOD
 
-#ifdef XP_MACOSX
-// This file uses IOSurfacePtr instead of IOSurfaceRef because IOSurfaceRef is
-// hard to forward declare, and including <IOSurface/IOSurface.h> brings in
-// MacTypes.h which defines Point and Rect which cause name lookup trouble.
-struct DummyIOSurface;
-typedef DummyIOSurface* IOSurfacePtr;
-#endif
-
 class nsIWidget;
 
 namespace mozilla {
 
 namespace layers {
 
 class CompositingRenderTarget;
 class CompositingRenderTargetOGL;
@@ -279,21 +271,16 @@ class CompositorOGL final : public Compo
     mSurfaceOrigin = aOrigin;
   }
 
   // Register TextureSource which own device data that have to be deleted before
   // destroying this CompositorOGL.
   void RegisterTextureSource(TextureSource* aTextureSource);
   void UnregisterTextureSource(TextureSource* aTextureSource);
 
-#ifdef XP_MACOSX
-  void RegisterIOSurface(IOSurfacePtr aSurface);
-  void UnregisterIOSurface(IOSurfacePtr aSurface);
-#endif
-
  private:
   template <typename Geometry>
   void DrawGeometry(const Geometry& aGeometry, const gfx::Rect& aRect,
                     const gfx::IntRect& aClipRect,
                     const EffectChain& aEffectChain, gfx::Float aOpacity,
                     const gfx::Matrix4x4& aTransform,
                     const gfx::Rect& aVisibleRect);
 
@@ -532,26 +519,14 @@ class CompositorOGL final : public Compo
 
   /**
    * Size of the OpenGL context's primary framebuffer in pixels. Used by
    * FlipY for the y-flipping calculation and by the DEAA shader.
    */
   gfx::IntSize mViewportSize;
 
   ShaderProgramOGL* mCurrentProgram;
-
-#ifdef XP_MACOSX
-  struct IOSurfaceRefHasher {
-    std::size_t operator()(const IOSurfacePtr& aSurface) const {
-      return HashGeneric(reinterpret_cast<uintptr_t>(aSurface));
-    }
-  };
-
-  std::unordered_map<IOSurfacePtr, RefPtr<CompositingRenderTargetOGL>,
-                     IOSurfaceRefHasher>
-      mRegisteredIOSurfaceRenderTargets;
-#endif
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif /* MOZILLA_GFX_COMPOSITOROGL_H */
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -4,18 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsRegion.h"
 #include "nsTArray.h"
 #include "gfxUtils.h"
 #include "mozilla/ToString.h"
 
-using namespace std;
-
 void nsRegion::AssertStateInternal() const {
   bool failed = false;
   // Verify consistent state inside the region.
   int32_t lastY = INT32_MIN;
   int32_t lowestX = INT32_MAX;
   int32_t highestX = INT32_MIN;
   for (auto iter = mBands.begin(); iter != mBands.end(); iter++) {
     const Band& band = *iter;
--- a/gfx/tests/gtest/TestRegion.cpp
+++ b/gfx/tests/gtest/TestRegion.cpp
@@ -7,17 +7,16 @@
 #include <algorithm>
 
 #include "gtest/gtest.h"
 #include "nsRegion.h"
 #include "RegionBuilder.h"
 #include "mozilla/gfx/TiledRegion.h"
 #include "mozilla/UniquePtr.h"
 
-using namespace std;
 using namespace mozilla::gfx;
 
 //#define REGION_RANDOM_STRESS_TESTS
 
 class TestLargestRegion {
  public:
   static void TestSingleRect(nsRect r) {
     nsRegion region(r);
@@ -1173,18 +1172,20 @@ struct RegionBitmap {
       }
     }
   }
 
   void dilate() {
     for (int y = 0; y < height; y++) {
       for (int x = 0; x < width; x++) {
         if (bitmap[x + y * width] == REGION_VALUE) {
-          for (int yn = max(y - 1, 0); yn <= min(y + 1, height - 1); yn++) {
-            for (int xn = max(x - 1, 0); xn <= min(x + 1, width - 1); xn++) {
+          for (int yn = std::max(y - 1, 0); yn <= std::min(y + 1, height - 1);
+               yn++) {
+            for (int xn = std::max(x - 1, 0); xn <= std::min(x + 1, width - 1);
+                 xn++) {
               if (bitmap[xn + yn * width] == 0)
                 bitmap[xn + yn * width] = DILATE_VALUE;
             }
           }
         }
       }
     }
   }
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -6,21 +6,16 @@
 
 #include "RenderCompositorOGL.h"
 
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "mozilla/StaticPrefs_gfx.h"
 #include "mozilla/widget/CompositorWidget.h"
 
-#ifdef XP_MACOSX
-#  include "GLContextCGL.h"
-#  include "mozilla/layers/NativeLayerCA.h"
-#endif
-
 namespace mozilla {
 namespace wr {
 
 /* static */
 UniquePtr<RenderCompositor> RenderCompositorOGL::Create(
     RefPtr<widget::CompositorWidget>&& aWidget) {
   RefPtr<gl::GLContext> gl;
   gl = gl::GLContextProvider::CreateForCompositorWidget(
@@ -55,84 +50,45 @@ RenderCompositorOGL::~RenderCompositorOG
   if (mPreviousFrameDoneSync) {
     mGL->fDeleteSync(mPreviousFrameDoneSync);
   }
   if (mThisFrameDoneSync) {
     mGL->fDeleteSync(mThisFrameDoneSync);
   }
 }
 
-#ifdef XP_MACOSX
-class SurfaceRegistryWrapperAroundGLContextCGL
-    : public layers::IOSurfaceRegistry {
- public:
-  explicit SurfaceRegistryWrapperAroundGLContextCGL(gl::GLContextCGL* aContext)
-      : mContext(aContext) {}
-  void RegisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) override {
-    mContext->RegisterIOSurface(aSurface.get());
-  }
-  void UnregisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) override {
-    mContext->UnregisterIOSurface(aSurface.get());
-  }
-  RefPtr<gl::GLContextCGL> mContext;
-};
-#endif
-
 bool RenderCompositorOGL::BeginFrame(layers::NativeLayer* aNativeLayer) {
   if (!mGL->MakeCurrent()) {
     gfxCriticalNote << "Failed to make render context current, can't draw.";
     return false;
   }
 
   if (aNativeLayer) {
-#ifdef XP_MACOSX
-    layers::NativeLayerCA* nativeLayer = aNativeLayer->AsNativeLayerCA();
-    MOZ_RELEASE_ASSERT(nativeLayer, "Unexpected native layer type");
-    nativeLayer->SetSurfaceIsFlipped(true);
-    auto glContextCGL = gl::GLContextCGL::Cast(mGL);
-    MOZ_RELEASE_ASSERT(glContextCGL, "Unexpected GLContext type");
-    RefPtr<layers::IOSurfaceRegistry> currentRegistry =
-        nativeLayer->GetSurfaceRegistry();
-    if (!currentRegistry) {
-      nativeLayer->SetSurfaceRegistry(
-          MakeAndAddRef<SurfaceRegistryWrapperAroundGLContextCGL>(
-              glContextCGL));
-    } else {
-      MOZ_RELEASE_ASSERT(static_cast<SurfaceRegistryWrapperAroundGLContextCGL*>(
-                             currentRegistry.get())
-                             ->mContext == glContextCGL);
-    }
-    CFTypeRefPtr<IOSurfaceRef> surf = nativeLayer->NextSurface();
-    if (!surf) {
+    aNativeLayer->SetSurfaceIsFlipped(true);
+    aNativeLayer->SetGLContext(mGL);
+    Maybe<GLuint> fbo = aNativeLayer->NextSurfaceAsFramebuffer(true);
+    if (!fbo) {
       return false;
     }
-    glContextCGL->UseRegisteredIOSurfaceForDefaultFramebuffer(surf.get());
+    mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, *fbo);
     mCurrentNativeLayer = aNativeLayer;
-#else
-    MOZ_CRASH("Unexpected native layer on this platform");
-#endif
+  } else {
+    mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGL->GetDefaultFramebuffer());
   }
 
-  mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGL->GetDefaultFramebuffer());
   return true;
 }
 
 void RenderCompositorOGL::EndFrame() {
   InsertFrameDoneSync();
   mGL->SwapBuffers();
 
   if (mCurrentNativeLayer) {
-#ifdef XP_MACOSX
-    layers::NativeLayerCA* nativeLayer = mCurrentNativeLayer->AsNativeLayerCA();
-    MOZ_RELEASE_ASSERT(nativeLayer, "Unexpected native layer type");
-    nativeLayer->NotifySurfaceReady();
+    mCurrentNativeLayer->NotifySurfaceReady();
     mCurrentNativeLayer = nullptr;
-#else
-    MOZ_CRASH("Unexpected native layer on this platform");
-#endif
   }
 }
 
 void RenderCompositorOGL::InsertFrameDoneSync() {
 #ifdef XP_MACOSX
   // Only do this on macOS.
   // On other platforms, SwapBuffers automatically applies back-pressure.
   if (StaticPrefs::gfx_core_animation_enabled_AtStartup()) {
--- a/layout/tools/layout-debug/ui/content/layoutdebug.js
+++ b/layout/tools/layout-debug/ui/content/layoutdebug.js
@@ -285,16 +285,23 @@ function OnLDBLoad() {
         );
       }
       Services.profiler.StartProfiler(
         1 << 20,
         1,
         ["default"],
         ["GeckoMain", "Compositor", "Renderer", "RenderBackend", "StyleThread"]
       );
+      if (gArgs.url) {
+        // Switch to the right kind of content process, and wait a bit so that
+        // the profiler has had a chance to attach to it.
+        updateBrowserRemotenessByURL(gArgs.url);
+        setTimeout(() => loadURI(gArgs.url), 3000);
+        return;
+      }
     } else {
       dump("Cannot profile Layout Debugger; profiler was not compiled in.\n");
     }
   }
 
   if (gArgs.url) {
     loadURI(gArgs.url);
   }
--- a/testing/web-platform/meta/service-workers/service-worker/skip-waiting-using-registration.https.html.ini
+++ b/testing/web-platform/meta/service-workers/service-worker/skip-waiting-using-registration.https.html.ini
@@ -1,7 +1,11 @@
 [skip-waiting-using-registration.https.html]
   disabled:
     if (os == "win") and (version == "6.1.7601"): https://bugzilla.mozilla.org/show_bug.cgi?id=1425175
-    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1425175
+    if (os == "linux"): https://bugzilla.mozilla.org/show_bug.cgi?id=1425175
   expected:
-    if os == "win": OK
-    [OK, FAIL]
+    if (os == "android"): [OK, FAIL]
+    if (os == "mac"): [OK, FAIL]
+  [Test skipWaiting while a client is using the registration]
+     expected:
+       if (os == "mac"): [PASS, FAIL]
+       if (os == "android"): [PASS, FAIL]
--- a/testing/web-platform/meta/webrtc/RTCPeerConnection-iceConnectionState.https.html.ini
+++ b/testing/web-platform/meta/webrtc/RTCPeerConnection-iceConnectionState.https.html.ini
@@ -12,12 +12,8 @@
   [iceConnectionState changes at the right time, with bundle policy max-compat]
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1307996
     expected: FAIL
 
   [iceConnectionState changes at the right time, with bundle policy balanced]
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1307996
     expected: FAIL
 
-  [Responder ICE connection state behaves as expected]
-    expected:
-      if (os == "mac") and debug: ["PASS", "FAIL"]
-
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-iceConnectionState.https.html
@@ -354,13 +354,14 @@ promise_test(async t => {
   await pc2.setRemoteDescription(offer);
   await pc1.setLocalDescription(offer);
   const answer = await pc2.createAnswer();
   await pc2.setLocalDescription(answer);
   await pc1.setRemoteDescription(answer);
   pc1.candidateBuffer.forEach(c => pc1.addIceCandidate(c));
   delete pc1.candidateBuffer;
   await listenToIceConnected(pc1);
+  await listenToIceConnected(pc2);
   assert_array_equals(pc1.iceStates, ['new', 'checking', 'connected']);
   assert_array_equals(pc2.iceStates, ['new', 'checking', 'connected']);
 }, 'Responder ICE connection state behaves as expected');
 
 </script>
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -1070,16 +1070,17 @@ devtools.main:
       - "firefox"
       - "fennec"
       - "geckoview"
     record_in_processes: ["main"]
     description: User has executed some JS in the Web Console.
     release_channel_collection: opt-out
     expiry_version: never
     extra_keys:
+      input: Indicates from which input the command was evaluated ("inline" for regular input, "multiline" for editor mode).
       lines: The number of lines contained in the command.
       session_id: The start time of the session in milliseconds since epoch (Unix Timestamp) e.g. 1396381378123.
   exit:
     objects: ["accessibility", "application", "dom", "inspector", "jsdebugger", "memory", "netmonitor", "options", "performance", "scratchpad", "storage", "styleeditor", "webconsole", "other", "fakeTool4242", "testBlankPanel", "testTool", "testtool1", "testTool1072208", "testtool2"]
     bug_numbers: [1455270]
     notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
     products:
       - "firefox"
--- a/widget/cocoa/CFTypeRefPtr.h
+++ b/widget/cocoa/CFTypeRefPtr.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CFTypeRefPtr_h
 #define CFTypeRefPtr_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DbgMacro.h"
+#include "mozilla/HashFunctions.h"
 
 // A smart pointer for CoreFoundation classes which does reference counting.
 //
 // Manual reference counting:
 //
 // UInt32 someNumber = 10;
 // CFNumberRef numberObject =
 //     CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &someNumber);
@@ -132,16 +133,28 @@ class CFTypeRefPtr {
   operator PtrT() const&& = delete;
   // Also don't allow implicit conversion of non-temporary CFTypeRefPtr.
   operator PtrT() const& = delete;
 
   // These let you null-check a pointer without calling get().
   explicit operator bool() const { return !!mRawPtr; }
 };
 
+template <class PtrT>
+inline bool operator==(const CFTypeRefPtr<PtrT>& aLhs,
+                       const CFTypeRefPtr<PtrT>& aRhs) {
+  return aLhs.get() == aRhs.get();
+}
+
+template <class PtrT>
+inline bool operator!=(const CFTypeRefPtr<PtrT>& aLhs,
+                       const CFTypeRefPtr<PtrT>& aRhs) {
+  return !(aLhs == aRhs);
+}
+
 // Comparing an |CFTypeRefPtr| to |nullptr|
 
 template <class PtrT>
 inline bool operator==(const CFTypeRefPtr<PtrT>& aLhs, decltype(nullptr)) {
   return aLhs.get() == nullptr;
 }
 
 template <class PtrT>
@@ -161,9 +174,21 @@ inline bool operator!=(decltype(nullptr)
 
 // MOZ_DBG support
 
 template <class PtrT>
 std::ostream& operator<<(std::ostream& aOut, const CFTypeRefPtr<PtrT>& aObj) {
   return mozilla::DebugValue(aOut, aObj.get());
 }
 
+// std::hash support (e.g. for unordered_map)
+namespace std {
+template <class PtrT>
+struct hash<CFTypeRefPtr<PtrT>> {
+  typedef CFTypeRefPtr<PtrT> argument_type;
+  typedef std::size_t result_type;
+  result_type operator()(argument_type const& aPtr) const {
+    return mozilla::HashGeneric(reinterpret_cast<uintptr_t>(aPtr.get()));
+  }
+};
+}  // namespace std
+
 #endif /* CFTypeRefPtr_h */