Merge m-c to inbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 05 Jun 2017 17:39:09 -0700
changeset 410582 2128f5860eb4774a5e3ab85eda1a0383e652afe0
parent 410581 187554939350e5c0c66be425eb3425eccabf6455 (current diff)
parent 410527 2c6289f56812c30254acfdddabcfec1e149c0336 (diff)
child 410583 a325d838180b07692bbb0e04094ad5e1dc06cca9
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound, a=merge MozReview-Commit-ID: EiG9icZCZ3x
--- a/accessible/tests/browser/.eslintrc.js
+++ b/accessible/tests/browser/.eslintrc.js
@@ -33,17 +33,16 @@ module.exports = {
     "new-parens": "error",
     "no-bitwise": "off",
     "no-caller": "error",
     "no-catch-shadow": "error",
     "no-comma-dangle": "off",
     "no-console": "off",
     "no-constant-condition": "off",
     "no-continue": "off",
-    "no-control-regex": "error",
     "no-div-regex": "off",
     "no-extend-native": "error",
     "no-extra-parens": "off",
     "no-extra-strict": "off",
     "no-fallthrough": "error",
     "no-floating-decimal": "off",
     "no-inline-comments": "off",
     "no-mixed-requires": "off",
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4080,17 +4080,17 @@ function FillHistoryMenu(aParent) {
   updateSessionHistory(sessionHistory, true);
   return true;
 }
 
 function addToUrlbarHistory(aUrlToAdd) {
   if (!PrivateBrowsingUtils.isWindowPrivate(window) &&
       aUrlToAdd &&
       !aUrlToAdd.includes(" ") &&
-      !/[\x00-\x1F]/.test(aUrlToAdd))
+      !/[\x00-\x1F]/.test(aUrlToAdd)) // eslint-disable-line no-control-regex
     PlacesUIUtils.markPageAsTyped(aUrlToAdd);
 }
 
 function BrowserDownloadsUI() {
   if (PrivateBrowsingUtils.isWindowPrivate(window)) {
     openUILinkIn("about:downloads", "tab");
   } else {
     PlacesCommandHook.showPlacesOrganizer("Downloads");
--- a/browser/components/migration/.eslintrc.js
+++ b/browser/components/migration/.eslintrc.js
@@ -9,17 +9,16 @@ module.exports = {
     "block-scoped-var": "error",
     "comma-dangle": "off",
     "comma-style": ["error", "last"],
     "complexity": ["error", {"max": 21}],
     "dot-notation": "error",
     "indent": ["error", 2, {"SwitchCase": 1, "ArrayExpression": "first", "ObjectExpression": "first"}],
     "max-nested-callbacks": ["error", 3],
     "new-parens": "error",
-    "no-control-regex": "error",
     "no-extend-native": "error",
     "no-fallthrough": ["error", { "commentPattern": ".*[Ii]ntentional(?:ly)?\\s+fall(?:ing)?[\\s-]*through.*" }],
     "no-multi-str": "error",
     "no-return-assign": "error",
     "no-sequences": "error",
     "no-shadow": "error",
     "no-throw-literal": "error",
     "no-unneeded-ternary": "error",
--- a/browser/extensions/formautofill/.eslintrc.js
+++ b/browser/extensions/formautofill/.eslintrc.js
@@ -93,19 +93,16 @@ module.exports = {
     "max-nested-callbacks": ["error", 4],
 
     // Disallow use of arguments.caller or arguments.callee.
     "no-caller": "error",
 
     // Disallow using the console API.
     "no-console": "error",
 
-    // Disallow control characters in regular expressions.
-    "no-control-regex": "error",
-
     // Disallow fallthrough of case statements, except if there is a comment.
     "no-fallthrough": "error",
 
     // Disallow use of multiline strings (use template strings instead).
     "no-multi-str": "warn",
 
     // Disallow multiple empty lines.
     "no-multiple-empty-lines": ["warn", {"max": 2}],
--- a/devtools/client/responsivedesign/responsivedesign.jsm
+++ b/devtools/client/responsivedesign/responsivedesign.jsm
@@ -358,17 +358,19 @@ ResponsiveUI.prototype = {
                 "max-height: none;" +
                 "min-height: 0;";
     debug("RESET STACK SIZE");
     this.stack.setAttribute("style", style);
 
     // Wait for resize message before stopping in the child when testing,
     // but only if we should expect to still get a message.
     if (flags.testing && this.tab.linkedBrowser.messageManager) {
+      debug("CLOSE: WAIT ON CONTENT RESIZE");
       yield this.waitForMessage("ResponsiveMode:OnContentResize");
+      debug("CLOSE: CONTENT RESIZE DONE");
     }
 
     if (this.isResizing) {
       this.stopResizing();
     }
 
     // Remove listeners.
     this.menulist.removeEventListener("select", this.boundPresetSelected, true);
@@ -400,29 +402,34 @@ ResponsiveUI.prototype = {
     this.container.removeAttribute("responsivemode");
     this.stack.removeAttribute("responsivemode");
 
     ActiveTabs.delete(this.tab);
     if (this.touchEventSimulator) {
       this.touchEventSimulator.stop();
     }
 
+    debug("CLOSE: WAIT ON CLIENT CLOSE");
     yield this.client.close();
+    debug("CLOSE: CLIENT CLOSE DONE");
     this.client = this.emulationFront = null;
 
     this._telemetry.toolClosed("responsive");
 
     if (this.tab.linkedBrowser && this.tab.linkedBrowser.messageManager) {
       let stopped = this.waitForMessage("ResponsiveMode:Stop:Done");
       this.tab.linkedBrowser.messageManager.sendAsyncMessage("ResponsiveMode:Stop");
+      debug("CLOSE: WAIT ON STOP");
       yield stopped;
+      debug("CLOSE: STOP DONE");
     }
 
     this.hideNewUINotification();
 
+    debug("CLOSE: DONE, EMIT OFF");
     this.inited = null;
     ResponsiveUIManager.emit("off", { tab: this.tab });
   }),
 
   waitForMessage(message) {
     return new Promise(resolve => {
       let listener = () => {
         this.mm.removeMessageListener(message, listener);
@@ -1124,16 +1131,20 @@ ResponsiveUI.prototype = {
     };
   },
 
   /**
    * Change the size of the viewport.
    */
   setViewportSize({ width, height }) {
     debug(`SET SIZE TO ${width} x ${height}`);
+    if (this.closing) {
+      debug(`ABORT SET SIZE, CLOSING`);
+      return;
+    }
     if (width) {
       this.setWidth(width);
     }
     if (height) {
       this.setHeight(height);
     }
   },
 
--- a/devtools/client/webconsole/new-console-output/components/console-output.js
+++ b/devtools/client/webconsole/new-console-output/components/console-output.js
@@ -10,16 +10,17 @@ const {
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const {
   getAllMessagesUiById,
   getAllMessagesTableDataById,
   getVisibleMessages,
+  getAllRepeatById,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
 
 const ConsoleOutput = createClass({
 
   displayName: "ConsoleOutput",
 
   propTypes: {
@@ -27,16 +28,17 @@ const ConsoleOutput = createClass({
     serviceContainer: PropTypes.shape({
       attachRefToHud: 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,
     visibleMessages: PropTypes.array.isRequired,
   },
 
   componentDidMount() {
     // Do the scrolling in the nextTick since this could hit console startup performances.
     // See https://bugzilla.mozilla.org/show_bug.cgi?id=1355869
     setTimeout(() => {
       scrollToBottom(this.outputNode);
@@ -70,31 +72,33 @@ const ConsoleOutput = createClass({
   },
 
   render() {
     let {
       dispatch,
       visibleMessages,
       messagesUi,
       messagesTableData,
+      messagesRepeat,
       serviceContainer,
       timestampsVisible,
     } = this.props;
 
     let messageNodes = visibleMessages.map((message) => {
       return (
         MessageContainer({
           dispatch,
           message,
           key: message.id,
           serviceContainer,
           open: messagesUi.includes(message.id),
           tableData: messagesTableData.get(message.id),
           indent: message.indent,
           timestampsVisible,
+          repeat: messagesRepeat[message.id]
         })
       );
     });
 
     return (
       dom.div({
         className: "webconsole-output",
         onContextMenu: this.onContextMenu,
@@ -118,13 +122,14 @@ function isScrolledToBottom(outputNode, 
          scrollNode.scrollHeight - lastNodeHeight / 2;
 }
 
 function mapStateToProps(state, props) {
   return {
     visibleMessages: getVisibleMessages(state),
     messagesUi: getAllMessagesUiById(state),
     messagesTableData: getAllMessagesTableDataById(state),
+    messagesRepeat: getAllRepeatById(state),
     timestampsVisible: state.ui.timestampsVisible,
   };
 }
 
 module.exports = connect(mapStateToProps)(ConsoleOutput);
--- a/devtools/client/webconsole/new-console-output/components/message-container.js
+++ b/devtools/client/webconsole/new-console-output/components/message-container.js
@@ -32,27 +32,28 @@ const MessageContainer = createClass({
 
   propTypes: {
     message: PropTypes.object.isRequired,
     open: PropTypes.bool.isRequired,
     serviceContainer: PropTypes.object.isRequired,
     indent: PropTypes.number.isRequired,
     tableData: PropTypes.object,
     timestampsVisible: PropTypes.bool.isRequired,
+    repeat: PropTypes.object,
   },
 
   getDefaultProps: function () {
     return {
       open: false,
       indent: 0,
     };
   },
 
   shouldComponentUpdate(nextProps, nextState) {
-    const repeatChanged = this.props.message.repeat !== nextProps.message.repeat;
+    const repeatChanged = this.props.repeat !== nextProps.repeat;
     const openChanged = this.props.open !== nextProps.open;
     const tableDataChanged = this.props.tableData !== nextProps.tableData;
     const responseChanged = this.props.message.response !== nextProps.message.response;
     const totalTimeChanged = this.props.message.totalTime !== nextProps.message.totalTime;
     const timestampVisibleChanged =
       this.props.timestampsVisible !== nextProps.timestampsVisible;
 
     return repeatChanged
--- a/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
@@ -37,23 +37,23 @@ function ConsoleApiCall(props) {
   const {
     dispatch,
     message,
     open,
     tableData,
     serviceContainer,
     indent,
     timestampsVisible,
+    repeat,
   } = props;
   const {
     id: messageId,
     source,
     type,
     level,
-    repeat,
     stacktrace,
     frame,
     timeStamp,
     parameters,
     messageText,
     userProvidedStyles,
   } = message;
 
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -30,25 +30,28 @@ const MessageState = Immutable.Record({
   // Map of the form {groupMessageId : groupArray},
   // where groupArray is the list of of all the parent groups' ids of the groupMessageId.
   groupsById: Immutable.Map(),
   // Message id of the current group (no corresponding console.groupEnd yet).
   currentGroup: null,
   // List of removed messages is used to release related (parameters) actors.
   // This array is not supposed to be consumed by any UI component.
   removedMessages: [],
+  // Map of the form {messageId : numberOfRepeat}
+  repeatById: {}
 });
 
 function messages(state = new MessageState(), action, filtersState, prefsState) {
   const {
     messagesById,
     messagesUiById,
     messagesTableDataById,
     groupsById,
     currentGroup,
+    repeatById,
     visibleMessages,
   } = state;
 
   const {logLimit} = prefsState;
 
   switch (action.type) {
     case constants.MESSAGE_ADD:
       let newMessage = action.message;
@@ -60,23 +63,25 @@ function messages(state = new MessageSta
 
       if (newMessage.type === constants.MESSAGE_TYPE.END_GROUP) {
         // Compute the new current group.
         return state.set("currentGroup", getNewCurrentGroup(currentGroup, groupsById));
       }
 
       if (newMessage.allowRepeating && messagesById.size > 0) {
         let lastMessage = messagesById.last();
-        if (lastMessage.repeatId === newMessage.repeatId) {
+        if (
+          lastMessage.repeatId === newMessage.repeatId
+          && lastMessage.groupId === currentGroup
+        ) {
           return state.set(
-            "messagesById",
-            messagesById.set(
-              lastMessage.id,
-              lastMessage.set("repeat", lastMessage.repeat + 1)
-            )
+            "repeatById",
+            Object.assign({}, repeatById, {
+              [lastMessage.id]: (repeatById[lastMessage.id] || 1) + 1
+            })
           );
         }
       }
 
       return state.withMutations(function (record) {
         // Add the new message with a reference to the parent group.
         let parentGroups = getParentGroups(currentGroup, groupsById);
         const addedMessage = newMessage.withMutations(function (message) {
@@ -316,16 +321,27 @@ function limitTopLevelMessageCount(state
   if (mapHasRemovedIdKey(record.messagesTableDataById)) {
     record.set("messagesTableDataById",
       record.messagesTableDataById.withMutations(cleanUpCollection));
   }
   if (mapHasRemovedIdKey(record.groupsById)) {
     record.set("groupsById", record.groupsById.withMutations(cleanUpCollection));
   }
 
+  if (Object.keys(record.repeatById).includes(removedMessagesId)) {
+    record.set("repeatById",
+      [...Object.entries(record.repeatById)].reduce((res, [id, repeat]) => {
+        if (!isInRemovedId(id)) {
+          res[id] = repeat;
+        }
+        return res;
+      }, {})
+    );
+  }
+
   return record;
 }
 
 /**
  * Returns total count of top level messages (those which are not
  * within a group).
  */
 function getToplevelMessageCount(record) {
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -28,17 +28,22 @@ function getAllGroupsById(state) {
 function getCurrentGroup(state) {
   return state.messages.currentGroup;
 }
 
 function getVisibleMessages(state) {
   return state.messages.visibleMessages.map(id => getMessage(state, id));
 }
 
+function getAllRepeatById(state) {
+  return state.messages.repeatById;
+}
+
 module.exports = {
   getMessage,
   getAllMessagesById,
   getAllMessagesUiById,
   getAllMessagesTableDataById,
   getAllGroupsById,
   getCurrentGroup,
   getVisibleMessages,
+  getAllRepeatById,
 };
--- a/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
@@ -62,20 +62,22 @@ describe("ConsoleAPICall component:", ()
       const secondElementStyle = elements.eq(1).prop("style");
       // Allowed styles are applied accordingly on the second element.
       expect(secondElementStyle.color).toBe(`red`);
       // Forbidden styles are not applied.
       expect(secondElementStyle.background).toBe(undefined);
     });
 
     it("renders repeat node", () => {
-      const message =
-        stubPreparedMessages.get("console.log('foobar', 'test')")
-        .set("repeat", 107);
-      const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
+      const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+      const wrapper = render(ConsoleApiCall({
+        message,
+        serviceContainer,
+        repeat: 107
+      }));
 
       expect(wrapper.find(".message-repeats").text()).toBe("107");
       expect(wrapper.find(".message-repeats").prop("title")).toBe("107 repeats");
 
       let selector = "span > span.message-flex-body > " +
         "span.message-body.devtools-monospace + span.message-repeats";
       expect(wrapper.find(selector).length).toBe(1);
     });
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
@@ -20,18 +20,17 @@ stubPreparedMessages.set("console.log('f
   "timeStamp": 1479159894798,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "foobar",
     "test"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foobar\",\"test\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foobar\",\"test\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -48,18 +47,17 @@ stubPreparedMessages.set("console.log(un
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     {
       "type": "undefined"
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"undefined\"}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"undefined\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -74,18 +72,17 @@ stubPreparedMessages.set("console.warn('
   "source": "console-api",
   "timeStamp": 1479159897333,
   "type": "warn",
   "level": "warn",
   "messageText": null,
   "parameters": [
     "danger, will robinson!"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"warn\",\"level\":\"warn\",\"messageText\":null,\"parameters\":[\"danger, will robinson!\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"warn\",\"level\":\"warn\",\"messageText\":null,\"parameters\":[\"danger, will robinson!\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -102,18 +99,17 @@ stubPreparedMessages.set("console.log(Na
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     {
       "type": "NaN"
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"NaN\"}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"NaN\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -130,18 +126,17 @@ stubPreparedMessages.set("console.log(nu
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     {
       "type": "null"
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"null\"}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"null\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -156,18 +151,17 @@ stubPreparedMessages.set("console.log('鼬')", new ConsoleMessage({
   "source": "console-api",
   "timeStamp": 1479159901470,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "鼬"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"鼬\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"鼬\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -182,18 +176,17 @@ stubPreparedMessages.set("console.clear(
   "source": "console-api",
   "timeStamp": 1479159902721,
   "type": "clear",
   "level": "log",
   "messageText": null,
   "parameters": [
     "Console was cleared."
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"clear\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"clear\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -206,18 +199,17 @@ stubPreparedMessages.set("console.count(
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159903982,
   "type": "log",
   "level": "debug",
   "messageText": "bar: 1",
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"debug\",\"messageText\":\"bar: 1\",\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"debug\",\"messageText\":\"bar: 1\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -253,18 +245,17 @@ stubPreparedMessages.set("console.assert
             "value": "foobar"
           }
         },
         "ownPropertiesLength": 1,
         "safeGetterValues": {}
       }
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"assert\",\"level\":\"error\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn8.child1/obj31\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"message\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"foobar\"}},\"ownPropertiesLength\":1,\"safeGetterValues\":{}}}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":27,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":1}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"assert\",\"level\":\"error\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn8.child1/obj31\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"message\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"foobar\"}},\"ownPropertiesLength\":1,\"safeGetterValues\":{}}}],\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":27,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":1}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": [
     {
       "columnNumber": 27,
       "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "functionName": "triggerPacket",
       "language": 2,
       "lineNumber": 1
     }
@@ -287,18 +278,17 @@ stubPreparedMessages.set("console.log('h
   "source": "console-api",
   "timeStamp": 1479159906444,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "hello \nfrom \rthe \"string world!"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"hello \\nfrom \\rthe \\\"string world!\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"hello \\nfrom \\rthe \\\"string world!\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -313,18 +303,17 @@ stubPreparedMessages.set("console.log('úṇĩçödê țĕșť')", new ConsoleMessage({
   "source": "console-api",
   "timeStamp": 1479159907704,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "úṇĩçödê țĕșť"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"úṇĩçödê țĕșť\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"úṇĩçödê țĕșť\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -351,18 +340,17 @@ stubPreparedMessages.set("console.dirxml
       "sealed": false,
       "ownPropertyLength": 815,
       "preview": {
         "kind": "ObjectWithURL",
         "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html"
       }
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn11.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":815,\"preview\":{\"kind\":\"ObjectWithURL\",\"url\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\"}}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn11.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":815,\"preview\":{\"kind\":\"ObjectWithURL\",\"url\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\"}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -395,18 +383,17 @@ stubPreparedMessages.set("console.log('m
         "items": [
           "red",
           "green",
           "blue"
         ]
       }
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myarray\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj32\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"red\",\"green\",\"blue\"]}}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myarray\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj32\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"red\",\"green\",\"blue\"]}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -431,18 +418,17 @@ stubPreparedMessages.set("console.log('m
       "class": "RegExp",
       "extensible": true,
       "frozen": false,
       "sealed": false,
       "ownPropertyLength": 1,
       "displayString": "/a.b.c/"
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myregex\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj33\",\"class\":\"RegExp\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"displayString\":\"/a.b.c/\"}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myregex\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj33\",\"class\":\"RegExp\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"displayString\":\"/a.b.c/\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -474,18 +460,17 @@ stubPreparedMessages.set("console.table(
         "items": [
           "red",
           "green",
           "blue"
         ]
       }
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj34\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"red\",\"green\",\"blue\"]}}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj34\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"red\",\"green\",\"blue\"]}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -534,18 +519,17 @@ stubPreparedMessages.set("console.log('m
             "value": "blueValue"
           }
         },
         "ownPropertiesLength": 3,
         "safeGetterValues": {}
       }
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myobject\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj35\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":3,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"red\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"redValue\"},\"green\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"greenValue\"},\"blue\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"blueValue\"}},\"ownPropertiesLength\":3,\"safeGetterValues\":{}}}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"myobject\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj35\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":3,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"red\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"redValue\"},\"green\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"greenValue\"},\"blue\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"blueValue\"}},\"ownPropertiesLength\":3,\"safeGetterValues\":{}}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -583,18 +567,17 @@ stubPreparedMessages.set("console.map('m
           [
             "key2",
             "value2"
           ]
         ]
       }
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"mymap\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj36\",\"class\":\"Map\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"MapLike\",\"size\":2,\"entries\":[[\"key1\",\"value1\"],[\"key2\",\"value2\"]]}}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"mymap\",{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj36\",\"class\":\"Map\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"kind\":\"MapLike\",\"size\":2,\"entries\":[[\"key1\",\"value1\"],[\"key2\",\"value2\"]]}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 5,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -607,18 +590,17 @@ stubPreparedMessages.set("console.trace(
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159910198,
   "type": "trace",
   "level": "log",
   "messageText": null,
   "parameters": [],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"trace\",\"level\":\"log\",\"messageText\":null,\"parameters\":[],\"repeat\":null,\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"testStacktraceFiltering\",\"language\":2,\"lineNumber\":3},{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"foo\",\"language\":2,\"lineNumber\":6},{\"columnNumber\":1,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":9}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":3},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"trace\",\"level\":\"log\",\"messageText\":null,\"parameters\":[],\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"testStacktraceFiltering\",\"language\":2,\"lineNumber\":3},{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"foo\",\"language\":2,\"lineNumber\":6},{\"columnNumber\":1,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":9}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":3},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": [
     {
       "columnNumber": 3,
       "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "functionName": "testStacktraceFiltering",
       "language": 2,
       "lineNumber": 3
     },
@@ -653,18 +635,17 @@ stubPreparedMessages.set("console.time('
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159911476,
   "type": "nullMessage",
   "level": "log",
   "messageText": null,
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"nullMessage\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"nullMessage\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -677,18 +658,17 @@ stubPreparedMessages.set("timerAlreadyEx
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1494362489620,
   "type": "time",
   "level": "warn",
   "messageText": "Timer “bar” already exists.",
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"time\",\"level\":\"warn\",\"messageText\":\"Timer “bar” already exists.\",\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"time\",\"level\":\"warn\",\"messageText\":\"Timer “bar” already exists.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -701,18 +681,17 @@ stubPreparedMessages.set("console.timeEn
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159911478,
   "type": "timeEnd",
   "level": "log",
   "messageText": "bar: 1.36ms",
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 1.36ms\",\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":4,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 1.36ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":4,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 4,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -725,18 +704,17 @@ stubPreparedMessages.set("timerDoesntExi
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1494362489622,
   "type": "timeEnd",
   "level": "warn",
   "messageText": "Timer “bar” doesn’t exist.",
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"timeEnd\",\"level\":\"warn\",\"messageText\":\"Timer “bar” doesn’t exist.\",\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"timeEnd\",\"level\":\"warn\",\"messageText\":\"Timer “bar” doesn’t exist.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":5,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 5,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -751,18 +729,17 @@ stubPreparedMessages.set("console.table(
   "source": "console-api",
   "timeStamp": 1479159912655,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "bar"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -793,18 +770,17 @@ stubPreparedMessages.set("console.table(
         "items": [
           "a",
           "b",
           "c"
         ]
       }
     }
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"a\",\"b\",\"c\"]}}],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"a\",\"b\",\"c\"]}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -819,18 +795,17 @@ stubPreparedMessages.set("console.group(
   "source": "console-api",
   "timeStamp": 1479159914984,
   "type": "startGroup",
   "level": "log",
   "messageText": null,
   "parameters": [
     "bar"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -843,18 +818,17 @@ stubPreparedMessages.set("console.groupE
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1492540770051,
   "type": "endGroup",
   "level": "log",
   "messageText": null,
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -869,18 +843,17 @@ stubPreparedMessages.set("console.groupC
   "source": "console-api",
   "timeStamp": 1479159916153,
   "type": "startGroupCollapsed",
   "level": "log",
   "messageText": null,
   "parameters": [
     "foo"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroupCollapsed\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroupCollapsed\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -893,18 +866,17 @@ stubPreparedMessages.set("console.groupE
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1492540770585,
   "type": "endGroup",
   "level": "log",
   "messageText": null,
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -919,18 +891,17 @@ stubPreparedMessages.set("console.group(
   "source": "console-api",
   "timeStamp": 1479159917524,
   "type": "startGroup",
   "level": "log",
   "messageText": null,
   "parameters": [
     "<no group label>"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"<no group label>\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"<no group label>\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -943,18 +914,17 @@ stubPreparedMessages.set("console.groupE
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1491902018685,
   "type": "endGroup",
   "level": "log",
   "messageText": null,
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -970,18 +940,17 @@ stubPreparedMessages.set("console.log(%c
   "timeStamp": 1479159919144,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "foo",
     "bar"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"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')\"],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"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')\"],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -1000,18 +969,17 @@ stubPreparedMessages.set("console.group(
   "timeStamp": 1491902018670,
   "type": "startGroup",
   "level": "log",
   "messageText": null,
   "parameters": [
     "foo",
     "bar"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"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')\"],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"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')\"],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -1027,18 +995,17 @@ stubPreparedMessages.set("console.groupE
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1492540772083,
   "type": "endGroup",
   "level": "log",
   "messageText": null,
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":6,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":6,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 6,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -1054,18 +1021,17 @@ stubPreparedMessages.set("console.groupC
   "timeStamp": 1491902018683,
   "type": "startGroupCollapsed",
   "level": "log",
   "messageText": null,
   "parameters": [
     "foo",
     "baz"
   ],
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroupCollapsed\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"baz\"],\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"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')\"],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"startGroupCollapsed\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"baz\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"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')\"],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
@@ -1081,18 +1047,17 @@ stubPreparedMessages.set("console.groupE
   "id": "1",
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1492540772669,
   "type": "endGroup",
   "level": "log",
   "messageText": null,
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":6,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":null,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":6,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 6,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/cssMessage.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/cssMessage.js
@@ -17,18 +17,17 @@ stubPreparedMessages.set("Unknown property ‘such-unknown-property’.  Declaration dropped.", new ConsoleMessage({
   "id": "1",
   "allowRepeating": true,
   "source": "css",
   "timeStamp": 1479159920406,
   "type": "log",
   "level": "warn",
   "messageText": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Unknown property ‘such-unknown-property’.  Declaration dropped.\",\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html\",\"line\":3,\"column\":23},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Unknown property ‘such-unknown-property’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html\",\"line\":3,\"column\":23},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
     "line": 3,
     "column": 23
   },
   "groupId": null,
   "userProvidedStyles": null,
@@ -40,18 +39,17 @@ stubPreparedMessages.set("Error in parsing value for ‘padding-top’.  Declaration dropped.", new ConsoleMessage({
   "id": "1",
   "allowRepeating": true,
   "source": "css",
   "timeStamp": 1479159920465,
   "type": "log",
   "level": "warn",
   "messageText": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Error in parsing value for ‘padding-top’.  Declaration dropped.\",\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html\",\"line\":3,\"column\":15},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Error in parsing value for ‘padding-top’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html\",\"line\":3,\"column\":15},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
     "line": 3,
     "column": 15
   },
   "groupId": null,
   "userProvidedStyles": null,
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/evaluationResult.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/evaluationResult.js
@@ -27,18 +27,17 @@ stubPreparedMessages.set("new Date(0)", 
     "extensible": true,
     "frozen": false,
     "sealed": false,
     "ownPropertyLength": 0,
     "preview": {
       "timestamp": 0
     }
   },
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"result\",\"level\":\"log\",\"parameters\":{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj30\",\"class\":\"Date\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"timestamp\":0}},\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":null,\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"result\",\"level\":\"log\",\"parameters\":{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj30\",\"class\":\"Date\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"timestamp\":0}},\"repeatId\":null,\"stacktrace\":null,\"frame\":null,\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": null,
   "groupId": null,
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0
 }));
 
@@ -48,18 +47,17 @@ stubPreparedMessages.set("asdf()", new C
   "source": "javascript",
   "timeStamp": 1479159921377,
   "type": "result",
   "level": "error",
   "messageText": "ReferenceError: asdf is not defined",
   "parameters": {
     "type": "undefined"
   },
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"result\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":{\"type\":\"undefined\"},\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":1},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"result\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":{\"type\":\"undefined\"},\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":1},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "debugger eval code",
     "line": 1,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
@@ -74,18 +72,17 @@ stubPreparedMessages.set("1 + @", new Co
   "source": "javascript",
   "timeStamp": 1479159921399,
   "type": "result",
   "level": "error",
   "messageText": "SyntaxError: illegal character",
   "parameters": {
     "type": "undefined"
   },
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"result\",\"level\":\"error\",\"messageText\":\"SyntaxError: illegal character\",\"parameters\":{\"type\":\"undefined\"},\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":4},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Illegal_character?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"result\",\"level\":\"error\",\"messageText\":\"SyntaxError: illegal character\",\"parameters\":{\"type\":\"undefined\"},\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":4},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Illegal_character?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": {
     "source": "debugger eval code",
     "line": 1,
     "column": 4
   },
   "groupId": null,
   "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Illegal_character?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
@@ -105,18 +102,17 @@ stubPreparedMessages.set("longString mes
     "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": {
     "type": "undefined"
   },
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"result\",\"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/longString37\"},\"parameters\":{\"type\":\"undefined\"},\"repeat\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":null,\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"result\",\"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/longString37\"},\"parameters\":{\"type\":\"undefined\"},\"repeatId\":null,\"stacktrace\":null,\"frame\":null,\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
   "stacktrace": null,
   "frame": null,
   "groupId": null,
   "userProvidedStyles": null,
   "notes": null,
   "indent": 0
 }));
 
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/pageError.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/pageError.js
@@ -17,18 +17,17 @@ stubPreparedMessages.set("ReferenceError
   "id": "1",
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1476573167137,
   "type": "log",
   "level": "error",
   "messageText": "ReferenceError: asdf is not defined",
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":5},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":5},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
   "stacktrace": [
     {
       "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 3,
       "columnNumber": 5,
       "functionName": "bar"
     },
     {
@@ -72,18 +71,17 @@ stubPreparedMessages.set("SyntaxError: r
   "id": "1",
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1487992945524,
   "type": "log",
   "level": "error",
   "messageText": "SyntaxError: redeclaration of let a",
   "parameters": null,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"SyntaxError: redeclaration of let a\",\"parameters\":null,\"repeat\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"resource://testing-common/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":9},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":[{\"messageBody\":\"Previously declared at line 2, column 6\",\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":6}}],\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"SyntaxError: redeclaration of let a\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"resource://testing-common/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":9},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":[{\"messageBody\":\"Previously declared at line 2, column 6\",\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":6}}],\"indent\":0}",
   "stacktrace": [
     {
       "filename": "resource://testing-common/content-task.js line 52 > eval",
       "lineNumber": 6,
       "columnNumber": 9,
       "functionName": null
     },
     {
@@ -122,18 +120,17 @@ stubPreparedMessages.set("TypeError long
   "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,
-  "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"log\",\"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,\"repeat\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":1,\"columnNumber\":7,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":7},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":null,\"type\":\"log\",\"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,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":1,\"columnNumber\":7,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":7},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null,\"indent\":0}",
   "stacktrace": [
     {
       "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 1,
       "columnNumber": 7,
       "functionName": null
     },
     {
--- a/devtools/client/webconsole/new-console-output/test/helpers.js
+++ b/devtools/client/webconsole/new-console-output/test/helpers.js
@@ -6,16 +6,19 @@
 let ReactDOM = require("devtools/client/shared/vendor/react-dom");
 let React = require("devtools/client/shared/vendor/react");
 var TestUtils = React.addons.TestUtils;
 
 const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
 const { IdGenerator } = require("devtools/client/webconsole/new-console-output/utils/id-generator");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
+const {
+  getAllMessagesById,
+} = require("devtools/client/webconsole/new-console-output/selectors/messages");
 
 /**
  * Prepare actions for use in testing.
  */
 function setupActions() {
   // Some actions use dependency injection. This helps them avoid using state in
   // a hard-to-test way. We need to inject stubbed versions of these dependencies.
   const wrappedActions = Object.assign({}, actions);
@@ -61,15 +64,28 @@ function shallowRenderComponent(componen
 
 /**
  * Create deep copy of given packet object.
  */
 function clonePacket(packet) {
   return JSON.parse(JSON.stringify(packet));
 }
 
+/**
+ * Return the message in the store at the given index.
+ *
+ * @param {object} state - The redux state of the console.
+ * @param {int} index - The index of the message in the map.
+ * @return {Message} - The message, or undefined if the index does not exists in the map.
+ */
+function getMessageAt(state, index) {
+  const messages = getAllMessagesById(state);
+  return messages.get([...messages.keys()][index]);
+}
+
 module.exports = {
+  clonePacket,
+  getMessageAt,
+  renderComponent,
   setupActions,
   setupStore,
-  renderComponent,
   shallowRenderComponent,
-  clonePacket
 };
--- a/devtools/client/webconsole/new-console-output/test/store/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/messages.test.js
@@ -2,23 +2,25 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const {
   getAllGroupsById,
   getAllMessagesById,
   getAllMessagesTableDataById,
   getAllMessagesUiById,
+  getAllRepeatById,
   getCurrentGroup,
   getVisibleMessages,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const {
+  clonePacket,
+  getMessageAt,
   setupActions,
   setupStore,
-  clonePacket
 } = require("devtools/client/webconsole/new-console-output/test/helpers");
 const { stubPackets, stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const {
   MESSAGE_TYPE,
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 const expect = require("expect");
 
@@ -52,33 +54,59 @@ describe("Message reducer:", () => {
       packet.message.timeStamp = 1;
       dispatch(actions.messageAdd(packet));
       packet.message.timeStamp = 2;
       dispatch(actions.messageAdd(packet));
 
       const messages = getAllMessagesById(getState());
 
       expect(messages.size).toBe(1);
-      expect(messages.first().repeat).toBe(4);
+
+      const repeat = getAllRepeatById(getState());
+      expect(repeat[messages.first().id]).toBe(4);
+    });
+
+    it("does not increment repeat after closing a group", () => {
+      const logKey = "console.log('foobar', 'test')";
+      const { getState } = setupStore([
+        logKey,
+        logKey,
+        "console.group('bar')",
+        logKey,
+        logKey,
+        logKey,
+        "console.groupEnd()",
+        logKey,
+      ]);
+
+      const messages = getAllMessagesById(getState());
+
+      expect(messages.size).toBe(4);
+      const repeat = getAllRepeatById(getState());
+      expect(repeat[messages.first().id]).toBe(2);
+      expect(repeat[getMessageAt(getState(), 2).id]).toBe(3);
+      expect(repeat[messages.last().id]).toBe(undefined);
     });
 
     it("does not clobber a unique message", () => {
       const key1 = "console.log('foobar', 'test')";
       const { dispatch, getState } = setupStore([key1, key1]);
 
       const packet = stubPackets.get(key1);
       dispatch(actions.messageAdd(packet));
 
       const packet2 = stubPackets.get("console.log(undefined)");
       dispatch(actions.messageAdd(packet2));
 
       const messages = getAllMessagesById(getState());
       expect(messages.size).toBe(2);
-      expect(messages.first().repeat).toBe(3);
-      expect(messages.last().repeat).toBe(1);
+
+      const repeat = getAllRepeatById(getState());
+      expect(repeat[messages.first().id]).toBe(3);
+      expect(repeat[messages.last().id]).toBe(undefined);
     });
 
     it("adds a message in response to console.clear()", () => {
       const { dispatch, getState } = setupStore([]);
 
       dispatch(actions.messageAdd(stubPackets.get("console.clear()")));
 
       const messages = getAllMessagesById(getState());
@@ -99,16 +127,17 @@ describe("Message reducer:", () => {
 
       const state = getState();
       expect(getAllMessagesById(state).size).toBe(0);
       expect(getVisibleMessages(state).length).toBe(0);
       expect(getAllMessagesUiById(state).size).toBe(0);
       expect(getAllGroupsById(state).size).toBe(0);
       expect(getAllMessagesTableDataById(state).size).toBe(0);
       expect(getCurrentGroup(state)).toBe(null);
+      expect(getAllRepeatById(state)).toEqual({});
     });
 
     it("properly limits number of messages", () => {
       const { dispatch, getState } = setupStore([]);
 
       const logLimit = 1000;
       const packet = clonePacket(stubPackets.get("console.log(undefined)"));
 
--- a/devtools/client/webconsole/new-console-output/types.js
+++ b/devtools/client/webconsole/new-console-output/types.js
@@ -28,17 +28,16 @@ exports.ConsoleMessage = Immutable.Recor
   id: null,
   allowRepeating: true,
   source: null,
   timeStamp: null,
   type: null,
   level: null,
   messageText: null,
   parameters: null,
-  repeat: 1,
   repeatId: null,
   stacktrace: null,
   frame: null,
   groupId: null,
   exceptionDocURL: null,
   userProvidedStyles: null,
   notes: null,
   indent: 0,
--- a/devtools/client/webconsole/new-console-output/utils/messages.js
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -232,17 +232,16 @@ function transformPacket(packet) {
       });
     }
   }
 }
 
 // Helpers
 function getRepeatId(message) {
   message = message.toJS();
-  message.repeat = null;
   message.timeStamp = null;
   return JSON.stringify(message);
 }
 
 function convertCachedPacket(packet) {
   // The devtools server provides cached message packets in a different shape, so we
   // transform them here.
   let convertPacket = {};
--- a/devtools/shared/discovery/discovery.js
+++ b/devtools/shared/discovery/discovery.js
@@ -171,17 +171,18 @@ LocalDevice.prototype = {
       // To hex and zero pad
       randomID = ("00000000" + randomID.toString(16)).slice(-8);
       this.name = name + "-" + randomID;
     } else if (Services.appinfo.widgetToolkit == "android") {
       // For Firefox for Android, use the device's model name.
       // TODO: Bug 1180997: Find the right way to expose an editable name
       this.name = sysInfo.get("device");
     } else {
-      this.name = sysInfo.get("host");
+      this.name = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService)
+                                                          .myHostName;
     }
   },
 
   get name() {
     return this._name;
   },
 
   set name(name) {
--- a/dom/html/crashtests/crashtests.list
+++ b/dom/html/crashtests/crashtests.list
@@ -50,17 +50,17 @@ load 673853.html
 load 680922-1.xul
 load 682058.xhtml
 load 682460.html
 load 738744.xhtml
 load 741218.json
 load 741250.xhtml
 load 795221-1.html
 load 795221-2.html
-skip-if(stylo) load 795221-3.html # bug 1342289
+load 795221-3.html
 load 795221-4.html
 load 795221-5.xml
 load 811226.html
 load 819745.html
 load 828180.html
 pref(dom.experimental_forms,true) load 828472.html
 load 837033.html
 load 838256-1.html
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -123,17 +123,16 @@
 #include "nsThreadManager.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsISpellChecker.h"
 #include "nsClipboardProxy.h"
 #include "nsDirectoryService.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsContentPermissionHelper.h"
-#include "nsPluginHost.h"
 #ifdef NS_PRINTING
 #include "nsPrintingProxy.h"
 #endif
 
 #include "IHistory.h"
 #include "nsNetUtil.h"
 
 #include "base/message_loop.h"
@@ -1277,34 +1276,42 @@ GetAppPaths(nsCString &aAppPath, nsCStri
     return false;
   }
   bool exists;
   rv = appDir->Exists(&exists);
   if (NS_FAILED(rv) || !exists) {
     return false;
   }
 
+  // appDir points to .app/Contents/Resources, for our purposes we want
+  // .app/Contents.
+  nsCOMPtr<nsIFile> appDirParent;
+  rv = appDir->GetParent(getter_AddRefs(appDirParent));
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
   bool isLink;
   app->IsSymlink(&isLink);
   if (isLink) {
     app->GetNativeTarget(aAppPath);
   } else {
     app->GetNativePath(aAppPath);
   }
   appBinary->IsSymlink(&isLink);
   if (isLink) {
     appBinary->GetNativeTarget(aAppBinaryPath);
   } else {
     appBinary->GetNativePath(aAppBinaryPath);
   }
-  appDir->IsSymlink(&isLink);
+  appDirParent->IsSymlink(&isLink);
   if (isLink) {
-    appDir->GetNativeTarget(aAppDir);
+    appDirParent->GetNativeTarget(aAppDir);
   } else {
-    appDir->GetNativePath(aAppDir);
+    appDirParent->GetNativePath(aAppDir);
   }
 
   return true;
 }
 
 // Returns whether or not the currently running build is a development build -
 // where development build means "the files in the .app are symlinks to the src
 // directory". This check is implemented by looking for omni.ja in
@@ -3372,20 +3379,10 @@ ContentChild::RecvRefreshScreens(nsTArra
 }
 
 already_AddRefed<nsIEventTarget>
 ContentChild::GetEventTargetFor(TabChild* aTabChild)
 {
   return IToplevelProtocol::GetActorEventTarget(aTabChild);
 }
 
-mozilla::ipc::IPCResult
-ContentChild::RecvSetPluginList(const uint32_t& aPluginEpoch,
-                                nsTArray<plugins::PluginTag>&& aPluginTags,
-                                nsTArray<plugins::FakePluginTag>&& aFakePluginTags)
-{
-  RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
-  host->SetPluginsInContent(aPluginEpoch, aPluginTags, aFakePluginTags);
-  return IPC_OK();
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -656,19 +656,16 @@ public:
                       const Optional<int64_t>& aLastModified,
                       bool aExistenceCheck, bool aIsFromNsIFile);
 
   typedef std::function<void(PRFileDesc*)> AnonymousTemporaryFileCallback;
   nsresult AsyncOpenAnonymousTemporaryFile(const AnonymousTemporaryFileCallback& aCallback);
 
   virtual already_AddRefed<nsIEventTarget> GetEventTargetFor(TabChild* aTabChild) override;
 
-  mozilla::ipc::IPCResult
-  RecvSetPluginList(const uint32_t& aPluginEpoch, nsTArray<PluginTag>&& aPluginTags, nsTArray<FakePluginTag>&& aFakePluginTags) override;
-
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1157,16 +1157,27 @@ ContentParent::RecvGetBlocklistState(con
   }
 
   if (NS_FAILED(tag->GetBlocklistState(aState))) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
+                               nsresult* aRv,
+                               nsTArray<PluginTag>* aPlugins,
+                               nsTArray<FakePluginTag>* aFakePlugins,
+                               uint32_t* aNewPluginEpoch)
+{
+  *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aFakePlugins, aNewPluginEpoch);
+  return IPC_OK();
+}
+
 /*static*/ TabParent*
 ContentParent::CreateBrowser(const TabContext& aContext,
                              Element* aFrameElement,
                              ContentParent* aOpenerContentParent,
                              TabParent* aSameTabGroupAs,
                              uint64_t aNextTabParentId)
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
@@ -2432,21 +2443,16 @@ ContentParent::InitInternal(ProcessPrior
 
   {
     nsTArray<BlobURLRegistrationData> registrations;
     if (nsHostObjectProtocolHandler::GetAllBlobURLEntries(registrations,
                                                           this)) {
       Unused << SendInitBlobURLs(registrations);
     }
   }
-
-  // Start up nsPluginHost and run FindPlugins to cache the plugin list.
-  // If this isn't our first content process, just send over cached list.
-  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
-  pluginHost->SendPluginsToContent();
 }
 
 bool
 ContentParent::IsAlive() const
 {
   return mIsAlive;
 }
 
@@ -5286,16 +5292,8 @@ ContentParent::CanCommunicateWith(Conten
   if (!cpm->GetParentProcessId(ChildID(), &parentId)) {
     return false;
   }
   if (IsForJSPlugin()) {
     return parentId == ContentParentId(0);
   }
   return parentId == aOtherProcess;
 }
-
-mozilla::ipc::IPCResult
-ContentParent::RecvMaybeReloadPlugins()
-{
-  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
-  pluginHost->ReloadPlugins();
-  return IPC_OK();
-}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -293,25 +293,29 @@ public:
                                                            Endpoint<PContentBridgeParent>* aEndpoint) override;
 
   virtual mozilla::ipc::IPCResult RecvCreateGMPService() override;
 
   virtual mozilla::ipc::IPCResult RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv,
                                                  uint32_t* aRunID,
                                                  Endpoint<PPluginModuleParent>* aEndpoint) override;
 
-  virtual mozilla::ipc::IPCResult RecvMaybeReloadPlugins() override;
-
   virtual mozilla::ipc::IPCResult RecvConnectPluginBridge(const uint32_t& aPluginId,
                                                           nsresult* aRv,
                                                           Endpoint<PPluginModuleParent>* aEndpoint) override;
 
   virtual mozilla::ipc::IPCResult RecvGetBlocklistState(const uint32_t& aPluginId,
                                                         uint32_t* aIsBlocklisted) override;
 
+  virtual mozilla::ipc::IPCResult RecvFindPlugins(const uint32_t& aPluginEpoch,
+                                                  nsresult* aRv,
+                                                  nsTArray<PluginTag>* aPlugins,
+                                                  nsTArray<FakePluginTag>* aFakePlugins,
+                                                  uint32_t* aNewPluginEpoch) override;
+
   virtual mozilla::ipc::IPCResult RecvUngrabPointer(const uint32_t& aTime) override;
 
   virtual mozilla::ipc::IPCResult RecvRemovePermission(const IPC::Principal& aPrincipal,
                                                        const nsCString& aPermissionType,
                                                        nsresult* aRv) override;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -606,27 +606,16 @@ child:
     async ProvideAnonymousTemporaryFile(uint64_t aID, FileDescOrError aFD);
 
     async SetPermissionsWithKey(nsCString aPermissionKey, Permission[] aPermissions);
 
     async RefreshScreens(ScreenDetails[] aScreens);
 
     async PIPCBlobInputStream(nsID aID, uint64_t aSize);
 
-    /**
-     * This call takes the set of plugins loaded in the chrome process, and
-     * sends them to the content process. However, in many cases this set will
-     * not have changed since the last SetPluginList message. To keep track of
-     * this, the chrome process increments an epoch number every time the set of
-     * plugins changes. The chrome process sends up the last epoch it observed.
-     * If the epoch last seen by the content process is the same, the content
-     * process ignores the update. Otherwise the content process updates its
-     * list and reloads its plugins.
-     **/
-    async SetPluginList(uint32_t pluginEpoch, PluginTag[] plugins, FakePluginTag[] fakePlugins);
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId,
                             TabId tabId)
         returns (ContentParentId cpId, bool isForBrowser);
@@ -652,16 +641,30 @@ parent:
     sync ConnectPluginBridge(uint32_t aPluginId)
         returns (nsresult rv, Endpoint<PPluginModuleParent> aEndpoint);
 
     /**
      * Return the current blocklist state for a particular plugin.
      */
     sync GetBlocklistState(uint32_t aPluginId) returns (uint32_t aState);
 
+    /**
+     * This call returns the set of plugins loaded in the chrome
+     * process. However, in many cases this set will not have changed since the
+     * last FindPlugins message. Consequently, the chrome process increments an
+     * epoch number every time the set of plugins changes. The content process
+     * sends up the last epoch it observed. If the epochs are the same, the
+     * chrome process returns no plugins. Otherwise it returns a complete list.
+     *
+     * |pluginEpoch| is the epoch last observed by the content
+     * process. |newPluginEpoch| is the current epoch in the chrome process. If
+     * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
+     */
+    sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, FakePluginTag[] fakePlugins, uint32_t newPluginEpoch);
+
     async PJavaScript();
 
     async PRemoteSpellcheckEngine();
 
     async InitCrashReporter(Shmem shmem, NativeThreadId tid);
 
     /**
      * Is this token compatible with the provided version?
@@ -1086,17 +1089,16 @@ parent:
 
     sync GetA11yContentId() returns (uint32_t aContentId);
     async A11yHandlerControl(uint32_t aPid,
                              IHandlerControlHolder aHandlerControl);
 
     async AddMemoryReport(MemoryReport aReport);
     async FinishMemoryReport(uint32_t aGeneration);
 
-    async MaybeReloadPlugins();
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -46,17 +46,16 @@
 #include "nsIScriptChannel.h"
 #include "nsIBlocklistService.h"
 #include "nsVersionComparator.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsICategoryManager.h"
 #include "nsPluginStreamListenerPeer.h"
 #include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/FakePluginTagInitBinding.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/plugins/PluginAsyncSurrogate.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ipc/URIUtils.h"
 
@@ -263,16 +262,24 @@ static bool UnloadPluginsASAP()
 }
 
 nsPluginHost::nsPluginHost()
   : mPluginsLoaded(false)
   , mOverrideInternalTypes(false)
   , mPluginsDisabled(false)
   , mPluginEpoch(0)
 {
+  // Bump the pluginchanged epoch on startup. This insures content gets a
+  // good plugin list the first time it requests it. Normally we'd just
+  // init this to 1, but due to the unique nature of our ctor we need to do
+  // this manually.
+  if (XRE_IsParentProcess()) {
+    IncrementChromeEpoch();
+  }
+
   // check to see if pref is set at startup to let plugins take over in
   // full page mode for certain image mime types that we handle internally
   mOverrideInternalTypes =
     Preferences::GetBool("plugin.override_internal_types", false);
 
   mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
 
   Preferences::AddStrongObserver(this, "plugin.disable");
@@ -291,23 +298,16 @@ nsPluginHost::nsPluginHost()
 #ifdef PLUGIN_LOGGING
   MOZ_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
   MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
   MOZ_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
 
   PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
   PR_LogFlush();
 #endif
-
-  // Load plugins on creation, as there's a good chance we'll need to send them
-  // to content processes directly after creation.
-  if (XRE_IsParentProcess())
-  {
-    LoadPlugins();
-  }
 }
 
 nsPluginHost::~nsPluginHost()
 {
   PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
 
   UnloadPlugins();
   sInst = nullptr;
@@ -355,30 +355,18 @@ bool nsPluginHost::IsRunningPlugin(nsPlu
   return false;
 }
 
 nsresult nsPluginHost::ReloadPlugins()
 {
   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
   ("nsPluginHost::ReloadPlugins Begin\n"));
 
-  // If we're calling this from a content process, forward the reload request to
-  // the parent process. If plugins actually changed, it will notify us
-  // asynchronously later.
-  if (XRE_IsContentProcess())
-  {
-    Unused << mozilla::dom::ContentChild::GetSingleton()->SendMaybeReloadPlugins();
-    // In content processes, always signal that plugins have not changed. We
-    // will never know if they changed here unless we make slow synchronous
-    // calls. This information will hopefully only be wrong once, as if there
-    // has been a plugin update, we expect to have gotten notification from the
-    // parent process and everything should be updated by the next time this is
-    // called. See Bug 1337058 for more info.
-    return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
-  }
+  nsresult rv = NS_OK;
+
   // this will create the initial plugin list out of cache
   // if it was not created yet
   if (!mPluginsLoaded)
     return LoadPlugins();
 
   // we are re-scanning plugins. New plugins may have been added, also some
   // plugins may have been removed, so we should probably shut everything down
   // but don't touch running (active and not stopped) plugins
@@ -389,24 +377,16 @@ nsresult nsPluginHost::ReloadPlugins()
   // look for possible changes
   bool pluginschanged = true;
   FindPlugins(false, &pluginschanged);
 
   // if no changed detected, return an appropriate error code
   if (!pluginschanged)
     return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
 
-  return ActuallyReloadPlugins();
-}
-
-nsresult
-nsPluginHost::ActuallyReloadPlugins()
-{
-  nsresult rv = NS_OK;
-
   // shutdown plugins and kill the list if there are no running plugins
   RefPtr<nsPluginTag> prev;
   RefPtr<nsPluginTag> next;
 
   for (RefPtr<nsPluginTag> p = mPlugins; p != nullptr;) {
     next = p->mNext;
 
     // only remove our plugin from the list if it's not running.
@@ -430,23 +410,16 @@ nsPluginHost::ActuallyReloadPlugins()
   }
 
   // set flags
   mPluginsLoaded = false;
 
   // load them again
   rv = LoadPlugins();
 
-  if (XRE_IsParentProcess())
-  {
-    // If the plugin list changed, update content. If the plugin list changed
-    // for the content process, it will also reload plugins.
-    SendPluginsToContent();
-  }
-
   PLUGIN_LOG(PLUGIN_LOG_NORMAL,
   ("nsPluginHost::ReloadPlugins End\n"));
 
   return rv;
 }
 
 #define NS_RETURN_UASTRING_SIZE 128
 
@@ -2298,21 +2271,21 @@ WatchRegKey(uint32_t aRoot, nsCOMPtr<nsI
     return;
   }
   aKey->StartWatching(true);
 }
 #endif
 
 nsresult nsPluginHost::LoadPlugins()
 {
-  // This should only be run in the parent process. On plugin list change, we'll
-  // update observers in the content process as part of SetPluginsInContent
+#ifdef ANDROID
   if (XRE_IsContentProcess()) {
     return NS_OK;
   }
+#endif
   // do not do anything if it is already done
   // use ReloadPlugins() to enforce loading
   if (mPluginsLoaded)
     return NS_OK;
 
   if (mPluginsDisabled)
     return NS_OK;
 
@@ -2337,34 +2310,42 @@ nsresult nsPluginHost::LoadPlugins()
     if (obsService)
       obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
   }
 
   return NS_OK;
 }
 
 nsresult
-nsPluginHost::SetPluginsInContent(uint32_t aPluginEpoch,
-                                  nsTArray<mozilla::plugins::PluginTag>& aPlugins,
-                                  nsTArray<mozilla::plugins::FakePluginTag>& aFakePlugins)
+nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged)
 {
   MOZ_ASSERT(XRE_IsContentProcess());
 
+  dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+  nsresult rv;
   nsTArray<PluginTag> plugins;
-
   nsTArray<FakePluginTag> fakePlugins;
-
-  if (aPluginEpoch != ChromeEpochForContent()) {
-    // Since we know we're going to be repopulating the lists anyways, trigger a
-    // reload now to clear out all old entries.
-    ActuallyReloadPlugins();
-
-    SetChromeEpochForContent(aPluginEpoch);
-
-    for (auto tag : aPlugins) {
+  uint32_t parentEpoch;
+  if (!cp->SendFindPlugins(ChromeEpochForContent(), &rv, &plugins, &fakePlugins, &parentEpoch) ||
+      NS_FAILED(rv)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  if (parentEpoch != ChromeEpochForContent()) {
+    *aPluginsChanged = true;
+    if (!aCreatePluginList) {
+      return NS_OK;
+    }
+
+    // Don't do this if aCreatePluginList is false. Otherwise, when we actually
+    // want to create the list, we'll come back here and do nothing.
+    SetChromeEpochForContent(parentEpoch);
+
+    for (size_t i = 0; i < plugins.Length(); i++) {
+      PluginTag& tag = plugins[i];
 
       // Don't add the same plugin again.
       if (nsPluginTag* existing = PluginWithId(tag.id())) {
         UpdateInMemoryPluginInfo(existing);
         continue;
       }
 
       nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
@@ -2381,17 +2362,17 @@ nsPluginHost::SetPluginsInContent(uint32
                                                tag.supportsAsyncInit(),
                                                tag.supportsAsyncRender(),
                                                tag.lastModifiedTime(),
                                                tag.isFromExtension(),
                                                tag.sandboxLevel());
       AddPluginTag(pluginTag);
     }
 
-    for (const auto& tag : aFakePlugins) {
+    for (const auto& tag : fakePlugins) {
       // Don't add the same plugin again.
       for (const auto& existingTag : mFakePlugins) {
         if (existingTag->Id() == tag.id()) {
           continue;
         }
       }
 
       RefPtr<nsFakePluginTag> pluginTag =
@@ -2408,22 +2389,16 @@ nsPluginHost::SetPluginsInContent(uint32
         Preferences::GetCString(kPrefDisableFullPage);
       for (uint32_t i = 0; i < pluginTag->MimeTypes().Length(); i++) {
         if (!IsTypeInList(pluginTag->MimeTypes()[i], disableFullPage)) {
           RegisterWithCategoryManager(pluginTag->MimeTypes()[i],
                                       ePluginRegister);
         }
       }
     }
-
-    nsCOMPtr<nsIObserverService> obsService =
-      mozilla::services::GetObserverService();
-    if (obsService) {
-      obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
-    }
   }
 
   mPluginsLoaded = true;
   return NS_OK;
 }
 
 // if aCreatePluginList is false we will just scan for plugins
 // and see if any changes have been made to the plugins.
@@ -2431,20 +2406,18 @@ nsPluginHost::SetPluginsInContent(uint32
 nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
 {
   Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
 
   NS_ENSURE_ARG_POINTER(aPluginsChanged);
 
   *aPluginsChanged = false;
 
-  // If plugins are found or change, the content process will be notified by the
-  // parent process. Bail out early if this is called from the content process.
   if (XRE_IsContentProcess()) {
-    return NS_OK;
+    return FindPluginsInContent(aCreatePluginList, aPluginsChanged);
   }
 
   nsresult rv;
 
   // Read cached plugins info. If the profile isn't yet available then don't
   // scan for plugins
   if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
     return NS_OK;
@@ -2563,80 +2536,90 @@ nsresult nsPluginHost::FindPlugins(bool 
   // No more need for cached plugins. Clear it up.
   NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
   return NS_OK;
 }
 
 nsresult
-nsPluginHost::SendPluginsToContent()
+mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
+                                        nsTArray<PluginTag>* aPlugins,
+                                        nsTArray<FakePluginTag>* aFakePlugins,
+                                        uint32_t* aNewPluginEpoch)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
-  nsTArray<PluginTag> pluginTags;
-  nsTArray<FakePluginTag> fakePluginTags;
+  RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+  return host->FindPluginsForContent(aPluginEpoch, aPlugins, aFakePlugins, aNewPluginEpoch);
+}
+
+nsresult
+nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
+                                    nsTArray<PluginTag>* aPlugins,
+                                    nsTArray<FakePluginTag>* aFakePlugins,
+                                    uint32_t* aNewPluginEpoch)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
   // Load plugins so that the epoch is correct.
   nsresult rv = LoadPlugins();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  uint32_t newPluginEpoch = ChromeEpoch();
+  *aNewPluginEpoch = ChromeEpoch();
+  if (aPluginEpoch == ChromeEpoch()) {
+    return NS_OK;
+  }
 
   nsTArray<nsCOMPtr<nsIInternalPluginTag>> plugins;
   GetPlugins(plugins, true);
 
   for (size_t i = 0; i < plugins.Length(); i++) {
     nsCOMPtr<nsIInternalPluginTag> basetag = plugins[i];
 
     nsCOMPtr<nsIFakePluginTag> faketag = do_QueryInterface(basetag);
     if (faketag) {
       /// FIXME-jsplugins - We need to add a nsIInternalPluginTag->AsNative() to
       /// avoid this hacky static cast
       nsFakePluginTag* tag = static_cast<nsFakePluginTag*>(basetag.get());
       mozilla::ipc::URIParams handlerURI;
       SerializeURI(tag->HandlerURI(), handlerURI);
-      fakePluginTags.AppendElement(FakePluginTag(tag->Id(),
-                                                 handlerURI,
-                                                 tag->Name(),
-                                                 tag->Description(),
-                                                 tag->MimeTypes(),
-                                                 tag->MimeDescriptions(),
-                                                 tag->Extensions(),
-                                                 tag->GetNiceFileName(),
-                                                 tag->SandboxScript()));
+      aFakePlugins->AppendElement(FakePluginTag(tag->Id(),
+                                                handlerURI,
+                                                tag->Name(),
+                                                tag->Description(),
+                                                tag->MimeTypes(),
+                                                tag->MimeDescriptions(),
+                                                tag->Extensions(),
+                                                tag->GetNiceFileName(),
+                                                tag->SandboxScript()));
       continue;
     }
 
     /// FIXME-jsplugins - We need to cleanup the various plugintag classes
     /// to be more sane and avoid this dance
     nsPluginTag *tag = static_cast<nsPluginTag *>(basetag.get());
 
-    pluginTags.AppendElement(PluginTag(tag->mId,
-                                       tag->Name(),
-                                       tag->Description(),
-                                       tag->MimeTypes(),
-                                       tag->MimeDescriptions(),
-                                       tag->Extensions(),
-                                       tag->mIsJavaPlugin,
-                                       tag->mIsFlashPlugin,
-                                       tag->mSupportsAsyncInit,
-                                       tag->mSupportsAsyncRender,
-                                       tag->FileName(),
-                                       tag->Version(),
-                                       tag->mLastModifiedTime,
-                                       tag->IsFromExtension(),
-                                       tag->mSandboxLevel));
-  }
-  nsTArray<dom::ContentParent*> parents;
-  dom::ContentParent::GetAll(parents);
-  for (auto p : parents)
-  {
-    Unused << p->SendSetPluginList(newPluginEpoch, pluginTags, fakePluginTags);
+    aPlugins->AppendElement(PluginTag(tag->mId,
+                                      tag->Name(),
+                                      tag->Description(),
+                                      tag->MimeTypes(),
+                                      tag->MimeDescriptions(),
+                                      tag->Extensions(),
+                                      tag->mIsJavaPlugin,
+                                      tag->mIsFlashPlugin,
+                                      tag->mSupportsAsyncInit,
+                                      tag->mSupportsAsyncRender,
+                                      tag->FileName(),
+                                      tag->Version(),
+                                      tag->mLastModifiedTime,
+                                      tag->IsFromExtension(),
+                                      tag->mSandboxLevel));
   }
   return NS_OK;
 }
 
 void
 nsPluginHost::UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag)
 {
   NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -247,20 +247,16 @@ public:
 
   void CreateWidget(nsPluginInstanceOwner* aOwner);
 
   nsresult EnumerateSiteData(const nsACString& domain,
                              const InfallibleTArray<nsCString>& sites,
                              InfallibleTArray<nsCString>& result,
                              bool firstMatchOnly);
 
-  nsresult SendPluginsToContent();
-  nsresult SetPluginsInContent(uint32_t aPluginEpoch,
-                               nsTArray<mozilla::plugins::PluginTag>& aPlugins,
-                               nsTArray<mozilla::plugins::FakePluginTag>& aFakePlugins);
 private:
   friend class nsPluginUnloadRunnable;
 
   void DestroyRunningInstances(nsPluginTag* aPluginTag);
 
   // Writes updated plugins settings to disk and unloads the plugin
   // if it is now disabled. Should only be called by the plugin tag in question
   void UpdatePluginInfo(nsPluginTag* aPluginTag);
@@ -300,16 +296,18 @@ private:
   // be filled in with the MIME type the plugin is registered for.
   nsPluginTag* FindNativePluginForExtension(const nsACString & aExtension,
                                             /* out */ nsACString & aMimeType,
                                             bool aCheckEnabled);
 
   nsresult
   FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
 
+  nsresult FindPluginsInContent(bool aCreatePluginList, bool * aPluginsChanged);
+
   nsresult
   FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
 
   // FIXME revisit, no ns prefix
   // Registers or unregisters the given mime type with the category manager
   enum nsRegisterType { ePluginRegister,
                         ePluginUnregister,
                         // Checks if this type should still be registered first
@@ -365,18 +363,16 @@ private:
 
   // To be used by the content process to get/set the last observed epoch value
   // from the chrome process.
   uint32_t ChromeEpochForContent();
   void SetChromeEpochForContent(uint32_t aEpoch);
 
   void UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag);
 
-  nsresult ActuallyReloadPlugins();
-
   RefPtr<nsPluginTag> mPlugins;
   RefPtr<nsPluginTag> mCachedPlugins;
   RefPtr<nsInvalidPluginTag> mInvalidPlugins;
 
   nsTArray< RefPtr<nsFakePluginTag> > mFakePlugins;
 
   bool mPluginsLoaded;
 
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -687,17 +687,17 @@ private:
 class TreeAutoIndent
 {
 public:
   explicit TreeAutoIndent(TreeLog& aTreeLog) : mTreeLog(aTreeLog) {
     mTreeLog.IncreaseIndent();
   }
 
   TreeAutoIndent(const TreeAutoIndent& aTreeAutoIndent) :
-      TreeAutoIndent(aTreeAutoIndent.mTreeLog) {
+      mTreeLog(aTreeAutoIndent.mTreeLog) {
     mTreeLog.IncreaseIndent();
   }
 
   TreeAutoIndent& operator=(const TreeAutoIndent& aTreeAutoIndent) = delete;
 
   ~TreeAutoIndent() {
     mTreeLog.DecreaseIndent();
   }
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -860,16 +860,18 @@ description =
 [PContent::BridgeToChildProcess]
 description =
 [PContent::LoadPlugin]
 description =
 [PContent::ConnectPluginBridge]
 description =
 [PContent::GetBlocklistState]
 description =
+[PContent::FindPlugins]
+description =
 [PContent::NSSU2FTokenIsCompatibleVersion]
 description =
 [PContent::NSSU2FTokenIsRegistered]
 description =
 [PContent::NSSU2FTokenRegister]
 description =
 [PContent::NSSU2FTokenSign]
 description =
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -784,17 +784,17 @@ ErrorReport::ErrorReport(JSContext* cx)
 {
 }
 
 ErrorReport::~ErrorReport()
 {
 }
 
 void
-ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx)
+ErrorReport::ReportAddonExceptionToTelemetry(JSContext* cx)
 {
     MOZ_ASSERT(exnObject);
     RootedObject unwrapped(cx, UncheckedUnwrap(exnObject));
     MOZ_ASSERT(unwrapped, "UncheckedUnwrap failed?");
 
     // There is not much we can report if the exception is not an ErrorObject, let's ignore those.
     if (!unwrapped->is<ErrorObject>())
         return;
@@ -864,18 +864,18 @@ ErrorReport::init(JSContext* cx, HandleV
 
         if (!reportp && sniffingBehavior == NoSideEffects) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                       JSMSG_ERR_DURING_THROW);
             return false;
         }
 
         // Let's see if the exception is from add-on code, if so, it should be reported
-        // to telementry.
-        ReportAddonExceptionToTelementry(cx);
+        // to telemetry.
+        ReportAddonExceptionToTelemetry(cx);
     }
 
 
     // Be careful not to invoke ToString if we've already successfully extracted
     // an error report, since the exception might be wrapped in a security
     // wrapper, and ToString-ing it might throw.
     if (reportp) {
         str = ErrorReportToString(cx, reportp);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1477,18 +1477,18 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(Err
     // but fills in an ErrorReport instead of reporting it.  Uses varargs to
     // make it simpler to call js::ExpandErrorArgumentsVA.
     //
     // Returns false if we fail to actually populate the ErrorReport
     // for some reason (probably out of memory).
     bool populateUncaughtExceptionReportUTF8(JSContext* cx, ...);
     bool populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap);
 
-    // Reports exceptions from add-on scopes to telementry.
-    void ReportAddonExceptionToTelementry(JSContext* cx);
+    // Reports exceptions from add-on scopes to telemetry.
+    void ReportAddonExceptionToTelemetry(JSContext* cx);
 
     // We may have a provided JSErrorReport, so need a way to represent that.
     JSErrorReport* reportp;
 
     // Or we may need to synthesize a JSErrorReport one of our own.
     JSErrorReport ownedReport;
 
     // And we have a string to maybe keep alive that has pointers into
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -2646,20 +2646,17 @@ Loader::StartAlternateLoads()
 NS_IMPL_CYCLE_COLLECTION_CLASS(Loader)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader)
   if (tmp->mSheets) {
     for (auto iter = tmp->mSheets->mCompleteSheets.Iter();
          !iter.Done();
          iter.Next()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "Sheet cache nsCSSLoader");
-      if (iter.UserData()->IsGecko()) {
-        CSSStyleSheet* sheet = iter.UserData()->AsGecko();
-        cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, sheet));
-      }
+      cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, iter.UserData()));
     }
   }
   nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>>::ForwardIterator
     it(tmp->mObservers);
   while (it.HasMore()) {
     ImplCycleCollectionTraverse(cb, it.GetNext(),
                                 "mozilla::css::Loader.mObservers");
   }
--- a/media/ffvpx/ffvpxcommon.mozbuild
+++ b/media/ffvpx/ffvpxcommon.mozbuild
@@ -71,16 +71,18 @@ elif CONFIG['_MSC_VER']:
         '-wd4133', # 'function' : incompatible types - from 'AVSampleFormat *' to 'int *'
         '-wd4221', # nonstandard extension used
         '-wd4206', # nonstandard extension used
         '-wd4702', # unreachable code
         '-wd4101', # unreferenced local variable
         '-wd4245', # conversion from 'int' to 'uint32_t', signed/unsigned mismatch
         '-wd4703', # potentially uninitialized local pointer
         '-wd4293', # '<<' : shift count negative or too big, undefined behavior
+        '-wd4334', # '<<' : result of 32-bit shift implicitly converted to 64 bits
+        '-wd4996', # The compiler encountered a deprecated declaration.
         # from FFmpeg configure
         '-wd4244', '-wd4127', '-wd4018', '-wd4389', '-wd4146', '-wd4701',
         '-wd4057', '-wd4204', '-wd4706', '-wd4305', '-wd4152', '-wd4324',
         '-we4013', '-wd4100', '-wd4214', '-wd4307', '-wd4273', '-wd4554',
     ]
 
 DEFINES['HAVE_AV_CONFIG_H'] = True
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2253,31 +2253,16 @@ var NativeWindow = {
   init: function() {
     GlobalEventDispatcher.registerListener(this, [
       "Doorhanger:Reply",
       "Menu:Clicked",
     ]);
     this.contextmenus.init();
   },
 
-  loadDex: function(zipFile, implClass) {
-    GlobalEventDispatcher.sendRequest({
-      type: "Dex:Load",
-      zipfile: zipFile,
-      impl: implClass || "Main"
-    });
-  },
-
-  unloadDex: function(zipFile) {
-    GlobalEventDispatcher.sendRequest({
-      type: "Dex:Unload",
-      zipfile: zipFile
-    });
-  },
-
   menu: {
     _callbacks: [],
     _menuId: 1,
     toolsMenuID: -1,
     add: function() {
       let options;
       if (arguments.length == 1) {
         options = arguments[0];
--- a/security/.eslintrc.js
+++ b/security/.eslintrc.js
@@ -46,19 +46,16 @@ module.exports = {
     "no-console": "error",
 
     // Disallow modifying variables that are declared using const.
     "no-const-assign": "error",
 
     // Disallow constant expressions in conditions (except for loops).
     "no-constant-condition": ["error", { "checkLoops": false }],
 
-    // Disallow control characters in regular expressions.
-    "no-control-regex": "error",
-
     // Disallow duplicate class members.
     "no-dupe-class-members": "error",
 
     // Disallow extending of native objects.
     "no-extend-native": "error",
 
     // Disallow case statement fallthrough without explicit `// falls through`
     // annotation.
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/customelementregistry.rs
@@ -0,0 +1,432 @@
+/* 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 dom::bindings::callback::CallbackContainer;
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::CustomElementRegistryBinding;
+use dom::bindings::codegen::Bindings::CustomElementRegistryBinding::CustomElementRegistryMethods;
+use dom::bindings::codegen::Bindings::CustomElementRegistryBinding::ElementDefinitionOptions;
+use dom::bindings::codegen::Bindings::FunctionBinding::Function;
+use dom::bindings::error::{Error, ErrorResult};
+use dom::bindings::inheritance::Castable;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
+use dom::bindings::str::DOMString;
+use dom::domexception::{DOMErrorName, DOMException};
+use dom::globalscope::GlobalScope;
+use dom::promise::Promise;
+use dom::window::Window;
+use dom_struct::dom_struct;
+use js::conversions::ToJSValConvertible;
+use js::jsapi::{IsConstructor, HandleObject, JS_GetProperty, JSAutoCompartment, JSContext};
+use js::jsval::{JSVal, UndefinedValue};
+use std::cell::Cell;
+use std::collections::HashMap;
+use std::rc::Rc;
+
+// https://html.spec.whatwg.org/multipage/#customelementregistry
+#[dom_struct]
+pub struct CustomElementRegistry {
+    reflector_: Reflector,
+
+    window: JS<Window>,
+
+    #[ignore_heap_size_of = "Rc"]
+    when_defined: DOMRefCell<HashMap<DOMString, Rc<Promise>>>,
+
+    element_definition_is_running: Cell<bool>,
+
+    definitions: DOMRefCell<HashMap<DOMString, CustomElementDefinition>>,
+}
+
+impl CustomElementRegistry {
+    fn new_inherited(window: &Window) -> CustomElementRegistry {
+        CustomElementRegistry {
+            reflector_: Reflector::new(),
+            window: JS::from_ref(window),
+            when_defined: DOMRefCell::new(HashMap::new()),
+            element_definition_is_running: Cell::new(false),
+            definitions: DOMRefCell::new(HashMap::new()),
+        }
+    }
+
+    pub fn new(window: &Window) -> Root<CustomElementRegistry> {
+        reflect_dom_object(box CustomElementRegistry::new_inherited(window),
+                           window,
+                           CustomElementRegistryBinding::Wrap)
+    }
+
+    // Cleans up any active promises
+    // https://github.com/servo/servo/issues/15318
+    pub fn teardown(&self) {
+        self.when_defined.borrow_mut().clear()
+    }
+
+    // https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define
+    // Steps 10.1, 10.2
+    #[allow(unsafe_code)]
+    fn check_prototype(&self, constructor: HandleObject) -> ErrorResult {
+        let global_scope = self.window.upcast::<GlobalScope>();
+        rooted!(in(global_scope.get_cx()) let mut prototype = UndefinedValue());
+        unsafe {
+            // Step 10.1
+            if !JS_GetProperty(global_scope.get_cx(),
+                               constructor,
+                               b"prototype\0".as_ptr() as *const _,
+                               prototype.handle_mut()) {
+                return Err(Error::JSFailed);
+            }
+
+            // Step 10.2
+            if !prototype.is_object() {
+                return Err(Error::Type("constructor.prototype is not an object".to_owned()));
+            }
+        }
+        Ok(())
+    }
+}
+
+impl CustomElementRegistryMethods for CustomElementRegistry {
+    #[allow(unsafe_code, unrooted_must_root)]
+    // https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define
+    fn Define(&self, name: DOMString, constructor_: Rc<Function>, options: &ElementDefinitionOptions) -> ErrorResult {
+        let global_scope = self.window.upcast::<GlobalScope>();
+        rooted!(in(global_scope.get_cx()) let constructor = constructor_.callback());
+
+        // Step 1
+        if unsafe { !IsConstructor(constructor.get()) } {
+            return Err(Error::Type("Second argument of CustomElementRegistry.define is not a constructor".to_owned()));
+        }
+
+        // Step 2
+        if !is_valid_custom_element_name(&name) {
+            return Err(Error::Syntax)
+        }
+
+        // Step 3
+        if self.definitions.borrow().contains_key(&name) {
+            return Err(Error::NotSupported);
+        }
+
+        // Step 4
+        if self.definitions.borrow().iter().any(|(_, ref def)| def.constructor == constructor_) {
+            return Err(Error::NotSupported);
+        }
+
+        // Step 6
+        let extends = &options.extends;
+
+        // Steps 5, 7
+        let local_name = if let Some(ref extended_name) = *extends {
+            // Step 7.1
+            if is_valid_custom_element_name(extended_name) {
+                return Err(Error::NotSupported)
+            }
+
+            // Step 7.2
+            if !is_known_element_interface(extended_name) {
+                return Err(Error::NotSupported)
+            }
+
+            extended_name.clone()
+        } else {
+            // Step 7.3
+            name.clone()
+        };
+
+        // Step 8
+        if self.element_definition_is_running.get() {
+            return Err(Error::NotSupported);
+        }
+
+        // Step 9
+        self.element_definition_is_running.set(true);
+
+        // Steps 10.1 - 10.2
+        let result = {
+            let _ac = JSAutoCompartment::new(global_scope.get_cx(), constructor.get());
+            self.check_prototype(constructor.handle())
+        };
+
+        // TODO: Steps 10.3 - 10.6
+        // 10.3 - 10.4 Handle lifecycle callbacks
+        // 10.5 - 10.6 Get observed attributes from the constructor
+
+        self.element_definition_is_running.set(false);
+        result?;
+
+        // Step 11
+        let definition = CustomElementDefinition::new(name.clone(), local_name, constructor_);
+
+        // Step 12
+        self.definitions.borrow_mut().insert(name.clone(), definition);
+
+        // TODO: Step 13, 14, 15
+        // Handle custom element upgrades
+
+        // Step 16, 16.3
+        if let Some(promise) = self.when_defined.borrow_mut().remove(&name) {
+            // 16.1
+            let cx = promise.global().get_cx();
+            // 16.2
+            promise.resolve_native(cx, &UndefinedValue());
+        }
+        Ok(())
+    }
+
+    // https://html.spec.whatwg.org/multipage/#dom-customelementregistry-get
+    #[allow(unsafe_code)]
+    unsafe fn Get(&self, cx: *mut JSContext, name: DOMString) -> JSVal {
+        match self.definitions.borrow().get(&name) {
+            Some(definition) => {
+                rooted!(in(cx) let mut constructor = UndefinedValue());
+                definition.constructor.to_jsval(cx, constructor.handle_mut());
+                constructor.get()
+            },
+            None => UndefinedValue(),
+        }
+    }
+
+    // https://html.spec.whatwg.org/multipage/#dom-customelementregistry-whendefined
+    #[allow(unrooted_must_root)]
+    fn WhenDefined(&self, name: DOMString) -> Rc<Promise> {
+        let global_scope = self.window.upcast::<GlobalScope>();
+
+        // Step 1
+        if !is_valid_custom_element_name(&name) {
+            let promise = Promise::new(global_scope);
+            promise.reject_native(global_scope.get_cx(), &DOMException::new(global_scope, DOMErrorName::SyntaxError));
+            return promise
+        }
+
+        // Step 2
+        if self.definitions.borrow().contains_key(&name) {
+            let promise = Promise::new(global_scope);
+            promise.resolve_native(global_scope.get_cx(), &UndefinedValue());
+            return promise
+        }
+
+        // Step 3
+        let mut map = self.when_defined.borrow_mut();
+
+        // Steps 4, 5
+        let promise = map.get(&name).cloned().unwrap_or_else(|| {
+            let promise = Promise::new(global_scope);
+            map.insert(name, promise.clone());
+            promise
+        });
+
+        // Step 6
+        promise
+    }
+}
+
+#[derive(HeapSizeOf, JSTraceable)]
+struct CustomElementDefinition {
+    name: DOMString,
+
+    local_name: DOMString,
+
+    #[ignore_heap_size_of = "Rc"]
+    constructor: Rc<Function>,
+}
+
+impl CustomElementDefinition {
+    fn new(name: DOMString, local_name: DOMString, constructor: Rc<Function>) -> CustomElementDefinition {
+        CustomElementDefinition {
+            name: name,
+            local_name: local_name,
+            constructor: constructor,
+        }
+    }
+}
+
+// https://html.spec.whatwg.org/multipage/#valid-custom-element-name
+fn is_valid_custom_element_name(name: &str) -> bool {
+    // Custom elment names must match:
+    // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
+
+    let mut chars = name.chars();
+    if !chars.next().map_or(false, |c| c >= 'a' && c <= 'z') {
+        return false;
+    }
+
+    let mut has_dash = false;
+
+    for c in chars {
+        if c == '-' {
+            has_dash = true;
+            continue;
+        }
+
+        if !is_potential_custom_element_char(c) {
+            return false;
+        }
+    }
+
+    if !has_dash {
+        return false;
+    }
+
+    if name == "annotation-xml" ||
+        name == "color-profile" ||
+        name == "font-face" ||
+        name == "font-face-src" ||
+        name == "font-face-uri" ||
+        name == "font-face-format" ||
+        name == "font-face-name" ||
+        name == "missing-glyph"
+    {
+        return false;
+    }
+
+    true
+}
+
+// Check if this character is a PCENChar
+// https://html.spec.whatwg.org/multipage/#prod-pcenchar
+fn is_potential_custom_element_char(c: char) -> bool {
+    c == '-' || c == '.' || c == '_' || c == '\u{B7}' ||
+    (c >= '0' && c <= '9') ||
+    (c >= 'a' && c <= 'z') ||
+    (c >= '\u{C0}' && c <= '\u{D6}') ||
+    (c >= '\u{D8}' && c <= '\u{F6}') ||
+    (c >= '\u{F8}' && c <= '\u{37D}') ||
+    (c >= '\u{37F}' && c <= '\u{1FFF}') ||
+    (c >= '\u{200C}' && c <= '\u{200D}') ||
+    (c >= '\u{203F}' && c <= '\u{2040}') ||
+    (c >= '\u{2070}' && c <= '\u{2FEF}') ||
+    (c >= '\u{3001}' && c <= '\u{D7FF}') ||
+    (c >= '\u{F900}' && c <= '\u{FDCF}') ||
+    (c >= '\u{FDF0}' && c <= '\u{FFFD}') ||
+    (c >= '\u{10000}' && c <= '\u{EFFFF}')
+}
+
+fn is_known_element_interface(element: &str) -> bool {
+    element == "a" ||
+    element == "abbr" ||
+    element == "acronym" ||
+    element == "address" ||
+    element == "applet" ||
+    element == "area" ||
+    element == "article" ||
+    element == "aside" ||
+    element == "audio" ||
+    element == "b" ||
+    element == "base" ||
+    element == "bdi" ||
+    element == "bdo" ||
+    element == "big" ||
+    element == "blockquote" ||
+    element == "body" ||
+    element == "br" ||
+    element == "button" ||
+    element == "canvas" ||
+    element == "caption" ||
+    element == "center" ||
+    element == "cite" ||
+    element == "code" ||
+    element == "col" ||
+    element == "colgroup" ||
+    element == "data" ||
+    element == "datalist" ||
+    element == "dd" ||
+    element == "del" ||
+    element == "details" ||
+    element == "dfn" ||
+    element == "dialog" ||
+    element == "dir" ||
+    element == "div" ||
+    element == "dl" ||
+    element == "dt" ||
+    element == "em" ||
+    element == "embed" ||
+    element == "fieldset" ||
+    element == "figcaption" ||
+    element == "figure" ||
+    element == "font" ||
+    element == "footer" ||
+    element == "form" ||
+    element == "frame" ||
+    element == "frameset" ||
+    element == "h1" ||
+    element == "h2" ||
+    element == "h3" ||
+    element == "h4" ||
+    element == "h5" ||
+    element == "h6" ||
+    element == "head" ||
+    element == "header" ||
+    element == "hgroup" ||
+    element == "hr" ||
+    element == "html" ||
+    element == "i" ||
+    element == "iframe" ||
+    element == "img" ||
+    element == "input" ||
+    element == "ins" ||
+    element == "kbd" ||
+    element == "label" ||
+    element == "legend" ||
+    element == "li" ||
+    element == "link" ||
+    element == "listing" ||
+    element == "main" ||
+    element == "map" ||
+    element == "mark" ||
+    element == "marquee" ||
+    element == "meta" ||
+    element == "meter" ||
+    element == "nav" ||
+    element == "nobr" ||
+    element == "noframes" ||
+    element == "noscript" ||
+    element == "object" ||
+    element == "ol" ||
+    element == "optgroup" ||
+    element == "option" ||
+    element == "output" ||
+    element == "p" ||
+    element == "param" ||
+    element == "plaintext" ||
+    element == "pre" ||
+    element == "progress" ||
+    element == "q" ||
+    element == "rp" ||
+    element == "rt" ||
+    element == "ruby" ||
+    element == "s" ||
+    element == "samp" ||
+    element == "script" ||
+    element == "section" ||
+    element == "select" ||
+    element == "small" ||
+    element == "source" ||
+    element == "span" ||
+    element == "strike" ||
+    element == "strong" ||
+    element == "style" ||
+    element == "sub" ||
+    element == "summary" ||
+    element == "sup" ||
+    element == "table" ||
+    element == "tbody" ||
+    element == "td" ||
+    element == "template" ||
+    element == "textarea" ||
+    element == "tfoot" ||
+    element == "th" ||
+    element == "thead" ||
+    element == "time" ||
+    element == "title" ||
+    element == "tr" ||
+    element == "tt" ||
+    element == "track" ||
+    element == "u" ||
+    element == "ul" ||
+    element == "var" ||
+    element == "video" ||
+    element == "wbr" ||
+    element == "xmp"
+}
--- a/servo/components/script/dom/mod.rs
+++ b/servo/components/script/dom/mod.rs
@@ -250,16 +250,17 @@ pub mod cssmediarule;
 pub mod cssnamespacerule;
 pub mod cssrule;
 pub mod cssrulelist;
 pub mod cssstyledeclaration;
 pub mod cssstylerule;
 pub mod cssstylesheet;
 pub mod csssupportsrule;
 pub mod cssviewportrule;
+pub mod customelementregistry;
 pub mod customevent;
 pub mod dedicatedworkerglobalscope;
 pub mod dissimilaroriginlocation;
 pub mod dissimilaroriginwindow;
 pub mod document;
 pub mod documentfragment;
 pub mod documenttype;
 pub mod domexception;
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/webidls/CustomElementRegistry.webidl
@@ -0,0 +1,18 @@
+/* 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/. */
+
+// https://html.spec.whatwg.org/multipage/#customelementregistry
+[Pref="dom.customelements.enabled"]
+interface CustomElementRegistry {
+  [Throws/*, CEReactions */]
+  void define(DOMString name, Function constructor_, optional ElementDefinitionOptions options);
+
+  any get(DOMString name);
+
+  Promise<void> whenDefined(DOMString name);
+};
+
+dictionary ElementDefinitionOptions {
+  DOMString extends;
+};
--- a/servo/components/script/dom/webidls/Window.webidl
+++ b/servo/components/script/dom/webidls/Window.webidl
@@ -10,16 +10,18 @@
   [BinaryName="Self_", Replaceable] readonly attribute WindowProxy self;
   [Unforgeable] readonly attribute Document document;
 
   // https://github.com/servo/servo/issues/14453
   // attribute DOMString name;
 
   [/*PutForwards=href, */Unforgeable] readonly attribute Location location;
   readonly attribute History history;
+  [Pref="dom.customelements.enabled"]
+  readonly attribute CustomElementRegistry customElements;
   //[Replaceable] readonly attribute BarProp locationbar;
   //[Replaceable] readonly attribute BarProp menubar;
   //[Replaceable] readonly attribute BarProp personalbar;
   //[Replaceable] readonly attribute BarProp scrollbars;
   //[Replaceable] readonly attribute BarProp statusbar;
   //[Replaceable] readonly attribute BarProp toolbar;
   attribute DOMString status;
   void close();
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -27,16 +27,17 @@ use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::DomObject;
 use dom::bindings::str::DOMString;
 use dom::bindings::structuredclone::StructuredCloneData;
 use dom::bindings::trace::RootedTraceableBox;
 use dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
 use dom::bluetooth::BluetoothExtraPermissionData;
 use dom::crypto::Crypto;
 use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
+use dom::customelementregistry::CustomElementRegistry;
 use dom::document::{AnimationFrameCallback, Document};
 use dom::element::Element;
 use dom::event::Event;
 use dom::globalscope::GlobalScope;
 use dom::history::History;
 use dom::htmliframeelement::build_mozbrowser_custom_event;
 use dom::location::Location;
 use dom::mediaquerylist::{MediaQueryList, WeakMediaQueryListVec};
@@ -174,16 +175,17 @@ pub struct Window {
     navigator: MutNullableJS<Navigator>,
     #[ignore_heap_size_of = "Arc"]
     image_cache: Arc<ImageCache>,
     #[ignore_heap_size_of = "channels are hard"]
     image_cache_chan: Sender<ImageCacheMsg>,
     window_proxy: MutNullableJS<WindowProxy>,
     document: MutNullableJS<Document>,
     history: MutNullableJS<History>,
+    custom_element_registry: MutNullableJS<CustomElementRegistry>,
     performance: MutNullableJS<Performance>,
     navigation_start: u64,
     navigation_start_precise: f64,
     screen: MutNullableJS<Screen>,
     session_storage: MutNullableJS<Storage>,
     local_storage: MutNullableJS<Storage>,
     status: DOMRefCell<DOMString>,
 
@@ -528,16 +530,21 @@ impl WindowMethods for Window {
         self.document.get().expect("Document accessed before initialization.")
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-history
     fn History(&self) -> Root<History> {
         self.history.or_init(|| History::new(self))
     }
 
+    // https://html.spec.whatwg.org/multipage/#dom-window-customelements
+    fn CustomElements(&self) -> Root<CustomElementRegistry> {
+        self.custom_element_registry.or_init(|| CustomElementRegistry::new(self))
+    }
+
     // https://html.spec.whatwg.org/multipage/#dom-location
     fn Location(&self) -> Root<Location> {
         self.Document().GetLocation().unwrap()
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-sessionstorage
     fn SessionStorage(&self) -> Root<Storage> {
         self.session_storage.or_init(|| Storage::new(self, StorageType::Session))
@@ -1026,16 +1033,22 @@ impl Window {
     }
 
     pub fn clear_js_runtime(&self) {
         // We tear down the active document, which causes all the attached
         // nodes to dispose of their layout data. This messages the layout
         // thread, informing it that it can safely free the memory.
         self.Document().upcast::<Node>().teardown();
 
+        // Clean up any active promises
+        // https://github.com/servo/servo/issues/15318
+        if let Some(custom_elements) = self.custom_element_registry.get() {
+            custom_elements.teardown();
+        }
+
         // The above code may not catch all DOM objects (e.g. DOM
         // objects removed from the tree that haven't been collected
         // yet). There should not be any such DOM nodes with layout
         // data, but if there are, then when they are dropped, they
         // will attempt to send a message to the closed layout thread.
         // This causes memory safety issues, because the DOM node uses
         // the layout channel from its window, and the window has
         // already been GC'd.  For nodes which do not have a live
@@ -1800,16 +1813,17 @@ impl Window {
             user_interaction_task_source: user_task_source,
             networking_task_source: network_task_source,
             history_traversal_task_source: history_task_source,
             file_reading_task_source: file_task_source,
             image_cache_chan: image_cache_chan,
             image_cache: image_cache.clone(),
             navigator: Default::default(),
             history: Default::default(),
+            custom_element_registry: Default::default(),
             window_proxy: Default::default(),
             document: Default::default(),
             performance: Default::default(),
             navigation_start: (current_time.sec * 1000 + current_time.nsec as i64 / 1000000) as u64,
             navigation_start_precise: time::precise_time_ns() as f64,
             screen: Default::default(),
             session_storage: Default::default(),
             local_storage: Default::default(),
--- a/servo/python/mach_bootstrap.py
+++ b/servo/python/mach_bootstrap.py
@@ -9,21 +9,16 @@ import platform
 import sys
 from distutils.spawn import find_executable
 from subprocess import PIPE, Popen
 
 SEARCH_PATHS = [
     os.path.join("python", "tidy"),
 ]
 
-WPT_SEARCH_PATHS = [
-    ".",
-    "harness",
-]
-
 # Individual files providing mach commands.
 MACH_MODULES = [
     os.path.join('python', 'servo', 'bootstrap_commands.py'),
     os.path.join('python', 'servo', 'build_commands.py'),
     os.path.join('python', 'servo', 'testing_commands.py'),
     os.path.join('python', 'servo', 'post_build_commands.py'),
     os.path.join('python', 'servo', 'package_commands.py'),
     os.path.join('python', 'servo', 'devenv_commands.py'),
@@ -101,25 +96,35 @@ def _get_exec_path(names, is_valid_path=
 
 def _get_virtualenv_script_dir():
     # Virtualenv calls its scripts folder "bin" on linux/OSX/MSYS64 but "Scripts" on Windows
     if os.name == "nt" and os.sep != "/":
         return "Scripts"
     return "bin"
 
 
-def wpt_path(topdir, paths, is_firefox):
+def wpt_path(is_firefox, topdir, *paths):
     if is_firefox:
         rel = os.path.join("..", "testing", "web-platform")
     else:
         rel = os.path.join("tests", "wpt")
 
     return os.path.join(topdir, rel, *paths)
 
 
+def wpt_harness_path(is_firefox, topdir, *paths):
+    wpt_root = wpt_path(is_firefox, topdir)
+    if is_firefox:
+        rel = os.path.join(wpt_root, "tests", "tools", "wptrunner")
+    else:
+        rel = os.path.join(wpt_root, "harness")
+
+    return os.path.join(topdir, rel, *paths)
+
+
 def _activate_virtualenv(topdir, is_firefox):
     virtualenv_path = os.path.join(topdir, "python", "_virtualenv")
     check_exec_path = lambda path: path.startswith(virtualenv_path)
     python = _get_exec_path(PYTHON_NAMES)   # If there was no python, mach wouldn't have run at all!
     if not python:
         sys.exit('Failed to find python executable for starting virtualenv.')
 
     script_dir = _get_virtualenv_script_dir()
@@ -152,19 +157,19 @@ def _activate_virtualenv(topdir, is_fire
     # TODO: Right now, we iteratively install all the requirements by invoking
     # `pip install` each time. If it were the case that there were conflicting
     # requirements, we wouldn't know about them. Once
     # https://github.com/pypa/pip/issues/988 is addressed, then we can just
     # chain each of the requirements files into the same `pip install` call
     # and it will check for conflicts.
     requirements_paths = [
         os.path.join("python", "requirements.txt"),
-        wpt_path(topdir, ("harness", "requirements.txt"), is_firefox),
-        wpt_path(topdir, ("harness", "requirements_firefox.txt"), is_firefox),
-        wpt_path(topdir, ("harness", "requirements_servo.txt"), is_firefox),
+        wpt_harness_path(is_firefox, topdir, "requirements.txt",),
+        wpt_harness_path(is_firefox, topdir, "requirements_firefox.txt"),
+        wpt_harness_path(is_firefox, topdir, "requirements_servo.txt"),
     ]
 
     if need_pip_upgrade:
         # Upgrade pip when virtualenv is created to fix the issue
         # https://github.com/servo/servo/issues/11074
         pip = _get_exec_path(PIP_NAMES, is_valid_path=check_exec_path)
         if not pip:
             sys.exit("Python pip is either not installed or not found in virtualenv.")
@@ -252,18 +257,20 @@ def bootstrap(topdir):
     def populate_context(context, key=None):
         if key is None:
             return
         if key == 'topdir':
             return topdir
         raise AttributeError(key)
 
     sys.path[0:0] = [os.path.join(topdir, path) for path in SEARCH_PATHS]
-    sys.path[0:0] = [wpt_path(topdir, (path,), is_firefox)
-                     for path in WPT_SEARCH_PATHS]
+
+    sys.path[0:0] = [wpt_path(is_firefox, topdir),
+                     wpt_harness_path(is_firefox, topdir)]
+
     import mach.main
     mach = mach.main.Mach(os.getcwd())
     mach.populate_context_handler = populate_context
 
     for category, meta in CATEGORIES.items():
         mach.define_category(category, meta['short'], meta['long'], meta['priority'])
 
     for path in MACH_MODULES:
--- a/servo/resources/prefs.json
+++ b/servo/resources/prefs.json
@@ -1,11 +1,12 @@
 {
   "dom.bluetooth.enabled": false,
   "dom.bluetooth.testing.enabled": false,
+  "dom.customelements.enabled": false,
   "dom.forcetouch.enabled": false,
   "dom.gamepad.enabled": false,
   "dom.mouseevent.which.enabled": false,
   "dom.mozbrowser.enabled": false,
   "dom.mutation_observer.enabled": false,
   "dom.permissions.enabled": false,
   "dom.permissions.testing.allowed_in_nonsecure_contexts": false,
   "dom.serviceworker.timeout_seconds": 60,
--- a/testing/mozbase/mozinfo/mozinfo/mozinfo.py
+++ b/testing/mozbase/mozinfo/mozinfo/mozinfo.py
@@ -82,17 +82,17 @@ if system in ["Microsoft", "Windows"]:
         # On windows >= 8.1 the system call that getwindowsversion uses has
         # been frozen to always return the same values. In this case we call
         # the RtlGetVersion API directly, which still provides meaningful
         # values, at least for now.
         major, minor, build_number = get_windows_version()
         version = "%d.%d.%d" % (major, minor, build_number)
 
     os_version = "%d.%d" % (major, minor)
-elif system.startswith('MINGW'):
+elif system.startswith(('MINGW', 'MSYS_NT')):
     # windows/mingw python build (msys)
     info['os'] = 'win'
     os_version = version = unknown
 elif system == "Linux":
     if hasattr(platform, "linux_distribution"):
         (distro, os_version, codename) = platform.linux_distribution()
     else:
         (distro, os_version, codename) = platform.dist()
--- a/testing/mozbase/mozinfo/setup.py
+++ b/testing/mozbase/mozinfo/setup.py
@@ -1,15 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from setuptools import setup
 
-PACKAGE_VERSION = '0.9'
+PACKAGE_VERSION = '0.10'
 
 # dependencies
 deps = ['mozfile >= 0.12']
 
 setup(name='mozinfo',
       version=PACKAGE_VERSION,
       description="Library to get system information for use in Mozilla testing",
       long_description="see http://mozbase.readthedocs.org",
--- a/toolkit/components/extensions/.eslintrc.js
+++ b/toolkit/components/extensions/.eslintrc.js
@@ -198,19 +198,16 @@ module.exports = {
     "no-console": "error",
 
     // Allow using constant expressions in conditions like while (true)
     "no-constant-condition": "off",
 
     // Allow use of the continue statement.
     "no-continue": "off",
 
-    // Disallow control characters in regular expressions.
-    "no-control-regex": "error",
-
     // Allow division operators explicitly at beginning of regular expression.
     "no-div-regex": "off",
 
     // Disallow adding to native types
     "no-extend-native": "error",
 
     // Allow unnecessary parentheses, as they may make the code more readable.
     "no-extra-parens": "off",
--- a/toolkit/components/narrate/.eslintrc.js
+++ b/toolkit/components/narrate/.eslintrc.js
@@ -18,17 +18,16 @@ module.exports = {
     "dot-location": ["warn", "property"],
     "dot-notation": "error",
     "generator-star-spacing": ["warn", "after"],
     "indent": ["warn", 2, {"SwitchCase": 1}],
     "max-len": ["warn", 80, 2, {"ignoreUrls": true}],
     "max-nested-callbacks": ["error", 3],
     "new-cap": ["error", {"capIsNew": false}],
     "new-parens": "error",
-    "no-control-regex": "error",
     "no-extend-native": "error",
     "no-fallthrough": "error",
     "no-inline-comments": "warn",
     "no-mixed-spaces-and-tabs": "error",
     "no-multi-spaces": "warn",
     "no-multi-str": "warn",
     "no-multiple-empty-lines": ["warn", {"max": 1}],
     "no-return-assign": "error",
--- a/toolkit/components/telemetry/histogram_tools.py
+++ b/toolkit/components/telemetry/histogram_tools.py
@@ -436,16 +436,18 @@ associated with the histogram.  Returns 
         for key, key_type in type_checked_list_fields.iteritems():
             if key not in definition:
                 continue
             if not all(isinstance(x, key_type) for x in definition[key]):
                 raise ParserError('All values for list "{0}" in histogram "{1}" should be of type'
                                   ' {2}.'.format(key, name, nice_type_name(key_type)))
 
     def check_keys(self, name, definition, allowed_keys):
+        if not self._strict_type_checks:
+            return
         for key in definition.iterkeys():
             if key not in allowed_keys:
                 raise ParserError('Key "%s" is not allowed for histogram "%s".' % (key, name))
 
     def set_bucket_parameters(self, low, high, n_buckets):
         self._low = low
         self._high = high
         self._n_buckets = n_buckets
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
@@ -141,16 +141,19 @@ module.exports = {
     // "new-parens": "error",
 
     // Use [] instead of Array()
     "no-array-constructor": "error",
 
     // Disallow assignment operators in conditional statements
     "no-cond-assign": "error",
 
+    // Disallow control characters in regular expressions.
+    "no-control-regex": "error",
+
     // Disallow the use of debugger
     "no-debugger": "error",
 
     // Disallow deleting variables
     "no-delete-var": "error",
 
     // No duplicate arguments in function declarations
     "no-dupe-args": "error",