Merge autoland to mozilla-central. a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Sat, 30 Mar 2019 23:43:56 +0200
changeset 466986 eab754e02657af34096c2a5a79c208d393a72830
parent 466923 9d74f5279e4f223d07fdfddbbe2340f39c6f7a53 (current diff)
parent 466985 801b729bae25ad6bd45bbb979e5839cc822ec7b3 (diff)
child 466987 040b3a4dc232ee725c756dc054bc23f1baae26d2
child 466995 9955b89cf73b5d755a9a309a9867a33f215b096e
push id112610
push usernbeleuzu@mozilla.com
push dateSat, 30 Mar 2019 21:47:41 +0000
treeherdermozilla-inbound@040b3a4dc232 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
eab754e02657 / 68.0a1 / 20190330214418 / files
nightly linux64
eab754e02657 / 68.0a1 / 20190330214418 / files
nightly mac
eab754e02657 / 68.0a1 / 20190330214418 / files
nightly win32
eab754e02657 / 68.0a1 / 20190330214418 / files
nightly win64
eab754e02657 / 68.0a1 / 20190330214418 / 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
layout/base/nsCSSFrameConstructor.cpp
layout/tables/nsTableWrapperFrame.cpp
--- a/accessible/tests/mochitest/events/docload/test_docload_embedded.html
+++ b/accessible/tests/mochitest/events/docload/test_docload_embedded.html
@@ -15,17 +15,17 @@
           src="../../role.js"></script>
   <script type="application/javascript"
           src="../../events.js"></script>
 
   <script type="application/javascript">
     // //////////////////////////////////////////////////////////////////////////
     // Invokers
 
-    function changeIframeSrc(aIdentifier, aURL) {
+    function changeIframeSrc(aIdentifier, aURL, aTitle) {
       this.DOMNode = getNode(aIdentifier);
 
       function getIframeDoc() {
         return getAccessible(getNode(aIdentifier).contentDocument);
       }
 
       this.eventSeq = [
         new invokerChecker(EVENT_REORDER, getAccessible(this.DOMNode)),
@@ -35,31 +35,31 @@
       this.invoke = () => (this.DOMNode.src = aURL);
 
       this.finalCheck = () =>
         testAccessibleTree(this.DOMNode, {
           role: ROLE_INTERNAL_FRAME,
           children: [
             {
               role: ROLE_DOCUMENT,
-              name: aURL == "about:license" ? "Licenses" : aURL,
+              name: aTitle,
             },
           ],
         });
 
       this.getID = () => `change iframe src on ${aURL}`;
     }
 
     // //////////////////////////////////////////////////////////////////////////
     // Do tests
 
     function doTests() {
       const gQueue = new eventQueue();
-      gQueue.push(new changeIframeSrc("iframe", "about:license"));
-      gQueue.push(new changeIframeSrc("iframe", "about:buildconfig"));
+      gQueue.push(new changeIframeSrc("iframe", "about:license", "Licenses"));
+      gQueue.push(new changeIframeSrc("iframe", "about:buildconfig", "Build Configuration"));
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
--- a/devtools/client/locales/en-US/webconsole.properties
+++ b/devtools/client/locales/en-US/webconsole.properties
@@ -339,8 +339,13 @@ webconsole.reverseSearch.result.nextButt
 # would return "Invoke getter y to retrieve the property list?".
 # Parameters: %S is the name of the getter.
 webconsole.confirmDialog.getter.label=Invoke getter %S to retrieve the property list?
 
 # LOCALIZATION NOTE (webconsole.confirmDialog.getter.invokeButtonLabel)
 # Label used for the confirm button in the "invoke getter" dialog that appears in the
 # console when a user tries to autocomplete a property with a getter.
 webconsole.confirmDialog.getter.invokeButtonLabel=Invoke
+
+# LOCALIZATION NOTE (webconsole.group.contentBlocked)
+# Label used as the group header in the console output when content blocking is enabled
+# and that we have several warning messages about resources being blocked.
+webconsole.group.contentBlocked=Content blocked messages
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -155,16 +155,20 @@ a {
 
 .message > .indent {
   flex: none;
   display: inline-block;
   margin-inline-start: 12px;
   border-inline-end: solid 1px var(--console-output-indent-border-color);
 }
 
+.message > .indent.warning-indent {
+  border-inline-end-color: var(--warning-color);
+}
+
 .message > .indent[data-indent="0"] {
   display: none;
 }
 
 /* Center first level indent within the left gutter */
 .message:not(.startGroup):not(.startGroupCollapsed) > .indent[data-indent="1"] {
   margin-inline-start: calc(1px + var(--console-icon-horizontal-offset));
   margin-inline-end: calc(11px - var(--console-icon-horizontal-offset));
@@ -244,33 +248,44 @@ a {
   margin: var(--console-output-vertical-padding) 0;
 }
 
 .message-body-wrapper .table-widget-body {
   overflow: visible;
 }
 
 /* The bubble that shows the number of times a message is repeated */
-.message-repeats {
+.message-repeats,
+.warning-group-badge {
   flex-shrink: 0;
   margin: 2px 5px 0 5px;
   padding: 0 6px;
   height: 1.25em;
-  color: white;
-  background-color: var(--repeat-bubble-background-color);
   border-radius: 40px;
   font: message-box;
   font-size: 0.8em;
   font-weight: normal;
 }
 
+.message-repeats {
+  display: inline-block;
+  color: white;
+  background-color: var(--repeat-bubble-background-color);
+}
+
 .message-repeats[value="1"] {
   display: none;
 }
 
+.warning-group-badge {
+  display: inline-block;
+  color: var(--warning-background-color);
+  background-color: var(--warning-color);
+}
+
 .message-location {
   max-width: 40vw;
   flex-shrink: 0;
   color: var(--frame-link-source);
   margin-left: 1ch;
   /* Makes the file name truncated (and ellipsis shown) on the left side */
   direction: rtl;
   white-space: nowrap;
--- a/devtools/client/webconsole/components/ConsoleOutput.js
+++ b/devtools/client/webconsole/components/ConsoleOutput.js
@@ -11,16 +11,18 @@ const {initialize} = require("devtools/c
 const {
   getAllMessagesById,
   getAllMessagesUiById,
   getAllMessagesTableDataById,
   getAllNetworkMessagesUpdateById,
   getVisibleMessages,
   getPausedExecutionPoint,
   getAllRepeatById,
+  getAllWarningGroupsById,
+  isMessageInWarningGroup,
 } = require("devtools/client/webconsole/selectors/messages");
 
 loader.lazyRequireGetter(this, "PropTypes", "devtools/client/shared/vendor/react-prop-types");
 loader.lazyRequireGetter(this, "sortBy", "devtools/client/shared/vendor/lodash", true);
 loader.lazyRequireGetter(this, "MessageContainer", "devtools/client/webconsole/components/MessageContainer", true);
 
 const {
   MESSAGE_TYPE,
@@ -53,16 +55,18 @@ class ConsoleOutput extends Component {
         attachRefToWebConsoleUI: PropTypes.func.isRequired,
         openContextMenu: PropTypes.func.isRequired,
         sourceMapService: PropTypes.object,
       }),
       dispatch: PropTypes.func.isRequired,
       timestampsVisible: PropTypes.bool,
       messagesTableData: PropTypes.object.isRequired,
       messagesRepeat: PropTypes.object.isRequired,
+      warningGroups: PropTypes.object.isRequired,
+      isInWarningGroup: PropTypes.isRequired,
       networkMessagesUpdate: PropTypes.object.isRequired,
       visibleMessages: PropTypes.array.isRequired,
       networkMessageActiveTabId: PropTypes.string.isRequired,
       onFirstMeaningfulPaint: PropTypes.func.isRequired,
       pausedExecutionPoint: PropTypes.any,
     };
   }
 
@@ -166,16 +170,18 @@ class ConsoleOutput extends Component {
   render() {
     let {
       dispatch,
       visibleMessages,
       messages,
       messagesUi,
       messagesTableData,
       messagesRepeat,
+      warningGroups,
+      isInWarningGroup,
       networkMessagesUpdate,
       networkMessageActiveTabId,
       serviceContainer,
       timestampsVisible,
       initialized,
       pausedExecutionPoint,
     } = this.props;
 
@@ -195,16 +201,20 @@ class ConsoleOutput extends Component {
         dispatch,
         key: messageId,
         messageId,
         serviceContainer,
         open: messagesUi.includes(messageId),
         tableData: messagesTableData.get(messageId),
         timestampsVisible,
         repeat: messagesRepeat[messageId],
+        badge: warningGroups.has(messageId) ? warningGroups.get(messageId).length : null,
+        inWarningGroup: isInWarningGroup
+          ? isInWarningGroup(messages.get(messageId))
+          : false,
         networkMessageUpdate: networkMessagesUpdate[messageId],
         networkMessageActiveTabId,
         pausedExecutionPoint,
         getMessage: () => messages.get(messageId),
         isPaused: !!pausedMessage && pausedMessage.id == messageId,
         maybeScrollToBottom: this.maybeScrollToBottom,
       }));
 
@@ -239,15 +249,19 @@ function mapStateToProps(state, props) {
   return {
     initialized: state.ui.initialized,
     pausedExecutionPoint: getPausedExecutionPoint(state),
     messages: getAllMessagesById(state),
     visibleMessages: getVisibleMessages(state),
     messagesUi: getAllMessagesUiById(state),
     messagesTableData: getAllMessagesTableDataById(state),
     messagesRepeat: getAllRepeatById(state),
+    warningGroups: getAllWarningGroupsById(state),
+    isInWarningGroup: state.prefs.groupWarnings
+      ? message => isMessageInWarningGroup(state, message)
+      : null,
     networkMessagesUpdate: getAllNetworkMessagesUpdateById(state),
     timestampsVisible: state.ui.timestampsVisible,
     networkMessageActiveTabId: state.ui.networkMessageActiveTabId,
   };
 }
 
 module.exports = connect(mapStateToProps)(ConsoleOutput);
--- a/devtools/client/webconsole/components/Message.js
+++ b/devtools/client/webconsole/components/Message.js
@@ -26,16 +26,17 @@ class Message extends Component {
     return {
       open: PropTypes.bool,
       collapsible: PropTypes.bool,
       collapseTitle: PropTypes.string,
       source: PropTypes.string.isRequired,
       type: PropTypes.string.isRequired,
       level: PropTypes.string.isRequired,
       indent: PropTypes.number.isRequired,
+      inWarningGroup: PropTypes.bool,
       topLevelClasses: PropTypes.array.isRequired,
       messageBody: PropTypes.any.isRequired,
       repeat: PropTypes.any,
       frame: PropTypes.any,
       attachment: PropTypes.any,
       stacktrace: PropTypes.any,
       messageId: PropTypes.string,
       executionPoint: PropTypes.shape({
@@ -126,17 +127,27 @@ class Message extends Component {
   onMouseEvent(ev) {
     const {messageId, serviceContainer, executionPoint} = this.props;
     if (serviceContainer.canRewind() && executionPoint) {
       serviceContainer.onMessageHover(ev.type, messageId);
     }
   }
 
   renderIcon() {
-    const { level, messageId, executionPoint, serviceContainer } = this.props;
+    const {
+      level,
+      messageId,
+      executionPoint,
+      serviceContainer,
+      inWarningGroup,
+    } = this.props;
+
+    if (inWarningGroup) {
+      return undefined;
+    }
 
     return MessageIcon({
       level,
       onRewindClick: (serviceContainer.canRewind() && executionPoint)
         ? () => serviceContainer.jumpToExecutionPoint(executionPoint, messageId)
         : null,
     });
   }
@@ -146,16 +157,17 @@ class Message extends Component {
       open,
       collapsible,
       collapseTitle,
       source,
       type,
       isPaused,
       level,
       indent,
+      inWarningGroup,
       topLevelClasses,
       messageBody,
       frame,
       stacktrace,
       serviceContainer,
       exceptionDocURL,
       timeStamp = Date.now(),
       timestampsVisible,
@@ -296,29 +308,32 @@ class Message extends Component {
       ...mouseEvents,
       ref: node => {
         this.messageNode = node;
       },
       "data-message-id": messageId,
       "aria-live": type === MESSAGE_TYPE.COMMAND ? "off" : "polite",
     },
       timestampEl,
-      MessageIndent({indent}),
+      MessageIndent({
+        indent,
+        inWarningGroup,
+      }),
       icon,
       collapse,
       dom.span({ className: "message-body-wrapper" },
         dom.span({
           className: "message-flex-body",
           onClick: collapsible ? this.toggleMessage : undefined,
         },
           // Add whitespaces for formatting when copying to the clipboard.
           timestampEl ? " " : null,
           dom.span({ className: "message-body devtools-monospace" },
             ...bodyElements,
-            learnMore
+            learnMore,
           ),
           repeat ? " " : null,
           repeat,
           " ", location
         ),
         attachment,
         ...notesNodes
       ),
--- a/devtools/client/webconsole/components/MessageContainer.js
+++ b/devtools/client/webconsole/components/MessageContainer.js
@@ -4,40 +4,44 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // React & Redux
 const { Component } = require("devtools/client/shared/vendor/react");
 loader.lazyRequireGetter(this, "PropTypes", "devtools/client/shared/vendor/react-prop-types");
+loader.lazyRequireGetter(this, "isWarningGroup", "devtools/client/webconsole/utils/messages", true);
 
 const {
   MESSAGE_SOURCE,
   MESSAGE_TYPE,
 } = require("devtools/client/webconsole/constants");
 
 const componentMap = new Map([
   ["ConsoleApiCall", require("./message-types/ConsoleApiCall")],
   ["ConsoleCommand", require("./message-types/ConsoleCommand")],
   ["DefaultRenderer", require("./message-types/DefaultRenderer")],
   ["EvaluationResult", require("./message-types/EvaluationResult")],
   ["NetworkEventMessage", require("./message-types/NetworkEventMessage")],
   ["PageError", require("./message-types/PageError")],
+  ["WarningGroup", require("./message-types/WarningGroup")],
 ]);
 
 class MessageContainer extends Component {
   static get propTypes() {
     return {
       messageId: PropTypes.string.isRequired,
       open: PropTypes.bool.isRequired,
       serviceContainer: PropTypes.object.isRequired,
       tableData: PropTypes.object,
       timestampsVisible: PropTypes.bool.isRequired,
       repeat: PropTypes.number,
+      badge: PropTypes.number,
+      indent: PropTypes.number,
       networkMessageUpdate: PropTypes.object,
       getMessage: PropTypes.func.isRequired,
       isPaused: PropTypes.bool.isRequired,
       pausedExecutionPoint: PropTypes.any,
     };
   }
 
   static get defaultProps() {
@@ -52,18 +56,20 @@ class MessageContainer extends Component
     const tableDataChanged = this.props.tableData !== nextProps.tableData;
     const timestampVisibleChanged =
       this.props.timestampsVisible !== nextProps.timestampsVisible;
     const networkMessageUpdateChanged =
       this.props.networkMessageUpdate !== nextProps.networkMessageUpdate;
     const pausedChanged = this.props.isPaused !== nextProps.isPaused;
     const executionPointChanged =
       this.props.pausedExecutionPoint !== nextProps.pausedExecutionPoint;
+    const badgeChanged = this.props.badge !== nextProps.badge;
 
     return repeatChanged
+      || badgeChanged
       || openChanged
       || tableDataChanged
       || timestampVisibleChanged
       || networkMessageUpdateChanged
       || pausedChanged
       || executionPointChanged;
   }
 
@@ -96,16 +102,21 @@ function getMessageComponent(message) {
         // Chrome doesn't distinguish between page errors and log messages. We
         // may want to remove the PageError component and just handle errors
         // with ConsoleApiCall.
         case MESSAGE_TYPE.LOG:
           return componentMap.get("PageError");
         default:
           return componentMap.get("DefaultRenderer");
       }
+    case MESSAGE_SOURCE.CONSOLE_FRONTEND:
+      if (isWarningGroup(message)) {
+        return componentMap.get("WarningGroup");
+      }
+      break;
   }
 
   return componentMap.get("DefaultRenderer");
 }
 
 module.exports.MessageContainer = MessageContainer;
 
 // Exported so we can test it with unit tests.
--- a/devtools/client/webconsole/components/MessageIndent.js
+++ b/devtools/client/webconsole/components/MessageIndent.js
@@ -5,31 +5,36 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
 const INDENT_WIDTH = 12;
 
-// Store common indents so they can be used without recreating the element
-// during render.
+// Store common indents so they can be used without recreating the element during render.
 const CONSTANT_INDENTS = [getIndentElement(0), getIndentElement(1)];
+const IN_WARNING_GROUP_INDENT = getIndentElement(1, "warning-indent");
 
-function getIndentElement(indent) {
+function getIndentElement(indent, className) {
   return dom.span({
     "data-indent": indent,
-    className: "indent",
+    className: `indent${className ? " " + className : ""}`,
     style: {
       "width": indent * INDENT_WIDTH,
     },
   });
 }
 
 function MessageIndent(props) {
-  const { indent } = props;
+  const { indent, inWarningGroup } = props;
+
+  if (inWarningGroup) {
+    return IN_WARNING_GROUP_INDENT;
+  }
+
   return CONSTANT_INDENTS[indent] || getIndentElement(indent);
 }
 
 module.exports.MessageIndent = MessageIndent;
 
 // Exported so we can test it with unit tests.
 module.exports.INDENT_WIDTH = INDENT_WIDTH;
--- a/devtools/client/webconsole/components/message-types/PageError.js
+++ b/devtools/client/webconsole/components/message-types/PageError.js
@@ -14,37 +14,38 @@ const Message = createFactory(require("d
 PageError.displayName = "PageError";
 
 PageError.propTypes = {
   message: PropTypes.object.isRequired,
   open: PropTypes.bool,
   timestampsVisible: PropTypes.bool.isRequired,
   serviceContainer: PropTypes.object,
   maybeScrollToBottom: PropTypes.func,
+  inWarningGroup: PropTypes.bool.isRequired,
 };
 
 PageError.defaultProps = {
   open: false,
 };
 
 function PageError(props) {
   const {
     dispatch,
     message,
     open,
     repeat,
     serviceContainer,
     timestampsVisible,
     isPaused,
     maybeScrollToBottom,
+    inWarningGroup,
   } = props;
   const {
     id: messageId,
     executionPoint,
-    indent,
     source,
     type,
     level,
     messageText,
     stacktrace,
     frame,
     exceptionDocURL,
     timeStamp,
@@ -64,17 +65,18 @@ function PageError(props) {
     executionPoint,
     isPaused,
     open,
     collapsible: Array.isArray(stacktrace),
     source,
     type,
     level,
     topLevelClasses: [],
-    indent,
+    indent: message.indent,
+    inWarningGroup,
     messageBody,
     repeat,
     frame,
     stacktrace,
     serviceContainer,
     exceptionDocURL,
     timeStamp,
     notes,
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/components/message-types/WarningGroup.js
@@ -0,0 +1,73 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// React & Redux
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const Message = createFactory(require("devtools/client/webconsole/components/Message"));
+
+WarningGroup.displayName = "WarningGroup";
+
+WarningGroup.propTypes = {
+  dispatch: PropTypes.func.isRequired,
+  message: PropTypes.object.isRequired,
+  timestampsVisible: PropTypes.bool.isRequired,
+  serviceContainer: PropTypes.object,
+  badge: PropTypes.number.isRequired,
+};
+
+function WarningGroup(props) {
+  const {
+    dispatch,
+    message,
+    serviceContainer,
+    timestampsVisible,
+    badge,
+    open,
+  } = props;
+
+  const {
+    source,
+    type,
+    level,
+    id: messageId,
+    indent,
+    timeStamp,
+  } = message;
+
+  const messageBody = [
+    message.messageText,
+    " ",
+    dom.span({
+      className: "warning-group-badge",
+      title: `${badge} messages`,
+    }, badge),
+  ];
+  const topLevelClasses = ["cm-s-mozilla"];
+
+  return Message({
+    badge,
+    collapsible: true,
+    dispatch,
+    indent,
+    level,
+    messageBody,
+    messageId,
+    open,
+    serviceContainer,
+    source,
+    timeStamp,
+    timestampsVisible,
+    topLevelClasses,
+    type,
+  });
+}
+
+module.exports = WarningGroup;
--- a/devtools/client/webconsole/components/message-types/moz.build
+++ b/devtools/client/webconsole/components/message-types/moz.build
@@ -5,9 +5,10 @@
 
 DevToolsModules(
     'ConsoleApiCall.js',
     'ConsoleCommand.js',
     'DefaultRenderer.js',
     'EvaluationResult.js',
     'NetworkEventMessage.js',
     'PageError.js',
+    'WarningGroup.js',
 )
--- a/devtools/client/webconsole/constants.js
+++ b/devtools/client/webconsole/constants.js
@@ -68,16 +68,17 @@ const prefs = {
       // Is editor mode enabled.
       EDITOR: "devtools.webconsole.input.editor",
     },
     FEATURES: {
       // We use the same pref to enable the sidebar on webconsole and browser console.
       SIDEBAR_TOGGLE: "devtools.webconsole.sidebarToggle",
       JSTERM_CODE_MIRROR: "devtools.webconsole.jsterm.codeMirror",
       AUTOCOMPLETE: "devtools.webconsole.input.autocomplete",
+      GROUP_WARNINGS: "devtools.webconsole.groupWarningMessages",
     },
   },
 };
 
 const FILTERS = {
   CSS: "css",
   DEBUG: "debug",
   ERROR: "error",
@@ -106,32 +107,38 @@ const DEFAULT_FILTERS = Object.keys(DEFA
 
 const chromeRDPEnums = {
   MESSAGE_SOURCE: {
     XML: "xml",
     CSS: "css",
     JAVASCRIPT: "javascript",
     NETWORK: "network",
     CONSOLE_API: "console-api",
+    // Messages emitted by the console frontend itself (i.e. similar messages grouping
+    // header).
+    CONSOLE_FRONTEND: "console-frontend",
     STORAGE: "storage",
     APPCACHE: "appcache",
     RENDERING: "rendering",
     SECURITY: "security",
     OTHER: "other",
     DEPRECATION: "deprecation",
   },
   MESSAGE_TYPE: {
     LOG: "log",
     DIR: "dir",
     TABLE: "table",
     TRACE: "trace",
     CLEAR: "clear",
     START_GROUP: "startGroup",
     START_GROUP_COLLAPSED: "startGroupCollapsed",
     END_GROUP: "endGroup",
+    CONTENT_BLOCKING_GROUP: "contentBlockingWarningGroup",
+    CORS_GROUP: "CORSWarningGroup",
+    CSP_GROUP: "CSPWarningGroup",
     ASSERT: "assert",
     DEBUG: "debug",
     PROFILE: "profile",
     PROFILE_END: "profileEnd",
     // Undocumented in Chrome RDP, but is used for evaluation results.
     RESULT: "result",
     // Undocumented in Chrome RDP, but is used for input.
     COMMAND: "command",
--- a/devtools/client/webconsole/reducers/messages.js
+++ b/devtools/client/webconsole/reducers/messages.js
@@ -16,16 +16,20 @@ const {
   FILTERS,
   MESSAGE_TYPE,
   MESSAGE_SOURCE,
 } = constants;
 
 loader.lazyRequireGetter(this, "getGripPreviewItems", "devtools/client/shared/components/reps/reps", true);
 loader.lazyRequireGetter(this, "getUnicodeUrlPath", "devtools/client/shared/unicode-url", true);
 loader.lazyRequireGetter(this, "getSourceNames", "devtools/client/shared/source-utils", true);
+loader.lazyRequireGetter(this, "createWarningGroupMessage", "devtools/client/webconsole/utils/messages", true);
+loader.lazyRequireGetter(this, "isWarningGroup", "devtools/client/webconsole/utils/messages", true);
+loader.lazyRequireGetter(this, "getWarningGroupType", "devtools/client/webconsole/utils/messages", true);
+loader.lazyRequireGetter(this, "getParentWarningGroupMessageId", "devtools/client/webconsole/utils/messages", true);
 
 const {
   UPDATE_REQUEST,
 } = require("devtools/client/netmonitor/src/constants");
 
 const {
   processNetworkUpdates,
 } = require("devtools/client/netmonitor/src/utils/request-utils");
@@ -41,19 +45,22 @@ const MessageState = overrides => Object
   filteredMessagesCount: getDefaultFiltersCounter(),
   // List of the message ids which are opened.
   messagesUiById: [],
   // Map of the form {messageId : tableData}, which represent the data passed
   // as an argument in console.table calls.
   messagesTableDataById: new Map(),
   // Map of the form {groupMessageId : groupArray},
   // where groupArray is the list of of all the parent groups' ids of the groupMessageId.
+  // This handles console API groups.
   groupsById: new Map(),
-  // Message id of the current group (no corresponding console.groupEnd yet).
+  // Message id of the current console API group (no corresponding console.groupEnd yet).
   currentGroup: null,
+  // This group handles "warning groups" (Content Blocking, CORS, CSP, …)
+  warningGroupsById: new Map(),
   // Array of removed actors (i.e. actors logged in removed messages) we keep track of
   // in order to properly release them.
   // This array is not supposed to be consumed by any UI component.
   removedActors: [],
   // Map of the form {messageId : numberOfRepeat}
   repeatById: {},
   // Map of the form {messageId : networkInformation}
   // `networkInformation` holds request, response, totalTime, ...
@@ -73,20 +80,30 @@ function cloneState(state) {
     messagesTableDataById: new Map(state.messagesTableDataById),
     groupsById: new Map(state.groupsById),
     currentGroup: state.currentGroup,
     removedActors: [...state.removedActors],
     repeatById: {...state.repeatById},
     networkMessagesUpdateById: {...state.networkMessagesUpdateById},
     removedLogpointIds: new Set(state.removedLogpointIds),
     pausedExecutionPoint: state.pausedExecutionPoint,
+    warningGroupsById: new Map(state.warningGroupsById),
   };
 }
 
-function addMessage(state, filtersState, prefsState, newMessage) {
+/**
+ * Add a console message to the state.
+ *
+ * @param {ConsoleMessage} newMessage: The message to add to the state.
+ * @param {MessageState} state: The message state ( = managed by this reducer).
+ * @param {FiltersState} filtersState: The filters state.
+ * @param {PrefsState} prefsState: The preferences state.
+ * @returns {MessageState} a new messages state.
+ */
+function addMessage(newMessage, state, filtersState, prefsState) {
   const {
     messagesById,
     replayProgressMessages,
     groupsById,
     currentGroup,
     repeatById,
   } = state;
 
@@ -116,17 +133,17 @@ function addMessage(state, filtersState,
 
   if (newMessage.type === constants.MESSAGE_TYPE.END_GROUP) {
     // Compute the new current group.
     state.currentGroup = getNewCurrentGroup(currentGroup, groupsById);
     return state;
   }
 
   if (newMessage.allowRepeating && messagesById.size > 0) {
-    const lastMessage = [...messagesById.values()][messagesById.size - 1];
+    const lastMessage = messagesById.get(getLastMessageId(state));
 
     if (
       lastMessage.repeatId === newMessage.repeatId
       && lastMessage.groupId === currentGroup
     ) {
       state.repeatById[lastMessage.id] = (repeatById[lastMessage.id] || 1) + 1;
       return state;
     }
@@ -134,39 +151,102 @@ function addMessage(state, filtersState,
 
   // Add the new message with a reference to the parent group.
   const parentGroups = getParentGroups(currentGroup, groupsById);
   newMessage.groupId = currentGroup;
   newMessage.indent = parentGroups.length;
 
   ensureExecutionPoint(state, newMessage);
 
+  // Check if the current message could be placed in a Warning Group.
+  // This needs to be done before setting the new message in messagesById so we have a
+  // proper message.
+  const warningGroupType = getWarningGroupType(newMessage);
+
+  // If the preference for warning grouping is true, and the new message could be in a
+  // warning group.
+  if (prefsState.groupWarnings && warningGroupType !== null) {
+    const warningGroupMessageId = getParentWarningGroupMessageId(newMessage);
+
+    // If there's no warning group for the type/innerWindowID yet
+    if (!state.messagesById.has(warningGroupMessageId)) {
+      // We create it and add it to the store.
+      const groupMessage = createWarningGroupMessage(
+        warningGroupMessageId, warningGroupType, newMessage);
+      state = addMessage(groupMessage, state, filtersState, prefsState);
+      state.warningGroupsById.set(warningGroupMessageId, []);
+    }
+
+    // We add the new message to the appropriate warningGroup.
+    state.warningGroupsById.get(warningGroupMessageId).push(newMessage.id);
+
+    // If the warningGroup message is not visible yet, but should be.
+    if (!state.visibleMessages.includes(warningGroupMessageId)
+      && getMessageVisibility(state.messagesById.get(warningGroupMessageId), {
+        messagesState: state,
+        filtersState,
+        prefsState,
+      }).visible
+    ) {
+      // Then we put it in the visibleMessages properties, at the position of the first
+      // warning message inside the warningGroup.
+      // TODO [Bug 1534927]: It should be added before the outermost console.group message
+      // a warning message could be in.
+      const index = state
+        .visibleMessages
+        .indexOf(state.warningGroupsById.get(warningGroupMessageId)[0]);
+      state.visibleMessages.splice(index, 1, warningGroupMessageId);
+    }
+  }
+
   const addedMessage = Object.freeze(newMessage);
   state.messagesById.set(newMessage.id, addedMessage);
 
   if (newMessage.type === "trace") {
     // We want the stacktrace to be open by default.
     state.messagesUiById.push(newMessage.id);
   } else if (isGroupType(newMessage.type)) {
     state.currentGroup = newMessage.id;
     state.groupsById.set(newMessage.id, parentGroups);
 
     if (newMessage.type === constants.MESSAGE_TYPE.START_GROUP) {
       // We want the group to be open by default.
       state.messagesUiById.push(newMessage.id);
     }
   }
 
-  const {
-    visible,
-    cause,
-  } = getMessageVisibility(addedMessage, state, filtersState);
+  const { visible, cause } = getMessageVisibility(addedMessage, {
+    messagesState: state,
+    filtersState,
+    prefsState,
+  });
 
   if (visible) {
-    state.visibleMessages.push(newMessage.id);
+    // If the message is part of a visible warning group, we want to add it after the last
+    // visible message of the group.
+    const warningGroupId = getParentWarningGroupMessageId(newMessage);
+    if (warningGroupId && state.visibleMessages.includes(warningGroupId)) {
+      // Defaults to the warning group message.
+      let index = state.visibleMessages.indexOf(warningGroupId);
+
+      // We loop backward through the warning group's messages to get the latest visible
+      // messages in it.
+      const messagesInWarningGroup = state.warningGroupsById.get(warningGroupId);
+      for (let i = messagesInWarningGroup.length - 1; i >= 0; i--) {
+        const idx = state.visibleMessages.indexOf(messagesInWarningGroup[i]);
+        if (idx > -1) {
+          index = idx;
+          break;
+        }
+      }
+      // Inserts the new warning message at the wanted location "in" the warning group.
+      state.visibleMessages.splice(index + 1, 0, newMessage.id);
+    } else {
+      state.visibleMessages.push(newMessage.id);
+    }
     maybeSortVisibleMessages(state);
   } else if (DEFAULT_FILTERS.includes(cause)) {
     state.filteredMessagesCount.global++;
     state.filteredMessagesCount[cause]++;
   }
 
   // Append received network-data also into networkMessagesUpdateById
   // that is responsible for collecting (lazy loaded) HTTP payload data.
@@ -217,17 +297,17 @@ function messages(state = MessageState()
         } else {
           list.unshift(message);
         }
         lastMessageRepeatId = message.repeatId;
       }
 
       newState = cloneState(state);
       list.forEach(message => {
-        newState = addMessage(newState, filtersState, prefsState, message);
+        newState = addMessage(message, newState, filtersState, prefsState);
       });
 
       return limitTopLevelMessageCount(newState, logLimit);
 
     case constants.MESSAGES_CLEAR:
       return MessageState({
         // Store all actors from removed messages. This array is used by
         // `releaseActorsEnhancer` to release all of those backend actors.
@@ -273,31 +353,37 @@ function messages(state = MessageState()
       }, removedIds);
     }
 
     case constants.MESSAGE_OPEN:
       const openState = {...state};
       openState.messagesUiById = [...messagesUiById, action.id];
       const currMessage = messagesById.get(action.id);
 
-      // If the message is a group
-      if (isGroupType(currMessage.type)) {
+      // If the message is a console.group/groupCollapsed or a warning group.
+      if (isGroupType(currMessage.type) || isWarningGroup(currMessage)) {
         // We want to make its children visible
         const messagesToShow = [...messagesById].reduce((res, [id, message]) => {
           if (
             !visibleMessages.includes(message.id)
-            && getParentGroups(message.groupId, groupsById).includes(action.id)
-            && getMessageVisibility(
-              message,
-              openState,
+            && (
+              (isWarningGroup(currMessage) && !!getWarningGroupType(message))
+              || (
+                isGroupType(currMessage.type)
+                && getParentGroups(message.groupId, groupsById).includes(action.id)
+              )
+            )
+            && getMessageVisibility(message, {
+              messagesState: openState,
               filtersState,
-              // We want to check if the message is in an open group
-              // only if it is not a direct child of the group we're opening.
-              message.groupId !== action.id
-            ).visible
+              prefsState,
+            // We want to check if the message is in an open group
+            // only if it is not a direct child of the group we're opening.
+              checkGroup: message.groupId !== action.id,
+            }).visible
           ) {
             res.push(id);
           }
           return res;
         }, []);
 
         // We can then insert the messages ids right after the one of the group.
         const insertIndex = visibleMessages.indexOf(action.id) + 1;
@@ -329,16 +415,21 @@ function messages(state = MessageState()
 
       // If the message is a group
       if (isGroupType(messagesById.get(messageId).type)) {
         // Hide all its children
         closeState.visibleMessages = visibleMessages.filter(id =>
           getParentGroups(messagesById.get(id).groupId, groupsById)
             .includes(messageId) === false
         );
+      } else if (isWarningGroup(messagesById.get(messageId))) {
+        // If the message was a warningGroup, we hide all the messages in the group.
+        const groupMessages = closeState.warningGroupsById.get(messageId);
+        closeState.visibleMessages =
+          visibleMessages.filter(id => !groupMessages.includes(id));
       }
       return closeState;
 
     case constants.MESSAGE_TABLE_RECEIVE:
       const {id, data} = action;
 
       return {
         ...state,
@@ -382,20 +473,22 @@ function messages(state = MessageState()
     case constants.FILTER_TOGGLE:
     case constants.FILTER_TEXT_SET:
     case constants.FILTERS_CLEAR:
     case constants.DEFAULT_FILTERS_RESET:
       const messagesToShow = [];
       const filtered = getDefaultFiltersCounter();
 
       messagesById.forEach((message, msgId) => {
-        const {
-          visible,
-          cause,
-        } = getMessageVisibility(message, state, filtersState);
+        const { visible, cause } = getMessageVisibility(message, {
+          messagesState: state,
+          filtersState,
+          prefsState,
+        });
+
         if (visible) {
           messagesToShow.push(msgId);
         } else if (DEFAULT_FILTERS.includes(cause)) {
           filtered.global = filtered.global + 1;
           filtered[cause] = filtered[cause] + 1;
         }
       });
 
@@ -630,28 +723,57 @@ function getToplevelMessageCount(state) 
 /**
  * Check if a message should be visible in the console output, and if not, what
  * causes it to be hidden.
  *
  * @return {Object} An object of the following form:
  *         - visible {Boolean}: true if the message should be visible
  *         - cause {String}: if visible is false, what causes the message to be hidden.
  */
-function getMessageVisibility(message, messagesState, filtersState, checkGroup = true) {
+function getMessageVisibility(message, {
+    messagesState,
+    filtersState,
+    prefsState,
+    checkGroup = true,
+}) {
   // Do not display the message if it's in closed group.
   if (
     checkGroup
     && !isInOpenedGroup(message, messagesState.groupsById, messagesState.messagesUiById)
   ) {
     return {
       visible: false,
       cause: "closedGroup",
     };
   }
 
+  // If the message is a warningGroup, check if it should be displayed.
+  if (
+    isWarningGroup(message)
+    && !shouldGroupWarningMessages(message, messagesState, prefsState)
+  ) {
+    return {
+      visible: false,
+      cause: "warningGroupHeuristicNotMet",
+    };
+  }
+
+  // Do not display the the message if it can be in a warningGroup, and the group is
+  // displayed but collapsed.
+  const warningGroupMessageId = getParentWarningGroupMessageId(message);
+  if (
+    messagesState.visibleMessages.includes(warningGroupMessageId)
+    && !messagesState.messagesUiById.includes(warningGroupMessageId)
+  ) {
+    return {
+      visible: false,
+      cause: "closedWarningGroup",
+    };
+  }
+
   // Some messages can't be filtered out (e.g. groups).
   // So, always return visible: true for those.
   if (isUnfilterable(message)) {
     return {
       visible: true,
     };
   }
 
@@ -1044,12 +1166,34 @@ function maybeSortVisibleMessages(state)
       // have an execution point.
       const countA = messageCountSinceLastExecutionPoint(state, a);
       const countB = messageCountSinceLastExecutionPoint(state, b);
       return countA > countB;
     });
   }
 }
 
+function getLastMessageId(state) {
+  return Array.from(state.messagesById.keys())[state.messagesById.size - 1];
+}
+
+/**
+ * Returns if a given type of warning message should be grouped.
+ *
+ * @param {ConsoleMessage} warningGroupMessage
+ * @param {MessageState} messagesState
+ * @param {PrefsState} prefsState
+ */
+function shouldGroupWarningMessages(warningGroupMessage, messagesState, prefsState) {
+  // Only group if the preference is ON.
+  if (!prefsState.groupWarnings) {
+    return false;
+  }
+
+  // We group warning messages if there are at least 2 messages that could go in it.
+  const warningGroup = messagesState.warningGroupsById.get(warningGroupMessage.id);
+  return warningGroup && warningGroup.length > 1;
+}
+
 exports.messages = messages;
 
 // Export for testing purpose.
 exports.ensureExecutionPoint = ensureExecutionPoint;
--- a/devtools/client/webconsole/reducers/prefs.js
+++ b/devtools/client/webconsole/reducers/prefs.js
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const PrefState = (overrides) => Object.freeze(Object.assign({
   logLimit: 1000,
   sidebarToggle: false,
   jstermCodeMirror: false,
+  groupWarnings: false,
   historyCount: 50,
 }, overrides));
 
 function prefs(state = PrefState(), action) {
   return state;
 }
 
 exports.PrefState = PrefState;
--- a/devtools/client/webconsole/selectors/messages.js
+++ b/devtools/client/webconsole/selectors/messages.js
@@ -1,15 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+loader.lazyRequireGetter(this, "getWarningGroupType", "devtools/client/webconsole/utils/messages", true);
+loader.lazyRequireGetter(this, "getParentWarningGroupMessageId", "devtools/client/webconsole/utils/messages", true);
+
 function getAllMessagesById(state) {
   return state.messages.messagesById;
 }
 
 function getMessage(state, id) {
   return getAllMessagesById(state).get(id);
 }
 
@@ -48,22 +51,36 @@ function getAllNetworkMessagesUpdateById
 function getGroupsById(state) {
   return state.messages.groupsById;
 }
 
 function getPausedExecutionPoint(state) {
   return state.messages.pausedExecutionPoint;
 }
 
+function getAllWarningGroupsById(state) {
+  return state.messages.warningGroupsById;
+}
+
+function isMessageInWarningGroup(state, message) {
+  if (!getWarningGroupType(message)) {
+    return false;
+  }
+
+  return getVisibleMessages(state).includes(getParentWarningGroupMessageId(message));
+}
+
 module.exports = {
   getAllGroupsById,
+  getAllWarningGroupsById,
   getAllMessagesById,
   getAllMessagesTableDataById,
   getAllMessagesUiById,
   getAllNetworkMessagesUpdateById,
   getAllRepeatById,
   getCurrentGroup,
   getFilteredMessagesCount,
   getGroupsById,
   getMessage,
   getVisibleMessages,
   getPausedExecutionPoint,
+  isMessageInWarningGroup,
 };
--- a/devtools/client/webconsole/store.js
+++ b/devtools/client/webconsole/store.js
@@ -45,25 +45,27 @@ function configureStore(webConsoleUI, op
     getIntPref,
   } = prefsService;
 
   const logLimit = options.logLimit
     || Math.max(getIntPref("devtools.hud.loglimit"), 1);
   const sidebarToggle = getBoolPref(PREFS.FEATURES.SIDEBAR_TOGGLE);
   const jstermCodeMirror = getBoolPref(PREFS.FEATURES.JSTERM_CODE_MIRROR);
   const autocomplete = getBoolPref(PREFS.FEATURES.AUTOCOMPLETE);
+  const groupWarnings = getBoolPref(PREFS.FEATURES.GROUP_WARNINGS);
   const historyCount = getIntPref(PREFS.UI.INPUT_HISTORY_COUNT);
 
   const initialState = {
     prefs: PrefState({
       logLimit,
       sidebarToggle,
       jstermCodeMirror,
       autocomplete,
       historyCount,
+      groupWarnings,
     }),
     filters: FilterState({
       error: getBoolPref(PREFS.FILTER.ERROR),
       warn: getBoolPref(PREFS.FILTER.WARN),
       info: getBoolPref(PREFS.FILTER.INFO),
       debug: getBoolPref(PREFS.FILTER.DEBUG),
       log: getBoolPref(PREFS.FILTER.LOG),
       css: getBoolPref(PREFS.FILTER.CSS),
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/components/warning-group.test.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test utils.
+const expect = require("expect");
+const { render } = require("enzyme");
+
+// Components under test.
+const WarningGroup = require("devtools/client/webconsole/components/message-types/WarningGroup");
+const { MESSAGE_SOURCE, MESSAGE_TYPE } = require("devtools/client/webconsole/constants");
+const { ConsoleMessage } = require("devtools/client/webconsole/types");
+const { createWarningGroupMessage } = require("devtools/client/webconsole/utils/messages");
+
+// Test fakes.
+const { stubPreparedMessages } = require("devtools/client/webconsole/test/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/test/fixtures/serviceContainer");
+const mockMessage = ConsoleMessage({
+  messageText: "this is a warning group",
+  source: MESSAGE_SOURCE.CONSOLE_FRONTEND,
+  timeStamp: Date.now(),
+});
+
+describe("WarningGroup component:", () => {
+  it("renders", () => {
+    const wrapper = render(WarningGroup({
+      message: mockMessage,
+      serviceContainer,
+      timestampsVisible: true,
+      badge: 42,
+    }));
+
+    const { timestampString } = require("devtools/client/webconsole/webconsole-l10n");
+    expect(wrapper.find(".timestamp").text())
+      .toBe(timestampString(mockMessage.timeStamp));
+    expect(wrapper.find(".message-body").text()).toBe("this is a warning group 42");
+    expect(wrapper.find(".arrow[aria-expanded=false]")).toExist();
+  });
+
+  it("does have an expanded arrow when `open` prop is true", () => {
+    const wrapper = render(WarningGroup({
+      message: mockMessage,
+      serviceContainer,
+      open: true,
+    }));
+
+    expect(wrapper.find(".arrow[aria-expanded=true]")).toExist();
+  });
+
+  it("does not have a timestamp when timestampsVisible prop is falsy", () => {
+    const wrapper = render(WarningGroup({
+      message: mockMessage,
+      serviceContainer,
+      timestampsVisible: false,
+    }));
+
+    expect(wrapper.find(".timestamp").length).toBe(0);
+  });
+
+  it("renders Content Blocking Group message", () => {
+    const firstMessage = stubPreparedMessages.get("ReferenceError: asdf is not defined");
+    const type = MESSAGE_TYPE.CONTENT_BLOCKING_GROUP;
+    const message = createWarningGroupMessage(`${type}-${firstMessage.innerWindowID}`,
+      type, firstMessage);
+
+    const wrapper = render(WarningGroup({
+      message,
+      serviceContainer,
+      badge: 24,
+    }));
+
+    expect(wrapper.find(".message-body").text()).toBe("Content blocked messages 24");
+    expect(wrapper.find(".arrow[aria-expanded=false]")).toExist();
+  });
+});
--- a/devtools/client/webconsole/test/fixtures/L10n.js
+++ b/devtools/client/webconsole/test/fixtures/L10n.js
@@ -36,16 +36,18 @@ class L10n {
       case "webconsole.xhrFilterButton.label":
         return "XHR";
       case "webconsole.requestsFilterButton.label":
         return "Requests";
       case "messageRepeats.tooltip2":
         return "#1 repeat;#1 repeats";
       case "webconsole.filteredMessages.label":
         return "#1 item hidden by filters;#1 items hidden by filters";
+      case "webconsole.group.contentBlocked":
+        return "Content blocked messages";
       default:
         return str;
     }
   }
 
   getFormatStr(str) {
     return this.getStr(str);
   }
--- a/devtools/client/webconsole/test/fixtures/stub-generators/head.js
+++ b/devtools/client/webconsole/test/fixtures/stub-generators/head.js
@@ -57,16 +57,20 @@ function getCleanedPacket(key, packet) {
     if (res.timestamp) {
       res.timestamp = existingPacket.timestamp;
     }
 
     if (res.timeStamp) {
       res.timeStamp = existingPacket.timeStamp;
     }
 
+    if (res.innerWindowID) {
+      res.innerWindowID = existingPacket.innerWindowID;
+    }
+
     if (res.startedDateTime) {
       res.startedDateTime = existingPacket.startedDateTime;
     }
 
     if (res.actor) {
       res.actor = existingPacket.actor;
     }
 
@@ -76,16 +80,18 @@ function getCleanedPacket(key, packet) {
       if (res.message.timer) {
         // Clean timer properties on the message.
         // Those properties are found on console.time, timeLog and timeEnd calls,
         // and those time can vary, which is why we need to clean them.
         if ("duration" in res.message.timer) {
           res.message.timer.duration = existingPacket.message.timer.duration;
         }
       }
+      // Clean innerWindowId on the message prop.
+      res.message.innerWindowID = existingPacket.message.innerWindowID;
 
       if (Array.isArray(res.message.arguments)) {
         res.message.arguments = res.message.arguments.map((argument, i) => {
           if (!argument || typeof argument !== "object") {
             return argument;
           }
 
           const newArgument = Object.assign({}, argument);
@@ -166,18 +172,19 @@ function getCleanedPacket(key, packet) {
     if (res.eventActor) {
       // Clean actor ids, timeStamp and startedDateTime on network messages.
       res.eventActor.actor = existingPacket.eventActor.actor;
       res.eventActor.startedDateTime = existingPacket.eventActor.startedDateTime;
       res.eventActor.timeStamp = existingPacket.eventActor.timeStamp;
     }
 
     if (res.pageError) {
-      // Clean timeStamp on pageError messages.
+      // Clean timeStamp and innerWindowID on pageError messages.
       res.pageError.timeStamp = existingPacket.pageError.timeStamp;
+      res.pageError.innerWindowID = existingPacket.pageError.innerWindowID;
 
       if (
         typeof res.pageError.errorMessage === "object"
         && res.pageError.errorMessage.type === "longString"
       ) {
         res.pageError.errorMessage.actor = existingPacket.pageError.errorMessage.actor;
       }
 
--- a/devtools/client/webconsole/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/test/fixtures/stubs/consoleApi.js
@@ -10,22 +10,24 @@
 
 const { ConsoleMessage } =
   require("devtools/client/webconsole/types");
 
 const stubPreparedMessages = new Map();
 const stubPackets = new Map();
 stubPreparedMessages.set(`console.log('foobar', 'test')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924471,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "foobar",
     "test"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foobar\",\"test\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
@@ -41,22 +43,24 @@ stubPreparedMessages.set(`console.log('f
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log(undefined)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924479,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     {
       "type": "undefined"
     }
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
@@ -73,22 +77,24 @@ stubPreparedMessages.set(`console.log(un
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.warn('danger, will robinson!')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924487,
   "type": "warn",
   "helperType": null,
   "level": "warn",
+  "category": null,
   "messageText": null,
   "parameters": [
     "danger, will robinson!"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":null,\"parameters\":[\"danger, will robinson!\"],\"source\":\"console-api\",\"type\":\"warn\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -103,22 +109,24 @@ stubPreparedMessages.set(`console.warn('
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log(NaN)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924495,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     {
       "type": "NaN"
     }
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"NaN\"}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
@@ -135,22 +143,24 @@ stubPreparedMessages.set(`console.log(Na
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log(null)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924501,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     {
       "type": "null"
     }
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"null\"}],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
@@ -167,22 +177,24 @@ stubPreparedMessages.set(`console.log(nu
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log('鼬')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924506,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "鼬"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"鼬\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -197,22 +209,24 @@ stubPreparedMessages.set(`console.log('鼬')`, new ConsoleMessage({
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.clear()`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924512,
   "type": "clear",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "Console was cleared."
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"source\":\"console-api\",\"type\":\"clear\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -227,22 +241,24 @@ stubPreparedMessages.set(`console.clear(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count('bar')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924515,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "bar: 1",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"bar: 1\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source25",
     "line": 1,
@@ -255,22 +271,24 @@ stubPreparedMessages.set(`console.count(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.assert(false, {message: 'foobar'})`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924521,
   "type": "assert",
   "helperType": null,
   "level": "error",
+  "category": null,
   "messageText": null,
   "parameters": [
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj30",
       "class": "Object",
       "extensible": true,
       "frozen": false,
@@ -316,22 +334,24 @@ stubPreparedMessages.set(`console.assert
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log('hello \nfrom \rthe \"string world!')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924528,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "hello \nfrom \rthe \"string world!"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"hello \\nfrom \\rthe \\\"string world!\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -346,22 +366,24 @@ stubPreparedMessages.set(`console.log('h
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log('úṇĩçödê țĕșť')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924586,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "úṇĩçödê țĕșť"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"úṇĩçödê țĕșť\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -376,22 +398,24 @@ stubPreparedMessages.set(`console.log('úṇĩçödê țĕșť')`, new ConsoleMessage({
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.dirxml(window)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924596,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj31",
       "class": "Window",
       "extensible": true,
       "frozen": false,
@@ -418,22 +442,24 @@ stubPreparedMessages.set(`console.dirxml
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log('myarray', ['red', 'green', 'blue'])`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924604,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "myarray",
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj32",
       "class": "Array",
       "extensible": true,
@@ -466,22 +492,24 @@ stubPreparedMessages.set(`console.log('m
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log('myregex', /a.b.c/)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924610,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "myregex",
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj33",
       "class": "RegExp",
       "extensible": true,
@@ -506,22 +534,24 @@ stubPreparedMessages.set(`console.log('m
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.table(['red', 'green', 'blue']);`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924612,
   "type": "table",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj34",
       "class": "Array",
       "extensible": true,
       "frozen": false,
@@ -553,22 +583,24 @@ stubPreparedMessages.set(`console.table(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924614,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "myobject",
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj35",
       "class": "Object",
       "extensible": true,
@@ -619,22 +651,24 @@ stubPreparedMessages.set(`console.log('m
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.debug('debug message');`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924621,
   "type": "debug",
   "helperType": null,
   "level": "debug",
+  "category": null,
   "messageText": null,
   "parameters": [
     "debug message"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"debug\",\"messageText\":null,\"parameters\":[\"debug message\"],\"source\":\"console-api\",\"type\":\"debug\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -649,22 +683,24 @@ stubPreparedMessages.set(`console.debug(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.info('info message');`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924625,
   "type": "info",
   "helperType": null,
   "level": "info",
+  "category": null,
   "messageText": null,
   "parameters": [
     "info message"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"info\",\"messageText\":null,\"parameters\":[\"info message\"],\"source\":\"console-api\",\"type\":\"info\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -679,22 +715,24 @@ stubPreparedMessages.set(`console.info('
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.error('error message');`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924628,
   "type": "error",
   "helperType": null,
   "level": "error",
+  "category": null,
   "messageText": null,
   "parameters": [
     "error message"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source33\",\"line\":1,\"column\":35},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":null,\"parameters\":[\"error message\"],\"source\":\"console-api\",\"type\":\"error\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":[{\"columnNumber\":35,\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"triggerPacket\",\"lineNumber\":1,\"sourceId\":159}]}",
   "stacktrace": [
     {
       "columnNumber": 35,
@@ -717,22 +755,24 @@ stubPreparedMessages.set(`console.error(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log('mymap')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924631,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "mymap",
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj36",
       "class": "Map",
       "extensible": true,
@@ -770,22 +810,24 @@ stubPreparedMessages.set(`console.log('m
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log('myset')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924746,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "myset",
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj37",
       "class": "Set",
       "extensible": true,
@@ -817,22 +859,24 @@ stubPreparedMessages.set(`console.log('m
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.trace()`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924752,
   "type": "trace",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source55\",\"line\":3,\"column\":11},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[],\"source\":\"console-api\",\"type\":\"trace\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":[{\"columnNumber\":11,\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"testStacktraceFiltering\",\"lineNumber\":3,\"sourceId\":193},{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"foo\",\"lineNumber\":6,\"sourceId\":193},{\"columnNumber\":1,\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"triggerPacket\",\"lineNumber\":9,\"sourceId\":193}]}",
   "stacktrace": [
     {
       "columnNumber": 11,
       "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
       "functionName": "testStacktraceFiltering",
@@ -867,22 +911,24 @@ stubPreparedMessages.set(`console.trace(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.trace('bar', {'foo': 'bar'}, [1,2,3])`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1538037986659,
   "type": "trace",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "bar",
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj35",
       "class": "Object",
       "extensible": true,
@@ -961,22 +1007,24 @@ stubPreparedMessages.set(`console.trace(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.time('bar')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924757,
   "type": "nullMessage",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source52\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"nullMessage\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source52",
     "line": 2,
@@ -989,22 +1037,24 @@ stubPreparedMessages.set(`console.time('
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`timerAlreadyExists`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924758,
   "type": "time",
   "helperType": null,
   "level": "warn",
+  "category": null,
   "messageText": "Timer “bar” already exists.",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":3,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Timer “bar” already exists.\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"time\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 3,
@@ -1017,22 +1067,24 @@ stubPreparedMessages.set(`timerAlreadyEx
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.timeLog('bar') - 1`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1526920999996,
   "type": "timeLog",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "bar: 1ms"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":4,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar: 1ms\"],\"source\":\"console-api\",\"type\":\"timeLog\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -1047,22 +1099,24 @@ stubPreparedMessages.set(`console.timeLo
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.timeLog('bar') - 2`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1526920999996,
   "type": "timeLog",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "bar: 1ms",
     "second call",
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj34",
       "class": "Object",
@@ -1102,22 +1156,24 @@ stubPreparedMessages.set(`console.timeLo
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.timeEnd('bar')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924759,
   "type": "timeEnd",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "bar: 1.21ms - timer ended",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":6,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"bar: 1.21ms - timer ended\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"timeEnd\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 6,
@@ -1130,22 +1186,24 @@ stubPreparedMessages.set(`console.timeEn
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`timeEnd.timerDoesntExist`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1526920999998,
   "type": "timeEnd",
   "helperType": null,
   "level": "warn",
+  "category": null,
   "messageText": "Timer “bar” doesn’t exist.",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":7,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Timer “bar” doesn’t exist.\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"timeEnd\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 7,
@@ -1158,22 +1216,24 @@ stubPreparedMessages.set(`timeEnd.timerD
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`timeLog.timerDoesntExist`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1526920999999,
   "type": "timeLog",
   "helperType": null,
   "level": "warn",
+  "category": null,
   "messageText": "Timer “bar” doesn’t exist.",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":8,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Timer “bar” doesn’t exist.\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"timeLog\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 8,
@@ -1186,22 +1246,24 @@ stubPreparedMessages.set(`timeLog.timerD
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.table('bar')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924801,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "bar"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source52\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -1216,22 +1278,24 @@ stubPreparedMessages.set(`console.table(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.table(['a', 'b', 'c'])`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924859,
   "type": "table",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj39",
       "class": "Array",
       "extensible": true,
       "frozen": false,
@@ -1263,22 +1327,24 @@ stubPreparedMessages.set(`console.table(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.group('bar')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924863,
   "type": "startGroup",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "bar"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source52\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"source\":\"console-api\",\"type\":\"startGroup\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -1293,22 +1359,24 @@ stubPreparedMessages.set(`console.group(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.groupEnd('bar')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924864,
   "type": "endGroup",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":3,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 3,
@@ -1321,22 +1389,24 @@ stubPreparedMessages.set(`console.groupE
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.groupCollapsed('foo')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924870,
   "type": "startGroupCollapsed",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "foo"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source52\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\"],\"source\":\"console-api\",\"type\":\"startGroupCollapsed\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -1351,22 +1421,24 @@ stubPreparedMessages.set(`console.groupC
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.groupEnd('foo')`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924871,
   "type": "endGroup",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":3,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 3,
@@ -1379,22 +1451,24 @@ stubPreparedMessages.set(`console.groupE
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.group()`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924878,
   "type": "startGroup",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "<no group label>"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source52\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"<no group label>\"],\"source\":\"console-api\",\"type\":\"startGroup\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -1409,22 +1483,24 @@ stubPreparedMessages.set(`console.group(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.groupEnd()`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924879,
   "type": "endGroup",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":3,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 3,
@@ -1437,22 +1513,24 @@ stubPreparedMessages.set(`console.groupE
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log(%cfoobar)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924883,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "foo",
     "bar"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source52\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[\"color:blue; font-size:1.3em; background:url('http://example.com/test'); position:absolute; top:10px; \",\"color:red; line-height: 1.5; background:url('http://example.com/test')\"],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
@@ -1471,22 +1549,24 @@ stubPreparedMessages.set(`console.log(%c
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.log("%cHello%c|%cWorld")`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1518681614352,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "Hello",
     "|",
     "World"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source68\",\"line\":2,\"column\":11},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Hello\",\"|\",\"World\"],\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[\"color:red\",\"\",\"color: blue\"],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
@@ -1507,22 +1587,24 @@ stubPreparedMessages.set(`console.log("%
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.group(%cfoo%cbar)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924887,
   "type": "startGroup",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "foo",
     "bar"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source52\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"source\":\"console-api\",\"type\":\"startGroup\",\"userProvidedStyles\":[\"color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px\",\"color:red;background:url('http://example.com/test')\"],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
@@ -1541,22 +1623,24 @@ stubPreparedMessages.set(`console.group(
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.groupEnd(%cfoo%cbar)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924887,
   "type": "endGroup",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":6,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 6,
@@ -1569,22 +1653,24 @@ stubPreparedMessages.set(`console.groupE
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.groupCollapsed(%cfoo%cbaz)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924892,
   "type": "startGroupCollapsed",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "foo",
     "baz"
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source52\",\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"baz\"],\"source\":\"console-api\",\"type\":\"startGroupCollapsed\",\"userProvidedStyles\":[\"color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px\",\"color:red;background:url('http://example.com/test')\"],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
@@ -1603,22 +1689,24 @@ stubPreparedMessages.set(`console.groupC
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.groupEnd(%cfoo%cbaz)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924893,
   "type": "endGroup",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source59\",\"line\":6,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"source\":\"console-api\",\"type\":\"endGroup\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source59",
     "line": 6,
@@ -1631,22 +1719,24 @@ stubPreparedMessages.set(`console.groupE
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.dir({C, M, Y, K})`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1502884924899,
   "type": "dir",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj36",
       "class": "Object",
       "extensible": true,
       "frozen": false,
@@ -1702,22 +1792,24 @@ stubPreparedMessages.set(`console.dir({C
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count | default: 1`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1511365913333,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "default: 1",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":2,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 1\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 2,
@@ -1730,22 +1822,24 @@ stubPreparedMessages.set(`console.count 
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count | default: 2`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1511365913334,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "default: 2",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":3,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 2\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 3,
@@ -1758,22 +1852,24 @@ stubPreparedMessages.set(`console.count 
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count | test counter: 1`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1511365913334,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "test counter: 1",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":4,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 1\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 4,
@@ -1786,22 +1882,24 @@ stubPreparedMessages.set(`console.count 
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count | test counter: 2`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1511365913334,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "test counter: 2",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":5,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 2\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 5,
@@ -1814,22 +1912,24 @@ stubPreparedMessages.set(`console.count 
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count | default: 3`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1511365913334,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "default: 3",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":6,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 3\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 6,
@@ -1842,22 +1942,24 @@ stubPreparedMessages.set(`console.count 
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count | clear`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1511365913334,
   "type": "clear",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": null,
   "parameters": [
     "Console was cleared."
   ],
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":7,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"source\":\"console-api\",\"type\":\"clear\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@@ -1872,22 +1974,24 @@ stubPreparedMessages.set(`console.count 
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count | default: 4`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1511365913335,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "default: 4",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":8,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"default: 4\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 8,
@@ -1900,22 +2004,24 @@ stubPreparedMessages.set(`console.count 
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.count | test counter: 3`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1511365913335,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "test counter: 3",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":9,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 3\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 9,
@@ -1928,22 +2034,24 @@ stubPreparedMessages.set(`console.count 
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.countReset | test counter: 0`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1526920412190,
   "type": "log",
   "helperType": null,
   "level": "log",
+  "category": null,
   "messageText": "test counter: 0",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":10,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"log\",\"messageText\":\"test counter: 0\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 10,
@@ -1956,22 +2064,24 @@ stubPreparedMessages.set(`console.countR
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`console.countReset | counterDoesntExist`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1526920412191,
   "type": "log",
   "helperType": null,
   "level": "warn",
+  "category": null,
   "messageText": "Counter “test counter” doesn’t exist.",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source73\",\"line\":11,\"column\":13},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Counter “test counter” doesn’t exist.\",\"parameters\":null,\"source\":\"console-api\",\"type\":\"log\",\"userProvidedStyles\":[],\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source73",
     "line": 11,
@@ -2003,17 +2113,18 @@ stubPackets.set(`console.log('foobar', '
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924471,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log(undefined)`, {
   "message": {
     "addonId": "",
@@ -2031,17 +2142,18 @@ stubPackets.set(`console.log(undefined)`
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924479,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.warn('danger, will robinson!')`, {
   "message": {
     "addonId": "",
@@ -2057,17 +2169,18 @@ stubPackets.set(`console.warn('danger, w
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924487,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log(NaN)`, {
   "message": {
     "addonId": "",
@@ -2085,17 +2198,18 @@ stubPackets.set(`console.log(NaN)`, {
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924495,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log(null)`, {
   "message": {
     "addonId": "",
@@ -2113,17 +2227,18 @@ stubPackets.set(`console.log(null)`, {
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924501,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log('鼬')`, {
   "message": {
     "addonId": "",
@@ -2139,17 +2254,18 @@ stubPackets.set(`console.log('鼬')`, {
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924506,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.clear()`, {
   "message": {
     "addonId": "",
@@ -2163,17 +2279,18 @@ stubPackets.set(`console.clear()`, {
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "timeStamp": 1502884924512,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count('bar')`, {
   "message": {
     "addonId": "",
@@ -2192,17 +2309,18 @@ stubPackets.set(`console.count('bar')`, 
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "timeStamp": 1502884924515,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.assert(false, {message: 'foobar'})`, {
   "message": {
     "addonId": "",
@@ -2250,17 +2368,18 @@ stubPackets.set(`console.assert(false, {
         "columnNumber": 35,
         "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
         "functionName": "triggerPacket",
         "lineNumber": 1,
         "sourceId": 159
       }
     ],
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log('hello \nfrom \rthe \"string world!')`, {
   "message": {
     "addonId": "",
@@ -2276,17 +2395,18 @@ stubPackets.set(`console.log('hello \nfr
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924528,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log('úṇĩçödê țĕșť')`, {
   "message": {
     "addonId": "",
@@ -2302,17 +2422,18 @@ stubPackets.set(`console.log('úṇĩçödê țĕșť')`, {
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924586,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.dirxml(window)`, {
   "message": {
     "addonId": "",
@@ -2340,17 +2461,18 @@ stubPackets.set(`console.dirxml(window)`
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "timeStamp": 1502884924596,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log('myarray', ['red', 'green', 'blue'])`, {
   "message": {
     "addonId": "",
@@ -2384,17 +2506,18 @@ stubPackets.set(`console.log('myarray', 
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924604,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log('myregex', /a.b.c/)`, {
   "message": {
     "addonId": "",
@@ -2420,17 +2543,18 @@ stubPackets.set(`console.log('myregex', 
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924610,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.table(['red', 'green', 'blue']);`, {
   "message": {
     "addonId": "",
@@ -2463,17 +2587,18 @@ stubPackets.set(`console.table(['red', '
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "timeStamp": 1502884924612,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log('myobject', {red: 'redValue', green: 'greenValue', blue: 'blueValue'});`, {
   "message": {
     "addonId": "",
@@ -2525,17 +2650,18 @@ stubPackets.set(`console.log('myobject',
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924614,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.debug('debug message');`, {
   "message": {
     "addonId": "",
@@ -2551,17 +2677,18 @@ stubPackets.set(`console.debug('debug me
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924621,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.info('info message');`, {
   "message": {
     "addonId": "",
@@ -2577,17 +2704,18 @@ stubPackets.set(`console.info('info mess
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "styles": [],
     "timeStamp": 1502884924625,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.error('error message');`, {
   "message": {
     "addonId": "",
@@ -2612,17 +2740,18 @@ stubPackets.set(`console.error('error me
         "columnNumber": 35,
         "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
         "functionName": "triggerPacket",
         "lineNumber": 1,
         "sourceId": 159
       }
     ],
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log('mymap')`, {
   "message": {
     "addonId": "",
@@ -2661,17 +2790,18 @@ stubPackets.set(`console.log('mymap')`, 
     "lineNumber": 5,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source50",
     "styles": [],
     "timeStamp": 1502884924631,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log('myset')`, {
   "message": {
     "addonId": "",
@@ -2704,17 +2834,18 @@ stubPackets.set(`console.log('myset')`, 
     "lineNumber": 2,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source52",
     "styles": [],
     "timeStamp": 1502884924746,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.trace()`, {
   "message": {
     "addonId": "",
@@ -2751,17 +2882,18 @@ stubPackets.set(`console.trace()`, {
         "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
         "functionName": "triggerPacket",
         "lineNumber": 9,
         "sourceId": 193
       }
     ],
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.trace('bar', {'foo': 'bar'}, [1,2,3])`, {
   "message": {
     "addonId": "",
@@ -2842,17 +2974,18 @@ stubPackets.set(`console.trace('bar', {'
         "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
         "functionName": "triggerPacket",
         "lineNumber": 9,
         "sourceId": 193
       }
     ],
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.time('bar')`, {
   "message": {
     "addonId": "",
@@ -2870,17 +3003,18 @@ stubPackets.set(`console.time('bar')`, {
     "private": false,
     "sourceId": "server1.conn0.child1/source52",
     "timeStamp": 1502884924757,
     "timer": {
       "name": "bar"
     },
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`timerAlreadyExists`, {
   "message": {
     "addonId": "",
@@ -2899,17 +3033,18 @@ stubPackets.set(`timerAlreadyExists`, {
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1502884924758,
     "timer": {
       "error": "timerAlreadyExists",
       "name": "bar"
     },
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.timeLog('bar') - 1`, {
   "message": {
     "addonId": "",
@@ -2928,17 +3063,18 @@ stubPackets.set(`console.timeLog('bar') 
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1526920999996,
     "timer": {
       "duration": 1,
       "name": "bar"
     },
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.timeLog('bar') - 2`, {
   "message": {
     "addonId": "",
@@ -2982,17 +3118,18 @@ stubPackets.set(`console.timeLog('bar') 
     "sourceId": "server1.conn0.child1/source50",
     "timeStamp": 1526920999996,
     "timer": {
       "duration": 1,
       "name": "bar"
     },
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.timeEnd('bar')`, {
   "message": {
     "addonId": "",
@@ -3011,17 +3148,18 @@ stubPackets.set(`console.timeEnd('bar')`
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1502884924759,
     "timer": {
       "duration": 1.2149999999999181,
       "name": "bar"
     },
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`timeEnd.timerDoesntExist`, {
   "message": {
     "addonId": "",
@@ -3040,17 +3178,18 @@ stubPackets.set(`timeEnd.timerDoesntExis
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1526920999998,
     "timer": {
       "error": "timerDoesntExist",
       "name": "bar"
     },
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`timeLog.timerDoesntExist`, {
   "message": {
     "addonId": "",
@@ -3069,17 +3208,18 @@ stubPackets.set(`timeLog.timerDoesntExis
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1526920999999,
     "timer": {
       "error": "timerDoesntExist",
       "name": "bar"
     },
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.table('bar')`, {
   "message": {
     "addonId": "",
@@ -3095,17 +3235,18 @@ stubPackets.set(`console.table('bar')`, 
     "lineNumber": 2,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source52",
     "timeStamp": 1502884924801,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.table(['a', 'b', 'c'])`, {
   "message": {
     "addonId": "",
@@ -3138,17 +3279,18 @@ stubPackets.set(`console.table(['a', 'b'
     "lineNumber": 2,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source52",
     "timeStamp": 1502884924859,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.group('bar')`, {
   "message": {
     "addonId": "",
@@ -3164,17 +3306,18 @@ stubPackets.set(`console.group('bar')`, 
     "lineNumber": 2,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source52",
     "styles": [],
     "timeStamp": 1502884924863,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd('bar')`, {
   "message": {
     "addonId": "",
@@ -3188,17 +3331,18 @@ stubPackets.set(`console.groupEnd('bar')
     "lineNumber": 3,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1502884924864,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupCollapsed('foo')`, {
   "message": {
     "addonId": "",
@@ -3214,17 +3358,18 @@ stubPackets.set(`console.groupCollapsed(
     "lineNumber": 2,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source52",
     "styles": [],
     "timeStamp": 1502884924870,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd('foo')`, {
   "message": {
     "addonId": "",
@@ -3238,17 +3383,18 @@ stubPackets.set(`console.groupEnd('foo')
     "lineNumber": 3,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1502884924871,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.group()`, {
   "message": {
     "addonId": "",
@@ -3262,17 +3408,18 @@ stubPackets.set(`console.group()`, {
     "lineNumber": 2,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source52",
     "styles": [],
     "timeStamp": 1502884924878,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd()`, {
   "message": {
     "addonId": "",
@@ -3286,17 +3433,18 @@ stubPackets.set(`console.groupEnd()`, {
     "lineNumber": 3,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1502884924879,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log(%cfoobar)`, {
   "message": {
     "addonId": "",
@@ -3316,17 +3464,18 @@ stubPackets.set(`console.log(%cfoobar)`,
     "sourceId": "server1.conn0.child1/source52",
     "styles": [
       "color:blue; font-size:1.3em; background:url('http://example.com/test'); position:absolute; top:10px; ",
       "color:red; line-height: 1.5; background:url('http://example.com/test')"
     ],
     "timeStamp": 1502884924883,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.log("%cHello%c|%cWorld")`, {
   "message": {
     "addonId": "",
@@ -3348,17 +3497,18 @@ stubPackets.set(`console.log("%cHello%c|
     "styles": [
       "color:red",
       "",
       "color: blue"
     ],
     "timeStamp": 1518681614352,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.group(%cfoo%cbar)`, {
   "message": {
     "addonId": "",
@@ -3378,17 +3528,18 @@ stubPackets.set(`console.group(%cfoo%cba
     "sourceId": "server1.conn0.child1/source52",
     "styles": [
       "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
       "color:red;background:url('http://example.com/test')"
     ],
     "timeStamp": 1502884924887,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd(%cfoo%cbar)`, {
   "message": {
     "addonId": "",
@@ -3402,17 +3553,18 @@ stubPackets.set(`console.groupEnd(%cfoo%
     "lineNumber": 6,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1502884924887,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupCollapsed(%cfoo%cbaz)`, {
   "message": {
     "addonId": "",
@@ -3432,17 +3584,18 @@ stubPackets.set(`console.groupCollapsed(
     "sourceId": "server1.conn0.child1/source52",
     "styles": [
       "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
       "color:red;background:url('http://example.com/test')"
     ],
     "timeStamp": 1502884924892,
     "timer": null,
     "workerType": "none",
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.groupEnd(%cfoo%cbaz)`, {
   "message": {
     "addonId": "",
@@ -3456,17 +3609,18 @@ stubPackets.set(`console.groupEnd(%cfoo%
     "lineNumber": 6,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source59",
     "timeStamp": 1502884924893,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.dir({C, M, Y, K})`, {
   "message": {
     "addonId": "",
@@ -3523,17 +3677,18 @@ stubPackets.set(`console.dir({C, M, Y, K
     "lineNumber": 1,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source25",
     "timeStamp": 1502884924899,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | default: 1`, {
   "message": {
     "addonId": "",
@@ -3552,17 +3707,18 @@ stubPackets.set(`console.count | default
     "lineNumber": 2,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1511365913333,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | default: 2`, {
   "message": {
     "addonId": "",
@@ -3581,17 +3737,18 @@ stubPackets.set(`console.count | default
     "lineNumber": 3,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1511365913334,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | test counter: 1`, {
   "message": {
     "addonId": "",
@@ -3610,17 +3767,18 @@ stubPackets.set(`console.count | test co
     "lineNumber": 4,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1511365913334,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | test counter: 2`, {
   "message": {
     "addonId": "",
@@ -3639,17 +3797,18 @@ stubPackets.set(`console.count | test co
     "lineNumber": 5,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1511365913334,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | default: 3`, {
   "message": {
     "addonId": "",
@@ -3668,17 +3827,18 @@ stubPackets.set(`console.count | default
     "lineNumber": 6,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1511365913334,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | clear`, {
   "message": {
     "addonId": "",
@@ -3692,17 +3852,18 @@ stubPackets.set(`console.count | clear`,
     "lineNumber": 7,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1511365913334,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | default: 4`, {
   "message": {
     "addonId": "",
@@ -3721,17 +3882,18 @@ stubPackets.set(`console.count | default
     "lineNumber": 8,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1511365913335,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.count | test counter: 3`, {
   "message": {
     "addonId": "",
@@ -3750,17 +3912,18 @@ stubPackets.set(`console.count | test co
     "lineNumber": 9,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1511365913335,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.countReset | test counter: 0`, {
   "message": {
     "addonId": "",
@@ -3779,17 +3942,18 @@ stubPackets.set(`console.countReset | te
     "lineNumber": 10,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1526920412190,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 stubPackets.set(`console.countReset | counterDoesntExist`, {
   "message": {
     "addonId": "",
@@ -3808,17 +3972,18 @@ stubPackets.set(`console.countReset | co
     "lineNumber": 11,
     "prefix": "",
     "private": false,
     "sourceId": "server1.conn0.child1/source73",
     "timeStamp": 1526920412191,
     "timer": null,
     "workerType": "none",
     "styles": [],
-    "category": "webdev"
+    "category": "webdev",
+    "innerWindowID": 10737418243
   },
   "type": "consoleAPICall",
   "from": "server1.conn0.child1/consoleActor2"
 });
 
 module.exports = {
   stubPreparedMessages,
   stubPackets,
--- a/devtools/client/webconsole/test/fixtures/stubs/cssMessage.js
+++ b/devtools/client/webconsole/test/fixtures/stubs/cssMessage.js
@@ -10,22 +10,24 @@
 
 const { ConsoleMessage } =
   require("devtools/client/webconsole/types");
 
 const stubPreparedMessages = new Map();
 const stubPackets = new Map();
 stubPreparedMessages.set(`Unknown property ‘such-unknown-property’.  Declaration dropped.`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": 15032385539,
   "allowRepeating": true,
   "source": "css",
   "timeStamp": 1479159920406,
   "type": "log",
   "helperType": null,
   "level": "warn",
+  "category": "CSS Parser",
   "messageText": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-css-message.html\",\"sourceId\":null,\"line\":3,\"column\":25},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Unknown property ‘such-unknown-property’.  Declaration dropped.\",\"parameters\":null,\"source\":\"css\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-css-message.html",
     "sourceId": null,
     "line": 3,
@@ -37,22 +39,24 @@ stubPreparedMessages.set(`Unknown property ‘such-unknown-property’.  Declaration dropped.`, new ConsoleMessage({
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`Error in parsing value for ‘padding-top’.  Declaration dropped.`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": 15032385539,
   "allowRepeating": true,
   "source": "css",
   "timeStamp": 1479159920465,
   "type": "log",
   "helperType": null,
   "level": "warn",
+  "category": "CSS Parser",
   "messageText": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-css-message.html\",\"sourceId\":null,\"line\":3,\"column\":16},\"groupId\":null,\"indent\":0,\"level\":\"warn\",\"messageText\":\"Error in parsing value for ‘padding-top’.  Declaration dropped.\",\"parameters\":null,\"source\":\"css\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-css-message.html",
     "sourceId": null,
     "line": 3,
@@ -72,16 +76,17 @@ stubPackets.set(`Unknown property ‘such-unknown-property’.  Declaration dropped.`, {
     "errorMessage": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
     "errorMessageName": "",
     "sourceName": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-css-message.html",
     "sourceId": null,
     "lineText": "",
     "lineNumber": 3,
     "columnNumber": 25,
     "category": "CSS Parser",
+    "innerWindowID": 15032385539,
     "timeStamp": 1479159920406,
     "warning": true,
     "error": false,
     "exception": false,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": null,
@@ -96,16 +101,17 @@ stubPackets.set(`Error in parsing value for ‘padding-top’.  Declaration dropped.`, {
     "errorMessage": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
     "errorMessageName": "",
     "sourceName": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-css-message.html",
     "sourceId": null,
     "lineText": "",
     "lineNumber": 3,
     "columnNumber": 16,
     "category": "CSS Parser",
+    "innerWindowID": 15032385539,
     "timeStamp": 1479159920465,
     "warning": true,
     "error": false,
     "exception": false,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": null,
--- a/devtools/client/webconsole/test/fixtures/stubs/evaluationResult.js
+++ b/devtools/client/webconsole/test/fixtures/stubs/evaluationResult.js
@@ -10,22 +10,24 @@
 
 const { ConsoleMessage } =
   require("devtools/client/webconsole/types");
 
 const stubPreparedMessages = new Map();
 const stubPackets = new Map();
 stubPreparedMessages.set(`new Date(0)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1479159921364,
   "type": "result",
   "helperType": null,
   "level": "log",
+  "category": null,
   "parameters": [
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj30",
       "class": "Date",
       "extensible": true,
       "frozen": false,
       "sealed": false,
@@ -42,22 +44,24 @@ stubPreparedMessages.set(`new Date(0)`, 
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": ""
 }));
 
 stubPreparedMessages.set(`asdf()`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1479159921377,
   "type": "result",
   "helperType": null,
   "level": "error",
+  "category": null,
   "messageText": "ReferenceError: asdf is not defined",
   "parameters": [
     {
       "type": "undefined"
     }
   ],
   "repeatId": "{\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null,\"stacktrace\":null}",
   "stacktrace": null,
@@ -72,22 +76,24 @@ stubPreparedMessages.set(`asdf()`, new C
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": ""
 }));
 
 stubPreparedMessages.set(`1 + @`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1479159921399,
   "type": "result",
   "helperType": null,
   "level": "error",
+  "category": null,
   "messageText": "SyntaxError: illegal character",
   "parameters": [
     {
       "type": "undefined"
     }
   ],
   "repeatId": "{\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":4},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"SyntaxError: illegal character\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null,\"stacktrace\":null}",
   "stacktrace": null,
@@ -102,22 +108,24 @@ stubPreparedMessages.set(`1 + @`, new Co
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": ""
 }));
 
 stubPreparedMessages.set(`inspect({a: 1})`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1499776070751,
   "type": "result",
   "helperType": "inspectObject",
   "level": "log",
+  "category": null,
   "parameters": [
     {
       "type": "object",
       "actor": "server1.conn0.child1/obj35",
       "class": "Object",
       "extensible": true,
       "frozen": false,
       "sealed": false,
@@ -146,22 +154,24 @@ stubPreparedMessages.set(`inspect({a: 1}
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": ""
 }));
 
 stubPreparedMessages.set(`cd(document)`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1510650094657,
   "type": "result",
   "helperType": "error",
   "level": "error",
+  "category": null,
   "messageText": "Cannot cd() to the given window. Invalid argument.",
   "parameters": [
     {
       "type": "undefined"
     }
   ],
   "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"Cannot cd() to the given window. Invalid argument.\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null,\"stacktrace\":null}",
   "stacktrace": null,
@@ -170,22 +180,24 @@ stubPreparedMessages.set(`cd(document)`,
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": ""
 }));
 
 stubPreparedMessages.set(`undefined`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1518606917356,
   "type": "result",
   "helperType": null,
   "level": "log",
+  "category": null,
   "parameters": [
     {
       "type": "undefined"
     }
   ],
   "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"log\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": null,
@@ -193,22 +205,24 @@ stubPreparedMessages.set(`undefined`, ne
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": ""
 }));
 
 stubPreparedMessages.set(`longString message Error`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1493108241073,
   "type": "result",
   "helperType": null,
   "level": "error",
+  "category": null,
   "messageText": {
     "type": "longString",
     "initial": "Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon",
     "length": 110007,
     "actor": "server1.conn0.child1/longString37"
   },
   "parameters": [
     {
@@ -222,22 +236,24 @@ stubPreparedMessages.set(`longString mes
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": ""
 }));
 
 stubPreparedMessages.set(`eval throw ""`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1517990289517,
   "type": "result",
   "helperType": null,
   "level": "error",
+  "category": null,
   "messageText": "Error",
   "parameters": [
     {
       "type": "undefined"
     }
   ],
   "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"Error\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null,\"stacktrace\":null}",
   "stacktrace": null,
@@ -246,22 +262,24 @@ stubPreparedMessages.set(`eval throw ""`
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": ""
 }));
 
 stubPreparedMessages.set(`eval throw "tomato"`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": null,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1517990289520,
   "type": "result",
   "helperType": null,
   "level": "error",
+  "category": null,
   "messageText": "Error: tomato",
   "parameters": [
     {
       "type": "undefined"
     }
   ],
   "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"Error: tomato\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null,\"stacktrace\":null}",
   "stacktrace": null,
--- a/devtools/client/webconsole/test/fixtures/stubs/pageError.js
+++ b/devtools/client/webconsole/test/fixtures/stubs/pageError.js
@@ -10,22 +10,24 @@
 
 const { ConsoleMessage } =
   require("devtools/client/webconsole/types");
 
 const stubPreparedMessages = new Map();
 const stubPackets = new Map();
 stubPreparedMessages.set(`ReferenceError: asdf is not defined`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": 6442450949,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1476573167137,
   "type": "log",
   "helperType": null,
   "level": "error",
+  "category": "content javascript",
   "messageText": "ReferenceError: asdf is not defined",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":null,\"line\":3,\"column\":5},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source25\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 59 > eval\",\"sourceId\":null,\"lineNumber\":7,\"columnNumber\":31,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"sourceId\":null,\"lineNumber\":60,\"columnNumber\":29,\"functionName\":null}]}",
   "stacktrace": [
     {
       "filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
       "sourceId": "server1.conn0.child1/source25",
       "lineNumber": 3,
@@ -74,22 +76,24 @@ stubPreparedMessages.set(`ReferenceError
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`SyntaxError: redeclaration of let a`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": 6442450949,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1487992945524,
   "type": "log",
   "helperType": null,
   "level": "error",
+  "category": "content javascript",
   "messageText": "SyntaxError: redeclaration of let a",
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":null,\"line\":2,\"column\":9},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"SyntaxError: redeclaration of let a\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":[{\"filename\":\"resource://testing-common/content-task.js line 59 > eval\",\"sourceId\":null,\"lineNumber\":7,\"columnNumber\":31,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"sourceId\":null,\"lineNumber\":60,\"columnNumber\":29,\"functionName\":null}]}",
   "stacktrace": [
     {
       "filename": "resource://testing-common/content-task.js line 59 > eval",
       "sourceId": null,
       "lineNumber": 7,
@@ -127,22 +131,24 @@ stubPreparedMessages.set(`SyntaxError: r
   ],
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`TypeError longString message`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": 6442450949,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1493109507061,
   "type": "log",
   "helperType": null,
   "level": "error",
+  "category": "content javascript",
   "messageText": {
     "type": "longString",
     "initial": "Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon",
     "length": 110007,
     "actor": "server1.conn0.child1/longString30"
   },
   "parameters": null,
   "repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source26\",\"line\":1,\"column\":7},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":{\"type\":\"longString\",\"initial\":\"Error: Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Long error Lon\",\"length\":110007,\"actor\":\"server1.conn0.child1/longString30\"},\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source26\",\"lineNumber\":1,\"columnNumber\":7,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 59 > eval\",\"sourceId\":null,\"lineNumber\":7,\"columnNumber\":31,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"sourceId\":null,\"lineNumber\":60,\"columnNumber\":29,\"functionName\":null}]}",
@@ -181,44 +187,48 @@ stubPreparedMessages.set(`TypeError long
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`throw ""`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": 6442450949,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1517942398629,
   "type": "log",
   "helperType": null,
   "level": "error",
+  "category": "content javascript",
   "messageText": "uncaught exception: ",
   "parameters": null,
   "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"uncaught exception: \",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": null,
   "groupId": null,
   "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0,
   "prefix": "",
   "private": false
 }));
 
 stubPreparedMessages.set(`throw "tomato"`, new ConsoleMessage({
   "id": "1",
+  "innerWindowID": 6442450949,
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1517942398637,
   "type": "log",
   "helperType": null,
   "level": "error",
+  "category": "content javascript",
   "messageText": "uncaught exception: tomato",
   "parameters": null,
   "repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"uncaught exception: tomato\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":null}",
   "stacktrace": null,
   "frame": null,
   "groupId": null,
   "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
   "userProvidedStyles": null,
@@ -234,16 +244,17 @@ stubPackets.set(`ReferenceError: asdf is
     "errorMessageName": "JSMSG_NOT_DEFINED",
     "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
     "sourceName": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": null,
     "lineText": "",
     "lineNumber": 3,
     "columnNumber": 5,
     "category": "content javascript",
+    "innerWindowID": 6442450949,
     "timeStamp": 1476573167137,
     "warning": false,
     "error": false,
     "exception": true,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": [
@@ -295,16 +306,17 @@ stubPackets.set(`SyntaxError: redeclarat
     "errorMessageName": "JSMSG_REDECLARED_VAR",
     "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Redeclared_parameter?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
     "sourceName": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": null,
     "lineText": "  let a, a;",
     "lineNumber": 2,
     "columnNumber": 9,
     "category": "content javascript",
+    "innerWindowID": 6442450949,
     "timeStamp": 1487992945524,
     "warning": false,
     "error": false,
     "exception": true,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": [
@@ -349,16 +361,17 @@ stubPackets.set(`TypeError longString me
     },
     "errorMessageName": "",
     "sourceName": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
     "sourceId": "server1.conn0.child1/source26",
     "lineText": "",
     "lineNumber": 1,
     "columnNumber": 7,
     "category": "content javascript",
+    "innerWindowID": 6442450949,
     "timeStamp": 1493109507061,
     "warning": false,
     "error": false,
     "exception": true,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": [
@@ -395,16 +408,17 @@ stubPackets.set(`throw ""`, {
     "errorMessage": "uncaught exception: ",
     "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
     "sourceName": "",
     "sourceId": null,
     "lineText": "",
     "lineNumber": 0,
     "columnNumber": 0,
     "category": "content javascript",
+    "innerWindowID": 6442450949,
     "timeStamp": 1517942398629,
     "warning": false,
     "error": false,
     "exception": false,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": null,
@@ -419,16 +433,17 @@ stubPackets.set(`throw "tomato"`, {
     "errorMessage": "uncaught exception: tomato",
     "errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
     "sourceName": "",
     "sourceId": null,
     "lineText": "",
     "lineNumber": 0,
     "columnNumber": 0,
     "category": "content javascript",
+    "innerWindowID": 6442450949,
     "timeStamp": 1517942398637,
     "warning": false,
     "error": false,
     "exception": false,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": null,
--- a/devtools/client/webconsole/test/mocha-test-setup.js
+++ b/devtools/client/webconsole/test/mocha-test-setup.js
@@ -19,16 +19,18 @@ pref("devtools.webconsole.filter.debug",
 pref("devtools.webconsole.filter.css", false);
 pref("devtools.webconsole.filter.net", false);
 pref("devtools.webconsole.filter.netxhr", false);
 pref("devtools.webconsole.inputHistoryCount", 300);
 pref("devtools.webconsole.persistlog", false);
 pref("devtools.webconsole.timestampMessages", false);
 pref("devtools.webconsole.sidebarToggle", true);
 pref("devtools.webconsole.jsterm.codeMirror", true);
+pref("devtools.webconsole.groupWarningMessages", false);
+pref("devtools.webconsole.input.editor", false);
 
 global.loader = {
   lazyServiceGetter: () => {},
   lazyGetter: (context, name, fn) => {
 
   },
   lazyRequireGetter: (context, name, path, destruct) => {
     if (path === "devtools/shared/async-storage") {
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -110,16 +110,17 @@ support-files =
   test-sourcemap-error-02.html
   test-sourcemap-error-02.js
   test-stacktrace-location-debugger-link.html
   test-subresource-security-error.html
   test-subresource-security-error.js
   test-subresource-security-error.js^headers^
   test-time-methods.html
   test-trackingprotection-securityerrors.html
+  test-warning-groups.html
   test-webconsole-error-observer.html
   test-websocket.html
   test-websocket.js
   testscript.js
   !/devtools/client/netmonitor/test/sjs_cors-test-server.sjs
   !/image/test/mochitest/blue.png
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -392,9 +393,11 @@ skip-if = verify
 [browser_webconsole_telemetry_object_expanded.js]
 [browser_webconsole_time_methods.js]
 [browser_webconsole_timestamps.js]
 [browser_webconsole_trackingprotection_errors.js]
 tags = trackingprotection
 [browser_webconsole_view_source.js]
 [browser_webconsole_visibility_messages.js]
 [browser_webconsole_warn_about_replaced_api.js]
+[browser_webconsole_warning_group_content_blocking.js]
+[browser_webconsole_warning_groups.js]
 [browser_webconsole_websocket.js]
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_trackingprotection_errors.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_trackingprotection_errors.js
@@ -113,24 +113,16 @@ add_task(async function testCookieBlocke
   await testLearnMoreClickOpenNewTab(message,
     getStorageErrorUrl("CookieBlockedByPermission"));
   win.close();
 
   // Remove the custom permission.
   Services.perms.removeFromPrincipal(p, "cookie");
 });
 
-async function openNewWindowAndConsole(url) {
-  const win = await openNewBrowserWindow();
-  const tab = await addTab(url, {window: win});
-  win.gBrowser.selectedTab = tab;
-  const hud = await openConsole(tab);
-  return {win, hud};
-}
-
 function getStorageErrorUrl(category) {
   const BASE_STORAGE_ERROR_URL = "https://developer.mozilla.org/docs/Mozilla/Firefox/" +
                                  "Privacy/Storage_access_policy/Errors/";
   const STORAGE_ERROR_URL_PARAMS = new URLSearchParams({
     utm_source: "devtools",
     utm_medium: "firefox-cookie-errors",
     utm_campaign: "default",
   }).toString();
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_warning_group_content_blocking.js
@@ -0,0 +1,194 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Load a page with tracking elements that get blocked and make sure that a
+// 'learn more' link shows up in the webconsole.
+
+"use strict";
+requestLongerTimeout(2);
+
+const TEST_FILE =
+  "browser/devtools/client/webconsole/test/mochitest/test-warning-groups.html";
+const TEST_URI = "http://example.com/" + TEST_FILE;
+
+const TRACKER_URL = "http://tracking.example.com/";
+const IMG_FILE = "browser/devtools/client/webconsole/test/mochitest/test-image.png";
+const TRACKER_IMG = "http://tracking.example.org/" + IMG_FILE;
+
+const CONTENT_BLOCKING_GROUP_LABEL = "Content blocked messages";
+
+const COOKIE_BEHAVIOR_PREF = "network.cookie.cookieBehavior";
+const COOKIE_BEHAVIORS = {
+// reject all third-party cookies
+  REJECT_FOREIGN: 1,
+// reject all cookies
+  REJECT: 2,
+// reject third-party cookies unless the eTLD already has at least one cookie
+  LIMIT_FOREIGN: 3,
+// reject trackers
+  REJECT_TRACKER: 4,
+};
+
+const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+UrlClassifierTestUtils.addTestTrackers();
+registerCleanupFunction(function() {
+  UrlClassifierTestUtils.cleanupTestTrackers();
+});
+
+pushPref("privacy.trackingprotection.enabled", true);
+pushPref("devtools.webconsole.groupWarningMessages", true);
+
+add_task(async function testContentBlockingMessage() {
+  const {hud, tab, win} = await openNewWindowAndConsole(
+    "http://tracking.example.org/" + TEST_FILE);
+  const now = Date.now();
+
+  info("Test content blocking message");
+  const message = `The resource at \u201chttp://tracking.example.com/?1&${now}\u201d ` +
+    `was blocked because content blocking is enabled`;
+  const onContentBlockingWarningMessage = waitForMessage(hud, message, ".warn");
+  emitContentBlockingMessage(tab, `${TRACKER_URL}?1&${now}`);
+  await onContentBlockingWarningMessage;
+
+  ok(true, "The content blocking message was displayed");
+
+  info("Emit a new content blocking message to check that it causes a grouping");
+  const onContentBlockingWarningGroupMessage =
+    waitForMessage(hud, CONTENT_BLOCKING_GROUP_LABEL, ".warn");
+  emitContentBlockingMessage(tab, `${TRACKER_URL}?2&${now}`);
+  const {node} = await onContentBlockingWarningGroupMessage;
+  is(node.querySelector(".warning-group-badge").textContent, "2",
+    "The badge has the expected text");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▶︎ ${CONTENT_BLOCKING_GROUP_LABEL} 2`,
+  ]);
+
+  info("Open the group");
+  node.querySelector(".arrow").click();
+  await waitFor(() => findMessage(hud, "http://tracking.example.com/?1"));
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL} 2`,
+    `| The resource at \u201chttp://tracking.example.com/?1&${now}\u201d was blocked`,
+    `| The resource at \u201chttp://tracking.example.com/?2&${now}\u201d was blocked`,
+  ]);
+  await win.close();
+});
+
+add_task(async function testForeignCookieBlockedMessage() {
+  info("Test foreign cookie blocked message");
+  // We change the pref and open a new window to ensure it will be taken into account.
+  await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS.REJECT_FOREIGN);
+  const getWarningMsg = url => `Request to access cookie or storage on “${url}” was ` +
+    `blocked because we are blocking all third-party`;
+  await testStorageAccessBlockedGrouping(getWarningMsg);
+});
+
+add_task(async function testLimitForeignCookieBlockedMessage() {
+  info("Test unvisited eTLD foreign cookies blocked message");
+  // We change the pref and open a new window to ensure it will be taken into account.
+  await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS.LIMIT_FOREIGN);
+  const getWarningMsg = url => `Request to access cookie or storage on “${url}” was ` +
+    `blocked because we are blocking all third-party`;
+  await testStorageAccessBlockedGrouping(getWarningMsg);
+});
+
+add_task(async function testAllCookieBlockedMessage() {
+  info("Test all cookies blocked message");
+  // We change the pref and open a new window to ensure it will be taken into account.
+  await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS.REJECT);
+  const getWarningMsg = url => `Request to access cookie or storage on “${url}” was ` +
+    `blocked because we are blocking all storage access requests`;
+  await testStorageAccessBlockedGrouping(getWarningMsg);
+});
+
+add_task(async function testTrackerCookieBlockedMessage() {
+  info("Test tracker cookie blocked message");
+  // We change the pref and open a new window to ensure it will be taken into account.
+  await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS.REJECT_TRACKER);
+  const getWarningMsg = url => `Request to access cookie or storage on “${url}” was ` +
+    `blocked because it came from a tracker`;
+  await testStorageAccessBlockedGrouping(getWarningMsg);
+});
+
+add_task(async function testCookieBlockedByPermissionMessage() {
+  info("Test cookie blocked by permission message");
+  // Turn off tracking protection and add a block permission on the URL.
+  await pushPref("privacy.trackingprotection.enabled", false);
+  const p = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("http://tracking.example.org/");
+  Services.perms.addFromPrincipal(p, "cookie", Ci.nsIPermissionManager.DENY_ACTION);
+
+  const getWarningMsg = url => `Request to access cookies or storage on “${url}” was ` +
+    `blocked because of custom cookie permission`;
+  await testStorageAccessBlockedGrouping(getWarningMsg);
+
+  // Remove the custom permission.
+  Services.perms.removeFromPrincipal(p, "cookie");
+});
+
+/**
+ * Test that storage access blocked messages are grouped by emitting 2 messages.
+ *
+ * @param {Function} getWarningMessage: A function that takes an URL string as a parameter
+ *                                  and returns the corresponding warning message.
+ */
+async function testStorageAccessBlockedGrouping(getWarningMessage) {
+  const {hud, win, tab} = await openNewWindowAndConsole(TEST_URI);
+  const now = Date.now();
+
+  hud.ui.clearOutput();
+  const onStorageAccessBlockedMessage =
+    waitForMessage(hud, getWarningMessage(`${TRACKER_IMG}?1&${now}`), ".warn");
+  emitStorageAccessBlockedMessage(tab, `${TRACKER_IMG}?1&${now}`);
+  await onStorageAccessBlockedMessage;
+
+  info("Emit a new content blocking message to check that it causes a grouping");
+  const onContentBlockingWarningGroupMessage =
+    waitForMessage(hud, CONTENT_BLOCKING_GROUP_LABEL, ".warn");
+  emitStorageAccessBlockedMessage(tab, `${TRACKER_IMG}?2&${now}`);
+  const {node} = await onContentBlockingWarningGroupMessage;
+  is(node.querySelector(".warning-group-badge").textContent, "2",
+    "The badge has the expected text");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▶︎ ${CONTENT_BLOCKING_GROUP_LABEL} 2`,
+  ]);
+
+  info("Open the group");
+  node.querySelector(".arrow").click();
+  await waitFor(() => findMessage(hud, TRACKER_IMG));
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL} 2`,
+    `| ${getWarningMessage(TRACKER_IMG + "?1&" + now)}`,
+    `| ${getWarningMessage(TRACKER_IMG + "?2&" + now)}`,
+  ]);
+
+  hud.ui.clearOutput();
+  await win.close();
+}
+
+/**
+ * Emit a Content Blocking message. This is done by loading an iframe from an origin
+ * tagged as tracker. The image is loaded with a incremented counter query parameter
+ * each time so we can get the warning message.
+ */
+function emitContentBlockingMessage(tab, url) {
+  ContentTask.spawn(tab.linkedBrowser, url, function(innerURL) {
+    content.wrappedJSObject.loadIframe(innerURL);
+  });
+}
+
+/**
+ * Emit a Storage blocked message. This is done by loading an image from an origin
+ * tagged as tracker. The image is loaded with a incremented counter query parameter
+ * each time so we can get the warning message.
+ */
+function emitStorageAccessBlockedMessage(tab, url) {
+  ContentTask.spawn(tab.linkedBrowser, url, async function(innerURL) {
+    content.wrappedJSObject.loadImage(innerURL);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_warning_groups.js
@@ -0,0 +1,241 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that warning messages can be grouped, per navigation and category, and that
+// interacting with these groups works as expected.
+
+"use strict";
+requestLongerTimeout(2);
+
+const TEST_FILE =
+  "browser/devtools/client/webconsole/test/mochitest/test-warning-groups.html";
+const TEST_URI = "http://example.com/" + TEST_FILE;
+
+const TRACKER_URL = "http://tracking.example.org/";
+const BLOCKED_URL = TRACKER_URL +
+  "browser/devtools/client/webconsole/test/mochitest/test-image.png";
+
+const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+registerCleanupFunction(function() {
+  UrlClassifierTestUtils.cleanupTestTrackers();
+});
+
+add_task(async function testContentBlockingMessage() {
+  const CONTENT_BLOCKING_GROUP_LABEL = "Content blocked messages";
+
+  // Tracking protection preferences
+  await UrlClassifierTestUtils.addTestTrackers();
+  await pushPref("privacy.trackingprotection.enabled", true);
+
+  // Enable groupWarning and persist log
+  await pushPref("devtools.webconsole.groupWarningMessages", true);
+  await pushPref("devtools.webconsole.persistlog", true);
+
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  info("Log a tracking protection message to check a single message isn't grouped");
+  let onContentBlockingWarningMessage = waitForMessage(hud, BLOCKED_URL, ".warn");
+  emitStorageAccessBlockedMessage(hud);
+  let {node} = await onContentBlockingWarningMessage;
+  is(node.querySelector(".warning-indent"), null, "The message has the expected style");
+  is(node.querySelector(".indent").getAttribute("data-indent"), "0",
+    "The message has the expected indent");
+
+  info("Log a simple message");
+  await logString(hud, "simple message 1");
+
+  info("Log a second tracking protection message to check that it causes the grouping");
+  let onContentBlockingWarningGroupMessage =
+    waitForMessage(hud, CONTENT_BLOCKING_GROUP_LABEL, ".warn");
+  emitStorageAccessBlockedMessage(hud);
+  ({node} = await onContentBlockingWarningGroupMessage);
+  is(node.querySelector(".warning-group-badge").textContent, "2",
+    "The badge has the expected text");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▶︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `simple message 1`,
+  ]);
+
+  info("Log another simple message");
+  await logString(hud, "simple message 2");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▶︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `simple message 1`,
+    `simple message 2`,
+  ]);
+
+  info("Log a third tracking protection message to check that the badge updates");
+  emitStorageAccessBlockedMessage(hud);
+  await waitFor(() => node.querySelector(".warning-group-badge").textContent == "3");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▶︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `simple message 1`,
+    `simple message 2`,
+  ]);
+
+  info("Open the group");
+  node.querySelector(".arrow").click();
+  await waitFor(() => findMessage(hud, BLOCKED_URL));
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `| ${BLOCKED_URL}?1`,
+    `| ${BLOCKED_URL}?2`,
+    `| ${BLOCKED_URL}?3`,
+    `simple message 1`,
+    `simple message 2`,
+  ]);
+
+  info("Log a new tracking protection message to check it appears inside the group");
+  onContentBlockingWarningMessage =
+    waitForMessage(hud, BLOCKED_URL, ".warn");
+  emitStorageAccessBlockedMessage(hud);
+  await onContentBlockingWarningMessage;
+  ok(true, "The new tracking protection message is displayed");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `| ${BLOCKED_URL}?1`,
+    `| ${BLOCKED_URL}?2`,
+    `| ${BLOCKED_URL}?3`,
+    `| ${BLOCKED_URL}?4`,
+    `simple message 1`,
+    `simple message 2`,
+  ]);
+
+  info("Reload the page and wait for it to be ready");
+  const onDomContentLoaded = BrowserTestUtils.waitForContentEvent(
+    hud.target.tab.linkedBrowser, "DOMContentLoaded", true);
+  ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
+    content.location.reload();
+  });
+  await onDomContentLoaded;
+
+  // Also wait for the navigation message to be displayed.
+  await waitFor(() => findMessage(hud, "Navigated to"));
+
+  info("Log a tracking protection message to check it is not grouped");
+  onContentBlockingWarningMessage =
+    waitForMessage(hud, BLOCKED_URL, ".warn");
+  emitStorageAccessBlockedMessage(hud);
+  await onContentBlockingWarningMessage;
+
+  await logString(hud, "simple message 3");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `| ${BLOCKED_URL}?1`,
+    `| ${BLOCKED_URL}?2`,
+    `| ${BLOCKED_URL}?3`,
+    `| ${BLOCKED_URL}?4`,
+    `simple message 1`,
+    `simple message 2`,
+    "Navigated to",
+    `${BLOCKED_URL}?5`,
+    `simple message 3`,
+  ]);
+
+  info("Log a second tracking protection message to check that it causes the grouping");
+  onContentBlockingWarningGroupMessage =
+    waitForMessage(hud, CONTENT_BLOCKING_GROUP_LABEL, ".warn");
+  emitStorageAccessBlockedMessage(hud);
+  ({node} = await onContentBlockingWarningGroupMessage);
+  is(node.querySelector(".warning-group-badge").textContent, "2",
+    "The badge has the expected text");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `| ${BLOCKED_URL}?1`,
+    `| ${BLOCKED_URL}?2`,
+    `| ${BLOCKED_URL}?3`,
+    `| ${BLOCKED_URL}?4`,
+    `simple message 1`,
+    `simple message 2`,
+    `Navigated to`,
+    `▶︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `simple message 3`,
+  ]);
+
+  info("Check that opening this group works");
+  node.querySelector(".arrow").click();
+  await waitFor(() => findMessages(hud, BLOCKED_URL).length === 6);
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `| ${BLOCKED_URL}?1`,
+    `| ${BLOCKED_URL}?2`,
+    `| ${BLOCKED_URL}?3`,
+    `| ${BLOCKED_URL}?4`,
+    `simple message 1`,
+    `simple message 2`,
+    `Navigated to`,
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `| ${BLOCKED_URL}?5`,
+    `| ${BLOCKED_URL}?6`,
+    `simple message 3`,
+  ]);
+
+  info("Check that closing this group works, and let the other one open");
+  node.querySelector(".arrow").click();
+  await waitFor(() => findMessages(hud, BLOCKED_URL).length === 4);
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `| ${BLOCKED_URL}?1`,
+    `| ${BLOCKED_URL}?2`,
+    `| ${BLOCKED_URL}?3`,
+    `| ${BLOCKED_URL}?4`,
+    `simple message 1`,
+    `simple message 2`,
+    `Navigated to`,
+    `▶︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `simple message 3`,
+  ]);
+
+  info("Log a third tracking protection message to check that the badge updates");
+  emitStorageAccessBlockedMessage(hud);
+  await waitFor(() => node.querySelector(".warning-group-badge").textContent == "3");
+
+  checkConsoleOutputForWarningGroup(hud, [
+    `▼︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `| ${BLOCKED_URL}?1`,
+    `| ${BLOCKED_URL}?2`,
+    `| ${BLOCKED_URL}?3`,
+    `| ${BLOCKED_URL}?4`,
+    `simple message 1`,
+    `simple message 2`,
+    `Navigated to`,
+    `▶︎ ${CONTENT_BLOCKING_GROUP_LABEL}`,
+    `simple message 3`,
+  ]);
+});
+
+let cpt = 0;
+/**
+ * Emit a Content Blocking message. This is done by loading an image from an origin
+ * tagged as tracker. The image is loaded with a incremented counter query parameter
+ * each time so we can get the warning message.
+ */
+function emitStorageAccessBlockedMessage() {
+  const url = `${BLOCKED_URL}?${++cpt}`;
+  ContentTask.spawn(gBrowser.selectedBrowser, url, function(innerURL) {
+    content.wrappedJSObject.loadImage(innerURL);
+  });
+}
+
+/**
+ * Log a string from the content page.
+ *
+ * @param {WebConsole} hud
+ * @param {String} str
+ */
+function logString(hud, str) {
+  const onMessage = waitForMessage(hud, str);
+  ContentTask.spawn(gBrowser.selectedBrowser, str, function(arg) {
+    content.console.log(arg);
+  });
+  return onMessage;
+}
--- a/devtools/client/webconsole/test/mochitest/head.js
+++ b/devtools/client/webconsole/test/mochitest/head.js
@@ -82,16 +82,33 @@ async function openNewTabAndConsole(url,
     // Clearing history that might have been set in previous tests.
     await hud.ui.wrapper.dispatchClearHistory();
   }
 
   return hud;
 }
 
 /**
+ * Open a new window with a tab,open the toolbox, and select the webconsole.
+ *
+ * @param string url
+ *        The URL for the tab to be opened.
+ * @return Promise<{win, hud, tab}>
+ *         Resolves when the tab has been added, loaded and the toolbox has been opened.
+ *         Resolves to the toolbox.
+ */
+async function openNewWindowAndConsole(url) {
+  const win = await openNewBrowserWindow();
+  const tab = await addTab(url, {window: win});
+  win.gBrowser.selectedTab = tab;
+  const hud = await openConsole(tab);
+  return {win, hud, tab};
+}
+
+/**
  * Subscribe to the store and log out stringinfied versions of messages.
  * This is a helper function for debugging, to make is easier to see what
  * happened during the test in the log.
  *
  * @param object hud
  */
 function logAllStoreChanges(hud) {
   const store = hud.ui.wrapper.getStore();
@@ -1216,8 +1233,48 @@ function hasVerticalOverflow(container) 
 function isScrolledToBottom(container) {
   if (!container.lastChild) {
     return true;
   }
   const lastNodeHeight = container.lastChild.clientHeight;
   return container.scrollTop + container.clientHeight >=
          container.scrollHeight - lastNodeHeight / 2;
 }
+
+/**
+ *
+ * @param {WebConsole} hud
+ * @param {Array<String>} expectedMessages: An array of string representing the messages
+ *                        from the output. This can only be a part of the string of the
+ *                        message.
+ *                        Start the string with "▶︎ " or "▼ " to indicate that the
+ *                        message is a warningGroup (with respectively an open or
+ *                        collapsed arrow).
+ *                        Start the string with "|︎ " to indicate that the message is
+ *                        inside a group and should be indented.
+ */
+function checkConsoleOutputForWarningGroup(hud, expectedMessages) {
+  const messages = findMessages(hud, "");
+  is(messages.length, expectedMessages.length, "Got the expected number of messages");
+  expectedMessages.forEach((expectedMessage, i) => {
+    const message = messages[i];
+    if (expectedMessage.startsWith("▶︎")) {
+      is(message.querySelector(".arrow").getAttribute("aria-expanded"), "false",
+        "There's a collapsed arrow");
+      expectedMessage = expectedMessage.replace("▶︎ ", "");
+    }
+
+    if (expectedMessage.startsWith("▼")) {
+      is(message.querySelector(".arrow").getAttribute("aria-expanded"), "true",
+        "There's an expanded arrow");
+      expectedMessage = expectedMessage.replace("▼︎ ", "");
+    }
+
+    if (expectedMessage.startsWith("|")) {
+      is(message.querySelector(".indent.warning-indent").getAttribute("data-indent"), "1",
+        "The message has the expected indent");
+      expectedMessage = expectedMessage.replace("| ", "");
+    }
+
+    ok(message.textContent.trim().includes(expectedMessage.trim()), `Message includes ` +
+      `the expected "${expectedMessage}" content - "${message.textContent.trim()}"`);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-warning-groups.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Warning groups webconsole test page</title>
+  </head>
+  <body>
+    <p>Warning groups webconsole test page</p>
+    <script>
+      "use strict";
+
+      /* exported loadImage, loadIframe */
+      function loadImage(src) {
+        const img = document.createElement("img");
+        img.src = src;
+        img.alt = (new URL(src)).search;
+        img.title = src;
+        document.body.appendChild(img);
+      }
+
+      function loadIframe(src) {
+        const iframe = document.createElement("iframe");
+        iframe.src = src;
+        document.body.appendChild(iframe);
+      }
+    </script>
+  </body>
+</html>
--- a/devtools/client/webconsole/types.js
+++ b/devtools/client/webconsole/types.js
@@ -24,22 +24,24 @@ exports.ConsoleCommand = function(props)
     private: false,
     timeStamp: null,
   }, props);
 };
 
 exports.ConsoleMessage = function(props) {
   return Object.assign({
     id: null,
+    innerWindowID: null,
     allowRepeating: true,
     source: null,
     timeStamp: null,
     type: null,
     helperType: null,
     level: null,
+    category: null,
     messageText: null,
     parameters: null,
     repeatId: null,
     stacktrace: null,
     frame: null,
     groupId: null,
     errorMessageName: null,
     exceptionDocURL: null,
--- a/devtools/client/webconsole/utils/messages.js
+++ b/devtools/client/webconsole/utils/messages.js
@@ -240,19 +240,21 @@ function transformPageErrorPacket(packet
     line: pageError.lineNumber,
     column: pageError.columnNumber,
   } : null;
 
   const matchesCSS = /^(?:CSS|Layout)\b/.test(pageError.category);
   const messageSource = matchesCSS ? MESSAGE_SOURCE.CSS
                                   : MESSAGE_SOURCE.JAVASCRIPT;
   return new ConsoleMessage({
+    innerWindowID: pageError.innerWindowID,
     source: messageSource,
     type: MESSAGE_TYPE.LOG,
     level,
+    category: pageError.category,
     messageText: pageError.errorMessage,
     stacktrace: pageError.stacktrace ? pageError.stacktrace : null,
     frame,
     errorMessageName: pageError.errorMessageName,
     exceptionDocURL: pageError.exceptionDocURL,
     timeStamp: pageError.timeStamp,
     notes: pageError.notes,
     private: pageError.private,
@@ -423,17 +425,86 @@ function isPacketPrivate(packet) {
   return (
     packet.private === true ||
     (packet.message && packet.message.private === true) ||
     (packet.pageError && packet.pageError.private === true) ||
     (packet.networkEvent && packet.networkEvent.private === true)
   );
 }
 
+function createWarningGroupMessage(id, type, firstMessage) {
+  let messageText;
+  if (type === MESSAGE_TYPE.CONTENT_BLOCKING_GROUP) {
+    messageText = l10n.getStr("webconsole.group.contentBlocked");
+  }
+  return new ConsoleMessage({
+    id,
+    level: MESSAGE_LEVEL.WARN,
+    source: MESSAGE_SOURCE.CONSOLE_FRONTEND,
+    type,
+    messageText,
+    timeStamp: firstMessage.timeStamp,
+    innerWindowID: firstMessage.innerWindowID,
+  });
+}
+
+/**
+ * Get the warningGroup type in which the message could be in.
+ * @param {ConsoleMessage} message
+ * @returns {String|null} null if the message can't be part of a warningGroup.
+ */
+function getWarningGroupType(message) {
+  if (isContentBlockingMessage(message)) {
+    return MESSAGE_TYPE.CONTENT_BLOCKING_GROUP;
+  }
+  return null;
+}
+
+/**
+ * Returns a computed id given a message
+ *
+ * @param {ConsoleMessage} type: the message type, from MESSAGE_TYPE.
+ * @param {Integer} innerWindowID: the message innerWindowID.
+ * @returns {String}
+ */
+function getParentWarningGroupMessageId(message) {
+  return `${message.type}-${message.innerWindowID}`;
+}
+
+/**
+ * Returns true if the message is a warningGroup message (i.e. the "Header").
+ * @param {ConsoleMessage} message
+ * @returns {Boolean}
+ */
+function isWarningGroup(message) {
+  return message.type === MESSAGE_TYPE.CONTENT_BLOCKING_GROUP
+   || message.type === MESSAGE_TYPE.CORS_GROUP
+   || message.type === MESSAGE_TYPE.CSP_GROUP;
+}
+
+/**
+ * Returns true if the message is a content blocking message.
+ * @param {ConsoleMessage} message
+ * @returns {Boolean}
+ */
+function isContentBlockingMessage(message) {
+  const {category} = message;
+  return category == "cookieBlockedPermission" ||
+    category == "cookieBlockedTracker" ||
+    category == "cookieBlockedAll" ||
+    category == "cookieBlockedForeign" ||
+    category == "Tracking Protection";
+}
+
 module.exports = {
+  createWarningGroupMessage,
   getInitialMessageCountForViewport,
+  getParentWarningGroupMessageId,
+  getWarningGroupType,
+  isContentBlockingMessage,
   isGroupType,
   isPacketPrivate,
+  isWarningGroup,
   l10n,
   prepareMessage,
   // Export for use in testing.
   getRepeatId,
 };
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1514,16 +1514,17 @@ WebConsoleActor.prototype =
       errorMessageName: pageError.errorMessageName,
       exceptionDocURL: ErrorDocs.GetURL(pageError),
       sourceName: pageError.sourceName,
       sourceId: this.getActorIdForInternalSourceId(pageError.sourceId),
       lineText: lineText,
       lineNumber: pageError.lineNumber,
       columnNumber: pageError.columnNumber,
       category: pageError.category,
+      innerWindowID: pageError.innerWindowID,
       timeStamp: pageError.timeStamp,
       warning: !!(pageError.flags & pageError.warningFlag),
       error: !!(pageError.flags & pageError.errorFlag),
       exception: !!(pageError.flags & pageError.exceptionFlag),
       strict: !!(pageError.flags & pageError.strictFlag),
       info: !!(pageError.flags & pageError.infoFlag),
       private: pageError.isFromPrivateWindow,
       stacktrace: stack,
@@ -1712,16 +1713,17 @@ WebConsoleActor.prototype =
       return this.createValueGrip(dbgObj);
     });
 
     result.styles = Array.map(message.styles || [], (string) => {
       return this.createValueGrip(string);
     });
 
     result.category = message.category || "webdev";
+    result.innerWindowID = message.innerID;
 
     return result;
   },
 
   /**
    * Find the XUL window that owns the content window.
    *
    * @return Window
--- a/docshell/base/nsDocShellEditorData.cpp
+++ b/docshell/base/nsDocShellEditorData.cpp
@@ -53,33 +53,16 @@ nsresult nsDocShellEditorData::MakeEdita
   }
   return NS_OK;
 }
 
 bool nsDocShellEditorData::GetEditable() {
   return mMakeEditable || (mHTMLEditor != nullptr);
 }
 
-nsresult nsDocShellEditorData::CreateEditor() {
-  nsCOMPtr<nsIEditingSession> editingSession;
-  nsresult rv = GetEditingSession(getter_AddRefs(editingSession));
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  nsCOMPtr<nsPIDOMWindowOuter> domWindow =
-      mDocShell ? mDocShell->GetWindow() : nullptr;
-  rv = editingSession->SetupEditorOnWindow(domWindow);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
 nsresult nsDocShellEditorData::GetEditingSession(nsIEditingSession** aResult) {
   EnsureEditingSession();
 
   NS_ADDREF(*aResult = mEditingSession);
 
   return NS_OK;
 }
 
--- a/docshell/base/nsDocShellEditorData.h
+++ b/docshell/base/nsDocShellEditorData.h
@@ -19,17 +19,16 @@ class nsIEditingSession;
 
 class nsDocShellEditorData {
  public:
   explicit nsDocShellEditorData(nsIDocShell* aOwningDocShell);
   ~nsDocShellEditorData();
 
   nsresult MakeEditable(bool aWaitForUriLoad);
   bool GetEditable();
-  nsresult CreateEditor();
   nsresult GetEditingSession(nsIEditingSession** aResult);
   mozilla::HTMLEditor* GetHTMLEditor() const { return mHTMLEditor; }
   nsresult SetHTMLEditor(mozilla::HTMLEditor* aHTMLEditor);
   void TearDownEditor();
   nsresult DetachFromWindow();
   nsresult ReattachToWindow(nsIDocShell* aDocShell);
   bool WaitingForLoad() const { return mMakeEditable; }
 
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1340,18 +1340,20 @@ nsresult nsTextEditorState::PrepareEdito
     // for its content manipulations, and it causes it to fail some security
     // checks deep inside when initializing. So we explictly make it clear that
     // we're native code.
     // Note that any script that's directly trying to access our value
     // has to be going through some scriptable object to do that and that
     // already does the relevant security checks.
     AutoNoJSAPI nojsapi;
 
-    rv = newTextEditor->Init(*doc, GetRootNode(), mSelCon, editorFlags,
-                             defaultValue);
+    RefPtr<Element> rootElement = GetRootNode();
+    RefPtr<nsTextInputSelectionImpl> selectionController = mSelCon;
+    rv = newTextEditor->Init(*doc, rootElement, selectionController,
+                             editorFlags, defaultValue);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Initialize the controller for the editor
 
   if (!SuppressEventHandlers(presContext)) {
     nsCOMPtr<nsIControllers> controllers;
     nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
--- a/dom/media/gtest/TestMP3Demuxer.cpp
+++ b/dom/media/gtest/TestMP3Demuxer.cpp
@@ -42,28 +42,38 @@ class MockMP3StreamMediaResource
 
   int64_t GetLength() override { return -1; }
 
  protected:
   virtual ~MockMP3StreamMediaResource() {}
 };
 
 struct MP3Resource {
+  enum class HeaderType { NONE, XING, VBRI };
+  struct Duration {
+    int64_t mMicroseconds;
+    float mTolerableRate;
+
+    Duration(int64_t aMicroseconds, float aTolerableRate)
+        : mMicroseconds(aMicroseconds), mTolerableRate(aTolerableRate) {}
+    int64_t Tolerance() const { return mTolerableRate * mMicroseconds; }
+  };
+
   const char* mFilePath;
   bool mIsVBR;
+  HeaderType mHeaderType;
   int64_t mFileSize;
   int32_t mMPEGLayer;
   int32_t mMPEGVersion;
   uint8_t mID3MajorVersion;
   uint8_t mID3MinorVersion;
   uint8_t mID3Flags;
   uint32_t mID3Size;
 
-  int64_t mDuration;
-  float mDurationError;
+  Maybe<Duration> mDuration;
   float mSeekError;
   int32_t mSampleRate;
   int32_t mSamplesPerFrame;
   uint32_t mNumSamples;
   // TODO: temp solution, we could parse them instead or account for them
   // otherwise.
   int32_t mNumTrailingFrames;
   int32_t mBitrate;
@@ -78,41 +88,40 @@ struct MP3Resource {
 
 class MP3DemuxerTest : public ::testing::Test {
  protected:
   void SetUp() override {
     {
       MP3Resource res;
       res.mFilePath = "noise.mp3";
       res.mIsVBR = false;
+      res.mHeaderType = MP3Resource::HeaderType::NONE;
       res.mFileSize = 965257;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 3;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 2141;
-      res.mDuration = 30067000;
-      res.mDurationError = 0.001f;
+      res.mDuration = Some(MP3Resource::Duration{30067000, 0.001f});
       res.mSeekError = 0.02f;
       res.mSampleRate = 44100;
       res.mSamplesPerFrame = 1152;
       res.mNumSamples = 1325952;
       res.mNumTrailingFrames = 2;
       res.mBitrate = 256000;
       res.mSlotSize = 1;
       res.mPrivate = 0;
       const int syncs[] = {2151, 2987, 3823, 4659, 5495, 6331};
       res.mSyncOffsets.insert(res.mSyncOffsets.begin(), syncs, syncs + 6);
 
       // No content length can be estimated for CBR stream resources.
       MP3Resource streamRes = res;
       streamRes.mFileSize = -1;
-      streamRes.mDuration = -1;
-      streamRes.mDurationError = 0.0f;
+      streamRes.mDuration = Nothing();
 
       res.mResource = new MockMP3MediaResource(res.mFilePath);
       res.mDemuxer = new MP3TrackDemuxer(res.mResource);
       mTargets.push_back(res);
 
       streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
@@ -122,64 +131,63 @@ class MP3DemuxerTest : public ::testing:
       MP3Resource res;
       // This file trips up the MP3 demuxer if ID3v2 tags aren't properly
       // skipped. If skipping is not properly implemented, depending on the
       // strictness of the MPEG frame parser a false sync will be detected
       // somewhere within the metadata at or after 112087, or failing that, at
       // the artificially added extraneous header at 114532.
       res.mFilePath = "id3v2header.mp3";
       res.mIsVBR = false;
+      res.mHeaderType = MP3Resource::HeaderType::NONE;
       res.mFileSize = 191302;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 3;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 115304;
-      res.mDuration = 3166167;
-      res.mDurationError = 0.001f;
+      res.mDuration = Some(MP3Resource::Duration{3166167, 0.001f});
       res.mSeekError = 0.02f;
       res.mSampleRate = 44100;
       res.mSamplesPerFrame = 1152;
       res.mNumSamples = 139392;
       res.mNumTrailingFrames = 0;
       res.mBitrate = 192000;
       res.mSlotSize = 1;
       res.mPrivate = 1;
       const int syncs[] = {115314, 115941, 116568, 117195, 117822, 118449};
       res.mSyncOffsets.insert(res.mSyncOffsets.begin(), syncs, syncs + 6);
 
       // No content length can be estimated for CBR stream resources.
       MP3Resource streamRes = res;
       streamRes.mFileSize = -1;
-      streamRes.mDuration = -1;
-      streamRes.mDurationError = 0.0f;
+      streamRes.mDuration = Nothing();
 
       res.mResource = new MockMP3MediaResource(res.mFilePath);
       res.mDemuxer = new MP3TrackDemuxer(res.mResource);
       mTargets.push_back(res);
 
       streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
     {
       MP3Resource res;
       res.mFilePath = "noise_vbr.mp3";
       res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::HeaderType::XING;
       res.mFileSize = 583679;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 3;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 2221;
-      res.mDuration = 30081000;
-      res.mDurationError = 0.005f;
+      res.mDuration = Some(MP3Resource::Duration{30081000, 0.005f});
       res.mSeekError = 0.02f;
       res.mSampleRate = 44100;
       res.mSamplesPerFrame = 1152;
       res.mNumSamples = 1326575;
       res.mNumTrailingFrames = 3;
       res.mBitrate = 154000;
       res.mSlotSize = 1;
       res.mPrivate = 0;
@@ -199,25 +207,25 @@ class MP3DemuxerTest : public ::testing:
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
     {
       MP3Resource res;
       res.mFilePath = "small-shot.mp3";
       res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::HeaderType::XING;
       res.mFileSize = 6825;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 4;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 24;
-      res.mDuration = 336686;
-      res.mDurationError = 0.01f;
+      res.mDuration = Some(MP3Resource::Duration{336686, 0.01f});
       res.mSeekError = 0.2f;
       res.mSampleRate = 44100;
       res.mSamplesPerFrame = 1152;
       res.mNumSamples = 12;
       res.mNumTrailingFrames = 0;
       res.mBitrate = 256000;
       res.mSlotSize = 1;
       res.mPrivate = 0;
@@ -239,25 +247,25 @@ class MP3DemuxerTest : public ::testing:
     }
 
     {
       MP3Resource res;
       // This file contains a false frame sync at 34, just after the ID3 tag,
       // which should be identified as a false positive and skipped.
       res.mFilePath = "small-shot-false-positive.mp3";
       res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::HeaderType::XING;
       res.mFileSize = 6845;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 4;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 24;
-      res.mDuration = 336686;
-      res.mDurationError = 0.01f;
+      res.mDuration = Some(MP3Resource::Duration{336686, 0.01f});
       res.mSeekError = 0.2f;
       res.mSampleRate = 44100;
       res.mSamplesPerFrame = 1152;
       res.mNumSamples = 12;
       res.mNumTrailingFrames = 0;
       res.mBitrate = 256000;
       res.mSlotSize = 1;
       res.mPrivate = 0;
@@ -277,25 +285,25 @@ class MP3DemuxerTest : public ::testing:
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
     {
       MP3Resource res;
       res.mFilePath = "small-shot-partial-xing.mp3";
       res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::HeaderType::XING;
       res.mFileSize = 6825;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 4;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 24;
-      res.mDuration = 336686;
-      res.mDurationError = 0.01f;
+      res.mDuration = Some(MP3Resource::Duration{336686, 0.01f});
       res.mSeekError = 0.2f;
       res.mSampleRate = 44100;
       res.mSamplesPerFrame = 1152;
       res.mNumSamples = 12;
       res.mNumTrailingFrames = 0;
       res.mBitrate = 256000;
       res.mSlotSize = 1;
       res.mPrivate = 0;
@@ -311,16 +319,54 @@ class MP3DemuxerTest : public ::testing:
       res.mDemuxer = new MP3TrackDemuxer(res.mResource);
       mTargets.push_back(res);
 
       streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
+    {
+      MP3Resource res;
+      res.mFilePath = "test_vbri.mp3";
+      res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::HeaderType::VBRI;
+      res.mFileSize = 16519;
+      res.mMPEGLayer = 3;
+      res.mMPEGVersion = 1;
+      res.mID3MajorVersion = 3;
+      res.mID3MinorVersion = 0;
+      res.mID3Flags = 0;
+      res.mID3Size = 4202;
+      res.mDuration = Some(MP3Resource::Duration{783660, 0.01f});
+      res.mSeekError = 0.02f;
+      res.mSampleRate = 44100;
+      res.mSamplesPerFrame = 1152;
+      res.mNumSamples = 29;
+      res.mNumTrailingFrames = 0;
+      res.mBitrate = 0;
+      res.mSlotSize = 1;
+      res.mPrivate = 0;
+      const int syncs[] = {4212, 4734, 5047, 5464, 5986, 6403};
+      res.mSyncOffsets.insert(res.mSyncOffsets.begin(), syncs, syncs + 6);
+
+      // VBR stream resources contain header info on total frames numbers, which
+      // is used to estimate the total duration.
+      MP3Resource streamRes = res;
+      streamRes.mFileSize = -1;
+
+      res.mResource = new MockMP3MediaResource(res.mFilePath);
+      res.mDemuxer = new MP3TrackDemuxer(res.mResource);
+      mTargets.push_back(res);
+
+      streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
+      streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
+      mTargets.push_back(streamRes);
+    }
+
     for (auto& target : mTargets) {
       ASSERT_EQ(NS_OK, target.mResource->Open());
       ASSERT_TRUE(target.mDemuxer->Init());
     }
   }
 
   std::vector<MP3Resource> mTargets;
 };
@@ -342,22 +388,25 @@ TEST_F(MP3DemuxerTest, ID3Tags) {
 
 TEST_F(MP3DemuxerTest, VBRHeader) {
   for (const auto& target : mTargets) {
     RefPtr<MediaRawData> frame(target.mDemuxer->DemuxSample());
     ASSERT_TRUE(frame);
 
     const auto& vbr = target.mDemuxer->VBRInfo();
 
-    if (target.mIsVBR) {
+    if (target.mHeaderType == MP3Resource::HeaderType::XING) {
       EXPECT_EQ(FrameParser::VBRHeader::XING, vbr.Type());
       // TODO: find reference number which accounts for trailing headers.
       // EXPECT_EQ(target.mNumSamples / target.mSamplesPerFrame,
       // vbr.NumAudioFrames().value());
-    } else {
+    } else if (target.mHeaderType == MP3Resource::HeaderType::VBRI) {
+      EXPECT_TRUE(target.mIsVBR);
+      EXPECT_EQ(FrameParser::VBRHeader::VBRI, vbr.Type());
+    } else {  // MP3Resource::HeaderType::NONE
       EXPECT_EQ(FrameParser::VBRHeader::NONE, vbr.Type());
       EXPECT_FALSE(vbr.NumAudioFrames());
     }
   }
 }
 
 TEST_F(MP3DemuxerTest, FrameParsing) {
   for (const auto& target : mTargets) {
@@ -423,36 +472,41 @@ TEST_F(MP3DemuxerTest, FrameParsing) {
 
 TEST_F(MP3DemuxerTest, Duration) {
   for (const auto& target : mTargets) {
     RefPtr<MediaRawData> frameData(target.mDemuxer->DemuxSample());
     ASSERT_TRUE(frameData);
     EXPECT_EQ(target.mFileSize, target.mDemuxer->StreamLength());
 
     while (frameData) {
-      EXPECT_NEAR(target.mDuration,
-                  target.mDemuxer->Duration().ToMicroseconds(),
-                  target.mDurationError * target.mDuration);
-
+      if (target.mDuration) {
+        ASSERT_TRUE(target.mDemuxer->Duration());
+        EXPECT_NEAR(target.mDuration->mMicroseconds,
+                    target.mDemuxer->Duration()->ToMicroseconds(),
+                    target.mDuration->Tolerance());
+      } else {
+        EXPECT_FALSE(target.mDemuxer->Duration());
+      }
       frameData = target.mDemuxer->DemuxSample();
     }
   }
 
   // Seek out of range tests.
   for (const auto& target : mTargets) {
     // Skip tests for stream media resources because of lacking duration.
     if (target.mFileSize <= 0) {
       continue;
     }
 
     target.mDemuxer->Reset();
     RefPtr<MediaRawData> frameData(target.mDemuxer->DemuxSample());
     ASSERT_TRUE(frameData);
 
-    const auto duration = target.mDemuxer->Duration();
+    ASSERT_TRUE(target.mDemuxer->Duration());
+    const auto duration = target.mDemuxer->Duration().value();
     const auto pos = duration + TimeUnit::FromMicroseconds(1e6);
 
     // Attempt to seek 1 second past the end of stream.
     target.mDemuxer->Seek(pos);
     // The seek should bring us to the end of the stream.
     EXPECT_NEAR(duration.ToMicroseconds(),
                 target.mDemuxer->SeekPosition().ToMicroseconds(),
                 target.mSeekError * duration.ToMicroseconds());
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -74,16 +74,17 @@ TEST_HARNESS_FILES.gtest += [
     'short-zero-inband.mov',
     'small-shot-false-positive.mp3',
     'small-shot-partial-xing.mp3',
     'small-shot.mp3',
     'test.webm',
     'test_case_1224361.vp8.ivf',
     'test_case_1224363.vp8.ivf',
     'test_case_1224369.vp8.ivf',
+    'test_vbri.mp3',
 ]
 
 TEST_DIRS += [
     'mp4_demuxer',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..efd74503385c9d8c6afaf59a8923cf67ebd71805
GIT binary patch
literal 16519
zc%1EeWmH>Hw=G`Wp+JG+#a)WK1h?Ss!JPud9YS%}6nFOm1&TYxA*E1UTeN5%eE0sk
z@5dYCjq(1x(V4N6ot>S1a`rlNt+`eXK!yhq?ip0}+LFpr&mDs2rsCoUa&$DOQn9uI
znNw+7dw6QA%W^+|fb-m>?Yx}XR6x#D(st&~w%XEas?USS&yA6bn@Jm>&GX!S^W0v(
zMr8wXw5IX_dD>CwNNED7?5xeLtlgg{y)1_N+?-uN9@bQzFH=2ToUP&h;UE6tAO7JV
z{^1|~;UE6tAO7JV{^9=<!0rnr{!epW$^~%nNWm|C&X<}51spjXJRAZXJOVnrAUx#x
z8S+0L&tp#i<Nbfe{_FMs?K%C!{|#XGt`cx?2$5)F=DVa+s*>;4MF!|l^xo1&M)wC#
zAr`yNfz?|T)<FW?70Ft0AwTB6%J`v{N|5}78~hyXNFGdU^)<waedrp8QB@i3Y%~Jg
zAc4{~7!yp!8x<R}vMk7p9Bk*JM;<x5(NvC-?n=#%Eq_I$WF1e#Gd~1S6-DD+`x+d;
z>Wl0xVM7dGsQ2}RO76h&M7$b1d>K*xQ!yKP!7>uV2_+t41H2{2XePPx_I(tV$@>n@
zPvYuW76?|j8~aoMODE&Uzq0*%zrp00hIN%BT8-Hun1y0N`mlO!?4Zg|qaFpY!&kR3
zrUEtHvZt|mi4fLMo}k-J0W6sND>wlk;*qR%z3rl{DfLC;d-vebS;6ts^8y%5;RCLh
z<Xvv!u<rF89F_wM2nbY<O$on+iFldA?h_?m1tUzOj+!5HQ5kODvWn93W0IP3?SWM)
z91*QAFEfF2H-=K`p2U4Wf8GJ2Wyxh!TQF$YM~Y)HLQpc^5AMteYY6J<48)1?WwiIC
zeTV-7ml25*?(sz|N3(E3$X@bz4fhL&Y66Vqq>hfVb11p85~9YA+El&7m7Xg3nK%Mh
zrut@CUKw(a$we(25l29U7KaSa)qh%|3#sM3S}@|y(B8wTK1CF8dtZh0$2ub>6O~mV
zUor`GzIsOAS5Wb_R_{~6qiHqJggQ&ZDycb@NSTh_tE)(d-YkOFn|%O-tc4@@{dRVi
z`9`*9$;FQqCIPEMZ_Ns0``EEAK%Td@jqU2h=z*<Csq<ZWTP4uGq~gY_&Bg?<AX9fj
zj0i>j-K`^$a~EpK@pMG%%$sa>u9L+w`GrP^1)qUj#|;a=<zR)Z;I@X|^uhn2(WjgE
zx^ubJvUhz7`otr6^!<=PsO6xGg(mwvp*_3~@2EXl)(m+kM(e_N=J!g%7Y|Q{{(wRx
zy=X5WfX;~?#j}{t!<PSsxuL!<gfbqX^u@<v!6C9(O!)qKV{hXqSRi8QggINpw+D8D
zK$><R{=_%C7~K2NWamY^3a+DFUyJ;quOlTs%{+;j5A{ntxQSAM86Oa&Wet5<%6je2
zl2f33!rUg5Z*+U^v*)ClV9GjDiaHyX94NL(UW5NL9`}4~k}jG5=TvI5W<Rh4qUf9D
zxcd0Zd{)R=ilSBKz3j67p{@Hh4Q$HeHZ8YUVerx2iY~l5f+>h;gLmS*m+AO{XEqju
z-m0GY#5A~_o^OS%`EJhJn{;(bRVRl7*Fv!+mR0|d8k$wpDhEX($c{jjsG6_1yck+K
zpVLNY%+WvwD7ly6ZTRizjK2mt-<a~KJ)U!1EU}@(7;4C2&4<~(Cess@rVf9%tz_qR
zwpq8;ro(M9E@-&BJ*r2ACl=Ut!tWYM&TkWB^X=(ZvQM0n)j@)d)l>#^td3xHy<dl#
zd_37nhUp*A{cuC?q>5!4%R%D3-<ftNJg2Bx-jc*7Swr$l4X4!Z9R6HS*;fw9<KBOK
zbP@=>$)w|nv{IC(=T7OM+?Ewt^}e{|WBnAPpa{UDtE{YVqZx*vL697Z{051B8~i|5
zaL~T?ux3fx!e}76ooM8jEj6)|Ph~pRzHO{a@_n!zpV?B{yJ#KAZ|KtTP;-2&d+hO*
zntYe!QJ@GwWyMVv(1T=V_7TRhy2j^q8oyALI~Xxdcf|bH8#7n*6-*HeN9=CXx0skt
zA<!z?laTlZtz`B2YRz4fP6{Wi>PAkPEP5a?BIb)Lv!j|A8&7Qa=|Sj`fpgrT=t8OP
z{E5ZX8%pIe*{%+<$Nj+Cz5N_xRVz>ynNQ|H5rg55f$F$}(9yy5au<y^bF#-u;sC<t
z9}=q&GdN$VcM+4Qp}z(ddu9A7;6EGkIq!Z9*IN-EgM9E>q&1B<SA@yl*kVtbr@VRH
z^uP%)omIQJp>g2W-#JRURn^n-P1C8#%J((Bg;_D2tmc{2F|X<hh%Xh}+itK_W~A$k
z70c+`+2~j>%>;ksm8_FYa^uq4Dpc3bn|@C~o{Gy+%6_?ZT6#28=b>UyZ{U`QB1Yj&
zax|cdSVWd&DGRnQ-5=tdOFDt)+QaSRYS!y4+IZ~kzF^Or41&!(y~{D47E`WMsHw2A
z@jaug9`#54lDWPeaVUS`KRu>(e3}(THRys*`E9n_G6Hnhv^@4>N3B=vDRR>A{^El5
zG_d>R@lRVbk%En_mHZngy1pm3Zbj!knX?xIp}`^iWmmf0!C$qlUU7b=s#38U-(ImT
zQ)1=rmp}0>d3=Hq$>y&-t*ks9nqv>awUp#HvuRzX`&t1tM!Ui+jT3U<MXJ3wOSf5*
zDE$f7PIdJJ90BD9isp6cuzX1gii0x`8Xm`^t0UL2Lu3UnD_ONWTg+xQ0}J`c`h3Ia
zm3EGz28=4b3cW1S1rH%d{RF@VuyJ^D0XpSiwwP6St{5H}Ye8Bb%>%ebx%G6?Td4?@
zDSu2Bu*ns0XWHq7zw4V-w#QtSlhxS(P&qqjyVTlD<f#_n;W2cK@1#F9+M=HBiBic6
zbyU^RDe_4D1s#Vrx`7xA-mQlDHhCTq@3sFTh4!*d)#xMB@`>#@+}<8mJra-#oKH19
znSazOIDRbsihAGr+V(K+H>JenyRW3BHu_n$t#aG=H{A2M5RHhzyibVxIcBvOULw<z
zPPF1*Cs1+ew6bhHXkn_(-yQlpZi`I6bnFC<?t+3g$y1+nr!yRDgOYBzMY36=k_z)h
z9V*|thQB?4()Mti8Tp@m`Abd=`VeX3;Phf3#9)Nq_>;Z|q@AX8fC7;LB_$CTzP(Hk
zbu_olm)|ih$pQ#E-;7oru-HVw$|0|9_Uh2$ouzEbl5wO)w>j9%@(RQ~aPMVLjCBre
zuYKGxm9^+58MNOg_KU*wKMMt&*SWc*j82vE2mNgphfNFJ{n~HhzB<Js*0c5YGPI6w
zgGc#hU3A2Zwqb@J3ZU8R{<Ws`_~(RG+U@ItZ4WfJMD#uby}fVXaW&*52zC|+7Se=<
zTYTuH`WAWG>4>{TjJhiv!(}+?q;}%5C&u*p=p;}x=j;-ZPkOwGuXSWCl%Bk&>q})(
zEzOsKalO0Z?cVIuKbL}R5H1v1?|WZWWJ>nZNeTmUL3B$Wl+W<;ugQiu@5rwN*~0Vr
z*WFmczMn4@*U;tG|03?_Oi{#Vev1OiNPphCq&68vK~bL%JiS@@avy|0edCQB4k0kJ
zVmeYB8T;rw(L4|Jws1+_(M?og_Zpixyawq_XbA&rx}ldXsMofEOJiBkB=$qE?}^|=
zetmk{mfVrb$`W4B;^8DB>*y$JeMh%81m+h5AqOMOqfOjykuuw|>L)xiNVz49=Q-F_
zm`5;(T!K@%hP0^2R|Y$1s4^T`qztO|5@3~oBkS-9HE-Hr0<|tUzqN?HGVxzs!LRdf
zr1kx2s#Z`FaIQFRrKjOI@$^l*dO9(7K2!APy;h<T^6Qw<$@K98Cd;j=%T;To*o2FU
z?2)P2)pPzz<Wc#cVY}8uT0t&4GCjI{evzF%dVFeA!JM>jGrSnIys4R(T}NSKRVz~l
zL(ktfW<PrnQo4yY(OBcC+ex=kh&Z^&t*yNqVV3d2%x!N{7Vcuvr;BeV1}!3ytrNv{
zbnp!%oq}V?`-f@i>a{zY2yUm{kMGYeI7WJ;xVh0(J`X?PDZQVDoEvOU?DhEPiBowS
z{ULk@jT1j0f{8XdWW9aJZ!?{#<}h5Cz{@C-*)imY&!$)b;sf3QxO8fyi5yfmZ;wYb
z5-}-fB>OG*(}fh!AqZG(Y!R8-=oRW%1Bfwp+j6hQQNjf>aPbGqw7smrahRNoS=FE2
zL$9?D8KDXgD%#CL{A)@DY$qNkC?lHai-C~f5aFp5&3DNZOw4bML<R(uJfR$AU?uao
z#$Db&F)gIU1}Bqx9{sxSf6`C`cngwm^a9^@P3_umA+c#@9o~_uC76;};Glgv&a{|Y
zV*Ng-0+&A%z+stBPgLJWRDwk;=&9Z>7ppE4_qSLdr(JHRl5zzGd%DcxYm;X`&@+^=
zvaQ&tr?7{EpoHRmN6+w+7itQPX#1rA>DCO2_p1tP=<g}|5Vl~jU*{BQd)>43H(`^X
zv*5!)Ge=2T02=CYeF_7g1NMF0Iv-!Vx#Nsil>x|$vCnqRm&L6$*?cHniBc#$+`QyQ
zH;l5_P;D73ghYAovQja-(?F4b?`cycOfwb<>|Ct%2c{^hRk!P*PCz1T1geY4q&HCp
zBfiOi6D*^?Y9p$1e%YVFHTytHL=kkq_AE?uF$^4+IQYPYHo%_6-GsN&6k9gUb7H>V
zD{*_7mP`dy4=RO-s`l?|K=wX1ivAtk^!^->+_`K2N*?hwX&yW!uLdb5;?4E4>QBjn
ztG_2~0L00eIaQ<~g24gAph$0^KAqEl8wgcb+4mQLy*^Dxf+(#ZM!PsuRHn_QK{!3<
zWo9ZpykiyD_mkst`?o|QeR5^@=#2=w6}MFUiyQT$75z_Y(c`7jtp-5wVXI#epFktf
zXw0Cg*9p2@eKfbL!dtXIR@tbTw^nqwl<T$EHZLS!97_<b5lp&B1bU{@^N*nG$DGsR
z8PKWHU*`n~f_Q)Bpx>fucCwkXh>>ok?y|<MQd{rE3NfEgn!RqXkgBn#@uX+x{8JJv
zmEzGqZhqP{Nq4iFfzICa&b|a|(Nzg2<~ptQFcryQlX@^ZDGwCkI^W0bKUqGobYYsR
zH)F+uMvhm$Unw1~#Uiql3fe2MD+HA2V~T`q9SoY6FxmWCTuC)5O*Q5~z(7I9W3-7t
zJJyUtF32J`o`5|>E!r;Ht!1m@?t!ByWJ*3_B7<M6hu80oxV-})&wcoi6A-Mi8HQjE
zCimP_U(-viEJ31_7&((ObLIRHS_LN(vll)sQt#J?d_*8GIE!&?c+H*?Hp?iN$gR%`
z2!Q=vR{#9!*44B8&q9uEm(|Y~17W}+%9sE)pH-Y`ABa_yfez!R(tiwOic8OVfkt<o
z#B&h=N+D}lDjibF$SufMsP_kaJR|dtcOWQS3E*eS+QFIV8UJ8A-66xhJQ_EV(Rwxt
z8}nen*|je`=b`UVQR3$QMro(~ULCt>M>Z(PBzWf&T^bOGE$bLXnix-ktVQQ^gfQTr
z70EeUr^iggh{TL!(f10Q2p<{Hy3{As63^aBxyC=+Kij8W<KnLp+hLoC=w~P$kHyVp
zv4pzWMd8aQ%Lc%hT{?rXz+lk3aDRm@FGU0qEDd+yVaz2pE*G^FnYk{vQ3B~iKgByj
z3$vJdUz!Tm*USVOeXNXL=*{F>vIyQfyc&?#W3f^taY&)JJj{A|=6HhGtn5@g2%L}P
z(u_@xfxpo<U3>4s)|B2nFRz02dB^?h)g-E|EiYrW7-vh*n)$~vd!4RaC^R5{$Y0?I
zh2EX)TWq4M+f4!?34}al{UmH`+KgV){7i2!(%@hV(<w}EsXCw7Z=QL+aTo{Rg*7>7
z;eBDWi_Fke>HeYJRN>r<fzaU)(QTEUWwyPcl%V5_i(Bw#8xB{gh-<L^^6cWUaV03i
zSG?+Mb$QBgcDh&g`c1_Yzql0i?p$!6?F9wN$nEPvj5n6SlQaQw=xlFj7-J9TNH_M>
z9$%kN9N1=<r<M6?HJ%99{yNHxl+8JzE4XasBS+JMjzr1GFj>N#e~6sT)xFLpq(!Is
zo6BkFm+|Q``$o8y=`yg`tG%S>6Li2=_V>U|D|YXQS9)vxc-8rX8-k@Wl3MGi?|rs1
z9-qk1lDcHhMC~Y`whXMq4fT^G!Bqr2Huz5uOV_+(5)Psj8w!uC48bZ1V+2sQ$=c95
zD^BGkJHE4ath4OK3aAUY<k0~zNik^w&y2e&du3$pxpoY5(>$$2sxa5YniR80<?zty
zm^(kyITZuUmOZHP>ZtE>92}keMShz!WKmTA>8e7Zvu|YW>3B^7C7LcU%wjL|wAYAB
z%8hegBi%?_1m}=7L&YLN-D-6PbwY}38`^WVpCYy0lZQN;gqU>9phkgG<@_+u<e<5F
zX7#2Jp{rh0J2+IP|H$lMgmI#A^IfhH!_wPl0~unJi?c=5+Z=eIm$YAFZqq1|A?3(q
zuV_dK90u_RZ1o5Z05SnP$+Vj41(IEuOc!S6nnPIc^e#ouS>-<P5PL~M&g)qF+z3$y
zqd<COBj`u#&g{u3rl-kjcRH__Mdtm9@>%>2vc^2;3u#VG)or;y@1Dl>LXk?fe8T0j
z_IB7zNb%^4uQ*@_Q2q=(qjR1Oajp?`9C3f;SahAin;wllI}&#5X-Ta=xk;6i#fI_t
z_g*h2CKtc;VclqXHP@UdS(m!c4t+d4uh|(D)k@4&vLfE_$Ctr?x=J;H_Tr_!rq<oJ
zPs8`>f%07(eF02f7>;Lx4(^9TngzG)OZ7P%f5k0e(lz6c*WkfEG9sV`{!>~Q>W_EH
zI0lq1>=5ix29*|GdWN$+$rRkdesNlBGq%q({qV8sLTJ*OXQ20+pB#iF<vDW|zjHVk
z%&Cdhz`LS;^xvLiCY|C>uV|bvLCGhvoXIHH2?o&PQN>oy+Uzov2p%S=u#n`p40-76
z)A^EwD)_&z(0D0X;KCs`=_;7-5SY2rRA7n>yvI_Irgo&W>FbXv<_ZV@bS_{K)L4?2
zrZMn59WFkecaiHLOV*l21MOJ_ICeT^u~=xH8yp3eY3ov`x2#~No8yzzYLoU!2G1@M
zlaRuvA#^HWz_-dpcRIK)IajT2Di10d)y4yhv!C6Zju^m>pjiJrU5FG;0+$UNpOE>N
zsl834G}-mrK{p0C{PGxc+>W1bFv3uM&4kcGaEC|e8FVPMP}m-<r$GpP>h*fxzK%k*
z<H{3g7~E#H@u^VAfqgT#657G63EiqUdMSTeXk{r}uGkx`Wj^aZ3BLpFMP(wHya6}U
zef|==tIQJH_U+YE`2-dBYn*KC!J-IO%i!;&KDn9->25y7jr-9?+Bt*aLyLS7%c!L6
zzRi3+4W5`(B&=({mc-a&lT!w)NH&)$Zg`9MJsazAi;pr*dL-ZBD<oHr6^ps%_}sS3
zZr#Y5M<-U&I5pDvReHaV!&@Is(L_oyd)F?6BNO$b4_F>j->xDpA+wkMM_#lzA}2Wi
z5300zYZ}gaqIyenid%?m9==#uHGUjpwsZpdz&`i)NYQy^NB7maPox{N;$C@hJPuZF
zOLO%gIgUX=g{iSfruv~pxZp$AKW&22!#fU|4s51Y@A7oGmpAEZ7pA$$w3wZ(92*yX
z!ap7aDxnwUjf;|0!tR45Xy6cuB*!*)$PP`UNR>rt1(d3ROi|Anar7r^*9$EA8sl+I
z;-HST&*mRgJ$viC{j?8*Hh!#}E_Kr9)zZwwt-Nurkc{Nr8Sy=WluKNW=Sk6)e_qiu
zh_y{IV)^)H?>%#sX+e%daM^(O=BbVN>S~awrGC(ai3aZ^$kB=N$$d7+QuTG1d1xA1
zM*SZJe(Cnoh`2&oqgOXUASsH5p|whNJiox$yW=(Y$Z)Jug{xE|XQs{0Ei?A7Zd0U0
ze1yJix5R$zO=r%xr~5&iWK9P09_aVC;B&hHjy9Mn|LdI#(Wld{Rjk&!I*~Jrq21M}
z(S)VbmbDG(wUOcs&uR{>qLVK}@3ZFWfab)Q8XT~=Q+`!^x@fIY5W%c6UUXg7`QMd+
zrd@-9`IOO&^_jv|evu#=UaNO?!*x2mU}4;Xp#9TPbKw;Vs*f0Y7V4FShKo;jLlo&9
zZ_I>EyIaR;L5z59e1b225d{w6gm?mUKsshz^&Ge?oRrONnWDfd6{gWUoEMnu?^spd
zgwCWE&vqc${IEW%-JKO)k1lj2DrMNv_1yzK-Uq0r5cq2Tb%+l*^&8)KBk53}(X#gE
zKH<1+qtWSsU;|b&w=F%rWbuk9_REt`Dp_ILNps3mT0j4j)<gsAqdr9MaeZx=P+et_
zVmp21I*R$?g10&_eHOampk&ngd1=SK$D(C1`e626!PqxJCHsK?lXjQ#CtG<7W@h6$
z|EA<IFi(JPn2bJo1a5(@@{+=umm(dT#k_cmO!2G~u-awr4^`(!iM+9f46DGL<+2lT
zi-6@%w#Ru(m;S;~9(8D`AGeM%`}TZo88kCi&Wl=m#?gb#NJ~G!UQu-wNM6&ipwGzF
zykfjs(s<#R#`+7RbLjaH2ttE@wzoX<-YHAClITfReqe+Wm)dNfr1epdy(6GB3|5P#
zpn9v{h@>-=XP@POaEO`ex0~*mH;};jhNA@|s4@T~6KqDME+Ud*kiJ{9SmYIq9n}5o
z;M*lW;oFUEu*Q6*7X0t}&<W1C`2lICQ9qi|GaHm63fP9hDv;>Lo%UyCu~U;r6<Tg@
z$6{ko`kRy!HRuBE)gdj0SZDXe$(|f_jnJbd@sJo*OAfSSe!8gkKCkXUoe|b8cmO?u
zp2?B>R<~GW@~*0T5bHerba5(nKTQkP<d&Ce<1<hkQMT@M21xevG4)JDi#zf7Mg?6j
z{LRspMA*8`#u1>57wpMR+<Ii_`i%h*!|4ad89LD3-40U>l4Rx74Q|ZGO1<gkq+PDf
z*RtyG4XwkBjgEmSZayg~eX}s}%KZbxR`v0()`(~k;8d3+((9_t-QMBm+h*R8bzdHP
zoSnO0Sda2QX=K{g?7byT->DOHe0*zo(I~p&udEG9=qxjxZyeiCPOa)hWf7`MTJ4|2
zYyd{c%Dku-TJm>@+=0TE^Z;*v6&}Q3vNbhE#qAhqBILLur5Pp+w5I0G_VUg>zR{*N
zCW|X8d*?2kgzDdzJqE2Vk1g{j_%fe)tpi=1=XgYKP0Leg9BFBsuY4eCU&eN@c3NP>
z)n>-L2-)iR&z~W1h^#nc&jxDOg}ZeWWgt?f&tQuJJ5@Nw(AQsJLMJhJsU&0(@5vX6
z8%7wDD;Zb#u@tp;(s5WJa*7=qrR%IiEun^n4OSF->N(4->Y78{RV2*sIYrhc=~S6f
zv$!RX`LUbr`hLnbz;S*UkdKm)d>sUPYGPOdynAFpmI}U+B9{4mfZP8`#9eZT2PoCF
z+T+tmBBX2}>KM>4Wjse3^>_tsKi|sYQ8L-+VD{-*#p>S;AQ9Y{nrzb&DAN^2yYYaX
z{rIgNaCM`j6?FZwY=qqd-EnD%sC(yww|)aZqon_5jCkUwY`T%dQI|Evhwcm7ZhP4=
z*xRW+qD4~&Ti8d)yUHMrHHdQl)#h9Gy<O{m{?zC+3>d6&N29TYhdbH7K!hlAYly$2
z>9P8S|58e?$RZQGn?dHxR3t~-Mr_p6$dcV7i8LbB$8mn$kNkD~bw>q%RT3JUokn5D
z4{sZIHm7vYLQb33KweBW8Kh@(1%JE1%BaKDwWCs|ml3lRxWq0k$)<D5;a|nUZGr|#
z%F5OM#UMm7qwyDQZU)j1dUkP2C;#Q*g4nG&f5)^eU3-$?;p!M4$_AWI-1B^Q4mduV
zs>8YUnu6VHP9&7IrU-wK%}t7@dze(CDZn{@xYE=!`PwO?>MRqJMF}2P2)TXJ+m+k*
zYu0iSl5-NwJZM0Xm!>iF9l)9R)kJWjWn7ci8NxH3p-3_TKoii2ez?uCnNmHs%@A{5
zGZs^X$uwm-Iq;?1?rW+SC+^8Zv73+W7fyZd`M_V#2X@pXPi$h&dmkYJZI)&}!oIYm
z0QVDerwYk<YW0r3n-+t+lA~~X85Sy1J6mkQ^8s!cAH9AQONMBvJkS=ZI_M;vZp?d}
z^5atqP^t4rKxJj?_Y!vwx#h8F6K^HqBt`4O!^m9&6l1m0^$cw*CQW*7ZsVNiYaBmX
z`)!r&kA;IYsem)rsSztXM?}0<^!OyX8F+Xwsyh)yt-BwSU3UI)oatxdG`rO#^mDcf
zK&$+zu8PO{)#Xu9-ldnJjTbN(Y;kIlw;qzKqfdxcSvNsPr~m}Yh~MeL2_r52cMO`r
zRB}5Y6|w}~zQiEK-$1sgVtMM1D7zw;U})e@Ngq7k=kcP@uPW3Q2!ciw(~_nqB7JQ=
z-3gjf(Ql)A&-}g^*HDdghW$0Y`NdIJB<!PAG3_OE!#I2fGurz*T(`X!xhEB&TTXnB
z&h7u@+08B^tY%M)`>H}>7#Q}QuA5MT+rXGd2wGPoX?NCXb)bNpmZ@!z?vXaf^{2}H
zv(zJ~$?<}m3XxHc9}jz1W>(9Nmt~fTk2aEXA2K?se~Da~ZR3YAk#@byAiPb4>7K^t
zG=RuPn|_L#{1}qzC!z0``arGDWxr-(!6RhemuSZM6U{a4jQ5>{0x;e_|NVxvcK=Tl
ze>noI_qjil-EQK3_O+H*W>TYn14}8mZRZa*#H8}F*8V=MoKsj;eFaJ_)m+6}yyT%a
zI0DkR6vxOEYR(-9QFCbpD48DdxR!GE7J||ES)F09(}2H*%SXMFpJP)3#ZULJimq^r
z3C1~;@|N``%}X<w3}Pu#J<lE_yoq@<T3!N>uT~nkwA!=i?X`V=L;ruW+hBx!n`Z{S
z)Q8YsvRf?IH2NqoNHP|OuKogxs;f8>8Vj<v-Lo+S%!%EUS6oynPOEs$taq+g-cc~y
zSnS`}eipNxt8U@0)!nrOsyv;xDz)*f%IfMUCN1eQ)G-Wl&0+mU;js;Dd`@wEEh(`X
zDm$19?!<VT>GI~Jd1_^f-(ez7Am?*xaDA1}*SoI0-A0tQj^)D6<qqxBbg|Pj)*mW2
ztK{%VFqo6S7UZFNy<}2cL#JqoRGQ3sb`@ToG*nsnMH;uy&re6T=})&AE*d#i=oTqu
zcAva5M3{zBQ6|MB<<v9g<GsiZl9|mBR%9qOJ+H4nc(9f`LUELcCP`Fo5rI(53jt0~
zd4`0F8a=Fnjtnao8)bvn2~5bxh}Knf{QYqlrpKZ%$6Q$%p194VfooqRM6zN=v>l$-
zOD2A&J|J4Qs}K-gvbrRyX+$DRhYTk%2mhqqIYIUsa2XjXcqqAl5swgwiZKD!!flRi
zb);t&@j9^#!zojeX0)mz6hvQu!S0%au}=7(35xfI_tQ|9(M$4#5{$^MJoaoLJ9CT`
zBN0m{6{aHksBotcyQq?96$d@=mA~kUYm4S_u27++xRWVe^CPpe<#xFC(Jk>{ThBHy
zY~o{T{iI3He7w5yx-yBILgT<D@45;qRLZo@yv5@9bw)@jnHUXBOn?w_QJ(|v_ETLR
z1~V-l4OkQ~R_ymSj2|Lqhv@Jz8U6Yek0-~^oRys0;SL0wsO?}AOyoRNbifu_uVb<o
zA1+27Qa}e&kMG1CAo3?sQyt@3Q*(k(?y_?EJ+c?#8<P{~l{;$o7@zX<hrQ3pMJ;RS
zfj}2I|ADLE`H!)>lHtSnh~~Kcc2v@+R8(1+R+hYSNs8Li6Oewk=#GZdY3zLWk9q83
zu5q;aT1FL+n{Z9lJ1vpk$H((Bqqx+^_r9>UdXZC2yKNg^oEpK@aMScdj#${q0oRG+
z^PUohiKbqK0X2%TaIs~Iv2D=@cdyK(uo$A9iDV`>3Oifq75`Rr@b_bv&cK48uQg6O
zewjYLwQkw>femjgrkP&D4&i%0_?TZ&Zbrrim=3RJu|@@Rk8Dy^R@Ps?{WU64t6GtY
zUd{4L2?eewvzbF`r0NTVRR099+VR;sSqS27E87&HmB!*gy8*mc@LIirSPP1T3ysp=
zs6hAVV>^;gkB^;B0wZ*&r<ofIW$DUu35gnw@R(H7XCcrRs`A1iIvS5WUkPSbQXLo~
z{qJ$sGpNB?Xp)=w#q>qgj14Uwd4UnE5xi<oim3Wv6*PpCi(p<H1<X&Ng+CC|P<@Kw
zK3l<Mx=orGj9Pu0&?Y6RSP95g`+WP6j-(%%gif3uJUWmLMVoyy43m=@azq|5$0D;;
z6#9yynOUl8_F;U^3K+BsYbqw7kx8s!F1&tCMBb`qj~ZCS`%_l0_>H!T@EVK5_R~ys
zV)W<J3%<!|;y>($V5;}X!R&9NwvH1c-$(8ZhJtIP5EA_wN^sH$sLj;};tJ5YAk=Jl
z2^h%G*SvmBdt+K+5(?@8IOMZ_%8BIc2J{)3jndY;D`j?6nz)>8Rf=B@na3&-abvV*
zmvss&DmskNQ*a*<K;KVYd?&9tzm==`i0-xp+%~(p|3F0v#wCi{y1|vAKAqhf((eu(
zQc*8HV^Ef3H^Uqq!q0RqiI++bMV{h8=Q_+FYj8i5T!_QtPR;nyS|g3k875+Eu&W6{
zjKFmMQ8kAn&YCQ@CX>oO8}0YAo5m-Kp&b7jQ#E;bchl>Is@QOdDvaZxU2X#xM^Yz|
zK_|?H_-9|L64Jy~T>mG&{s`|JpN^1_?v0PqG(C^C1HHjT-5S4x{--L&BrD?%8UQ}B
z<?$i+KF0;{G^jVeE)a0sArJHOnpG7$tlg!5m|U|AOzVw(KoN1KBU;WmuyW^LrGwaj
zIp$C_y}BFjwpfgs41T73E86g4#UdvpRA5x#qiEdNod=QwJdZ6Jz@TDoOs<Gf>RH05
zu&?G8X~|Zl+A^IwcP2TbY3V?E>|(=8)_Z~qCgvPZL#9@PIdG%>M6@mRl5+kq9(jKa
z00inwftgaO*_ki$YCK5O>FQC#753A<$P%jeA;@X1V7~Lrn9fkb^I2Hn+22;`k)u`m
ze5YyJd0G}^zYp#eKFdnuy)*7c&saQ^16Db^a{<q1QTtY@@fazEeiQ`OJvF9!6)$+{
zewwr6pJnS9V>{eM#Qyx%3XQBoB?d78E1P$@T_4?zF-_&<p|>??&H46#2bNVvmPAUJ
z<y%k+NK*4@f}MjeX3oxRh-id7(@7~ClXMLA;_P?Ok4Ml5#_Bs=-Q0HdT^4&y<?p_7
z8^3XaGUDo@#Wu+3OG>=wYil)O>9)$%8Gj|+a_aWl$5Es|m)Ud7|J%Yhx{At&*o?-W
zAxmZ@n;$x}YA!OK9*uTh7jL`*3|bdzy|s!S@raELf0)12IauHjfx3$3`vi8D2o=hr
zjDnauwrpjYDixvNo#qQtI>s<Li6|^mvO-1@ls>rj_yQOV21q_!b3TxeK^4@9*5K4B
zO@#+4GDO@Gf!Oi}*Q0e+{nS)g!6M99*;ok}h?r!_4;7aL8pGWcX5mG$Q^{D(pBHHK
zD+swIzP1q(xpGN<y)V<{@zvWBI^(4Tgj6U%pAVsz;bfgQG)LFKXi@m)HF_$$&`v`{
z(hmFi{<4r$<UJ?3u;8iDY^Up2-u-zVAIl$I%XtMdK88t2S$oFB_s&z<3rV%Wg`ggv
zS^cgyKuy=&h_NZa+g?!LWPv0^F>LWk&+{__z5=7tQ={T95WaddNhz)7f!UE#*&4h8
zZm0(+$9i6V9G8u!<L^`PM_gh`jOd<0lGiu{Gs+sDUOS|I?VN6)J*}W2Eu^3H4bGNf
zCMRG6X0$7Z5M+M{C^Am-H+z#Vby$J`NA$rq(#FXtsqKY!NWqBkuP1$XxfQLXZtFxD
z0IGyJ&zn68>P>mh(n#6h^+v9>*(9=j(mfIJoSio(nv?=ViG^>_<t8JS4Oza^+j=Wl
zeLt8AeD4!0Td)?;!oYviyS%XnTiW%Z80QiDt$8wk_Lp69dPH2AtZYFl(0}7`=j<<r
zh>Ua>-{QzxaJ7}bhkPEUy}%eR7-DRFgE$n&gL0sd)_kt$`;7(v{j<`G^^qejkl?HI
znfz_hi?h_&025HNam&pZ<mnxcxUX)05cM}7Pi!^>?ZD`}dT*FEdP##s<TvEhyprX9
z*(HY-ZxWEq><JiVh|ESaWD&_Inye;FaDEdheQR<Cy$-H#A7@arJPum<Vhp|RH$zsq
zi=^C#tct!_zs*W76gpFkSIr+W-Qn3$@1Zm55wyV2AAD*{xi#7Y3}eAsZ=N{CVK0cK
zW%qY{sYJ&@=oqF?J<DEmBlW>=_xUk6*|dqden$0S5Owhh{(C2p3A^RpfVIN7f3bM?
zFjx?0qI~qS&A|D1vOcyO_hUx%{tv5JsqrFNmI0girb)R)x23$8%P551(E3nptEX+{
zKZm>1x)#{81hlU?waUe`M+|dg^Wm?@rVBdJhGw*$w8PllNB5a|v8Z%HXe7Rc<|b}i
z&sF-W8WN?-mWx3`W(d(EH8iuzzsYN}uwDhdL`Zr##AV|#&@PLa#}1m2$fyz)*pS+`
z*cQGp){6B4^QZeIl>O6=ab3=h=9B?p^##+&u)D~%%3iEUj_>Nnu}V%48RaZ81*AUi
zP9aGM@dq*ie=8N30v-+5U=LGb#>WKjahkcW;RHWU>VI2Kq^nBd8(|0n%4az%@EOyw
zWKCkT+qIh<b_R2owraJdA_!Y>5WC0FP=ZGr%G3h`EFR+v6<gs=(%MC$Vx)xDrIl4#
zt+I4cbpWNAW>QYF2|=-VHt!jYG>QSR$vHyVe*UHGIVlT#8(nw8T0y}k^ycc|<>;J_
z%EZrlz`yP4?^!;`jDKZ>%Lj$FK^eZHH422%X@5Mpqtvk)$Tg3OW0F<LS;psb{BVgZ
zg|{Da%4@~3VtvY+@>m0ao(8?Y_Y$y={X`cde(OOs$Yb7?ewjok6`~X4_;F~CS)LqB
z-K>O;yRAoxhN`})E9K3^3PHn92<RX8n=6?wj+4e$Vb!jA1y_U$_1gM2)*X%p=$I!a
z)m&vf-14HvHb~wnGiqu1aX)<Q9V^6Dh~9z!LRI)+#89LENElayEKE_v_sX8eOi`Jm
z)ZrM`&ti7x<;FQ&*{rSdFIkH8+FrB)B%ful0oqs8D;WE}P5HY&X5u%*Pn55?@to&I
zuDU8%oNBKc&KW+P)WH0O{Ik7gPZXh8vZwCird2ea+R~y>>vF4Vf8}`8P^!(67(4bR
zcJLp`YSSaxoRuc`&?Np*=E`c6=q6sqSfH*3aui1V-2OMOa2i5$79|bZIT4_&sh`jF
zVOwWJ?JJm%@8$i;qEcWPsGudyv|YocXURFp^%q~q&5FoW&xMsRE-xD9Hh?G|M6jDR
z^0CD<;B2z@tU7QBwnPrSf?c16`HdR;C?ZiQ;(cg66rx!KQQqYqG9TwO4LfD*h<!U?
z&nDEf$ZYROrbS*Rr%firE|H8i7{HzNp;vYoFO(6s&$Y5LckO;v(9xfwD&#bcn}nH%
zj1HGdgO0FCHj(40-wT_0bEmM@sZLF%oq+U_RCSmOF|5cIMPPj9+KWLNcYA06c?%nr
zlZ(2$tNL`Znk53LZUVD^cXj|><G2=)=u#d1;RDV6W3Tq8@GBt26!zocqGs0&U4em`
zR>RF%*L+NlkpF8oJ(jixH3wa^Q7|VzH+OVMH5n@wq9RII&xZ!vdH+#2T`MKZL+rmk
zEU+i-v*@;Yig`cn<Fi}Rt2TXoR|EPYB-lN!1PvVgJks#yF<hdEIhu+H4ILaDJUbj5
z92FcK{Z}|Rq~r{X?MV{lbty;$4oW_jb!b#bv9|wsV1dTYqr{94>}R=8ej6K`5RE7c
RO7CGzPyM{||8^tz{{j-3?G*q3
--- a/dom/media/mp3/MP3Demuxer.cpp
+++ b/dom/media/mp3/MP3Demuxer.cpp
@@ -115,30 +115,31 @@ bool MP3TrackDemuxer::Init() {
   if (!mInfo) {
     mInfo = MakeUnique<AudioInfo>();
   }
 
   mInfo->mRate = mSamplesPerSecond;
   mInfo->mChannels = mChannels;
   mInfo->mBitDepth = 16;
   mInfo->mMimeType = "audio/mpeg";
-  mInfo->mDuration = Duration();
+  mInfo->mDuration = Duration().valueOr(TimeUnit::FromInfinity());
 
   MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64
          "}",
          mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
          mInfo->mDuration.ToMicroseconds());
 
   return mSamplesPerSecond && mChannels;
 }
 
 media::TimeUnit MP3TrackDemuxer::SeekPosition() const {
   TimeUnit pos = Duration(mFrameIndex);
-  if (Duration() > TimeUnit()) {
-    pos = std::min(Duration(), pos);
+  auto duration = Duration();
+  if (duration) {
+    pos = std::min(*duration, pos);
   }
   return pos;
 }
 
 const FrameParser::Frame& MP3TrackDemuxer::LastFrame() const {
   return mParser.PrevFrame();
 }
 
@@ -171,20 +172,21 @@ TimeUnit MP3TrackDemuxer::FastSeek(const
          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
          aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
          mFrameIndex, mOffset);
 
   const auto& vbr = mParser.VBRInfo();
   if (!aTime.ToMicroseconds()) {
     // Quick seek to the beginning of the stream.
     mFrameIndex = 0;
-  } else if (vbr.IsTOCPresent() && Duration().ToMicroseconds() > 0) {
+  } else if (vbr.IsTOCPresent() && Duration() &&
+             *Duration() != TimeUnit::Zero()) {
     // Use TOC for more precise seeking.
     const float durationFrac = static_cast<float>(aTime.ToMicroseconds()) /
-                               Duration().ToMicroseconds();
+                               Duration()->ToMicroseconds();
     mFrameIndex = FrameIndexFromOffset(vbr.Offset(durationFrac));
   } else if (AverageFrameLength() > 0) {
     mFrameIndex = FrameIndexFromTime(aTime);
   }
 
   mOffset = OffsetFromFrameIndex(mFrameIndex);
 
   if (mOffset > mFirstFrameOffset && StreamLength() > 0) {
@@ -294,21 +296,21 @@ MP3TrackDemuxer::SkipToNextRandomAccessP
 }
 
 int64_t MP3TrackDemuxer::GetResourceOffset() const { return mOffset; }
 
 TimeIntervals MP3TrackDemuxer::GetBuffered() {
   AutoPinned<MediaResource> stream(mSource.GetResource());
   TimeIntervals buffered;
 
-  if (Duration() > TimeUnit() && stream->IsDataCachedToEndOfResource(0)) {
+  if (Duration() && stream->IsDataCachedToEndOfResource(0)) {
     // Special case completely cached files. This also handles local files.
-    buffered += TimeInterval(TimeUnit(), Duration());
+    buffered += TimeInterval(TimeUnit(), *Duration());
     MP3LOGV("buffered = [[%" PRId64 ", %" PRId64 "]]",
-            TimeUnit().ToMicroseconds(), Duration().ToMicroseconds());
+            TimeUnit().ToMicroseconds(), Duration()->ToMicroseconds());
     return buffered;
   }
 
   MediaByteRangeSet ranges;
   nsresult rv = stream->GetCachedRanges(ranges);
   NS_ENSURE_SUCCESS(rv, buffered);
 
   for (const auto& range : ranges) {
@@ -317,58 +319,65 @@ TimeIntervals MP3TrackDemuxer::GetBuffer
     }
     TimeUnit start = Duration(FrameIndexFromOffset(range.mStart));
     TimeUnit end = Duration(FrameIndexFromOffset(range.mEnd));
     MP3LOGV("buffered += [%" PRId64 ", %" PRId64 "]", start.ToMicroseconds(),
             end.ToMicroseconds());
     buffered += TimeInterval(start, end);
   }
 
+  // If the number of frames reported by the header is valid,
+  // the duration calculated from it is the maximal duration.
+  if (ValidNumAudioFrames() && Duration()) {
+    TimeInterval duration = TimeInterval(TimeUnit(), *Duration());
+    return buffered.Intersection(duration);
+  }
+
   return buffered;
 }
 
 int64_t MP3TrackDemuxer::StreamLength() const { return mSource.GetLength(); }
 
-TimeUnit MP3TrackDemuxer::Duration() const {
+media::NullableTimeUnit MP3TrackDemuxer::Duration() const {
   if (!mNumParsedFrames) {
-    return TimeUnit::FromMicroseconds(-1);
+    return Nothing();
   }
 
   int64_t numFrames = 0;
-  const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames();
-  if (mParser.VBRInfo().IsValid() && numAudioFrames.valueOr(0) + 1 > 1) {
+  const auto numAudioFrames = ValidNumAudioFrames();
+  if (numAudioFrames) {
     // VBR headers don't include the VBR header frame.
     numFrames = numAudioFrames.value() + 1;
-    return Duration(numFrames);
+    return Some(Duration(numFrames));
   }
 
   const int64_t streamLen = StreamLength();
   if (streamLen < 0) {  // Live streams.
     // Unknown length, we can't estimate duration.
-    return TimeUnit::FromMicroseconds(-1);
+    return Nothing();
   }
   // We can't early return when streamLen < 0 before checking numAudioFrames
   // since some live radio will give an opening remark before playing music
   // and the duration of the opening talk can be calculated by numAudioFrames.
 
   const int64_t size = streamLen - mFirstFrameOffset;
   MOZ_ASSERT(size);
 
   // If it's CBR, calculate the duration by bitrate.
   if (!mParser.VBRInfo().IsValid()) {
     const int32_t bitrate = mParser.CurrentFrame().Header().Bitrate();
-    return media::TimeUnit::FromSeconds(static_cast<double>(size) * 8 /
-                                        bitrate);
+    return Some(
+        media::TimeUnit::FromSeconds(static_cast<double>(size) * 8 / bitrate));
   }
 
   if (AverageFrameLength() > 0) {
     numFrames = size / AverageFrameLength();
   }
 
-  return Duration(numFrames);
+  return Some(Duration(numFrames));
 }
 
 TimeUnit MP3TrackDemuxer::Duration(int64_t aNumFrames) const {
   if (!mSamplesPerSecond) {
     return TimeUnit::FromMicroseconds(-1);
   }
 
   const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond;
@@ -723,12 +732,19 @@ double MP3TrackDemuxer::AverageFrameLeng
   const auto& vbr = mParser.VBRInfo();
   if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) {
     return static_cast<double>(vbr.NumBytes().value()) /
            (vbr.NumAudioFrames().value() + 1);
   }
   return 0.0;
 }
 
+Maybe<uint32_t> MP3TrackDemuxer::ValidNumAudioFrames() const {
+  return mParser.VBRInfo().IsValid() &&
+                 mParser.VBRInfo().NumAudioFrames().valueOr(0) + 1 > 1
+             ? mParser.VBRInfo().NumAudioFrames()
+             : Nothing();
+}
+
 }  // namespace mozilla
 
 #undef MP3LOG
 #undef MP3LOGV
--- a/dom/media/mp3/MP3Demuxer.h
+++ b/dom/media/mp3/MP3Demuxer.h
@@ -48,17 +48,17 @@ class MP3TrackDemuxer : public MediaTrac
   // Initializes the track demuxer by reading the first frame for meta data.
   // Returns initialization success state.
   bool Init();
 
   // Returns the total stream length if known, -1 otherwise.
   int64_t StreamLength() const;
 
   // Returns the estimated stream duration, or a 0-duration if unknown.
-  media::TimeUnit Duration() const;
+  media::NullableTimeUnit Duration() const;
 
   // Returns the estimated duration up to the given frame number,
   // or a 0-duration if unknown.
   media::TimeUnit Duration(int64_t aNumFrames) const;
 
   // Returns the estimated current seek position time.
   media::TimeUnit SeekPosition() const;
 
@@ -117,16 +117,20 @@ class MP3TrackDemuxer : public MediaTrac
 
   // Reads aSize bytes into aBuffer from the source starting at aOffset.
   // Returns the actual size read.
   int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize);
 
   // Returns the average frame length derived from the previously parsed frames.
   double AverageFrameLength() const;
 
+  // Returns the number of frames reported by the header if it's valid. Nothing
+  // otherwise.
+  Maybe<uint32_t> ValidNumAudioFrames() const;
+
   // The (hopefully) MPEG resource.
   MediaResourceIndex mSource;
 
   // MPEG frame parser used to detect frames and extract side info.
   FrameParser mParser;
 
   // Whether we've locked onto a valid sequence of frames or not.
   bool mFrameLock;
--- a/dom/media/mp3/MP3FrameParser.cpp
+++ b/dom/media/mp3/MP3FrameParser.cpp
@@ -8,16 +8,17 @@
 
 #include <algorithm>
 #include <inttypes.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/Pair.h"
 #include "mozilla/ResultExtensions.h"
+#include "mozilla/ScopeExit.h"
 #include "VideoUtils.h"
 
 extern mozilla::LazyLogModule gMediaDemuxerLog;
 #define MP3LOG(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
 #define MP3LOGV(msg, ...)                      \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, \
           ("MP3Demuxer " msg, ##__VA_ARGS__))
@@ -77,18 +78,17 @@ Result<bool, nsresult> FrameParser::Pars
   MOZ_ASSERT(aReader && aBytesToSkip);
   *aBytesToSkip = 0;
 
   if (!mID3Parser.Header().Size() && !mFirstFrame.Length()) {
     // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin.
     // ID3v1 tags may only be at file end.
     // TODO: should we try to read ID3 tags at end of file/mid-stream, too?
     const size_t prevReaderOffset = aReader->Offset();
-    uint32_t tagSize;
-    MOZ_TRY_VAR(tagSize, mID3Parser.Parse(aReader));
+    const uint32_t tagSize = mID3Parser.Parse(aReader).unwrapOr(0);
     if (!!tagSize) {
       // ID3 tag found, skip past it.
       const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE;
 
       if (skipSize > aReader->Remaining()) {
         // Skipping across the ID3v2 tag would take us past the end of the
         // buffer, therefore we return immediately and let the calling function
         // handle skipping the rest of the tag.
@@ -351,17 +351,20 @@ Result<bool, nsresult> FrameParser::VBRH
   enum Flags {
     NUM_FRAMES = 0x01,
     NUM_BYTES = 0x02,
     TOC = 0x04,
     VBR_SCALE = 0x08
   };
 
   MOZ_ASSERT(aReader);
+
+  // Seek backward to the original position before leaving this scope.
   const size_t prevReaderOffset = aReader->Offset();
+  auto scopeExit = MakeScopeExit([&] { aReader->Seek(prevReaderOffset); });
 
   // We have to search for the Xing header as its position can change.
   for (auto res = aReader->PeekU32();
        res.isOk() && res.unwrap() != XING_TAG && res.unwrap() != INFO_TAG;) {
     aReader->Read(1);
     res = aReader->PeekU32();
   }
 
@@ -397,17 +400,16 @@ Result<bool, nsresult> FrameParser::VBRH
     }
   }
   if (flags & VBR_SCALE) {
     uint32_t scale;
     MOZ_TRY_VAR(scale, aReader->ReadU32());
     mScale = Some(scale);
   }
 
-  aReader->Seek(prevReaderOffset);
   return mType == XING;
 }
 
 Result<bool, nsresult> FrameParser::VBRHeader::ParseVBRI(
     BufferReader* aReader) {
   static const uint32_t TAG = BigEndian::readUint32("VBRI");
   static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE;
   static const uint32_t FRAME_COUNT_OFFSET = OFFSET + 14;
@@ -416,33 +418,34 @@ Result<bool, nsresult> FrameParser::VBRH
   MOZ_ASSERT(aReader);
   // ParseVBRI assumes that the ByteReader offset points to the beginning of a
   // frame, therefore as a simple check, we look for the presence of a frame
   // sync at that position.
   auto sync = aReader->PeekU16();
   if (sync.isOk()) {  // To avoid compiler complains 'set but unused'.
     MOZ_ASSERT((sync.unwrap() & 0xFFE0) == 0xFFE0);
   }
+
+  // Seek backward to the original position before leaving this scope.
   const size_t prevReaderOffset = aReader->Offset();
+  auto scopeExit = MakeScopeExit([&] { aReader->Seek(prevReaderOffset); });
 
   // VBRI have a fixed relative position, so let's check for it there.
   if (aReader->Remaining() > MIN_FRAME_SIZE) {
     aReader->Seek(prevReaderOffset + OFFSET);
     uint32_t tag, frames;
     MOZ_TRY_VAR(tag, aReader->ReadU32());
     if (tag == TAG) {
       aReader->Seek(prevReaderOffset + FRAME_COUNT_OFFSET);
       MOZ_TRY_VAR(frames, aReader->ReadU32());
       mNumAudioFrames = Some(frames);
       mType = VBRI;
-      aReader->Seek(prevReaderOffset);
       return true;
     }
   }
-  aReader->Seek(prevReaderOffset);
   return false;
 }
 
 bool FrameParser::VBRHeader::Parse(BufferReader* aReader) {
   auto res = MakePair(ParseVBRI(aReader), ParseXing(aReader));
   const bool rv = (res.first().isOk() && res.first().unwrap()) ||
                   (res.second().isOk() && res.second().unwrap());
   if (rv) {
--- a/editor/composer/nsEditingSession.h
+++ b/editor/composer/nsEditingSession.h
@@ -67,16 +67,17 @@ class nsEditingSession final : public ns
   nsresult PrepareForEditing(nsPIDOMWindowOuter* aWindow);
 
   static void TimerCallback(nsITimer* aTimer, void* aClosure);
   nsCOMPtr<nsITimer> mLoadBlankDocTimer;
 
   // progress load stuff
   nsresult StartDocumentLoad(nsIWebProgress* aWebProgress,
                              bool isToBeMadeEditable);
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult EndDocumentLoad(nsIWebProgress* aWebProgress, nsIChannel* aChannel,
                            nsresult aStatus, bool isToBeMadeEditable);
   nsresult StartPageLoad(nsIChannel* aChannel);
   nsresult EndPageLoad(nsIWebProgress* aWebProgress, nsIChannel* aChannel,
                        nsresult aStatus);
 
   bool IsProgressForTargetDocument(nsIWebProgress* aWebProgress);
 
--- a/editor/composer/nsIEditingSession.idl
+++ b/editor/composer/nsIEditingSession.idl
@@ -42,16 +42,17 @@ interface nsIEditingSession : nsISupport
    *  @param aWindow nsIDOMWindow, the window the embedder needs to make editable
    *  @param aEditorType string, "html" "htmlsimple" "text" "textsimple"
    *  @param aMakeWholeDocumentEditable if PR_TRUE make the whole document in
    *                                    aWindow editable, otherwise it's the
    *                                    embedder who should make the document
    *                                    (or part of it) editable.
    *  @param aInteractive if PR_FALSE turn off scripting and plugins
    */
+  [can_run_script]
   void makeWindowEditable(in mozIDOMWindowProxy window,
                           in string aEditorType,
                           in boolean doAfterUriLoad,
                           in boolean aMakeWholeDocumentEditable,
                           in boolean aInteractive);
 
   /**
    *  Test whether a specific window has had its editable flag set; it may have an editor
@@ -65,16 +66,17 @@ interface nsIEditingSession : nsISupport
   /**
    *  Get the editor for this window. May return null
    */
 	nsIEditor getEditorForWindow(in mozIDOMWindowProxy window);
 
   /**
    *  Setup editor and related support objects
    */
+  [can_run_script]
   void setupEditorOnWindow(in mozIDOMWindowProxy window);
 
   /**
    *   Destroy editor and related support objects
    */
   void tearDownEditorOnWindow(in mozIDOMWindowProxy window);
 
   void setEditorOnControllers(in mozIDOMWindowProxy aWindow,
--- a/editor/libeditor/CSSEditUtils.cpp
+++ b/editor/libeditor/CSSEditUtils.cpp
@@ -490,29 +490,29 @@ already_AddRefed<nsComputedDOMStyle> CSS
 
   return style.forget();
 }
 
 // remove the CSS style "aProperty : aPropertyValue" and possibly remove the
 // whole node if it is a span and if its only attribute is _moz_dirty
 nsresult CSSEditUtils::RemoveCSSInlineStyle(nsINode& aNode, nsAtom* aProperty,
                                             const nsAString& aPropertyValue) {
-  RefPtr<Element> element = aNode.AsElement();
-  NS_ENSURE_STATE(element);
+  OwningNonNull<Element> element(*aNode.AsElement());
 
   // remove the property from the style attribute
-  nsresult rv = RemoveCSSProperty(*element, *aProperty, aPropertyValue);
+  nsresult rv = RemoveCSSProperty(element, *aProperty, aPropertyValue);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!element->IsHTMLElement(nsGkAtoms::span) ||
       HTMLEditor::HasAttributes(element)) {
     return NS_OK;
   }
 
-  return mHTMLEditor->RemoveContainerWithTransaction(*element);
+  OwningNonNull<HTMLEditor> htmlEditor(*mHTMLEditor);
+  return htmlEditor->RemoveContainerWithTransaction(element);
 }
 
 // Answers true if the property can be removed by setting a "none" CSS value
 // on a node
 
 // static
 bool CSSEditUtils::IsCSSInvertible(nsAtom& aProperty, nsAtom* aAttribute) {
   return nsGkAtoms::b == &aProperty;
@@ -842,18 +842,19 @@ nsresult CSSEditUtils::RemoveCSSEquivale
   nsTArray<nsString> cssValueArray;
   GenerateCSSDeclarationsFromHTMLStyle(aElement, aHTMLProperty, aAttribute,
                                        aValue, cssPropertyArray, cssValueArray,
                                        true);
 
   // remove the individual CSS inline styles
   int32_t count = cssPropertyArray.Length();
   for (int32_t index = 0; index < count; index++) {
-    nsresult rv = RemoveCSSProperty(*aElement, *cssPropertyArray[index],
-                                    cssValueArray[index], aSuppressTransaction);
+    nsresult rv =
+        RemoveCSSProperty(*aElement, MOZ_KnownLive(*cssPropertyArray[index]),
+                          cssValueArray[index], aSuppressTransaction);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 // returns in aValueString the list of values for the CSS equivalences to
 // the HTML style aHTMLProperty/aAttribute/aValueString for the node aNode;
 // the value of aStyleType controls the styles we retrieve : specified or
--- a/editor/libeditor/CSSEditUtils.h
+++ b/editor/libeditor/CSSEditUtils.h
@@ -95,16 +95,17 @@ class CSSEditUtils final {
    *                            property.
    * @param aSuppressTransaction [IN] A boolean indicating, when true,
    *                                  that no transaction should be recorded.
    */
   nsresult SetCSSProperty(dom::Element& aElement, nsAtom& aProperty,
                           const nsAString& aValue, bool aSuppressTxn = false);
   nsresult SetCSSPropertyPixels(dom::Element& aElement, nsAtom& aProperty,
                                 int32_t aIntValue);
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveCSSProperty(dom::Element& aElement, nsAtom& aProperty,
                              const nsAString& aPropertyValue,
                              bool aSuppressTxn = false);
 
   /**
    * Gets the specified/computed style value of a CSS property for a given
    * node (or its element ancestor if it is not an element).
    *
@@ -122,16 +123,17 @@ class CSSEditUtils final {
    * and removes the node if it is an useless span.
    *
    * @param aNode           [IN] The specific node we want to remove a style
    *                             from.
    * @param aProperty       [IN] The CSS property atom to remove.
    * @param aPropertyValue  [IN] The value of the property we have to remove
    *                             if the property accepts more than one value.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveCSSInlineStyle(nsINode& aNode, nsAtom* aProperty,
                                 const nsAString& aPropertyValue);
 
   /**
    * Answers true is the property can be removed by setting a "none" CSS value
    * on a node.
    *
    * @param aProperty     [IN] An atom containing a CSS property.
@@ -253,16 +255,17 @@ class CSSEditUtils final {
    * @param aElement       [IN] A DOM Element (must not be null).
    * @param aHTMLProperty  [IN] An atom containing an HTML property.
    * @param aAttribute     [IN] An atom to an attribute name or nullptr if
    *                            irrelevant.
    * @param aValue         [IN] The attribute value.
    * @param aSuppressTransaction [IN] A boolean indicating, when true,
    *                                  that no transaction should be recorded.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveCSSEquivalentToHTMLStyle(dom::Element* aElement,
                                           nsAtom* aHTMLProperty,
                                           nsAtom* aAttribute,
                                           const nsAString* aValue,
                                           bool aSuppressTransaction);
 
   /**
    * Parses a "xxxx.xxxxxuuu" string where x is a digit and u an alpha char.
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -2367,23 +2367,25 @@ void EditorBase::CloneAttributesWithTran
   // Set just the attributes that the source element has
   RefPtr<nsDOMAttributeMap> sourceAttributes = sourceElement->Attributes();
   uint32_t sourceCount = sourceAttributes->Length();
   for (uint32_t i = 0; i < sourceCount; i++) {
     RefPtr<Attr> attr = sourceAttributes->Item(i);
     nsAutoString value;
     attr->GetValue(value);
     if (isDestElementInBody) {
-      SetAttributeOrEquivalent(destElement, attr->NodeInfo()->NameAtom(), value,
-                               false);
+      SetAttributeOrEquivalent(destElement,
+                               MOZ_KnownLive(attr->NodeInfo()->NameAtom()),
+                               value, false);
     } else {
       // The element is not inserted in the document yet, we don't want to put
       // a transaction on the UndoStack
-      SetAttributeOrEquivalent(destElement, attr->NodeInfo()->NameAtom(), value,
-                               true);
+      SetAttributeOrEquivalent(destElement,
+                               MOZ_KnownLive(attr->NodeInfo()->NameAtom()),
+                               value, true);
     }
   }
 }
 
 nsresult EditorBase::ScrollSelectionIntoView(bool aScrollToAnchor) {
   nsISelectionController* selectionController = GetSelectionController();
   if (!selectionController) {
     return NS_OK;
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -166,16 +166,17 @@ class EditorBase : public nsIEditor,
    * @param aRoot         This is the root of the editable section of this
    *                      document. If it is null then we get root
    *                      from document body.
    * @param aSelCon       this should be used to get the selection location
    *                      (will be null for HTML editors)
    * @param aFlags        A bitmask of flags for specifying the behavior
    *                      of the editor.
    */
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult Init(Document& doc, Element* aRoot,
                         nsISelectionController* aSelCon, uint32_t aFlags,
                         const nsAString& aInitialValue);
 
   /**
    * PostCreate should be called after Init, and is the time that the editor
    * tells its documentStateObservers that the document has been created.
    */
@@ -880,16 +881,17 @@ class EditorBase : public nsIEditor,
    *                        The point after inserted aStringToInsert.
    *                        So, when this method actually inserts string,
    *                        this is set to a point in the text node.
    *                        Otherwise, this may be set to aPointToInsert.
    * @return                When this succeeds to insert the string or
    *                        does nothing during composition, returns NS_OK.
    *                        Otherwise, an error code.
    */
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult InsertTextWithTransaction(
       Document& aDocument, const nsAString& aStringToInsert,
       const EditorRawDOMPoint& aPointToInsert,
       EditorRawDOMPoint* aPointAfterInsertedString = nullptr);
 
   /**
    * InsertTextIntoTextNodeWithTransaction() inserts aStringToInsert into
    * aOffset of aTextNode with transaction.
@@ -921,45 +923,47 @@ class EditorBase : public nsIEditor,
    * @param aContentToInsert    The node to be inserted.
    * @param aPointToInsert      The insertion point of aContentToInsert.
    *                            If this refers end of the container, the
    *                            transaction will append the node to the
    *                            container.  Otherwise, will insert the node
    *                            before child node referred by this.
    */
   template <typename PT, typename CT>
-  nsresult InsertNodeWithTransaction(
-      nsIContent& aContentToInsert,
-      const EditorDOMPointBase<PT, CT>& aPointToInsert);
+  MOZ_CAN_RUN_SCRIPT nsresult
+  InsertNodeWithTransaction(nsIContent& aContentToInsert,
+                            const EditorDOMPointBase<PT, CT>& aPointToInsert);
 
   /**
    * ReplaceContainerWithTransaction() creates new element whose name is
    * aTagName, moves all children in aOldContainer to the new element, then,
    * removes aOldContainer from the DOM tree.
    *
    * @param aOldContainer       The element node which should be replaced
    *                            with new element.
    * @param aTagName            The name of new element node.
    */
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<Element> ReplaceContainerWithTransaction(
       Element& aOldContainer, nsAtom& aTagName) {
     return ReplaceContainerWithTransactionInternal(
         aOldContainer, aTagName, *nsGkAtoms::_empty, EmptyString(), false);
   }
 
   /**
    * ReplaceContainerAndCloneAttributesWithTransaction() creates new element
    * whose name is aTagName, copies all attributes from aOldContainer to the
    * new element, moves all children in aOldContainer to the new element, then,
    * removes aOldContainer from the DOM tree.
    *
    * @param aOldContainer       The element node which should be replaced
    *                            with new element.
    * @param aTagName            The name of new element node.
    */
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<Element> ReplaceContainerAndCloneAttributesWithTransaction(
       Element& aOldContainer, nsAtom& aTagName) {
     return ReplaceContainerWithTransactionInternal(
         aOldContainer, aTagName, *nsGkAtoms::_empty, EmptyString(), true);
   }
 
   /**
    * ReplaceContainerWithTransaction() creates new element whose name is
@@ -968,53 +972,57 @@ class EditorBase : public nsIEditor,
    * aOldContainer from the DOM tree.
    *
    * @param aOldContainer       The element node which should be replaced
    *                            with new element.
    * @param aTagName            The name of new element node.
    * @param aAttribute          Attribute name to be set to the new element.
    * @param aAttributeValue     Attribute value to be set to aAttribute.
    */
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<Element> ReplaceContainerWithTransaction(
       Element& aOldContainer, nsAtom& aTagName, nsAtom& aAttribute,
       const nsAString& aAttributeValue) {
     return ReplaceContainerWithTransactionInternal(
         aOldContainer, aTagName, aAttribute, aAttributeValue, false);
   }
 
   /**
    * CloneAttributesWithTransaction() clones all attributes from
    * aSourceElement to aDestElement after removing all attributes in
    * aDestElement.
    */
+  MOZ_CAN_RUN_SCRIPT
   void CloneAttributesWithTransaction(Element& aDestElement,
                                       Element& aSourceElement);
 
   /**
    * RemoveContainerWithTransaction() removes aElement from the DOM tree and
    * moves all its children to the parent of aElement.
    *
    * @param aElement            The element to be removed.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveContainerWithTransaction(Element& aElement);
 
   /**
    * InsertContainerWithTransaction() creates new element whose name is
    * aTagName, moves aContent into the new element, then, inserts the new
    * element into where aContent was.
    * Note that this method does not check if aContent is valid child of
    * the new element.  So, callers need to guarantee it.
    *
    * @param aContent            The content which will be wrapped with new
    *                            element.
    * @param aTagName            Element name of new element which will wrap
    *                            aContent and be inserted into where aContent
    *                            was.
    * @return                    The new element.
    */
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<Element> InsertContainerWithTransaction(nsIContent& aContent,
                                                            nsAtom& aTagName) {
     return InsertContainerWithTransactionInternal(
         aContent, aTagName, *nsGkAtoms::_empty, EmptyString());
   }
 
   /**
    * InsertContainerWithTransaction() creates new element whose name is
@@ -1028,16 +1036,17 @@ class EditorBase : public nsIEditor,
    * @param aTagName            Element name of new element which will wrap
    *                            aContent and be inserted into where aContent
    *                            was.
    * @param aAttribute          Attribute which should be set to the new
    *                            element.
    * @param aAttributeValue     Value to be set to aAttribute.
    * @return                    The new element.
    */
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<Element> InsertContainerWithTransaction(
       nsIContent& aContent, nsAtom& aTagName, nsAtom& aAttribute,
       const nsAString& aAttributeValue) {
     return InsertContainerWithTransactionInternal(aContent, aTagName,
                                                   aAttribute, aAttributeValue);
   }
 
   /**
@@ -1070,26 +1079,27 @@ class EditorBase : public nsIEditor,
   nsresult JoinNodesWithTransaction(nsINode& aLeftNode, nsINode& aRightNode);
 
   /**
    * MoveNodeWithTransaction() moves aContent to aPointToInsert.
    *
    * @param aContent        The node to be moved.
    */
   template <typename PT, typename CT>
-  nsresult MoveNodeWithTransaction(
+  MOZ_CAN_RUN_SCRIPT nsresult MoveNodeWithTransaction(
       nsIContent& aContent, const EditorDOMPointBase<PT, CT>& aPointToInsert);
 
   /**
    * MoveNodeToEndWithTransaction() moves aContent to end of aNewContainer.
    *
    * @param aContent        The node to be moved.
    * @param aNewContainer   The new container which will contain aContent as
    *                        its last child.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult MoveNodeToEndWithTransaction(nsIContent& aContent,
                                         nsINode& aNewContainer) {
     EditorRawDOMPoint pointToInsert;
     pointToInsert.SetToEndOf(&aNewContainer);
     return MoveNodeWithTransaction(aContent, pointToInsert);
   }
 
   /**
@@ -1166,30 +1176,32 @@ class EditorBase : public nsIEditor,
    * RemoveAttributeWithTransaction() removes aAttribute from aElement.
    *
    * @param aElement        Element node which will lose aAttribute.
    * @param aAttribute      Attribute name to be removed from aElement.
    */
   nsresult RemoveAttributeWithTransaction(Element& aElement,
                                           nsAtom& aAttribute);
 
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult RemoveAttributeOrEquivalent(Element* aElement,
                                                nsAtom* aAttribute,
                                                bool aSuppressTransaction) = 0;
 
   /**
    * SetAttributeWithTransaction() sets aAttribute of aElement to aValue.
    *
    * @param aElement        Element node which will have aAttribute.
    * @param aAttribute      Attribute name to be set.
    * @param aValue          Attribute value be set to aAttribute.
    */
   nsresult SetAttributeWithTransaction(Element& aElement, nsAtom& aAttribute,
                                        const nsAString& aValue);
 
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult SetAttributeOrEquivalent(Element* aElement,
                                             nsAtom* aAttribute,
                                             const nsAString& aValue,
                                             bool aSuppressTransaction) = 0;
 
   /**
    * Method to replace certain CreateElementNS() calls.
    *
@@ -1276,16 +1288,17 @@ class EditorBase : public nsIEditor,
    * @param aAttribute          Attribute name which will be set to the new
    *                            element.  This will be ignored if
    *                            aCloneAllAttributes is set to true.
    * @param aAttributeValue     Attribute value which will be set to
    *                            aAttribute.
    * @param aCloneAllAttributes If true, all attributes of aOldContainer will
    *                            be copied to the new element.
    */
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<Element> ReplaceContainerWithTransactionInternal(
       Element& aElement, nsAtom& aTagName, nsAtom& aAttribute,
       const nsAString& aAttributeValue, bool aCloneAllAttributes);
 
   /**
    * InsertContainerWithTransactionInternal() creates new element whose name is
    * aTagName, moves aContent into the new element, then, inserts the new
    * element into where aContent was.  If aAttribute is not nsGkAtoms::_empty,
@@ -1298,16 +1311,17 @@ class EditorBase : public nsIEditor,
    *                            was.
    * @param aAttribute          Attribute which should be set to the new
    *                            element.  If this is nsGkAtoms::_empty,
    *                            this does not set any attributes to the new
    *                            element.
    * @param aAttributeValue     Value to be set to aAttribute.
    * @return                    The new element.
    */
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<Element> InsertContainerWithTransactionInternal(
       nsIContent& aContent, nsAtom& aTagName, nsAtom& aAttribute,
       const nsAString& aAttributeValue);
 
   /**
    * DoSplitNode() creates a new node (left node) identical to an existing
    * node (right node), and split the contents between the same point in both
    * nodes.
@@ -1734,16 +1748,17 @@ class EditorBase : public nsIEditor,
    */
   virtual void OnStartToHandleTopLevelEditSubAction(
       EditSubAction aEditSubAction, nsIEditor::EDirection aDirection);
 
   /**
    * OnEndHandlingTopLevelEditSubAction() is called after
    * SetTopLevelEditSubAction() is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   virtual void OnEndHandlingTopLevelEditSubAction();
 
   /**
    * Routines for managing the preservation of selection across
    * various editor actions.
    */
   bool ArePreservingSelection();
   void PreserveSelectionAcrossActions();
@@ -2111,19 +2126,20 @@ class EditorBase : public nsIEditor,
       if (!mEditorBase.GetTopLevelEditSubAction()) {
         mEditorBase.OnStartToHandleTopLevelEditSubAction(aEditSubAction,
                                                          aDirection);
       } else {
         mDoNothing = true;  // nested calls will end up here
       }
     }
 
+    MOZ_CAN_RUN_SCRIPT_BOUNDARY
     ~AutoTopLevelEditSubActionNotifier() {
       if (!mDoNothing) {
-        mEditorBase.OnEndHandlingTopLevelEditSubAction();
+        MOZ_KnownLive(mEditorBase).OnEndHandlingTopLevelEditSubAction();
       }
     }
 
    protected:
     EditorBase& mEditorBase;
     bool mDoNothing;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   };
--- a/editor/libeditor/EditorEventListener.h
+++ b/editor/libeditor/EditorEventListener.h
@@ -69,16 +69,17 @@ class EditorEventListener : public nsIDO
   nsresult KeyPress(WidgetKeyboardEvent* aKeyboardEvent);
   MOZ_CAN_RUN_SCRIPT
   nsresult HandleChangeComposition(WidgetCompositionEvent* aCompositionEvent);
   nsresult HandleStartComposition(WidgetCompositionEvent* aCompositionEvent);
   MOZ_CAN_RUN_SCRIPT
   void HandleEndComposition(WidgetCompositionEvent* aCompositionEvent);
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseDown(dom::MouseEvent* aMouseEvent);
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseUp(dom::MouseEvent* aMouseEvent) { return NS_OK; }
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseClick(WidgetMouseEvent* aMouseClickEvent);
   nsresult Focus(InternalFocusEvent* aFocusEvent);
   nsresult Blur(InternalFocusEvent* aBlurEvent);
   MOZ_CAN_RUN_SCRIPT nsresult DragEnter(dom::DragEvent* aDragEvent);
   MOZ_CAN_RUN_SCRIPT nsresult DragOver(dom::DragEvent* aDragEvent);
   nsresult DragExit(dom::DragEvent* aDragEvent);
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1338,18 +1338,19 @@ nsresult HTMLEditRules::WillInsertText(E
     // the insertion point.
     int32_t IMESelectionOffset = HTMLEditorRef().GetIMESelectionStartOffsetIn(
         pointToInsert.GetContainer());
     if (IMESelectionOffset >= 0) {
       pointToInsert.Set(pointToInsert.GetContainer(), IMESelectionOffset);
     }
 
     if (inString->IsEmpty()) {
-      rv = HTMLEditorRef().InsertTextWithTransaction(
-          *doc, *inString, EditorRawDOMPoint(pointToInsert));
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .InsertTextWithTransaction(*doc, *inString,
+                                          EditorRawDOMPoint(pointToInsert));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       return NS_OK;
     }
@@ -1439,19 +1440,20 @@ nsresult HTMLEditRules::WillInsertText(E
           NS_WARNING_ASSERTION(
               advanced, "Failed to advance offset after the new <br> element");
           NS_WARNING_ASSERTION(currentPoint == pointToInsert,
                                "Perhaps, <br> element position has been moved "
                                "to different point "
                                "by mutation observer");
         } else {
           EditorRawDOMPoint pointAfterInsertedString;
-          rv = HTMLEditorRef().InsertTextWithTransaction(
-              *doc, subStr, EditorRawDOMPoint(currentPoint),
-              &pointAfterInsertedString);
+          rv = MOZ_KnownLive(HTMLEditorRef())
+                   .InsertTextWithTransaction(*doc, subStr,
+                                              EditorRawDOMPoint(currentPoint),
+                                              &pointAfterInsertedString);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
           currentPoint = pointAfterInsertedString;
           pointToInsert = pointAfterInsertedString;
@@ -1726,18 +1728,18 @@ EditActionResult HTMLEditRules::WillInse
 
   if (host == blockParent && separator != ParagraphSeparator::br) {
     // Insert a new block first
     MOZ_ASSERT(separator == ParagraphSeparator::div ||
                separator == ParagraphSeparator::p);
     // MakeBasicBlock() creates AutoSelectionRestorer.
     // Therefore, even if it returns NS_OK, editor might have been destroyed
     // at restoring Selection.
-    OwningNonNull<nsAtom> separatorTag = ParagraphSeparatorElement(separator);
-    nsresult rv = MakeBasicBlock(separatorTag);
+    nsresult rv =
+        MakeBasicBlock(MOZ_KnownLive(ParagraphSeparatorElement(separator)));
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
         NS_WARN_IF(!CanHandleEditAction())) {
       return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     // We warn on failure, but don't handle it, because it might be harmless.
     // Instead we just check that a new block was actually created.
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "HTMLEditRules::MakeBasicBlock() failed");
@@ -1791,19 +1793,19 @@ EditActionResult HTMLEditRules::WillInse
     }
     if (NS_WARN_IF(!brElement)) {
       return EditActionIgnored(NS_ERROR_FAILURE);
     }
   }
 
   nsCOMPtr<Element> listItem = IsInListItem(blockParent);
   if (listItem && listItem != host) {
-    nsresult rv =
-        ReturnInListItem(*listItem, *atStartOfSelection.GetContainer(),
-                         atStartOfSelection.Offset());
+    nsresult rv = ReturnInListItem(
+        *listItem, MOZ_KnownLive(*atStartOfSelection.GetContainer()),
+        atStartOfSelection.Offset());
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "Failed to insert break into list item");
     return EditActionHandled();
   }
 
@@ -1960,18 +1962,20 @@ nsresult HTMLEditRules::InsertBRElement(
     // The next thing after the break we inserted is another break.  Move the
     // second break to be the first break's sibling.  This will prevent them
     // from being in different inline nodes, which would break
     // SetInterlinePosition().  It will also assure that if the user clicks
     // away and then clicks back on their new blank line, they will still get
     // the style from the line above.
     EditorDOMPoint atSecondBRElement(maybeSecondBRNode);
     if (brElement->GetNextSibling() != maybeSecondBRNode) {
-      nsresult rv = HTMLEditorRef().MoveNodeWithTransaction(
-          *maybeSecondBRNode->AsContent(), afterBRElement);
+      nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                        .MoveNodeWithTransaction(
+                            MOZ_KnownLive(*maybeSecondBRNode->AsContent()),
+                            afterBRElement);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
@@ -2583,17 +2587,17 @@ nsresult HTMLEditRules::WillDeleteSelect
       if (sibling) {
         stepbrother = HTMLEditorRef().GetNextHTMLSibling(sibling);
       }
       // Are they both text nodes?  If so, join them!
       if (startPoint.GetContainer() == stepbrother &&
           startPoint.GetContainerAsText() && sibling->GetAsText()) {
         EditorDOMPoint pt;
         nsresult rv = JoinNearestEditableNodesWithTransaction(
-            *sibling, *startPoint.GetContainerAsContent(), &pt);
+            *sibling, MOZ_KnownLive(*startPoint.GetContainerAsContent()), &pt);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         if (NS_WARN_IF(!pt.IsSet())) {
           return NS_ERROR_FAILURE;
         }
         // Fix up selection
         ErrorResult error;
@@ -2695,17 +2699,18 @@ nsresult HTMLEditRules::WillDeleteSelect
       EditorDOMPoint selPoint(startPoint);
       {
         AutoTrackDOMPoint tracker(HTMLEditorRef().RangeUpdaterRef(), &selPoint);
         if (NS_WARN_IF(!leftNode) || NS_WARN_IF(!leftNode->IsContent()) ||
             NS_WARN_IF(!rightNode) || NS_WARN_IF(!rightNode->IsContent())) {
           return NS_ERROR_FAILURE;
         }
         EditActionResult ret = TryToJoinBlocksWithTransaction(
-            *leftNode->AsContent(), *rightNode->AsContent());
+            MOZ_KnownLive(*leftNode->AsContent()),
+            MOZ_KnownLive(*rightNode->AsContent()));
         *aHandled |= ret.Handled();
         *aCancel |= ret.Canceled();
         if (NS_WARN_IF(ret.Failed())) {
           return ret.Rv();
         }
       }
 
       // If TryToJoinBlocksWithTransaction() didn't handle it  and it's not
@@ -2775,17 +2780,18 @@ nsresult HTMLEditRules::WillDeleteSelect
       EditorDOMPoint selPoint(startPoint);
       {
         AutoTrackDOMPoint tracker(HTMLEditorRef().RangeUpdaterRef(), &selPoint);
         if (NS_WARN_IF(!leftNode->IsContent()) ||
             NS_WARN_IF(!rightNode->IsContent())) {
           return NS_ERROR_FAILURE;
         }
         EditActionResult ret = TryToJoinBlocksWithTransaction(
-            *leftNode->AsContent(), *rightNode->AsContent());
+            MOZ_KnownLive(*leftNode->AsContent()),
+            MOZ_KnownLive(*rightNode->AsContent()));
         // This should claim that trying to join the block means that
         // this handles the action because the caller shouldn't do anything
         // anymore in this case.
         *aHandled = true;
         *aCancel |= ret.Canceled();
         if (NS_WARN_IF(ret.Failed())) {
           return ret.Rv();
         }
@@ -3453,18 +3459,18 @@ EditActionResult HTMLEditRules::TryToJoi
           previousContent.Set(previousContentParent, previousContentOffset);
         }
       }
 
       if (NS_WARN_IF(!previousContent.IsSet())) {
         return EditActionIgnored(NS_ERROR_NULL_POINTER);
       }
 
-      ret |= MoveBlock(*previousContent.GetContainerAsElement(), *rightBlock,
-                       previousContent.Offset(), 0);
+      ret |= MoveBlock(MOZ_KnownLive(*previousContent.GetContainerAsElement()),
+                       *rightBlock, previousContent.Offset(), 0);
       if (NS_WARN_IF(ret.Failed())) {
         return ret;
       }
     }
     if (brNode) {
       nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(*brNode);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return ret.SetResult(NS_ERROR_EDITOR_DESTROYED);
@@ -3504,18 +3510,18 @@ EditActionResult HTMLEditRules::TryToJoi
     // Nodes are same type.  merge them.
     EditorDOMPoint pt;
     nsresult rv =
         JoinNearestEditableNodesWithTransaction(*leftBlock, *rightBlock, &pt);
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     if (pt.IsSet() && mergeLists) {
-      CreateElementResult convertListTypeResult =
-          ConvertListType(*rightBlock, *existingList, *nsGkAtoms::li);
+      CreateElementResult convertListTypeResult = ConvertListType(
+          *rightBlock, MOZ_KnownLive(*existingList), *nsGkAtoms::li);
       if (NS_WARN_IF(convertListTypeResult.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
         return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
     }
     ret.MarkAsHandled();
   } else {
     // Nodes are dissimilar types.
     ret |= MoveBlock(*leftBlock, *rightBlock, -1, 0);
@@ -3554,31 +3560,31 @@ EditActionResult HTMLEditRules::MoveBloc
     return EditActionIgnored(rv);
   }
 
   EditActionResult ret(NS_OK);
   for (uint32_t i = 0; i < arrayOfNodes.Length(); i++) {
     // get the node to act on
     if (IsBlockNode(arrayOfNodes[i])) {
       // For block nodes, move their contents only, then delete block.
-      ret |=
-          MoveContents(*arrayOfNodes[i]->AsElement(), aLeftBlock, &aLeftOffset);
+      ret |= MoveContents(MOZ_KnownLive(*arrayOfNodes[i]->AsElement()),
+                          aLeftBlock, &aLeftOffset);
       if (NS_WARN_IF(ret.Failed())) {
         return ret;
       }
       rv = HTMLEditorRef().DeleteNodeWithTransaction(*arrayOfNodes[i]);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return ret.SetResult(NS_ERROR_EDITOR_DESTROYED);
       }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove a block node");
       ret.MarkAsHandled();
     } else {
       // Otherwise move the content as is, checking against the DTD.
-      ret |= MoveNodeSmart(*arrayOfNodes[i]->AsContent(), aLeftBlock,
-                           &aLeftOffset);
+      ret |= MoveNodeSmart(MOZ_KnownLive(*arrayOfNodes[i]->AsContent()),
+                           aLeftBlock, &aLeftOffset);
       if (NS_WARN_IF(ret.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
         return ret;
       }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                            "Failed to move current node to the left block");
     }
   }
 
@@ -3595,28 +3601,28 @@ EditActionResult HTMLEditRules::MoveNode
                                               int32_t* aInOutDestOffset) {
   MOZ_ASSERT(IsEditorDataAvailable());
   MOZ_ASSERT(aInOutDestOffset);
 
   // Check if this node can go into the destination node
   if (HTMLEditorRef().CanContain(aDestElement, aNode)) {
     // If it can, move it there.
     if (*aInOutDestOffset == -1) {
-      nsresult rv =
-          HTMLEditorRef().MoveNodeToEndWithTransaction(aNode, aDestElement);
+      nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                        .MoveNodeToEndWithTransaction(aNode, aDestElement);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return EditActionIgnored(rv);
       }
     } else {
       EditorRawDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset);
-      nsresult rv =
-          HTMLEditorRef().MoveNodeWithTransaction(aNode, pointToInsert);
+      nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                        .MoveNodeWithTransaction(aNode, pointToInsert);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return EditActionIgnored(rv);
       }
     }
     if (*aInOutDestOffset != -1) {
@@ -3624,17 +3630,18 @@ EditActionResult HTMLEditRules::MoveNode
     }
     // XXX Should we check if the node is actually moved in this case?
     return EditActionHandled();
   }
 
   // If it can't, move its children (if any), and then delete it.
   EditActionResult ret(NS_OK);
   if (aNode.IsElement()) {
-    ret = MoveContents(*aNode.AsElement(), aDestElement, aInOutDestOffset);
+    ret = MoveContents(MOZ_KnownLive(*aNode.AsElement()), aDestElement,
+                       aInOutDestOffset);
     if (NS_WARN_IF(ret.Failed())) {
       return ret;
     }
   }
 
   nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(aNode);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return ret.SetResult(NS_ERROR_EDITOR_DESTROYED);
@@ -3651,17 +3658,17 @@ EditActionResult HTMLEditRules::MoveCont
   MOZ_ASSERT(aInOutDestOffset);
 
   if (NS_WARN_IF(&aElement == &aDestElement)) {
     return EditActionIgnored(NS_ERROR_ILLEGAL_VALUE);
   }
 
   EditActionResult ret(NS_OK);
   while (aElement.GetFirstChild()) {
-    ret |= MoveNodeSmart(*aElement.GetFirstChild(), aDestElement,
+    ret |= MoveNodeSmart(MOZ_KnownLive(*aElement.GetFirstChild()), aDestElement,
                          aInOutDestOffset);
     if (NS_WARN_IF(ret.Failed())) {
       return ret;
     }
   }
   return ret;
 }
 
@@ -3959,41 +3966,43 @@ nsresult HTMLEditRules::MakeList(nsAtom&
 
     if (HTMLEditUtils::IsList(curNode)) {
       // do we have a curList already?
       if (curList && !EditorUtils::IsDescendantOf(*curNode, *curList)) {
         // move all of our children into curList.  cheezy way to do it: move
         // whole list and then RemoveContainerWithTransaction() on the list.
         // ConvertListType first: that routine handles converting the list
         // item types, if needed.
-        rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode, *curList);
+        rv = MOZ_KnownLive(HTMLEditorRef())
+                 .MoveNodeToEndWithTransaction(*curNode, *curList);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
-        CreateElementResult convertListTypeResult =
-            ConvertListType(*curNode->AsElement(), aListType, aItemType);
+        CreateElementResult convertListTypeResult = ConvertListType(
+            MOZ_KnownLive(*curNode->AsElement()), aListType, aItemType);
         if (NS_WARN_IF(convertListTypeResult.Failed())) {
           return convertListTypeResult.Rv();
         }
-        rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
-            *convertListTypeResult.GetNewNode());
+        rv = MOZ_KnownLive(HTMLEditorRef())
+                 .RemoveBlockContainerWithTransaction(
+                     MOZ_KnownLive(*convertListTypeResult.GetNewNode()));
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         newBlock = convertListTypeResult.forget();
       } else {
         // replace list with new list type
-        CreateElementResult convertListTypeResult =
-            ConvertListType(*curNode->AsElement(), aListType, aItemType);
+        CreateElementResult convertListTypeResult = ConvertListType(
+            MOZ_KnownLive(*curNode->AsElement()), aListType, aItemType);
         if (NS_WARN_IF(convertListTypeResult.Failed())) {
           return convertListTypeResult.Rv();
         }
         curList = convertListTypeResult.forget();
       }
       prevListItem = nullptr;
       continue;
     }
@@ -4028,52 +4037,56 @@ nsresult HTMLEditRules::MakeList(nsAtom&
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!curList)) {
             return NS_ERROR_FAILURE;
           }
         }
         // move list item to new list
-        rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode, *curList);
+        rv = MOZ_KnownLive(HTMLEditorRef())
+                 .MoveNodeToEndWithTransaction(*curNode, *curList);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         // convert list item type if needed
         if (!curNode->IsHTMLElement(&aItemType)) {
-          newBlock = HTMLEditorRef().ReplaceContainerWithTransaction(
-              *curNode->AsElement(), aItemType);
+          newBlock = MOZ_KnownLive(HTMLEditorRef())
+                         .ReplaceContainerWithTransaction(
+                             MOZ_KnownLive(*curNode->AsElement()), aItemType);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!newBlock)) {
             return NS_ERROR_FAILURE;
           }
         }
       } else {
         // item is in right type of list.  But we might still have to move it.
         // and we might need to convert list item types.
         if (!curList) {
           curList = atCurNode.GetContainerAsElement();
         } else if (atCurNode.GetContainer() != curList) {
           // move list item to new list
-          rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode, *curList);
+          rv = MOZ_KnownLive(HTMLEditorRef())
+                   .MoveNodeToEndWithTransaction(*curNode, *curList);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
         }
         if (!curNode->IsHTMLElement(&aItemType)) {
-          newBlock = HTMLEditorRef().ReplaceContainerWithTransaction(
-              *curNode->AsElement(), aItemType);
+          newBlock = MOZ_KnownLive(HTMLEditorRef())
+                         .ReplaceContainerWithTransaction(
+                             MOZ_KnownLive(*curNode->AsElement()), aItemType);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!newBlock)) {
             return NS_ERROR_FAILURE;
           }
         }
       }
@@ -4104,18 +4117,19 @@ nsresult HTMLEditRules::MakeList(nsAtom&
     }
 
     // if we hit a div clear our prevListItem, insert divs contents
     // into our node array, and remove the div
     if (curNode->IsHTMLElement(nsGkAtoms::div)) {
       prevListItem = nullptr;
       int32_t j = i + 1;
       GetInnerContent(*curNode, arrayOfNodes, &j);
-      rv =
-          HTMLEditorRef().RemoveContainerWithTransaction(*curNode->AsElement());
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .RemoveContainerWithTransaction(
+                   MOZ_KnownLive(*curNode->AsElement()));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       listCount = arrayOfNodes.Length();
       continue;
@@ -4147,38 +4161,39 @@ nsresult HTMLEditRules::MakeList(nsAtom&
     }
 
     // if curNode isn't a list item, we must wrap it in one
     nsCOMPtr<Element> listItem;
     if (!HTMLEditUtils::IsListItem(curNode)) {
       if (IsInlineNode(curNode) && prevListItem) {
         // this is a continuation of some inline nodes that belong together in
         // the same list item.  use prevListItem
-        rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode,
-                                                          *prevListItem);
+        rv = MOZ_KnownLive(HTMLEditorRef())
+                 .MoveNodeToEndWithTransaction(*curNode, *prevListItem);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       } else {
         // don't wrap li around a paragraph.  instead replace paragraph with li
         if (curNode->IsHTMLElement(nsGkAtoms::p)) {
-          listItem = HTMLEditorRef().ReplaceContainerWithTransaction(
-              *curNode->AsElement(), aItemType);
+          listItem = MOZ_KnownLive(HTMLEditorRef())
+                         .ReplaceContainerWithTransaction(
+                             MOZ_KnownLive(*curNode->AsElement()), aItemType);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!listItem)) {
             return NS_ERROR_FAILURE;
           }
         } else {
-          listItem = HTMLEditorRef().InsertContainerWithTransaction(*curNode,
-                                                                    aItemType);
+          listItem = MOZ_KnownLive(HTMLEditorRef())
+                         .InsertContainerWithTransaction(*curNode, aItemType);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!listItem)) {
             return NS_ERROR_FAILURE;
           }
         }
         if (IsInlineNode(curNode)) {
@@ -4189,17 +4204,18 @@ nsresult HTMLEditRules::MakeList(nsAtom&
       }
     } else {
       listItem = curNode->AsElement();
     }
 
     if (listItem) {
       // if we made a new list item, deal with it: tuck the listItem into the
       // end of the active list
-      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*listItem, *curList);
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .MoveNodeToEndWithTransaction(*listItem, *curList);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
@@ -4244,25 +4260,25 @@ nsresult HTMLEditRules::WillRemoveList(b
 
   // Only act on lists or list items in the array
   for (auto& curNode : arrayOfNodes) {
     // here's where we actually figure out what to do
     if (HTMLEditUtils::IsListItem(curNode)) {
       // unlist this listitem
       bool bOutOfList;
       do {
-        rv = PopListItem(*curNode->AsContent(), &bOutOfList);
+        rv = PopListItem(MOZ_KnownLive(*curNode->AsContent()), &bOutOfList);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       } while (
           !bOutOfList);  // keep popping it out until it's not in a list anymore
     } else if (HTMLEditUtils::IsList(curNode)) {
       // node is a list, move list items out
-      rv = RemoveListStructure(*curNode->AsElement());
+      rv = RemoveListStructure(MOZ_KnownLive(*curNode->AsElement()));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
   return NS_OK;
 }
 
@@ -4678,18 +4694,20 @@ nsresult HTMLEditRules::IndentAroundSele
       // We do this if the next element is a list, and the list is of the
       // same type (li/ol) as curNode was a part it.
       sibling = HTMLEditorRef().GetNextHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
               sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
               sibling->NodeInfo()->NamespaceID()) {
-        nsresult rv = HTMLEditorRef().MoveNodeWithTransaction(
-            *curNode->AsContent(), EditorRawDOMPoint(sibling, 0));
+        nsresult rv =
+            MOZ_KnownLive(HTMLEditorRef())
+                .MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
+                                         EditorRawDOMPoint(sibling, 0));
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         continue;
       }
@@ -4698,18 +4716,19 @@ nsresult HTMLEditRules::IndentAroundSele
       // We do this if the previous element is a list, and the list is of
       // the same type (li/ol) as curNode was a part of.
       sibling = HTMLEditorRef().GetPriorHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
               sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
               sibling->NodeInfo()->NamespaceID()) {
-        nsresult rv = HTMLEditorRef().MoveNodeToEndWithTransaction(
-            *curNode->AsContent(), *sibling);
+        nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                          .MoveNodeToEndWithTransaction(
+                              MOZ_KnownLive(*curNode->AsContent()), *sibling);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         continue;
       }
@@ -4739,31 +4758,33 @@ nsresult HTMLEditRules::IndentAroundSele
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in
         // remember our new block for postprocessing
         mNewBlock = curList;
       }
       // tuck the node into the end of the active list
-      nsresult rv = HTMLEditorRef().MoveNodeToEndWithTransaction(
-          *curNode->AsContent(), *curList);
+      nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                        .MoveNodeToEndWithTransaction(
+                            MOZ_KnownLive(*curNode->AsContent()), *curList);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       continue;
     }
 
     // Not a list item.
 
     if (IsBlockNode(*curNode)) {
-      nsresult rv = IncreaseMarginToIndent(*curNode->AsElement());
+      nsresult rv =
+          IncreaseMarginToIndent(MOZ_KnownLive(*curNode->AsElement()));
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to inrease indentation");
       curQuote = nullptr;
       continue;
     }
 
@@ -4794,18 +4815,19 @@ nsresult HTMLEditRules::IndentAroundSele
       }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to increase indentation");
       // remember our new block for postprocessing
       mNewBlock = curQuote;
       // curQuote is now the correct thing to put curNode in
     }
 
     // tuck the node into the end of the active blockquote
-    nsresult rv = HTMLEditorRef().MoveNodeToEndWithTransaction(
-        *curNode->AsContent(), *curQuote);
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .MoveNodeToEndWithTransaction(
+                          MOZ_KnownLive(*curNode->AsContent()), *curQuote);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   return NS_OK;
@@ -4945,18 +4967,19 @@ nsresult HTMLEditRules::IndentAroundSele
       // We do this if the next element is a list, and the list is of the
       // same type (li/ol) as curNode was a part it.
       sibling = HTMLEditorRef().GetNextHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
               sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
               sibling->NodeInfo()->NamespaceID()) {
-        rv = HTMLEditorRef().MoveNodeWithTransaction(
-            *curNode->AsContent(), EditorRawDOMPoint(sibling, 0));
+        rv = MOZ_KnownLive(HTMLEditorRef())
+                 .MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
+                                          EditorRawDOMPoint(sibling, 0));
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         continue;
       }
@@ -4965,18 +4988,19 @@ nsresult HTMLEditRules::IndentAroundSele
       // We do this if the previous element is a list, and the list is of
       // the same type (li/ol) as curNode was a part of.
       sibling = HTMLEditorRef().GetPriorHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
               sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
               sibling->NodeInfo()->NamespaceID()) {
-        rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
-                                                          *sibling);
+        rv = MOZ_KnownLive(HTMLEditorRef())
+                 .MoveNodeToEndWithTransaction(
+                     MOZ_KnownLive(*curNode->AsContent()), *sibling);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         continue;
       }
@@ -5006,18 +5030,19 @@ nsresult HTMLEditRules::IndentAroundSele
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in
         // remember our new block for postprocessing
         mNewBlock = curList;
       }
       // tuck the node into the end of the active list
-      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
-                                                        *curList);
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .MoveNodeToEndWithTransaction(
+                   MOZ_KnownLive(*curNode->AsContent()), *curList);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       // forget curQuote, if any
       curQuote = nullptr;
@@ -5064,17 +5089,18 @@ nsresult HTMLEditRules::IndentAroundSele
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
       }
 
-      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*listItem, *curList);
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .MoveNodeToEndWithTransaction(*listItem, *curList);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // remember we indented this li
@@ -5113,18 +5139,19 @@ nsresult HTMLEditRules::IndentAroundSele
         return NS_ERROR_FAILURE;
       }
       // remember our new block for postprocessing
       mNewBlock = curQuote;
       // curQuote is now the correct thing to put curNode in
     }
 
     // tuck the node into the end of the active blockquote
-    rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
-                                                      *curQuote);
+    rv = MOZ_KnownLive(HTMLEditorRef())
+             .MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
+                                           *curQuote);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     // forget curList, if any
     curList = nullptr;
@@ -5270,18 +5297,19 @@ SplitRangeOffFromNodeResult HTMLEditRule
         leftContentOfLastOutdented = outdentResult.GetLeftContent();
         middleContentOfLastOutdented = outdentResult.GetMiddleContent();
         rightContentOfLastOutdented = outdentResult.GetRightContent();
         curBlockQuote = nullptr;
         firstBQChild = nullptr;
         lastBQChild = nullptr;
         curBlockQuoteIsIndentedWithCSS = false;
       }
-      rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
-          *curNode->AsElement());
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .RemoveBlockContainerWithTransaction(
+                   MOZ_KnownLive(*curNode->AsElement()));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return SplitRangeOffFromNodeResult(rv);
       }
       continue;
     }
@@ -5293,17 +5321,18 @@ SplitRangeOffFromNodeResult HTMLEditRule
       CSSEditUtils::GetSpecifiedProperty(curNode, marginProperty, value);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
       }
       float f;
       RefPtr<nsAtom> unit;
       CSSEditUtils::ParseLength(value, &f, getter_AddRefs(unit));
       if (f > 0) {
-        nsresult rv = DecreaseMarginToOutdent(*curNode->AsElement());
+        nsresult rv =
+            DecreaseMarginToOutdent(MOZ_KnownLive(*curNode->AsElement()));
         if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
           return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
         }
         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                              "Failed to decrease indentation");
         continue;
       }
     }
@@ -5323,17 +5352,17 @@ SplitRangeOffFromNodeResult HTMLEditRule
         leftContentOfLastOutdented = outdentResult.GetLeftContent();
         middleContentOfLastOutdented = outdentResult.GetMiddleContent();
         rightContentOfLastOutdented = outdentResult.GetRightContent();
         curBlockQuote = nullptr;
         firstBQChild = nullptr;
         lastBQChild = nullptr;
         curBlockQuoteIsIndentedWithCSS = false;
       }
-      rv = PopListItem(*curNode->AsContent());
+      rv = PopListItem(MOZ_KnownLive(*curNode->AsContent()));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return SplitRangeOffFromNodeResult(rv);
       }
       continue;
     }
 
     // Do we have a blockquote that we are already committed to removing?
     if (curBlockQuote) {
@@ -5410,18 +5439,19 @@ SplitRangeOffFromNodeResult HTMLEditRule
       continue;
     }
 
     // Couldn't find enclosing blockquote.
     if (HTMLEditUtils::IsList(curParent)) {
       // Move node out of list
       if (HTMLEditUtils::IsList(curNode)) {
         // Just unwrap this sublist
-        rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
-            *curNode->AsElement());
+        rv = MOZ_KnownLive(HTMLEditorRef())
+                 .RemoveBlockContainerWithTransaction(
+                     MOZ_KnownLive(*curNode->AsElement()));
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return SplitRangeOffFromNodeResult(rv);
         }
       }
       continue;
@@ -5436,18 +5466,18 @@ SplitRangeOffFromNodeResult HTMLEditRule
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return SplitRangeOffFromNodeResult(rv);
           }
         } else if (HTMLEditUtils::IsList(child)) {
           // We have an embedded list, so move it out from under the parent
           // list. Be sure to put it after the parent list because this
           // loop iterates backwards through the parent's list of children.
           EditorRawDOMPoint afterCurrentList(curParent, offset + 1);
-          rv =
-              HTMLEditorRef().MoveNodeWithTransaction(*child, afterCurrentList);
+          rv = MOZ_KnownLive(HTMLEditorRef())
+                   .MoveNodeWithTransaction(*child, afterCurrentList);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return SplitRangeOffFromNodeResult(rv);
           }
         } else {
           // Delete any non-list items for now
@@ -5457,18 +5487,19 @@ SplitRangeOffFromNodeResult HTMLEditRule
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return SplitRangeOffFromNodeResult(rv);
           }
         }
         child = curNode->GetLastChild();
       }
       // Delete the now-empty list
-      rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
-          *curNode->AsElement());
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .RemoveBlockContainerWithTransaction(
+                   MOZ_KnownLive(*curNode->AsElement()));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return SplitRangeOffFromNodeResult(rv);
       }
       continue;
     }
@@ -5517,18 +5548,18 @@ HTMLEditRules::SplitRangeOffFromBlockAnd
 
   SplitRangeOffFromNodeResult splitResult =
       SplitRangeOffFromBlock(aBlockElement, aStartOfRange, aEndOfRange);
   if (NS_WARN_IF(splitResult.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
     return splitResult;
   }
   NS_WARNING_ASSERTION(splitResult.Succeeded(),
                        "Failed to split the range off from the block element");
-  nsresult rv =
-      HTMLEditorRef().RemoveBlockContainerWithTransaction(aBlockElement);
+  nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                    .RemoveBlockContainerWithTransaction(aBlockElement);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return SplitRangeOffFromNodeResult(rv);
   }
   return SplitRangeOffFromNodeResult(splitResult.GetLeftContent(), nullptr,
                                      splitResult.GetRightContent());
@@ -5582,31 +5613,32 @@ SplitRangeOffFromNodeResult HTMLEditRule
     return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
   }
 
   if (NS_WARN_IF(!splitResult.GetMiddleContentAsElement())) {
     return SplitRangeOffFromNodeResult(NS_ERROR_FAILURE);
   }
 
   if (!aIsBlockIndentedWithCSS) {
-    nsresult rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
-        *splitResult.GetMiddleContentAsElement());
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .RemoveBlockContainerWithTransaction(MOZ_KnownLive(
+                          *splitResult.GetMiddleContentAsElement()));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return SplitRangeOffFromNodeResult(rv);
     }
     return SplitRangeOffFromNodeResult(splitResult.GetLeftContent(), nullptr,
                                        splitResult.GetRightContent());
   }
 
   if (splitResult.GetMiddleContentAsElement()) {
-    nsresult rv =
-        DecreaseMarginToOutdent(*splitResult.GetMiddleContentAsElement());
+    nsresult rv = DecreaseMarginToOutdent(
+        MOZ_KnownLive(*splitResult.GetMiddleContentAsElement()));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return SplitRangeOffFromNodeResult(rv);
     }
     return splitResult;
   }
 
   return splitResult;
 }
@@ -5617,45 +5649,47 @@ CreateElementResult HTMLEditRules::Conve
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsCOMPtr<nsINode> child = aListElement.GetFirstChild();
   while (child) {
     if (child->IsElement()) {
       Element* element = child->AsElement();
       if (HTMLEditUtils::IsListItem(element) &&
           !element->IsHTMLElement(&aNewListItemTag)) {
-        child = HTMLEditorRef().ReplaceContainerWithTransaction(
-            *element, aNewListItemTag);
+        child = MOZ_KnownLive(HTMLEditorRef())
+                    .ReplaceContainerWithTransaction(MOZ_KnownLive(*element),
+                                                     aNewListItemTag);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
         }
         if (NS_WARN_IF(!child)) {
           return CreateElementResult(NS_ERROR_FAILURE);
         }
       } else if (HTMLEditUtils::IsList(element) &&
                  !element->IsHTMLElement(&aNewListTag)) {
         // XXX List elements shouldn't have other list elements as their
         //     child.  Why do we handle such invalid tree?
-        CreateElementResult convertListTypeResult =
-            ConvertListType(*child->AsElement(), aNewListTag, aNewListItemTag);
+        CreateElementResult convertListTypeResult = ConvertListType(
+            MOZ_KnownLive(*child->AsElement()), aNewListTag, aNewListItemTag);
         if (NS_WARN_IF(convertListTypeResult.Failed())) {
           return convertListTypeResult;
         }
         child = convertListTypeResult.forget();
       }
     }
     child = child->GetNextSibling();
   }
 
   if (aListElement.IsHTMLElement(&aNewListTag)) {
     return CreateElementResult(&aListElement);
   }
 
-  RefPtr<Element> listElement = HTMLEditorRef().ReplaceContainerWithTransaction(
-      aListElement, aNewListTag);
+  RefPtr<Element> listElement =
+      MOZ_KnownLive(HTMLEditorRef())
+          .ReplaceContainerWithTransaction(aListElement, aNewListTag);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
   }
   NS_WARNING_ASSERTION(listElement != nullptr, "Failed to create list element");
   return CreateElementResult(listElement.forget());
 }
 
 nsresult HTMLEditRules::CreateStyleForInsertText(Document& aDocument) {
@@ -5680,18 +5714,20 @@ nsresult HTMLEditRules::CreateStyleForIn
 
   {
     // Transactions may set selection, but we will set selection if necessary.
     AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
 
     while (item && node != rootElement) {
       // XXX If we redesign ClearStyle(), we can use EditorDOMPoint in this
       //     method.
-      nsresult rv = HTMLEditorRef().ClearStyle(address_of(node), &offset,
-                                               item->tag, item->attr);
+      nsresult rv =
+          MOZ_KnownLive(HTMLEditorRef())
+              .ClearStyle(address_of(node), &offset, MOZ_KnownLive(item->tag),
+                          MOZ_KnownLive(item->attr));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       item = HTMLEditorRef().mTypeInState->TakeClearProperty();
       weDidSomething = true;
@@ -5721,46 +5757,50 @@ nsresult HTMLEditRules::CreateStyleForIn
       node = splitPoint.GetContainer();
       offset = splitPoint.Offset();
     }
     if (!HTMLEditorRef().IsContainer(node)) {
       return NS_OK;
     }
     OwningNonNull<Text> newNode =
         EditorBase::CreateTextNode(aDocument, EmptyString());
-    nsresult rv = HTMLEditorRef().InsertNodeWithTransaction(
-        *newNode, EditorRawDOMPoint(node, offset));
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .InsertNodeWithTransaction(
+                          *newNode, EditorRawDOMPoint(node, offset));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     node = newNode;
     offset = 0;
     weDidSomething = true;
 
     if (relFontSize) {
       // dir indicated bigger versus smaller.  1 = bigger, -1 = smaller
       HTMLEditor::FontSize dir = relFontSize > 0 ? HTMLEditor::FontSize::incr
                                                  : HTMLEditor::FontSize::decr;
       for (int32_t j = 0; j < DeprecatedAbs(relFontSize); j++) {
-        rv = HTMLEditorRef().RelativeFontChangeOnTextNode(dir, newNode, 0, -1);
+        rv = MOZ_KnownLive(HTMLEditorRef())
+                 .RelativeFontChangeOnTextNode(dir, newNode, 0, -1);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
 
     while (item) {
-      rv = HTMLEditorRef().SetInlinePropertyOnNode(
-          *node->AsContent(), *item->tag, item->attr, item->value);
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .SetInlinePropertyOnNode(MOZ_KnownLive(*node->AsContent()),
+                                        MOZ_KnownLive(*item->tag),
+                                        MOZ_KnownLive(item->attr), item->value);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       item = HTMLEditorRef().mTypeInState->TakeSetProperty();
     }
@@ -5849,17 +5889,17 @@ nsresult HTMLEditRules::AlignContentsAtS
   if (nodeArray.Length() == 1) {
     OwningNonNull<nsINode> node = nodeArray[0];
 
     if (HTMLEditUtils::SupportsAlignAttr(*node)) {
       // The node is a table element, an hr, a paragraph, a div or a section
       // header; in HTML 4, it can directly carry the ALIGN attribute and we
       // don't need to make a div! If we are in CSS mode, all the work is done
       // in AlignBlock
-      rv = AlignBlock(*node->AsElement(), aAlignType,
+      rv = AlignBlock(MOZ_KnownLive(*node->AsElement()), aAlignType,
                       ResetAlignOf::OnlyDescendants);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       return NS_OK;
     }
 
     if (TextEditUtils::IsBreak(node)) {
@@ -5988,17 +6028,17 @@ nsresult HTMLEditRules::AlignContentsAtS
       continue;
     }
 
     // The node is a table element, an hr, a paragraph, a div or a section
     // header; in HTML 4, it can directly carry the ALIGN attribute and we
     // don't need to nest it, just set the alignment.  In CSS, assign the
     // corresponding CSS styles in AlignBlock
     if (HTMLEditUtils::SupportsAlignAttr(*curNode)) {
-      rv = AlignBlock(*curNode->AsElement(), aAlignType,
+      rv = AlignBlock(MOZ_KnownLive(*curNode->AsElement()), aAlignType,
                       ResetAlignOf::ElementAndDescendants);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       // Clear out curDiv so that we don't put nodes after this one into it
       curDiv = nullptr;
       continue;
     }
@@ -6085,18 +6125,19 @@ nsresult HTMLEditRules::AlignContentsAtS
       rv = AlignBlock(*curDiv, aAlignType, ResetAlignOf::OnlyDescendants);
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to align the <div>");
     }
 
     // Tuck the node into the end of the active div
-    rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
-                                                      *curDiv);
+    rv = MOZ_KnownLive(HTMLEditorRef())
+             .MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
+                                           *curDiv);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
@@ -6134,18 +6175,20 @@ nsresult HTMLEditRules::AlignBlockConten
     // this cell has no content, nothing to align
     return NS_OK;
   }
 
   nsCOMPtr<nsIContent> lastChild = HTMLEditorRef().GetLastEditableChild(aNode);
   if (firstChild == lastChild && firstChild->IsHTMLElement(nsGkAtoms::div)) {
     // the cell already has a div containing all of its content: just
     // act on this div.
-    nsresult rv = HTMLEditorRef().SetAttributeOrEquivalent(
-        firstChild->AsElement(), nsGkAtoms::align, aAlignType, false);
+    nsresult rv =
+        MOZ_KnownLive(HTMLEditorRef())
+            .SetAttributeOrEquivalent(MOZ_KnownLive(firstChild->AsElement()),
+                                      nsGkAtoms::align, aAlignType, false);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
@@ -6157,28 +6200,30 @@ nsresult HTMLEditRules::AlignBlockConten
       HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div, atStartOfNode);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(!divElem)) {
     return NS_ERROR_FAILURE;
   }
   // set up the alignment on the div
-  nsresult rv = HTMLEditorRef().SetAttributeOrEquivalent(
-      divElem, nsGkAtoms::align, aAlignType, false);
+  nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                    .SetAttributeOrEquivalent(divElem, nsGkAtoms::align,
+                                              aAlignType, false);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   // tuck the children into the end of the active div
   while (lastChild && (lastChild != divElem)) {
-    nsresult rv = HTMLEditorRef().MoveNodeWithTransaction(
-        *lastChild, EditorRawDOMPoint(divElem, 0));
+    nsresult rv =
+        MOZ_KnownLive(HTMLEditorRef())
+            .MoveNodeWithTransaction(*lastChild, EditorRawDOMPoint(divElem, 0));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     lastChild = HTMLEditorRef().GetLastEditableChild(aNode);
   }
@@ -7167,17 +7212,18 @@ nsresult HTMLEditRules::GetNodesForOpera
       aEditSubAction == EditSubAction::eIndent ||
       aEditSubAction == EditSubAction::eOutdent) {
     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
       // XXX Why do we run this loop even when aTouchContent is "no"?
       if (aTouchContent == TouchContent::yes && IsInlineNode(node) &&
           HTMLEditorRef().IsContainer(node) && !EditorBase::IsTextNode(node)) {
         nsTArray<OwningNonNull<nsINode>> arrayOfInlines;
-        nsresult rv = BustUpInlinesAtBRs(*node->AsContent(), arrayOfInlines);
+        nsresult rv = BustUpInlinesAtBRs(MOZ_KnownLive(*node->AsContent()),
+                                         arrayOfInlines);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
         // Put these nodes in aOutArrayOfNodes, replacing the current node
         aOutArrayOfNodes.RemoveElementAt(i);
         aOutArrayOfNodes.InsertElementsAt(i, arrayOfInlines);
       }
@@ -7455,18 +7501,19 @@ nsresult HTMLEditRules::BustUpInlinesAtB
       // Might not be a left node.  A break might have been at the very
       // beginning of inline container, in which case
       // SplitNodeDeepWithTransaction() would not actually split anything.
       aOutArrayOfNodes.AppendElement(*splitNodeResult.GetPreviousNode());
     }
 
     // Move break outside of container and also put in node list
     EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode());
-    nsresult rv = HTMLEditorRef().MoveNodeWithTransaction(*brNode->AsContent(),
-                                                          atNextNode);
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .MoveNodeWithTransaction(
+                          MOZ_KnownLive(*brNode->AsContent()), atNextNode);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     aOutArrayOfNodes.AppendElement(*brNode);
 
@@ -8064,18 +8111,19 @@ nsresult HTMLEditRules::ReturnInListItem
 
     // Are we in a sublist?
     EditorRawDOMPoint atNextSiblingOfLeftList(leftListNode);
     DebugOnly<bool> advanced = atNextSiblingOfLeftList.AdvanceOffset();
     NS_WARNING_ASSERTION(advanced,
                          "Failed to advance offset after the right list node");
     if (HTMLEditUtils::IsList(atNextSiblingOfLeftList.GetContainer())) {
       // If so, move item out of this list and into the grandparent list
-      nsresult rv = HTMLEditorRef().MoveNodeWithTransaction(
-          aListItem, atNextSiblingOfLeftList);
+      nsresult rv =
+          MOZ_KnownLive(HTMLEditorRef())
+              .MoveNodeWithTransaction(aListItem, atNextSiblingOfLeftList);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       ErrorResult error;
       SelectionRefPtr()->Collapse(RawRangeBoundary(&aListItem, 0), error);
@@ -8217,19 +8265,20 @@ nsresult HTMLEditRules::ReturnInListItem
           }
           if (NS_WARN_IF(error.Failed())) {
             return error.StealNSResult();
           }
           return NS_OK;
         }
 
         RefPtr<Element> brElement;
-        nsresult rv =
-            HTMLEditorRef().CopyLastEditableChildStylesWithTransaction(
-                *prevItem->AsElement(), aListItem, address_of(brElement));
+        nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                          .CopyLastEditableChildStylesWithTransaction(
+                              MOZ_KnownLive(*prevItem->AsElement()), aListItem,
+                              address_of(brElement));
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NS_ERROR_FAILURE;
         }
         if (brElement) {
           EditorRawDOMPoint atBrNode(brElement);
@@ -8356,18 +8405,19 @@ nsresult HTMLEditRules::MakeBlockquote(
       if (NS_WARN_IF(!curBlock)) {
         return NS_ERROR_FAILURE;
       }
       // remember our new block for postprocessing
       mNewBlock = curBlock;
       // note: doesn't matter if we set mNewBlock multiple times.
     }
 
-    nsresult rv = HTMLEditorRef().MoveNodeToEndWithTransaction(
-        *curNode->AsContent(), *curBlock);
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .MoveNodeToEndWithTransaction(
+                          MOZ_KnownLive(*curNode->AsContent()), *curBlock);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   return NS_OK;
@@ -8394,18 +8444,19 @@ nsresult HTMLEditRules::RemoveBlockStyle
           return removeMiddleContainerResult.Rv();
         }
         firstNode = lastNode = curBlock = nullptr;
       }
       if (!HTMLEditorRef().IsEditable(curNode)) {
         continue;
       }
       // Remove current block
-      nsresult rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
-          *curNode->AsElement());
+      nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                        .RemoveBlockContainerWithTransaction(
+                            MOZ_KnownLive(*curNode->AsElement()));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       continue;
     }
@@ -8519,19 +8570,19 @@ nsresult HTMLEditRules::ApplyBlockStyle(
 
     // If curNode is a address, p, header, address, or pre, replace it with a
     // new block of correct type.
     // XXX: pre can't hold everything the others can
     if (HTMLEditUtils::IsMozDiv(curNode) ||
         HTMLEditUtils::IsFormatNode(curNode)) {
       // Forget any previous block used for previous inline nodes
       curBlock = nullptr;
-      newBlock =
-          HTMLEditorRef().ReplaceContainerAndCloneAttributesWithTransaction(
-              *curNode->AsElement(), aBlockTag);
+      newBlock = MOZ_KnownLive(HTMLEditorRef())
+                     .ReplaceContainerAndCloneAttributesWithTransaction(
+                         MOZ_KnownLive(*curNode->AsElement()), aBlockTag);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!newBlock)) {
         return NS_ERROR_FAILURE;
       }
       continue;
     }
@@ -8601,18 +8652,19 @@ nsresult HTMLEditRules::ApplyBlockStyle(
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!curBlock)) {
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       mNewBlock = curBlock;
       // Note: doesn't matter if we set mNewBlock multiple times.
-      nsresult rv = HTMLEditorRef().MoveNodeToEndWithTransaction(
-          *curNode->AsContent(), *curBlock);
+      nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                        .MoveNodeToEndWithTransaction(
+                            MOZ_KnownLive(*curNode->AsContent()), *curBlock);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       continue;
     }
@@ -8657,18 +8709,19 @@ nsresult HTMLEditRules::ApplyBlockStyle(
         // This is possible due to mutation events, let's not assert
         return NS_ERROR_UNEXPECTED;
       }
 
       // XXX If curNode is a br, replace it with a return if going to <pre>
 
       // This is a continuation of some inline nodes that belong together in
       // the same block item.  Use curBlock.
-      nsresult rv = HTMLEditorRef().MoveNodeToEndWithTransaction(
-          *curNode->AsContent(), *curBlock);
+      nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                        .MoveNodeToEndWithTransaction(
+                            MOZ_KnownLive(*curNode->AsContent()), *curBlock);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
@@ -8746,18 +8799,19 @@ nsresult HTMLEditRules::JoinNearestEdita
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsINode> rightParent = aNodeRight.GetParentNode();
 
   // If they don't have the same parent, first move the right node to after the
   // left one
   if (parent != rightParent) {
     int32_t parOffset = parent->ComputeIndexOf(&aNodeLeft);
-    nsresult rv = HTMLEditorRef().MoveNodeWithTransaction(
-        aNodeRight, EditorRawDOMPoint(parent, parOffset));
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .MoveNodeWithTransaction(
+                          aNodeRight, EditorRawDOMPoint(parent, parOffset));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
@@ -9679,18 +9733,18 @@ nsresult HTMLEditRules::PopListItem(nsIC
   // be inserted between the both list elements.  This allows user to hit
   // Enter twice at a list item breaks the parent list node.
   if (!isFirstListItem) {
     DebugOnly<bool> advanced = pointToInsertListItem.AdvanceOffset();
     NS_WARNING_ASSERTION(advanced,
                          "Failed to advance offset to right list node");
   }
 
-  nsresult rv =
-      HTMLEditorRef().MoveNodeWithTransaction(*listItem, pointToInsertListItem);
+  nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                    .MoveNodeWithTransaction(*listItem, pointToInsertListItem);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // unwrap list item contents if they are no longer in a list
@@ -9698,18 +9752,19 @@ nsresult HTMLEditRules::PopListItem(nsIC
   //     (although invalid tree), the list item element won't be unwrapped.
   //     That makes the parent ancestor element tree valid, but might be
   //     unexpected result.
   // XXX If aListItem is <dl> or <dd> and current parent is <ul> or <ol>,
   //     the list items won't be unwrapped.  If aListItem is <li> and its
   //     current parent is <dl>, there is same issue.
   if (!HTMLEditUtils::IsList(pointToInsertListItem.GetContainer()) &&
       HTMLEditUtils::IsListItem(listItem)) {
-    rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
-        *listItem->AsElement());
+    rv = MOZ_KnownLive(HTMLEditorRef())
+             .RemoveBlockContainerWithTransaction(
+                 MOZ_KnownLive(*listItem->AsElement()));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (aOutOfList) {
       *aOutOfList = true;
@@ -9742,17 +9797,17 @@ nsresult HTMLEditRules::RemoveListStruct
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       } while (!isOutOfList);
       continue;
     }
 
     if (HTMLEditUtils::IsList(child)) {
-      nsresult rv = RemoveListStructure(*child->AsElement());
+      nsresult rv = RemoveListStructure(MOZ_KnownLive(*child->AsElement()));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       continue;
     }
 
     // Delete any non-list items for now
     // XXX This is not HTML5 aware.  HTML5 allows all list elements to have
@@ -9763,18 +9818,18 @@ nsresult HTMLEditRules::RemoveListStruct
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // Delete the now-empty list
-  nsresult rv =
-      HTMLEditorRef().RemoveBlockContainerWithTransaction(aListElement);
+  nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                    .RemoveBlockContainerWithTransaction(aListElement);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
@@ -10150,17 +10205,19 @@ nsresult HTMLEditRules::RemoveAlignment(
       // we may have to insert BRs in first and last position of element's
       // children if the nodes before/after are not blocks and not BRs
       rv = MakeSureElemStartsAndEndsOnCR(*child);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // now remove the CENTER container
-      rv = HTMLEditorRef().RemoveContainerWithTransaction(*child->AsElement());
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .RemoveContainerWithTransaction(
+                   MOZ_KnownLive(*child->AsElement()));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else if (IsBlockNode(*child) || child->IsHTMLElement(nsGkAtoms::hr)) {
       // the current node is a block element
@@ -10172,18 +10229,20 @@ nsresult HTMLEditRules::RemoveAlignment(
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       if (useCSS) {
         if (child->IsAnyOfHTMLElements(nsGkAtoms::table, nsGkAtoms::hr)) {
-          nsresult rv = HTMLEditorRef().SetAttributeOrEquivalent(
-              child->AsElement(), nsGkAtoms::align, aAlignType, false);
+          nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                            .SetAttributeOrEquivalent(
+                                MOZ_KnownLive(child->AsElement()),
+                                nsGkAtoms::align, aAlignType, false);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
         } else {
           nsAutoString dummyCssValue;
@@ -10286,36 +10345,38 @@ nsresult HTMLEditRules::AlignBlock(Eleme
   nsresult rv = RemoveAlignment(aElement, aAlignType,
                                 aResetAlignOf == ResetAlignOf::OnlyDescendants);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (HTMLEditorRef().IsCSSEnabled()) {
     // Let's use CSS alignment; we use margin-left and margin-right for tables
     // and text-align for other block-level elements
-    nsresult rv = HTMLEditorRef().SetAttributeOrEquivalent(
-        &aElement, nsGkAtoms::align, aAlignType, false);
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .SetAttributeOrEquivalent(&aElement, nsGkAtoms::align,
+                                                aAlignType, false);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   // HTML case; this code is supposed to be called ONLY if the element
   // supports the align attribute but we'll never know...
   if (NS_WARN_IF(!HTMLEditUtils::SupportsAlignAttr(aElement))) {
     // XXX error?
     return NS_OK;
   }
 
-  rv = HTMLEditorRef().SetAttributeOrEquivalent(&aElement, nsGkAtoms::align,
-                                                aAlignType, false);
+  rv = MOZ_KnownLive(HTMLEditorRef())
+           .SetAttributeOrEquivalent(&aElement, nsGkAtoms::align, aAlignType,
+                                     false);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
@@ -10365,31 +10426,32 @@ nsresult HTMLEditRules::ChangeMarginStar
     HTMLEditorRef().mCSSEditUtils->SetCSSProperty(aElement, marginProperty,
                                                   newValue);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     return NS_OK;
   }
 
-  HTMLEditorRef().mCSSEditUtils->RemoveCSSProperty(aElement, marginProperty,
-                                                   value);
+  HTMLEditorRef().mCSSEditUtils->RemoveCSSProperty(
+      aElement, MOZ_KnownLive(marginProperty), value);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   // Remove unnecessary divs
   if (!aElement.IsHTMLElement(nsGkAtoms::div) ||
       &aElement == HTMLEditorRef().GetActiveEditingHost() ||
       !HTMLEditorRef().IsDescendantOfEditorRoot(&aElement) ||
       HTMLEditor::HasAttributes(&aElement)) {
     return NS_OK;
   }
 
-  nsresult rv = HTMLEditorRef().RemoveContainerWithTransaction(aElement);
+  nsresult rv =
+      MOZ_KnownLive(HTMLEditorRef()).RemoveContainerWithTransaction(aElement);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
@@ -10570,18 +10632,19 @@ nsresult HTMLEditRules::PrepareToMakeEle
         }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in.  Remember our
         // new block for postprocessing.
       }
       // Tuck the node into the end of the active list
-      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
-                                                        *curList);
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .MoveNodeToEndWithTransaction(
+                   MOZ_KnownLive(*curNode->AsContent()), *curList);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       continue;
     }
@@ -10636,17 +10699,18 @@ nsresult HTMLEditRules::PrepareToMakeEle
             *containerName, atEndOfCurPositionedDiv);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
       }
-      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*listItem, *curList);
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .MoveNodeToEndWithTransaction(*listItem, *curList);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       // Remember we indented this li
       indentedLI = listItem;
@@ -10676,18 +10740,19 @@ nsresult HTMLEditRules::PrepareToMakeEle
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       *aTargetElement = curPositionedDiv;
       // curPositionedDiv is now the correct thing to put curNode in
     }
 
     // Tuck the node into the end of the active blockquote
-    rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
-                                                      *curPositionedDiv);
+    rv = MOZ_KnownLive(HTMLEditorRef())
+             .MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
+                                           *curPositionedDiv);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     // Forget curList, if any
     curList = nullptr;
@@ -10696,17 +10761,19 @@ nsresult HTMLEditRules::PrepareToMakeEle
 }
 
 nsresult HTMLEditRules::DidAbsolutePosition() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   if (!mNewBlock) {
     return NS_OK;
   }
-  nsresult rv = HTMLEditorRef().SetPositionToAbsoluteOrStatic(*mNewBlock, true);
+  OwningNonNull<Element> newBlock(*mNewBlock);
+  nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                    .SetPositionToAbsoluteOrStatic(*newBlock, true);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
@@ -10733,18 +10800,18 @@ nsresult HTMLEditRules::WillRemoveAbsolu
       HTMLEditorRef().GetAbsolutelyPositionedSelectionContainer();
   if (NS_WARN_IF(!element)) {
     return NS_ERROR_FAILURE;
   }
 
   {
     AutoSelectionRestorer restoreSelectionLater(HTMLEditorRef());
 
-    nsresult rv =
-        HTMLEditorRef().SetPositionToAbsoluteOrStatic(*element, false);
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .SetPositionToAbsoluteOrStatic(*element, false);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -73,37 +73,49 @@ struct StyleCache final : public PropIte
 class HTMLEditRules : public TextEditRules {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditRules, TextEditRules)
 
   HTMLEditRules();
 
   // TextEditRules methods
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult Init(TextEditor* aTextEditor) override;
   virtual nsresult DetachEditor() override;
   virtual nsresult BeforeEdit(EditSubAction aEditSubAction,
                               nsIEditor::EDirection aDirection) override;
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult AfterEdit(EditSubAction aEditSubAction,
                              nsIEditor::EDirection aDirection) override;
+  // NOTE: Don't mark WillDoAction() nor DidDoAction() as MOZ_CAN_RUN_SCRIPT
+  //       because they are too generic and doing it makes a lot of public
+  //       editor methods marked as MOZ_CAN_RUN_SCRIPT too, but some of them
+  //       may not causes running script.  So, ideal fix must be that we make
+  //       each method callsed by this method public.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual nsresult WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
                                 bool* aHandled) override;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual nsresult DidDoAction(EditSubActionInfo& aInfo,
                                nsresult aResult) override;
   virtual bool DocumentIsEmpty() override;
 
   /**
    * DocumentModified() is called when editor content is changed.
    */
   MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult DocumentModified();
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
+  MOZ_CAN_RUN_SCRIPT
   nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
+  MOZ_CAN_RUN_SCRIPT
   nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
+  MOZ_CAN_RUN_SCRIPT
   nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
 
   /**
    * MakeSureElemStartsAndEndsOnCR() inserts <br> element at start (and/or end)
    * of aNode if neither:
    * - first (last) editable child of aNode is a block or a <br>,
    * - previous (next) sibling of aNode is block or a <br>
    * - nor no previous (next) sibling of aNode.
@@ -169,16 +181,17 @@ class HTMLEditRules : public TextEditRul
    *                            or EditSubAction::eInsertText.
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    * @param inString            String to be inserted.
    * @param outString           String actually inserted.
    * @param aMaxLength          The maximum string length which the editor
    *                            allows to set.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillInsertText(EditSubAction aEditSubAction,
                                        bool* aCancel, bool* aHandled,
                                        const nsAString* inString,
                                        nsAString* outString,
                                        int32_t aMaxLength);
 
   /**
    * WillLoadHTML() is called before loading enter document from source.
@@ -205,16 +218,17 @@ class HTMLEditRules : public TextEditRul
   MOZ_MUST_USE nsresult DeleteNodeIfCollapsedText(nsINode& aNode);
 
   /**
    * InsertBRElement() inserts a <br> element into aInsertToBreak.
    *
    * @param aInsertToBreak      The point where new <br> element will be
    *                            inserted before.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult InsertBRElement(const EditorDOMPoint& aInsertToBreak);
 
   /**
    * SplitMailCites() splits mail-cite elements at start of Selection if
    * Selection starts from inside a mail-cite element.  Of course, if it's
    * necessary, this inserts <br> node to new left nodes or existing right
    * nodes.
    */
@@ -310,55 +324,59 @@ class HTMLEditRules : public TextEditRul
    * @return            Sets canceled to true if the operation should do
    *                    nothing anymore even if this doesn't join the blocks.
    *                    Sets handled to true if this actually handles the
    *                    request.  Note that this may set it to true even if this
    *                    does not join the block.  E.g., if the blocks shouldn't
    *                    be joined or it's impossible to join them but it's not
    *                    unexpected case, this returns true with this.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE EditActionResult
   TryToJoinBlocksWithTransaction(nsIContent& aLeftNode, nsIContent& aRightNode);
 
   /**
    * MoveBlock() moves the content from aRightBlock starting from aRightOffset
    * into aLeftBlock at aLeftOffset. Note that the "block" can be inline nodes
    * between <br>s, or between blocks, etc.  DTD containment rules are followed
    * throughout.
    *
    * @return            Sets handled to true if this actually joins the nodes.
    *                    canceled is always false.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE EditActionResult MoveBlock(Element& aLeftBlock,
                                           Element& aRightBlock,
                                           int32_t aLeftOffset,
                                           int32_t aRightOffset);
 
   /**
    * MoveNodeSmart() moves aNode to (aDestElement, aInOutDestOffset).
    * DTD containment rules are followed throughout.
    *
    * @param aOffset                 returns the point after inserted content.
    * @return                        Sets true to handled if this actually moves
    *                                the nodes.
    *                                canceled is always false.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE EditActionResult MoveNodeSmart(nsIContent& aNode,
                                               Element& aDestElement,
                                               int32_t* aInOutDestOffset);
 
   /**
    * MoveContents() moves the contents of aElement to (aDestElement,
    * aInOutDestOffset).  DTD containment rules are followed throughout.
    *
    * @param aInOutDestOffset        updated to point after inserted content.
    * @return                        Sets true to handled if this actually moves
    *                                the nodes.
    *                                canceled is always false.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE EditActionResult MoveContents(Element& aElement,
                                              Element& aDestElement,
                                              int32_t* aInOutDestOffset);
 
   /**
    * DeleteElementsExceptTableRelatedElements() removes elements except
    * table related elements (except <table> itself) and their contents
    * from the DOM tree.
@@ -450,16 +468,17 @@ class HTMLEditRules : public TextEditRul
    * Called before changing absolute positioned element to static positioned.
    * This method actually changes the position property of nearest absolute
    * positioned element.  Therefore, this might cause destroying the HTML
    * editor.
    *
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult WillRemoveAbsolutePosition(bool* aCancel,
                                                    bool* aHandled);
 
   /**
    * Called before changing z-index.
    * This method actually changes z-index of nearest absolute positioned
    * element relatively.  Therefore, this might cause destroying the HTML
    * editor.
@@ -539,64 +558,69 @@ class HTMLEditRules : public TextEditRul
    * with AutoSelectionRestorer.  So, all callers have to check if
    * CanHandleEditAction() still returns true after a call of this method.
    * XXX Should be documented outline of this method.
    *
    * @param aHandled            Returns true if the edit action is handled.
    * @param aTargetElement      Returns target element which should be
    *                            changed to absolute positioned.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult PrepareToMakeElementAbsolutePosition(
       bool* aHandled, RefPtr<Element>* aTargetElement);
 
   /**
    * Called if nobody handles the edit action to make an element absolute
    * positioned.
    * This method actually changes the element which is computed by
    * WillAbsolutePosition() to absolute positioned.
    * Therefore, this might cause destroying the HTML editor.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult DidAbsolutePosition();
 
   /**
    * AlignInnerBlocks() calls AlignBlockContents() for every list item element
    * and table cell element in aNode.
    *
    * @param aNode               The node whose descendants should be aligned
    *                            to aAlignType.
    * @param aAlignType          New value of align attribute of <div>.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult AlignInnerBlocks(nsINode& aNode,
                                          const nsAString& aAlignType);
 
   /**
    * AlignBlockContents() sets align attribute of <div> element which is
    * only child of aNode to aAlignType.  If aNode has 2 or more children or
    * does not have a <div> element has only child, inserts a <div> element
    * into aNode and move all children of aNode into the new <div> element.
    *
    * @param aNode               The node whose contents should be aligned
    *                            to aAlignType.
    * @param aAlignType          New value of align attribute of <div> which
    *                            is only child of aNode.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult AlignBlockContents(nsINode& aNode,
                                            const nsAString& aAlignType);
 
   /**
    * AlignContentsAtSelection() aligns contents around Selection to aAlignType.
    * This creates AutoSelectionRestorer.  Therefore, even if this returns
    * NS_OK, CanHandleEditAction() may return false if the editor is destroyed
    * during restoring the Selection.  So, every caller needs to check if
    * CanHandleEditAction() returns true before modifying the DOM tree or
    * changing Selection.
    *
    * @param aAlignType          New align attribute value where the contents
    *                            should be aligned to.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult AlignContentsAtSelection(const nsAString& aAlignType);
 
   nsresult AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
                                   nsINode* aNode);
   nsresult GetFormatString(nsINode* aNode, nsAString& outFormat);
 
   /**
    * aLists and aTables allow the caller to specify what kind of content to
@@ -672,55 +696,60 @@ class HTMLEditRules : public TextEditRul
    * Enter key press) in a list item element.
    *
    * @param aListItem           The list item which has the following point.
    * @param aNode               Typically, Selection start container, where to
    *                            insert a break.
    * @param aOffset             Typically, Selection start offset in the
    *                            start container, where to insert a break.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult ReturnInListItem(Element& aListItem, nsINode& aNode,
                                          int32_t aOffset);
 
   /**
    * Called after handling edit action.  This may adjust Selection, remove
    * unnecessary empty nodes, create <br> elements if needed, etc.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult AfterEditInner(EditSubAction aEditSubAction,
                                        nsIEditor::EDirection aDirection);
 
   /**
    * IndentAroundSelectionWithCSS() indents around Selection with CSS.
    * This method creates AutoSelectionRestorer.  Therefore, each caller
    * need to check if the editor is still available even if this returns
    * NS_OK.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult IndentAroundSelectionWithCSS();
 
   /**
    * IndentAroundSelectionWithHTML() indents around Selection with HTML.
    * This method creates AutoSelectionRestorer.  Therefore, each caller
    * need to check if the editor is still available even if this returns
    * NS_OK.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult IndentAroundSelectionWithHTML();
 
   /**
    * OutdentAroundSelection() outdents contents around Selection.
    * This method creates AutoSelectionRestorer.  Therefore, each caller
    * need to check if the editor is still available even if this returns
    * NS_OK.
    *
    * @return                    The left content is left content of last
    *                            outdented element.
    *                            The right content is right content of last
    *                            outdented element.
    *                            The middle content is middle content of last
    *                            outdented element.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE SplitRangeOffFromNodeResult OutdentAroundSelection();
 
   /**
    * SplitRangeOffFromBlockAndRemoveMiddleContainer() splits the nodes
    * between aStartOfRange and aEndOfRange, then, removes the middle element
    * and moves its content to where the middle element was.
    *
    * @param aBlockElement           The node which will be split.
@@ -730,16 +759,17 @@ class HTMLEditRules : public TextEditRul
    *                                aBlockElement.
    * @return                        The left content is new created left
    *                                element of aBlockElement.
    *                                The right content is split element,
    *                                i.e., must be aBlockElement.
    *                                The middle content is nullptr since
    *                                removing it is the job of this method.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE SplitRangeOffFromNodeResult
   SplitRangeOffFromBlockAndRemoveMiddleContainer(Element& aBlockElement,
                                                  nsIContent& aStartOfRange,
                                                  nsIContent& aEndOfRange);
 
   /**
    * SplitRangeOffFromBlock() splits aBlock at two points, before aStartChild
    * and after aEndChild.  If they are very start or very end of aBlcok, this
@@ -771,26 +801,28 @@ class HTMLEditRules : public TextEditRul
    *                                or something.
    * @return                        The left content is new created element
    *                                splitting before aStartOfOutdent.
    *                                The right content is existing element.
    *                                The middle content is outdented element
    *                                if aIsBlockIndentedWithCSS is true.
    *                                Otherwise, nullptr.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE SplitRangeOffFromNodeResult
   OutdentPartOfBlock(Element& aBlockElement, nsIContent& aStartOfOutdent,
                      nsIContent& aEndOutdent, bool aIsBlockIndentedWithCSS);
 
   /**
    * XXX Should document what this does.
    * This method creates AutoSelectionRestorer.  Therefore, each caller
    * need to check if the editor is still available even if this returns
    * NS_OK.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult MakeList(nsAtom& aListType, bool aEntireList,
                                  const nsAString* aBulletType, bool* aCancel,
                                  nsAtom& aItemType);
 
   /**
    * ConvertListType() replaces child list items of aListElement with
    * new list item element whose tag name is aNewListItemTag.
    * Note that if there are other list elements as children of aListElement,
@@ -799,26 +831,28 @@ class HTMLEditRules : public TextEditRul
    * @param aListElement        The list element whose list items will be
    *                            replaced.
    * @param aNewListTag         New list tag name.
    * @param aNewListItemTag     New list item tag name.
    * @return                    New list element or an error code if it fails.
    *                            New list element may be aListElement if its
    *                            tag name is same as aNewListTag.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE CreateElementResult ConvertListType(Element& aListElement,
                                                    nsAtom& aListType,
                                                    nsAtom& aItemType);
 
   /**
    * CreateStyleForInsertText() sets CSS properties which are stored in
    * TypeInState to proper element node.
    *
    * @param aDocument           The document of the editor.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult CreateStyleForInsertText(dom::Document& aDocument);
 
   /**
    * IsEmptyBlockElement() returns true if aElement is a block level element
    * and it doesn't have any visible content.
    */
   enum class IgnoreSingleBR { eYes, eNo };
   bool IsEmptyBlockElement(Element& aElement, IgnoreSingleBR aIgnoreSingleBR);
@@ -896,47 +930,52 @@ class HTMLEditRules : public TextEditRul
   /**
    * GetNodesForOperation() runs through the ranges in the array and construct a
    * new array of nodes to be acted on.
    *
    * XXX This name stats with "Get" but actually this modifies the DOM tree with
    *     transaction.  We should rename this to making clearer what this does.
    */
   enum class TouchContent { no, yes };
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult GetNodesForOperation(
       nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
       nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
       EditSubAction aEditSubAction, TouchContent aTouchContent);
 
   void GetChildNodesForOperation(
       nsINode& aNode, nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
 
   /**
    * GetNodesFromPoint() constructs a list of nodes from a point that will be
    * operated on.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult
   GetNodesFromPoint(const EditorDOMPoint& aPoint, EditSubAction aEditSubAction,
                     nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
                     TouchContent aTouchContent);
 
   /**
    * GetNodesFromSelection() constructs a list of nodes from the selection that
    * will be operated on.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult
   GetNodesFromSelection(EditSubAction aEditSubAction,
                         nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
                         TouchContent aTouchContent);
 
   enum class EntireList { no, yes };
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult
   GetListActionNodes(nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
                      EntireList aEntireList, TouchContent aTouchContent);
   void GetDefinitionListItemTypes(Element* aElement, bool* aDT, bool* aDD);
+  MOZ_CAN_RUN_SCRIPT
   nsresult GetParagraphFormatNodes(
       nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
   void LookInsideDivBQandList(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
 
   /**
    * BustUpInlinesAtRangeEndpoints() splits nodes at both start and end of
    * aRangeItem.  If this splits at every point, this modifies aRangeItem
    * to point each split point (typically, right node).  Note that this splits
@@ -954,16 +993,17 @@ class HTMLEditRules : public TextEditRul
    * this returns all <br> elements, every left node and aNode with
    * aOutArrayNodes.
    *
    * @param aNode               An inline container element.
    * @param aOutArrayOfNodes    All found <br> elements, left nodes (may not
    *                            be set if <br> is at start edge of aNode) and
    *                            aNode itself.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult BustUpInlinesAtBRs(
       nsIContent& aNode, nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes);
 
   /**
    * GetHiestInlineParent() returns the highest inline node parent between
    * aNode and the editing host.  Even if the editing host is an inline
    * element, this method never returns the editing host as the result.
    */
@@ -980,16 +1020,17 @@ class HTMLEditRules : public TextEditRul
   /**
    * RemoveBlockStyle() removes all format blocks, table related element,
    * etc in aNodeArray.
    * If aNodeArray has a format node, it will be removed and its contents
    * will be moved to where it was.
    * If aNodeArray has a table related element, <li>, <blockquote> or <div>,
    * it will removed and its contents will be moved to where it was.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
 
   /**
    * ApplyBlockStyle() formats all nodes in aNodeArray with block elements
    * whose name is aBlockTag.
    * If aNodeArray has an inline element, a block element is created and the
    * inline element and following inline elements are moved into the new block
    * element.
@@ -998,28 +1039,30 @@ class HTMLEditRules : public TextEditRul
    * elements.
    * If aNodeArray has a block element, this calls itself with children of
    * the block element.  Then, new block element will be created when there
    * are some remaining inline elements.
    *
    * @param aNodeArray      Must be descendants of a node.
    * @param aBlockTag       The element name of new block elements.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult ApplyBlockStyle(
       nsTArray<OwningNonNull<nsINode>>& aNodeArray, nsAtom& aBlockTag);
 
   /**
    * MakeBlockquote() inserts at least one <blockquote> element and moves
    * nodes in aNodeArray into new <blockquote> elements.  If aNodeArray
    * includes a table related element except <table>, this calls itself
    * recursively to insert <blockquote> into the cell.
    *
    * @param aNodeArray          Nodes which will be moved into created
    *                            <blockquote> elements.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult
   MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
 
   /**
    * MaybeSplitAncestorsForInsertWithTransaction() does nothing if container of
    * aStartOfDeepestRightNode can have an element whose tag name is aTag.
    * Otherwise, looks for an ancestor node which is or is in active editing
    * host and can have an element whose name is aTag.  If there is such
@@ -1055,16 +1098,17 @@ class HTMLEditRules : public TextEditRul
    *     any users...
    *
    * @param aLeftNode   The node which will be removed.
    * @param aRightNode  The node which will be inserted the content of
    *                    aLeftNode.
    * @param aNewFirstChildOfRightNode
    *                    The point at the first child of aRightNode.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult JoinNearestEditableNodesWithTransaction(
       nsIContent& aLeftNode, nsIContent& aRightNode,
       EditorDOMPoint* aNewFirstChildOfRightNode);
 
   Element* GetTopEnclosingMailCite(nsINode& aNode);
 
   /**
    * PopListItem() tries to move aListItem outside its parent.  If it's
@@ -1076,31 +1120,33 @@ class HTMLEditRules : public TextEditRul
    * element was.
    *
    * @param aListItem           Should be a <li>, <dt> or <dd> element.
    *                            If it's not so, returns NS_ERROR_FAILURE.
    * @param aOutOfList          Returns true if the list item element is
    *                            removed (i.e., unwrapped contents of
    *                            aListItem).  Otherwise, false.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult PopListItem(nsIContent& aListItem,
                                     bool* aOutOfList = nullptr);
 
   /**
    * RemoveListStructure() destroys the list structure of aListElement.
    * If aListElement has <li>, <dl> or <dt> as a child, the element is removed
    * but its descendants are moved to where the list item element was.
    * If aListElement has another <ul>, <ol> or <dl> as a child, this method
    * is called recursively.
    * If aListElement has other nodes as its child, they are just removed.
    * Finally, aListElement is removed. and its all children are moved to
    * where the aListElement was.
    *
    * @param aListElement        A <ul>, <ol> or <dl> element.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult RemoveListStructure(Element& aListElement);
 
   /**
    * CacheInlineStyles() caches style of aNode into mCachedStyles.
    * This may cause flushing layout at retrieving computed value of CSS
    * properties.
    */
   MOZ_MUST_USE nsresult CacheInlineStyles(nsINode* aNode);
@@ -1211,16 +1257,17 @@ class HTMLEditRules : public TextEditRul
    * @param aAlignType          New align value to be set only when it's in
    *                            CSS mode and this method meets <table> or <hr>.
    *                            XXX This is odd and not clear when you see
    *                                caller of this method.  Do you have better
    *                                idea?
    * @param aDescendantsOnly    true if align information of aNode itself
    *                            shouldn't be removed.  Otherwise, false.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult RemoveAlignment(nsINode& aNode,
                                         const nsAString& aAlignType,
                                         bool aDescendantsOnly);
 
   /**
    * MakeSureElemStartsOrEndsOnCR() inserts <br> element at start (end) of
    * aNode if neither:
    * - first (last) editable child of aNode is a block or a <br>,
@@ -1240,52 +1287,56 @@ class HTMLEditRules : public TextEditRul
    *
    * @param aElement            The element whose contents will be aligned.
    * @param aAlignType          Boundary or "center" which contents should be
    *                            aligned on.
    * @param aResetAlignOf       Resets align of whether element and its
    *                            descendants or only descendants.
    */
   enum class ResetAlignOf { ElementAndDescendants, OnlyDescendants };
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult AlignBlock(Element& aElement,
                                    const nsAString& aAlignType,
                                    ResetAlignOf aResetAlignOf);
 
   /**
    * IncreaseMarginToIndent() increases the margin of aElement.  See the
    * document of ChangeMarginStart() for the detail.
    * XXX This is not aware of vertical writing-mode.
    *
    * @param aElement            The element to be indented.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult IncreaseMarginToIndent(Element& aElement) {
     return ChangeMarginStart(aElement, true);
   }
 
   /**
    * DecreaseMarginToOutdent() decreases the margin of aElement.  See the
    * document of ChangeMarginStart() for the detail.
    * XXX This is not aware of vertical writing-mode.
    *
    * @param aElement            The element to be outdented.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult DecreaseMarginToOutdent(Element& aElement) {
     return ChangeMarginStart(aElement, false);
   }
 
   /**
    * ChangeMarginStart() changes margin of aElement to indent or outdent.
    * However, use IncreaseMarginToIndent() and DecreaseMarginToOutdent()
    * instead.  If it's rtl text, margin-right will be changed.  Otherwise,
    * margin-left.
    * XXX This is not aware of vertical writing-mode.
    *
    * @param aElement            The element to be indented or outdented.
    * @param aIncrease           true for indent, false for outdent.
    */
+  MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult ChangeMarginStart(Element& aElement, bool aIncrease);
 
   /**
    * DocumentModifiedWorker() is called by DocumentModified() either
    * synchronously or asynchronously.
    */
   MOZ_CAN_RUN_SCRIPT void DocumentModifiedWorker();
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -495,17 +495,18 @@ HTMLEditor::SetFlags(uint32_t aFlags) {
 
 nsresult HTMLEditor::InitRules() {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (!mRules) {
     // instantiate the rules for the html editor
     mRules = new HTMLEditRules();
   }
-  return mRules->Init(this);
+  RefPtr<TextEditRules> rules(mRules);
+  return rules->Init(this);
 }
 
 NS_IMETHODIMP
 HTMLEditor::BeginningOfDocument() {
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
@@ -1479,17 +1480,18 @@ HTMLEditor::RebuildDocumentFromSource(co
       range->CreateContextualFragment(bodyTag, erv);
   NS_ENSURE_TRUE(!erv.Failed(), erv.StealNSResult());
   NS_ENSURE_TRUE(docfrag, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIContent> child = docfrag->GetFirstChild();
   NS_ENSURE_TRUE(child && child->IsElement(), NS_ERROR_NULL_POINTER);
 
   // Copy all attributes from the div child to current body element
-  CloneAttributesWithTransaction(*rootElement, *child->AsElement());
+  CloneAttributesWithTransaction(*rootElement,
+                                 MOZ_KnownLive(*child->AsElement()));
 
   // place selection at first editable content
   return MaybeCollapseSelectionAtFirstEditableNode(false);
 }
 
 EditorRawDOMPoint HTMLEditor::GetBetterInsertionPointFor(
     nsINode& aNodeToInsert, const EditorRawDOMPoint& aPointToInsert) {
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
@@ -2940,17 +2942,17 @@ HTMLEditor::InsertLinkAroundSelection(El
       // We must clear the string buffers
       //   because GetValue appends to previous string!
       value.Truncate();
 
       nsAtom* name = attribute->NodeInfo()->NameAtom();
 
       attribute->GetValue(value);
 
-      rv = SetInlinePropertyInternal(*nsGkAtoms::a, name, value);
+      rv = SetInlinePropertyInternal(*nsGkAtoms::a, MOZ_KnownLive(name), value);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
   return NS_OK;
 }
 
@@ -4567,18 +4569,18 @@ nsresult HTMLEditor::CopyLastEditableChi
       // Clone all attributes.
       // XXX Looks like that this clones id attribute too.
       CloneAttributesWithTransaction(*lastClonedElement,
                                      *elementInPreviousBlock);
       continue;
     }
     // Otherwise, inserts new parent inline container to the previous inserted
     // inline container.
-    lastClonedElement =
-        InsertContainerWithTransaction(*lastClonedElement, *tagName);
+    lastClonedElement = InsertContainerWithTransaction(*lastClonedElement,
+                                                       MOZ_KnownLive(*tagName));
     if (NS_WARN_IF(!lastClonedElement)) {
       return NS_ERROR_FAILURE;
     }
     CloneAttributesWithTransaction(*lastClonedElement, *elementInPreviousBlock);
   }
 
   if (!firstClonsedElement) {
     // XXX Even if no inline elements are cloned, shouldn't we create new
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -121,16 +121,17 @@ class HTMLEditor final : public TextEdit
 
   nsHTMLDocument* GetHTMLDocument() const;
 
   virtual void PreDestroy(bool aDestroyingFrames) override;
 
   bool GetReturnInParagraphCreatesNewParagraph();
 
   // TextEditor overrides
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult Init(Document& aDoc, Element* aRoot,
                         nsISelectionController* aSelCon, uint32_t aFlags,
                         const nsAString& aValue) override;
   NS_IMETHOD BeginningOfDocument() override;
   NS_IMETHOD SetFlags(uint32_t aFlags) override;
 
   NS_IMETHOD CanPaste(int32_t aSelectionType, bool* aCanPaste) override;
 
@@ -195,16 +196,17 @@ class HTMLEditor final : public TextEdit
    *       the DOM tree nor undo transactions, and does not refer Selection,
    *       HTMLEditRules, etc.
    *
    * @param aTagName            The new element's tag name.  If the name is
    *                            one of "href", "anchor" or "namedanchor",
    *                            this creates an <a> element.
    * @return                    Newly created element.
    */
+  MOZ_CAN_RUN_SCRIPT
   already_AddRefed<Element> CreateElementWithDefaults(const nsAtom& aTagName);
 
   /**
    * Indent or outdent content around Selection.
    */
   nsresult IndentAsAction();
   nsresult OutdentAsAction();
 
@@ -219,16 +221,17 @@ class HTMLEditor final : public TextEdit
                        dom::Event* aMouseEvent);
 
   /**
    * event callback when a mouse button is released
    * @param aX      [IN] horizontal position of the pointer
    * @param aY      [IN] vertical position of the pointer
    * @param aTarget [IN] the element triggering the event
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult OnMouseUp(int32_t aX, int32_t aY, Element* aTarget);
 
   /**
    * event callback when the mouse pointer is moved
    * @param aMouseEvent [IN] the event
    */
   MOZ_CAN_RUN_SCRIPT nsresult OnMouseMove(dom::MouseEvent* aMouseEvent);
 
@@ -340,16 +343,17 @@ class HTMLEditor final : public TextEdit
   nsresult AddZIndex(int32_t aChange);
 
   /**
    * SetInlinePropertyAsAction() sets a property which changes inline style of
    * text.  E.g., bold, italic, super and sub.
    * This automatically removes exclusive style, however, treats all changes
    * as a transaction.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetInlinePropertyAsAction(nsAtom& aProperty, nsAtom* aAttribute,
                                      const nsAString& aValue);
 
   nsresult GetInlineProperty(nsAtom* aProperty, nsAtom* aAttribute,
                              const nsAString& aValue, bool* aFirst, bool* aAny,
                              bool* aAll);
   nsresult GetInlinePropertyWithAttrValue(nsAtom* aProperty, nsAtom* aAttr,
                                           const nsAString& aValue, bool* aFirst,
@@ -366,16 +370,17 @@ class HTMLEditor final : public TextEdit
    *                    attribute will be removed.
    *                    If nsGkAtoms::name, <a> element which has non-empty
    *                    name attribute will be removed.
    * @param aAttribute  If aProperty is nsGkAtoms::font, aAttribute should be
    *                    nsGkAtoms::fase, nsGkAtoms::size, nsGkAtoms::color or
    *                    nsGkAtoms::bgcolor.  Otherwise, set nullptr.
    *                    Must not use nsGkAtoms::_empty here.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveInlinePropertyAsAction(nsAtom& aProperty, nsAtom* aAttribute);
 
   /**
    * GetFontColorState() returns foreground color information in first
    * range of Selection.
    * If first range of Selection is collapsed and there is a cache of style for
    * new text, aIsMixed is set to false and aColor is set to the cached color.
    * If first range of Selection is collapsed and there is no cached color,
@@ -457,18 +462,19 @@ class HTMLEditor final : public TextEdit
 
   /** Insert a string as quoted text
    * (whose representation is dependant on the editor type),
    * replacing the selected text (if any).
    *
    * @param aQuotedText    The actual text to be quoted
    * @parem aNodeInserted  Return the node which was inserted.
    */
-  nsresult InsertAsQuotation(const nsAString& aQuotedText,
-                             nsINode** aNodeInserted);
+  MOZ_CAN_RUN_SCRIPT  // USED_BY_COMM_CENTRAL
+      nsresult
+      InsertAsQuotation(const nsAString& aQuotedText, nsINode** aNodeInserted);
 
   /**
    * Inserts a plaintext string at the current location,
    * with special processing for lines beginning with ">",
    * which will be treated as mail quotes and inserted
    * as plaintext quoted blocks.
    * If the selection is not collapsed, the selection is deleted
    * and the insertion takes place at the resulting collapsed selection.
@@ -517,16 +523,17 @@ class HTMLEditor final : public TextEdit
    * @param aLength             Length of removing text.
    */
   nsresult DeleteTextWithTransaction(dom::CharacterData& aTextNode,
                                      uint32_t aOffset, uint32_t aLength);
 
   /**
    * InsertTextWithTransaction() inserts aStringToInsert at aPointToInsert.
    */
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult InsertTextWithTransaction(
       Document& aDocument, const nsAString& aStringToInsert,
       const EditorRawDOMPoint& aPointToInsert,
       EditorRawDOMPoint* aPointAfterInsertedString = nullptr) override;
 
   /**
    * CopyLastEditableChildStyles() clones inline container elements into
    * aPreviousBlock to aNewBlock to keep using same style in it.
@@ -534,35 +541,39 @@ class HTMLEditor final : public TextEdit
    * @param aPreviousBlock      The previous block element.  All inline
    *                            elements which are last sibling of each level
    *                            are cloned to aNewBlock.
    * @param aNewBlock           New block container element.
    * @param aNewBrElement       If this method creates a new <br> element for
    *                            placeholder, this is set to the new <br>
    *                            element.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult CopyLastEditableChildStylesWithTransaction(
       Element& aPreviousBlock, Element& aNewBlock,
       RefPtr<Element>* aNewBrElement);
 
   /**
    * RemoveBlockContainerWithTransaction() removes aElement from the DOM tree
    * but moves its all children to its parent node and if its parent needs <br>
    * element to have at least one line-height, this inserts <br> element
    * automatically.
    *
    * @param aElement            Block element to be removed.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveBlockContainerWithTransaction(Element& aElement);
 
   virtual Element* GetEditorRoot() const override;
   using EditorBase::IsEditable;
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult RemoveAttributeOrEquivalent(
       Element* aElement, nsAtom* aAttribute,
       bool aSuppressTransaction) override;
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult SetAttributeOrEquivalent(Element* aElement,
                                             nsAtom* aAttribute,
                                             const nsAString& aValue,
                                             bool aSuppressTransaction) override;
   using EditorBase::RemoveAttributeOrEquivalent;
   using EditorBase::SetAttributeOrEquivalent;
 
   /**
@@ -662,16 +673,17 @@ class HTMLEditor final : public TextEdit
 
   /**
    * extracts an element from the normal flow of the document and
    * positions it, and puts it back in the normal flow.
    * @param aElement [IN] the element
    * @param aEnabled [IN] true to absolutely position the element,
    *                      false to put it back in the normal flow
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetPositionToAbsoluteOrStatic(Element& aElement, bool aEnabled);
 
   /**
    * adds aChange to the z-index of an arbitrary element.
    * @param aElement [IN] the element
    * @param aChange  [IN] relative change to apply to current z-index of
    *                      the element
    * @param aReturn  [OUT] the new z-index of the element
@@ -759,20 +771,22 @@ class HTMLEditor final : public TextEdit
    * Small utility routine to test if a break node is visible to user.
    */
   bool IsVisibleBRElement(nsINode* aNode);
 
   /**
    * Helper routines for font size changing.
    */
   enum class FontSize { incr, decr };
+  MOZ_CAN_RUN_SCRIPT
   nsresult RelativeFontChangeOnTextNode(FontSize aDir, Text& aTextNode,
                                         int32_t aStartOffset,
                                         int32_t aEndOffset);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetInlinePropertyOnNode(nsIContent& aNode, nsAtom& aProperty,
                                    nsAtom* aAttribute, const nsAString& aValue);
 
   nsresult SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
                                 nsAtom* aProperty, nsAtom* aAttribute,
                                 nsIContent** aOutLeftNode = nullptr,
                                 nsIContent** aOutRightNode = nullptr);
 
@@ -925,33 +939,36 @@ class HTMLEditor final : public TextEdit
 
   nsIContent* GetFirstEditableLeaf(nsINode& aNode);
   nsIContent* GetLastEditableLeaf(nsINode& aNode);
 
   nsresult GetInlinePropertyBase(nsAtom& aProperty, nsAtom* aAttribute,
                                  const nsAString* aValue, bool* aFirst,
                                  bool* aAny, bool* aAll, nsAString* outValue);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
                       nsAtom* aProperty, nsAtom* aAttribute);
 
   nsresult SetPositionToAbsolute(Element& aElement);
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetPositionToStatic(Element& aElement);
 
   /**
    * OnModifyDocument() is called when the editor is changed.  This should
    * be called only by HTMLEditRules::DocumentModifiedWorker() to call
    * HTMLEditRules::OnModifyDocument() with AutoEditActionDataSetter
    * instance.
    */
   MOZ_CAN_RUN_SCRIPT void OnModifyDocument();
 
  protected:  // Called by helper classes.
   virtual void OnStartToHandleTopLevelEditSubAction(
       EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;
+  MOZ_CAN_RUN_SCRIPT
   virtual void OnEndHandlingTopLevelEditSubAction() override;
 
  protected:  // Shouldn't be used by friend classes
   virtual ~HTMLEditor();
 
   /**
    * InsertParagraphSeparatorAsSubAction() inserts a line break if it's
    * HTMLEditor and it's possible.
@@ -1430,16 +1447,17 @@ class HTMLEditor final : public TextEdit
    *                        element.
    * @param aCitation       cite attribute value of new <blockquote> element.
    * @param aInsertHTML     true if aQuotedText should be treated as HTML
    *                        source.
    *                        false if aQuotedText should be treated as plain
    *                        text.
    * @param aNodeInserted   [OUT] The new <blockquote> element.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult InsertAsCitedQuotationInternal(const nsAString& aQuotedText,
                                           const nsAString& aCitation,
                                           bool aInsertHTML,
                                           nsINode** aNodeInserted);
 
   /**
    * InsertNodeIntoProperAncestorWithTransaction() attempts to insert aNode
    * into the document, at aPointToInsert.  Checks with strict dtd to see if
@@ -1453,17 +1471,17 @@ class HTMLEditor final : public TextEdit
    *
    * @param aNode             Node to insert.
    * @param aPointToInsert    Insertion point.
    * @param aSplitAtEdges     Splitting can result in empty nodes?
    * @return                  Returns inserted point if succeeded.
    *                          Otherwise, the result is not set.
    */
   template <typename PT, typename CT>
-  EditorDOMPoint InsertNodeIntoProperAncestorWithTransaction(
+  MOZ_CAN_RUN_SCRIPT EditorDOMPoint InsertNodeIntoProperAncestorWithTransaction(
       nsIContent& aNode, const EditorDOMPointBase<PT, CT>& aPointToInsert,
       SplitAtEdges aSplitAtEdges);
 
   /**
    * InsertBrElementAtSelectionWithTransaction() inserts a new <br> element at
    * selection.  If there is non-collapsed selection ranges, the selected
    * ranges is deleted first.
    */
@@ -1484,29 +1502,33 @@ class HTMLEditor final : public TextEdit
    * Selection.  Callers have to guarantee that there is a placeholder
    * transaction.
    *
    * @param aEditSubAction      Must be EditSubAction::eIndent or
    *                            EditSubAction::eOutdent.
    */
   nsresult IndentOrOutdentAsSubAction(EditSubAction aEditSubAction);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult LoadHTML(const nsAString& aInputString);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetInlinePropertyInternal(nsAtom& aProperty, nsAtom* aAttribute,
                                      const nsAString& aValue);
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveInlinePropertyInternal(nsAtom* aProperty, nsAtom* aAttribute);
 
   /**
    * ReplaceHeadContentsWithSourceWithTransaction() replaces all children of
    * <head> element with given source code.  This is undoable.
    *
    * @param aSourceToInsert     HTML source fragment to replace the children
    *                            of <head> element.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult ReplaceHeadContentsWithSourceWithTransaction(
       const nsAString& aSourceToInsert);
 
   nsresult GetCSSBackgroundColorState(bool* aMixed, nsAString& aOutColor,
                                       bool aBlockLevel);
   nsresult GetHTMLBackgroundColorState(bool* aMixed, nsAString& outColor);
 
   nsresult GetLastCellInRow(nsINode* aRowNode, nsINode** aCellNode);
@@ -1625,31 +1647,33 @@ class HTMLEditor final : public TextEdit
    public:
     BlobReader(dom::BlobImpl* aBlob, HTMLEditor* aHTMLEditor, bool aIsSafe,
                Document* aSourceDoc, const EditorDOMPoint& aPointToInsert,
                bool aDoDeleteSelection);
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BlobReader)
     NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(BlobReader)
 
+    MOZ_CAN_RUN_SCRIPT
     nsresult OnResult(const nsACString& aResult);
     nsresult OnError(const nsAString& aErrorName);
 
    private:
     ~BlobReader() {}
 
     RefPtr<dom::BlobImpl> mBlob;
     RefPtr<HTMLEditor> mHTMLEditor;
     nsCOMPtr<Document> mSourceDoc;
     EditorDOMPoint mPointToInsert;
     EditAction mEditAction;
     bool mIsSafe;
     bool mDoDeleteSelection;
   };
 
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult InitRules() override;
 
   virtual void CreateEventListeners() override;
   virtual nsresult InstallEventListeners() override;
   virtual void RemoveEventListeners() override;
 
   bool ShouldReplaceRootElement();
   void NotifyRootChanged();
@@ -1738,16 +1762,17 @@ class HTMLEditor final : public TextEdit
                                           InsertPosition aInsertPosition);
 
   /**
    * Insert a new cell after or before supplied aCell.
    * Optional: If aNewCell supplied, returns the newly-created cell (addref'd,
    * of course)
    * This doesn't change or use the current selection.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult InsertCell(Element* aCell, int32_t aRowSpan, int32_t aColSpan,
                       bool aAfter, bool aIsHeader, Element** aNewCell);
 
   /**
    * DeleteSelectedTableColumnsWithTransaction() removes cell elements which
    * belong to same columns of selected cell elements.
    * If only one cell element is selected or first selection range is
    * in a cell, removes cell elements which belong to same column.
@@ -1840,16 +1865,17 @@ class HTMLEditor final : public TextEdit
    *
    * @param aElement        The element whose children you want to remove.
    */
   nsresult DeleteAllChildrenWithTransaction(Element& aElement);
 
   /**
    * Move all contents from aCellToMerge into aTargetCell (append at end).
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult MergeCells(RefPtr<Element> aTargetCell, RefPtr<Element> aCellToMerge,
                       bool aDeleteCellToMerge);
 
   /**
    * DeleteTableElementAndChildren() removes aTableElement (and its children)
    * from the DOM tree with transaction.
    *
    * @param aTableElement   The <table> element which you want to remove.
@@ -1897,20 +1923,22 @@ class HTMLEditor final : public TextEdit
    */
   nsresult GetCellContext(Element** aTable, Element** aCell,
                           nsINode** aCellParent, int32_t* aCellOffset,
                           int32_t* aRowIndex, int32_t* aColIndex);
 
   nsresult GetCellSpansAt(Element* aTable, int32_t aRowIndex, int32_t aColIndex,
                           int32_t& aActualRowSpan, int32_t& aActualColSpan);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult SplitCellIntoColumns(Element* aTable, int32_t aRowIndex,
                                 int32_t aColIndex, int32_t aColSpanLeft,
                                 int32_t aColSpanRight, Element** aNewCell);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult SplitCellIntoRows(Element* aTable, int32_t aRowIndex,
                              int32_t aColIndex, int32_t aRowSpanAbove,
                              int32_t aRowSpanBelow, Element** aNewCell);
 
   nsresult CopyCellBackgroundColor(Element* aDestCell, Element* aSourceCell);
 
   /**
    * Reduce rowspan/colspan when cells span into nonexistent rows/columns.
@@ -1925,16 +1953,17 @@ class HTMLEditor final : public TextEdit
    *     bigger or smaller rowspan or colspan than actual number of cells,
    *     this always failed to scan the table.  Therefore, this does nothing
    *     when the table should be normalized.
    *
    * @param aTableOrElementInTable  An element which is in a <table> element
    *                                or <table> element itself.  Otherwise,
    *                                this returns NS_OK but does nothing.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult NormalizeTableInternal(Element& aTableOrElementInTable);
 
   /**
    * Fallback method: Call this after using ClearSelection() and you
    * failed to set selection to some other content in the document.
    */
   nsresult SetSelectionAtDocumentStart();
 
@@ -2048,43 +2077,49 @@ class HTMLEditor final : public TextEdit
    * @param aTagName            A block level element name.  Must NOT be
    *                            nsGkAtoms::dt nor nsGkAtoms::dd.
    */
   nsresult InsertBasicBlockWithTransaction(nsAtom& aTagName);
 
   /**
    * Increase/decrease the font size of selection.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult RelativeFontChange(FontSize aDir);
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult RelativeFontChangeOnNode(int32_t aSizeChange, nsIContent* aNode);
+  MOZ_CAN_RUN_SCRIPT
   nsresult RelativeFontChangeHelper(int32_t aSizeChange, nsINode* aNode);
 
   /**
    * Helper routines for inline style.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetInlinePropertyOnTextNode(Text& aData, int32_t aStartOffset,
                                        int32_t aEndOffset, nsAtom& aProperty,
                                        nsAtom* aAttribute,
                                        const nsAString& aValue);
 
   nsresult PromoteInlineRange(nsRange& aRange);
   nsresult PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange);
   nsresult SplitStyleAboveRange(nsRange* aRange, nsAtom* aProperty,
                                 nsAtom* aAttribute);
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveStyleInside(nsIContent& aNode, nsAtom* aProperty,
                              nsAtom* aAttribute,
                              const bool aChildrenOnly = false);
 
   bool NodeIsProperty(nsINode& aNode);
   bool IsAtFrontOfNode(nsINode& aNode, int32_t aOffset);
   bool IsAtEndOfNode(nsINode& aNode, int32_t aOffset);
   bool IsOnlyAttribute(const Element* aElement, nsAtom* aAttribute);
 
   bool HasStyleOrIdOrClass(Element* aElement);
+  MOZ_CAN_RUN_SCRIPT
   nsresult RemoveElementIfNoStyleOrIdOrClass(Element& aElement);
 
   /**
    * Whether the outer window of the DOM event target has focus or not.
    */
   bool OurWindowHasFocus();
 
   /**
@@ -2094,17 +2129,17 @@ class HTMLEditor final : public TextEdit
    * from the InsertHTML method.  We may want the HTML input to be sanitized
    * (for example, if it's coming from a transferable object), in which case
    * aTrustedInput should be set to false, otherwise, the caller should set it
    * to true, which means that the HTML will be inserted in the DOM verbatim.
    *
    * aClearStyle should be set to false if you want the paste to be affected by
    * local style (e.g., for the insertHTML command).
    */
-  MOZ_CAN_RUN_SCRIPT_BOUNDARY
+  MOZ_CAN_RUN_SCRIPT
   nsresult DoInsertHTMLWithContext(
       const nsAString& aInputString, const nsAString& aContextStr,
       const nsAString& aInfoStr, const nsAString& aFlavor, Document* aSourceDoc,
       const EditorDOMPoint& aPointToInsert, bool aDeleteSelection,
       bool aTrustedInput, bool aClearStyle = true);
 
   /**
    * sets the position of an element; warning it does NOT check if the
@@ -2221,16 +2256,17 @@ class HTMLEditor final : public TextEdit
   int32_t GetNewResizingIncrement(int32_t aX, int32_t aY, ResizeAt aResizeAt);
 
   nsresult StartResizing(Element* aHandle);
   int32_t GetNewResizingX(int32_t aX, int32_t aY);
   int32_t GetNewResizingY(int32_t aX, int32_t aY);
   int32_t GetNewResizingWidth(int32_t aX, int32_t aY);
   int32_t GetNewResizingHeight(int32_t aX, int32_t aY);
   void HideShadowAndInfo();
+  MOZ_CAN_RUN_SCRIPT
   void SetFinalSize(int32_t aX, int32_t aY);
   void SetResizeIncrements(int32_t aX, int32_t aY, int32_t aW, int32_t aH,
                            bool aPreserveRatio);
 
   /**
    * HideAnonymousEditingUIs() forcibly hides all editing UIs (resizers,
    * inline-table-editing UI, absolute positioning UI).
    */
@@ -2313,16 +2349,17 @@ class HTMLEditor final : public TextEdit
   /**
    * IsEmptyTextNode() returns true if aNode is a text node and does not have
    * any visible characters.
    */
   bool IsEmptyTextNode(nsINode& aNode);
 
   bool IsSimpleModifiableNode(nsIContent* aContent, nsAtom* aProperty,
                               nsAtom* aAttribute, const nsAString* aValue);
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetInlinePropertyOnNodeImpl(nsIContent& aNode, nsAtom& aProperty,
                                        nsAtom* aAttribute,
                                        const nsAString& aValue);
   typedef enum { eInserted, eAppended } InsertedOrAppended;
   void DoContentInserted(nsIContent* aChild, InsertedOrAppended);
 
   /**
    * Returns an anonymous Element of type aTag,
--- a/editor/libeditor/HTMLEditorCommands.cpp
+++ b/editor/libeditor/HTMLEditorCommands.cpp
@@ -29,18 +29,19 @@
 #include "nsStringFwd.h"      // for nsString
 
 class nsISupports;
 
 namespace mozilla {
 using dom::Element;
 
 // prototype
-static nsresult GetListState(HTMLEditor* aHTMLEditor, bool* aMixed,
-                             nsAString& aLocalName);
+MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+    static nsresult
+    GetListState(HTMLEditor* aHTMLEditor, bool* aMixed, nsAString& aLocalName);
 
 // defines
 #define STATE_ENABLED "state_enabled"
 #define STATE_ALL "state_all"
 #define STATE_ANY "state_any"
 #define STATE_MIXED "state_mixed"
 #define STATE_BEGIN "state_begin"
 #define STATE_END "state_end"
@@ -79,17 +80,17 @@ StateUpdatingCommandBase::DoCommand(cons
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
-  return ToggleState(htmlEditor);
+  return ToggleState(MOZ_KnownLive(htmlEditor));
 }
 
 NS_IMETHODIMP
 StateUpdatingCommandBase::DoCommandParams(const char* aCommandName,
                                           nsICommandParams* aParams,
                                           nsISupports* refCon) {
   return DoCommand(aCommandName, refCon);
 }
@@ -240,25 +241,26 @@ nsresult StyleUpdatingCommand::ToggleSta
     } else if (mTagName == nsGkAtoms::strike) {
       nsresult rv =
           aHTMLEditor->RemoveInlinePropertyAsAction(*nsGkAtoms::s, nullptr);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
-    nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(*mTagName, nullptr);
+    nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
+        MOZ_KnownLive(*mTagName), nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  nsresult rv =
-      aHTMLEditor->SetInlinePropertyAsAction(*mTagName, nullptr, EmptyString());
+  nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(MOZ_KnownLive(*mTagName),
+                                                       nullptr, EmptyString());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 ListCommand::ListCommand(nsAtom* aTagName)
     : StateUpdatingCommandBase(aTagName) {}
@@ -395,17 +397,17 @@ RemoveListCommand::IsCommandEnabled(cons
   // It is enabled if we are in any list type
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
 
   bool bMixed;
   nsAutoString localName;
-  nsresult rv = GetListState(htmlEditor, &bMixed, localName);
+  nsresult rv = GetListState(MOZ_KnownLive(htmlEditor), &bMixed, localName);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *outCmdEnabled = bMixed || !localName.IsEmpty();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 RemoveListCommand::DoCommand(const char* aCommandName, nsISupports* refCon) {
@@ -584,32 +586,32 @@ MultiStateCommandBase::DoCommandParams(c
     nsAutoCString asciiAttribute;
     nsresult rv = params->GetCString(STATE_ATTRIBUTE, asciiAttribute);
     if (NS_SUCCEEDED(rv)) {
       CopyASCIItoUTF16(asciiAttribute, attribute);
     } else {
       params->GetString(STATE_ATTRIBUTE, attribute);
     }
   }
-  return SetState(htmlEditor, attribute);
+  return SetState(MOZ_KnownLive(htmlEditor), attribute);
 }
 
 NS_IMETHODIMP
 MultiStateCommandBase::GetCommandStateParams(const char* aCommandName,
                                              nsICommandParams* aParams,
                                              nsISupports* refCon) {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     return NS_OK;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
-  return GetCurrentState(htmlEditor, aParams);
+  return GetCurrentState(MOZ_KnownLive(htmlEditor), aParams);
 }
 
 ParagraphStateCommand::ParagraphStateCommand() : MultiStateCommandBase() {}
 
 nsresult ParagraphStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
                                                 nsICommandParams* aParams) {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
@@ -1150,17 +1152,17 @@ RemoveStylesCommand::DoCommand(const cha
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     return NS_OK;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (!htmlEditor) {
     return NS_OK;
   }
-  return htmlEditor->RemoveAllInlineProperties();
+  return MOZ_KnownLive(htmlEditor)->RemoveAllInlineProperties();
 }
 
 NS_IMETHODIMP
 RemoveStylesCommand::DoCommandParams(const char* aCommandName,
                                      nsICommandParams* aParams,
                                      nsISupports* refCon) {
   return DoCommand(aCommandName, refCon);
 }
@@ -1196,17 +1198,17 @@ IncreaseFontSizeCommand::DoCommand(const
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     return NS_OK;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (!htmlEditor) {
     return NS_OK;
   }
-  return htmlEditor->IncreaseFontSize();
+  return MOZ_KnownLive(htmlEditor)->IncreaseFontSize();
 }
 
 NS_IMETHODIMP
 IncreaseFontSizeCommand::DoCommandParams(const char* aCommandName,
                                          nsICommandParams* aParams,
                                          nsISupports* refCon) {
   return DoCommand(aCommandName, refCon);
 }
@@ -1242,17 +1244,17 @@ DecreaseFontSizeCommand::DoCommand(const
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(refCon);
   if (!editor) {
     return NS_OK;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (!htmlEditor) {
     return NS_OK;
   }
-  return htmlEditor->DecreaseFontSize();
+  return MOZ_KnownLive(htmlEditor)->DecreaseFontSize();
 }
 
 NS_IMETHODIMP
 DecreaseFontSizeCommand::DoCommandParams(const char* aCommandName,
                                          nsICommandParams* aParams,
                                          nsISupports* refCon) {
   return DoCommand(aCommandName, refCon);
 }
@@ -1292,17 +1294,17 @@ InsertHTMLCommand::DoCommand(const char*
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
   nsAutoString html;
-  return htmlEditor->InsertHTML(html);
+  return MOZ_KnownLive(htmlEditor)->InsertHTML(html);
 }
 
 NS_IMETHODIMP
 InsertHTMLCommand::DoCommandParams(const char* aCommandName,
                                    nsICommandParams* aParams,
                                    nsISupports* refCon) {
   NS_ENSURE_ARG_POINTER(aParams);
   NS_ENSURE_ARG_POINTER(refCon);
@@ -1317,17 +1319,17 @@ InsertHTMLCommand::DoCommandParams(const
   }
 
   // Get HTML source string to insert from command params
   nsAutoString html;
   nsresult rv = aParams->AsCommandParams()->GetString(STATE_DATA, html);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
-  return htmlEditor->InsertHTML(html);
+  return MOZ_KnownLive(htmlEditor)->InsertHTML(html);
 }
 
 NS_IMETHODIMP
 InsertHTMLCommand::GetCommandStateParams(const char* aCommandName,
                                          nsICommandParams* aParams,
                                          nsISupports* refCon) {
   NS_ENSURE_ARG_POINTER(aParams);
   NS_ENSURE_ARG_POINTER(refCon);
@@ -1368,21 +1370,24 @@ InsertTagCommand::DoCommand(const char* 
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
   mozilla::HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (NS_WARN_IF(!htmlEditor)) {
     return NS_ERROR_FAILURE;
   }
 
-  RefPtr<Element> newElement = htmlEditor->CreateElementWithDefaults(*mTagName);
+  RefPtr<Element> newElement =
+      MOZ_KnownLive(htmlEditor)
+          ->CreateElementWithDefaults(MOZ_KnownLive(*mTagName));
   if (NS_WARN_IF(!newElement)) {
     return NS_ERROR_FAILURE;
   }
-  nsresult rv = htmlEditor->InsertElementAtSelection(newElement, true);
+  nsresult rv =
+      MOZ_KnownLive(htmlEditor)->InsertElementAtSelection(newElement, true);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InsertTagCommand::DoCommandParams(const char* aCommandName,
@@ -1423,37 +1428,39 @@ InsertTagCommand::DoCommandParams(const 
   if (mTagName == nsGkAtoms::a) {
     attribute = nsGkAtoms::href;
   } else if (mTagName == nsGkAtoms::img) {
     attribute = nsGkAtoms::src;
   } else {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  RefPtr<Element> newElement = htmlEditor->CreateElementWithDefaults(*mTagName);
+  RefPtr<Element> newElement =
+      MOZ_KnownLive(htmlEditor)
+          ->CreateElementWithDefaults(MOZ_KnownLive(*mTagName));
   if (NS_WARN_IF(!newElement)) {
     return NS_ERROR_FAILURE;
   }
 
   ErrorResult err;
   newElement->SetAttr(attribute, value, err);
   if (NS_WARN_IF(err.Failed())) {
     return err.StealNSResult();
   }
 
   // do actual insertion
   if (mTagName == nsGkAtoms::a) {
-    rv = htmlEditor->InsertLinkAroundSelection(newElement);
+    rv = MOZ_KnownLive(htmlEditor)->InsertLinkAroundSelection(newElement);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  rv = htmlEditor->InsertElementAtSelection(newElement, true);
+  rv = MOZ_KnownLive(htmlEditor)->InsertElementAtSelection(newElement, true);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InsertTagCommand::GetCommandStateParams(const char* aCommandName,
@@ -1467,17 +1474,18 @@ InsertTagCommand::GetCommandStateParams(
   return aParams->AsCommandParams()->SetBool(STATE_ENABLED, outCmdEnabled);
 }
 
 /****************************/
 // HELPER METHODS
 /****************************/
 
 static nsresult GetListState(HTMLEditor* aHTMLEditor, bool* aMixed,
-                             nsAString& aLocalName) {
+                             nsAString& aLocalName)
+    MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
   MOZ_ASSERT(aHTMLEditor);
   MOZ_ASSERT(aMixed);
 
   *aMixed = false;
   aLocalName.Truncate();
 
   bool bOL, bUL, bDL;
   nsresult rv = aHTMLEditor->GetListState(aMixed, &bOL, &bUL, &bDL);
--- a/editor/libeditor/HTMLEditorCommands.h
+++ b/editor/libeditor/HTMLEditorCommands.h
@@ -53,38 +53,42 @@ class StateUpdatingCommandBase : public 
                                        HTMLEditorCommandBase)
 
   NS_DECL_NSICONTROLLERCOMMAND
 
  protected:
   virtual ~StateUpdatingCommandBase();
 
   // get the current state (on or off) for this style or block format
-  virtual nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                                   nsICommandParams* aParams) = 0;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      virtual nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) = 0;
 
   // add/remove the style
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult ToggleState(HTMLEditor* aHTMLEditor) = 0;
 
  protected:
   nsAtom* mTagName;
 };
 
 // Shared class for the various style updating commands like bold, italics etc.
 // Suitable for commands whose state is either 'on' or 'off'.
 class StyleUpdatingCommand final : public StateUpdatingCommandBase {
  public:
   explicit StyleUpdatingCommand(nsAtom* aTagName);
 
  protected:
   // get the current state (on or off) for this style or block format
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
 
   // add/remove the style
+  MOZ_CAN_RUN_SCRIPT
   nsresult ToggleState(HTMLEditor* aHTMLEditor) final;
 };
 
 class InsertTagCommand final : public HTMLEditorCommandBase {
  public:
   explicit InsertTagCommand(nsAtom* aTagName);
 
   NS_INLINE_DECL_REFCOUNTING_INHERITED(InsertTagCommand, HTMLEditorCommandBase)
@@ -98,135 +102,157 @@ class InsertTagCommand final : public HT
 };
 
 class ListCommand final : public StateUpdatingCommandBase {
  public:
   explicit ListCommand(nsAtom* aTagName);
 
  protected:
   // get the current state (on or off) for this style or block format
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
 
   // add/remove the style
+  MOZ_CAN_RUN_SCRIPT
   nsresult ToggleState(HTMLEditor* aHTMLEditor) final;
 };
 
 class ListItemCommand final : public StateUpdatingCommandBase {
  public:
   explicit ListItemCommand(nsAtom* aTagName);
 
  protected:
   // get the current state (on or off) for this style or block format
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
 
   // add/remove the style
+  MOZ_CAN_RUN_SCRIPT
   nsresult ToggleState(HTMLEditor* aHTMLEditor) final;
 };
 
 // Base class for commands whose state consists of a string (e.g. para format)
 class MultiStateCommandBase : public HTMLEditorCommandBase {
  public:
   MultiStateCommandBase();
 
   NS_INLINE_DECL_REFCOUNTING_INHERITED(MultiStateCommandBase,
                                        HTMLEditorCommandBase)
   NS_DECL_NSICONTROLLERCOMMAND
 
  protected:
   virtual ~MultiStateCommandBase();
 
-  virtual nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                                   nsICommandParams* aParams) = 0;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      virtual nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) = 0;
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult SetState(HTMLEditor* aHTMLEditor,
                             const nsString& newState) = 0;
 };
 
 class ParagraphStateCommand final : public MultiStateCommandBase {
  public:
   ParagraphStateCommand();
 
  protected:
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class FontFaceStateCommand final : public MultiStateCommandBase {
  public:
   FontFaceStateCommand();
 
  protected:
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class FontSizeStateCommand final : public MultiStateCommandBase {
  public:
   FontSizeStateCommand();
 
  protected:
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class HighlightColorStateCommand final : public MultiStateCommandBase {
  public:
   HighlightColorStateCommand();
 
  protected:
   NS_IMETHOD IsCommandEnabled(const char* aCommandName,
                               nsISupports* aCommandRefCon, bool* _retval) final;
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class FontColorStateCommand final : public MultiStateCommandBase {
  public:
   FontColorStateCommand();
 
  protected:
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class AlignCommand final : public MultiStateCommandBase {
  public:
   AlignCommand();
 
  protected:
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class BackgroundColorStateCommand final : public MultiStateCommandBase {
  public:
   BackgroundColorStateCommand();
 
  protected:
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetState(HTMLEditor* aHTMLEditor, const nsString& newState) final;
 };
 
 class AbsolutePositioningCommand final : public StateUpdatingCommandBase {
  public:
   AbsolutePositioningCommand();
 
  protected:
   NS_IMETHOD IsCommandEnabled(const char* aCommandName,
                               nsISupports* aCommandRefCon, bool* _retval) final;
-  nsresult GetCurrentState(HTMLEditor* aHTMLEditor,
-                           nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY  // XXX Needs to change nsIControllerCommand.idl
+      nsresult
+      GetCurrentState(HTMLEditor* aHTMLEditor, nsICommandParams* aParams) final;
+  MOZ_CAN_RUN_SCRIPT
   nsresult ToggleState(HTMLEditor* aHTMLEditor) final;
 };
 
 // composer commands
 
 NS_DECL_COMPOSER_COMMAND(DocumentStateCommand)
 NS_DECL_COMPOSER_COMMAND(SetDocumentStateCommand)
 
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -518,17 +518,17 @@ nsresult HTMLEditor::DoInsertHTMLWithCon
                                "Failed to advance offset from inserted point");
         }
       }
 
       if (!bDidInsert || NS_FAILED(rv)) {
         // Try to insert.
         EditorDOMPoint insertedPoint =
             InsertNodeIntoProperAncestorWithTransaction(
-                *curNode->AsContent(), pointToInsert,
+                MOZ_KnownLive(*curNode->AsContent()), pointToInsert,
                 SplitAtEdges::eDoNotCreateEmptyContainer);
         if (insertedPoint.IsSet()) {
           lastInsertNode = curNode->AsContent();
           pointToInsert = insertedPoint;
         }
 
         // Assume failure means no legal parent in the document hierarchy,
         // try again with the parent of curNode in the paste hierarchy.
@@ -537,17 +537,17 @@ nsresult HTMLEditor::DoInsertHTMLWithCon
              content && !insertedPoint.IsSet();
              content = content->GetParent()) {
           if (NS_WARN_IF(!content->GetParent()) ||
               NS_WARN_IF(TextEditUtils::IsBody(content->GetParent()))) {
             continue;
           }
           nsCOMPtr<nsINode> oldParent = content->GetParentNode();
           insertedPoint = InsertNodeIntoProperAncestorWithTransaction(
-              *content->GetParent(), pointToInsert,
+              MOZ_KnownLive(*content->GetParent()), pointToInsert,
               SplitAtEdges::eDoNotCreateEmptyContainer);
           if (insertedPoint.IsSet()) {
             insertedContextParent = oldParent;
             pointToInsert = insertedPoint;
           }
         }
       }
       if (lastInsertNode) {
@@ -977,19 +977,23 @@ nsresult HTMLEditor::BlobReader::OnResul
   NS_ConvertUTF16toUTF8 type(blobType);
   nsAutoString stuffToPaste;
   nsresult rv = ImgFromData(type, aResult, stuffToPaste);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
 
   AutoPlaceholderBatch treatAsOneTransaction(*mHTMLEditor);
-  rv = mHTMLEditor->DoInsertHTMLWithContext(
-      stuffToPaste, EmptyString(), EmptyString(), NS_LITERAL_STRING(kFileMime),
-      mSourceDoc, mPointToInsert, mDoDeleteSelection, mIsSafe, false);
+  RefPtr<Document> sourceDocument(mSourceDoc);
+  EditorDOMPoint pointToInsert(mPointToInsert);
+  rv = MOZ_KnownLive(mHTMLEditor)
+           ->DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(),
+                                     NS_LITERAL_STRING(kFileMime),
+                                     sourceDocument, pointToInsert,
+                                     mDoDeleteSelection, mIsSafe, false);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::BlobReader::OnError(const nsAString& aError) {
   const nsPromiseFlatString& flat = PromiseFlatString(aError);
@@ -1004,16 +1008,17 @@ nsresult HTMLEditor::BlobReader::OnError
 class SlurpBlobEventListener final : public nsIDOMEventListener {
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(SlurpBlobEventListener)
 
   explicit SlurpBlobEventListener(HTMLEditor::BlobReader* aListener)
       : mListener(aListener) {}
 
+  MOZ_CAN_RUN_SCRIPT
   NS_IMETHOD HandleEvent(Event* aEvent) override;
 
  private:
   ~SlurpBlobEventListener() = default;
 
   RefPtr<HTMLEditor::BlobReader> mListener;
 };
 
@@ -1036,26 +1041,27 @@ SlurpBlobEventListener::HandleEvent(Even
 
   RefPtr<FileReader> reader = do_QueryObject(target);
   if (!reader) {
     return NS_OK;
   }
 
   EventMessage message = aEvent->WidgetEventPtr()->mMessage;
 
+  RefPtr<HTMLEditor::BlobReader> listener(mListener);
   if (message == eLoad) {
     MOZ_ASSERT(reader->DataFormat() == FileReader::FILE_AS_BINARY);
 
     // The original data has been converted from Latin1 to UTF-16, this just
     // undoes that conversion.
-    mListener->OnResult(NS_LossyConvertUTF16toASCII(reader->Result()));
+    listener->OnResult(NS_LossyConvertUTF16toASCII(reader->Result()));
   } else if (message == eLoadError) {
     nsAutoString errorMessage;
     reader->GetError()->GetErrorMessage(errorMessage);
-    mListener->OnError(errorMessage);
+    listener->OnError(errorMessage);
   }
 
   return NS_OK;
 }
 
 // static
 nsresult HTMLEditor::SlurpBlob(Blob* aBlob, nsPIDOMWindowOuter* aWindow,
                                BlobReader* aBlobReader) {
--- a/editor/libeditor/HTMLEditorEventListener.cpp
+++ b/editor/libeditor/HTMLEditorEventListener.cpp
@@ -223,17 +223,17 @@ nsresult HTMLEditorEventListener::Listen
 
 nsresult HTMLEditorEventListener::MouseUp(MouseEvent* aMouseEvent) {
   if (DetachedFromEditor()) {
     return NS_OK;
   }
 
   // FYI: We need to notify HTML editor of mouseup even if it's consumed
   //      because HTML editor always needs to release grabbing resizer.
-  HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
+  RefPtr<HTMLEditor> htmlEditor = mEditorBase->AsHTMLEditor();
   MOZ_ASSERT(htmlEditor);
 
   RefPtr<EventTarget> target = aMouseEvent->GetTarget();
   NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
   nsCOMPtr<Element> element = do_QueryInterface(target);
 
   int32_t clientX = aMouseEvent->ClientX();
   int32_t clientY = aMouseEvent->ClientY();
--- a/editor/libeditor/HTMLEditorEventListener.h
+++ b/editor/libeditor/HTMLEditorEventListener.h
@@ -69,16 +69,17 @@ class HTMLEditorEventListener final : pu
    * ListenToWindowResizeEvent() starts to listen to or stop listening to
    * "resize" events of the document.
    */
   nsresult ListenToWindowResizeEvent(bool aListen);
 
  protected:
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseDown(dom::MouseEvent* aMouseEvent) override;
+  MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseUp(dom::MouseEvent* aMouseEvent) override;
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult MouseClick(WidgetMouseEvent* aMouseClickEvent) override;
 
   nsresult ListenToMouseMoveEventForResizersOrGrabber(bool aListen,
                                                       bool aForGrabber);
 
   bool mListeningToMouseMoveEventForResizers;
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
+++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
@@ -965,34 +965,35 @@ void HTMLEditor::SetFinalSize(int32_t aX
                   ? mResizedObjectBorderLeft + mResizedObjectMarginLeft
                   : 0);
   y = top - ((mResizedObjectIsAbsolutelyPositioned)
                  ? mResizedObjectBorderTop + mResizedObjectMarginTop
                  : 0);
 
   // we want one transaction only from a user's point of view
   AutoPlaceholderBatch treatAsOneTransaction(*this);
+  RefPtr<Element> resizedObject(mResizedObject);
 
   if (mResizedObjectIsAbsolutelyPositioned) {
     if (setHeight) {
-      mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::top, y);
+      mCSSEditUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::top, y);
     }
     if (setWidth) {
-      mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::left, x);
+      mCSSEditUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::left, x);
     }
   }
   if (IsCSSEnabled() || mResizedObjectIsAbsolutelyPositioned) {
     if (setWidth &&
-        mResizedObject->HasAttr(kNameSpaceID_None, nsGkAtoms::width)) {
-      RemoveAttributeWithTransaction(*mResizedObject, *nsGkAtoms::width);
+        resizedObject->HasAttr(kNameSpaceID_None, nsGkAtoms::width)) {
+      RemoveAttributeWithTransaction(*resizedObject, *nsGkAtoms::width);
     }
 
     if (setHeight &&
-        mResizedObject->HasAttr(kNameSpaceID_None, nsGkAtoms::height)) {
-      RemoveAttributeWithTransaction(*mResizedObject, *nsGkAtoms::height);
+        resizedObject->HasAttr(kNameSpaceID_None, nsGkAtoms::height)) {
+      RemoveAttributeWithTransaction(*resizedObject, *nsGkAtoms::height);
     }
 
     if (setWidth) {
       mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::width,
                                           width);
     }
     if (setHeight) {
       mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::height,
@@ -1000,40 +1001,40 @@ void HTMLEditor::SetFinalSize(int32_t aX
     }
   } else {
     // we use HTML size and remove all equivalent CSS properties
 
     // we set the CSS width and height to remove it later,
     // triggering an immediate reflow; otherwise, we have problems
     // with asynchronous reflow
     if (setWidth) {
-      mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::width,
+      mCSSEditUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::width,
                                           width);
     }
     if (setHeight) {
-      mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::height,
+      mCSSEditUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::height,
                                           height);
     }
     if (setWidth) {
       nsAutoString w;
       w.AppendInt(width);
-      SetAttributeWithTransaction(*mResizedObject, *nsGkAtoms::width, w);
+      SetAttributeWithTransaction(*resizedObject, *nsGkAtoms::width, w);
     }
     if (setHeight) {
       nsAutoString h;
       h.AppendInt(height);
-      SetAttributeWithTransaction(*mResizedObject, *nsGkAtoms::height, h);
+      SetAttributeWithTransaction(*resizedObject, *nsGkAtoms::height, h);
     }
 
     if (setWidth) {
-      mCSSEditUtils->RemoveCSSProperty(*mResizedObject, *nsGkAtoms::width,
+      mCSSEditUtils->RemoveCSSProperty(*resizedObject, *nsGkAtoms::width,
                                        EmptyString());
     }
     if (setHeight) {
-      mCSSEditUtils->RemoveCSSProperty(*mResizedObject, *nsGkAtoms::height,
+      mCSSEditUtils->RemoveCSSProperty(*resizedObject, *nsGkAtoms::height,
                                        EmptyString());
     }
   }
 
   // keep track of that size
   mResizedObjectWidth = width;
   mResizedObjectHeight = height;
 
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -181,18 +181,18 @@ nsresult HTMLEditor::SetInlinePropertyIn
         return rv;
       }
 
       // Check for easy case: both range endpoints in same text node
       nsCOMPtr<nsINode> startNode = range->GetStartContainer();
       nsCOMPtr<nsINode> endNode = range->GetEndContainer();
       if (startNode && startNode == endNode && startNode->GetAsText()) {
         rv = SetInlinePropertyOnTextNode(
-            *startNode->GetAsText(), range->StartOffset(), range->EndOffset(),
-            aProperty, aAttribute, aValue);
+            MOZ_KnownLive(*startNode->GetAsText()), range->StartOffset(),
+            range->EndOffset(), aProperty, aAttribute, aValue);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         continue;
       }
 
       // Not the easy case.  Range not contained in single text node.  There
       // are up to three phases here.  There are all the nodes reported by the
@@ -222,18 +222,18 @@ nsresult HTMLEditor::SetInlinePropertyIn
           }
         }
       }
       // First check the start parent of the range to see if it needs to be
       // separately handled (it does if it's a text node, due to how the
       // subtree iterator works - it will not have reported it).
       if (startNode && startNode->GetAsText() && IsEditable(startNode)) {
         rv = SetInlinePropertyOnTextNode(
-            *startNode->GetAsText(), range->StartOffset(), startNode->Length(),
-            aProperty, aAttribute, aValue);
+            MOZ_KnownLive(*startNode->GetAsText()), range->StartOffset(),
+            startNode->Length(), aProperty, aAttribute, aValue);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
 
       // Then loop through the list, set the property on each node
       for (auto& node : arrayOfNodes) {
         rv = SetInlinePropertyOnNode(*node, aProperty, aAttribute, aValue);
@@ -241,18 +241,18 @@ nsresult HTMLEditor::SetInlinePropertyIn
           return rv;
         }
       }
 
       // Last check the end parent of the range to see if it needs to be
       // separately handled (it does if it's a text node, due to how the
       // subtree iterator works - it will not have reported it).
       if (endNode && endNode->GetAsText() && IsEditable(endNode)) {
-        rv = SetInlinePropertyOnTextNode(*endNode->GetAsText(), 0,