merge autoland to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 19 Oct 2016 16:59:21 +0200
changeset 318488 8b2d92343bcb398075c909c587ec020286528059
parent 318452 03e795912fdbb000ac95f040d84e26d3eaa8c20a (current diff)
parent 318487 27eef31f9434af59344a313481ada7366c151f59 (diff)
child 318493 2e74103df7d22aace4daa0dee2684e9fb79caaef
push id30841
push usercbook@mozilla.com
push dateWed, 19 Oct 2016 14:59:31 +0000
treeherdermozilla-central@8b2d92343bcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.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 autoland to mozilla-central a=merge
dom/media/test/test_eme_persistent_sessions.html
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
media/gmp-clearkey/0.1/ClearKeyPersistence.h
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -179,17 +179,17 @@ file, You can obtain one at http://mozil
           } else {
             let originalUrl = ReaderMode.getOriginalUrl(aValue);
             if (originalUrl) {
               returnValue = originalUrl;
             }
           }
 
           // Set the actiontype only if the user is not overriding actions.
-          if (action && this._noActionsKeys.size == 0) {
+          if (action && this._pressedNoActionKeys.size == 0) {
             this.setAttribute("actiontype", action.type);
           } else {
             this.removeAttribute("actiontype");
           }
           return returnValue;
         ]]></body>
       </method>
 
@@ -995,24 +995,32 @@ file, You can obtain one at http://mozil
               action.params.displayUrl = action.params.url;
             }
           }
 
           return action;
         ]]></body>
       </method>
 
-      <field name="_noActionsKeys"><![CDATA[
-        new Set();
+      <field name="_noActionKeys"><![CDATA[
+        [
+          KeyEvent.DOM_VK_ALT,
+          KeyEvent.DOM_VK_SHIFT,
+          KeyEvent.DOM_VK_META,
+        ]
+      ]]></field>
+
+      <field name="_pressedNoActionKeys"><![CDATA[
+        new Set()
       ]]></field>
 
       <method name="_clearNoActions">
         <parameter name="aURL"/>
         <body><![CDATA[
-          this._noActionsKeys.clear();
+          this._pressedNoActionKeys.clear();
           this.popup.removeAttribute("noactions");
           let action = this._parseActionUrl(this._value);
           if (action)
             this.setAttribute("actiontype", action.type);
         ]]></body>
       </method>
 
       <method name="onInput">
@@ -1109,34 +1117,32 @@ file, You can obtain one at http://mozil
                  this._prefs.getIntPref("daysBeforeHidingSuggestionsPrompt");
         ]]></getter>
       </property>
 
     </implementation>
 
     <handlers>
       <handler event="keydown"><![CDATA[
-        if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
-             event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
+        if (this._noActionKeys.includes(event.keyCode) &&
             this.popup.selectedIndex >= 0 &&
-            !this._noActionsKeys.has(event.keyCode)) {
-          if (this._noActionsKeys.size == 0) {
+            !this._pressedNoActionKeys.has(event.keyCode)) {
+          if (this._pressedNoActionKeys.size == 0) {
             this.popup.setAttribute("noactions", "true");
             this.removeAttribute("actiontype");
           }
-          this._noActionsKeys.add(event.keyCode);
+          this._pressedNoActionKeys.add(event.keyCode);
         }
       ]]></handler>
 
       <handler event="keyup"><![CDATA[
-        if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
-             event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
-            this._noActionsKeys.has(event.keyCode)) {
-          this._noActionsKeys.delete(event.keyCode);
-          if (this._noActionsKeys.size == 0)
+        if (this._noActionKeys.includes(event.keyCode) &&
+            this._pressedNoActionKeys.has(event.keyCode)) {
+          this._pressedNoActionKeys.delete(event.keyCode);
+          if (this._pressedNoActionKeys.size == 0)
             this._clearNoActions();
         }
       ]]></handler>
 
       <handler event="focus"><![CDATA[
         if (event.originalTarget == this.inputField) {
           this._hideURLTooltip();
           this.formatValue();
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -473,17 +473,17 @@ BrowserGlue.prototype = {
 
     if (AppConstants.NIGHTLY_BUILD) {
       os.addObserver(this, AddonWatcher.TOPIC_SLOW_ADDON_DETECTED, false);
     }
 
     this._flashHangCount = 0;
     this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
 
-    if (AppConstants.platform == "win" ||
+    if (AppConstants.isPlatformAndVersionAtMost("win", "5.2") ||
         AppConstants.platform == "macosx") {
       // Handles prompting to inform about incompatibilites when accessibility
       // and e10s are active together.
       E10SAccessibilityCheck.init();
     }
   },
 
   // cleanup (called on application shutdown)
--- a/browser/config/mozconfigs/linux32/artifact
+++ b/browser/config/mozconfigs/linux32/artifact
@@ -1,9 +1,10 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
 . "$topsrcdir/build/mozconfig.common.override"
 
 ac_add_options --enable-artifact-builds
+ac_add_options --enable-artifact-build-symbols
 unset CC
 unset CXX
--- a/browser/config/mozconfigs/linux64/artifact
+++ b/browser/config/mozconfigs/linux64/artifact
@@ -1,9 +1,10 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
 . "$topsrcdir/build/mozconfig.common.override"
 
 ac_add_options --enable-artifact-builds
+ac_add_options --enable-artifact-build-symbols
 unset CC
 unset CXX
--- a/browser/config/mozconfigs/macosx64/artifact
+++ b/browser/config/mozconfigs/macosx64/artifact
@@ -1,7 +1,8 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/macosx/mozconfig.common"
 . "$topsrcdir/build/mozconfig.common.override"
 
 ac_add_options --enable-artifact-builds
+ac_add_options --enable-artifact-build-symbols
--- a/browser/config/mozconfigs/win32/artifact
+++ b/browser/config/mozconfigs/win32/artifact
@@ -2,8 +2,9 @@ MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/common"
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/build/win32/mozconfig.vs-latest"
 . "$topsrcdir/build/mozconfig.common.override"
 
 ac_add_options --enable-artifact-builds
+ac_add_options --enable-artifact-build-symbols
--- a/browser/config/mozconfigs/win64/artifact
+++ b/browser/config/mozconfigs/win64/artifact
@@ -3,8 +3,9 @@ MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/common"
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/build/win64/mozconfig.vs-latest"
 . "$topsrcdir/build/mozconfig.common.override"
 
 ac_add_options --enable-artifact-builds
+ac_add_options --enable-artifact-build-symbols
--- a/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
@@ -27,16 +27,17 @@ EvaluationResult.defaultProps = {
 
 function EvaluationResult(props) {
   const { message, serviceContainer, indent } = props;
   const {
     source,
     type,
     level,
     id: messageId,
+    exceptionDocURL,
   } = message;
 
   let messageBody;
   if (message.messageText) {
     messageBody = message.messageText;
   } else {
     messageBody = GripMessageBody({grip: message.parameters});
   }
@@ -48,13 +49,14 @@ function EvaluationResult(props) {
     type,
     level,
     indent,
     topLevelClasses,
     messageBody,
     messageId,
     scrollToMessage: props.autoscroll,
     serviceContainer,
+    exceptionDocURL,
   };
   return Message(childProps);
 }
 
 module.exports = EvaluationResult;
--- a/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
@@ -37,17 +37,18 @@ function PageError(props) {
   const {
     id: messageId,
     source,
     type,
     level,
     messageText: messageBody,
     repeat,
     stacktrace,
-    frame
+    frame,
+    exceptionDocURL,
   } = message;
 
   const childProps = {
     dispatch,
     messageId,
     open,
     collapsible: Array.isArray(stacktrace),
     source,
@@ -55,13 +56,14 @@ function PageError(props) {
     level,
     topLevelClasses: [],
     indent,
     messageBody,
     repeat,
     frame,
     stacktrace,
     serviceContainer,
+    exceptionDocURL,
   };
   return Message(childProps);
 }
 
 module.exports = PageError;
--- a/devtools/client/webconsole/new-console-output/components/message.js
+++ b/devtools/client/webconsole/new-console-output/components/message.js
@@ -8,16 +8,17 @@
 
 // React & Redux
 const {
   createClass,
   createFactory,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
+const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
 const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button"));
 const MessageIndent = createFactory(require("devtools/client/webconsole/new-console-output/components/message-indent").MessageIndent);
 const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon"));
 const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat"));
 const FrameView = createFactory(require("devtools/client/shared/components/frame"));
 const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
 
@@ -35,16 +36,17 @@ const Message = createClass({
     topLevelClasses: PropTypes.array.isRequired,
     messageBody: PropTypes.any.isRequired,
     repeat: PropTypes.any,
     frame: PropTypes.any,
     attachment: PropTypes.any,
     stacktrace: PropTypes.any,
     messageId: PropTypes.string,
     scrollToMessage: PropTypes.bool,
+    exceptionDocURL: PropTypes.string,
     serviceContainer: PropTypes.shape({
       emitNewMessage: PropTypes.func.isRequired,
       onViewSourceInDebugger: PropTypes.func.isRequired,
       sourceMapService: PropTypes.any,
     }),
   },
 
   getDefaultProps: function () {
@@ -61,32 +63,38 @@ const Message = createClass({
       // Event used in tests. Some message types don't pass it in because existing tests
       // did not emit for them.
       if (this.props.serviceContainer) {
         this.props.serviceContainer.emitNewMessage(this.messageNode, this.props.messageId);
       }
     }
   },
 
+  onLearnMoreClick: function () {
+    let {exceptionDocURL} = this.props;
+    this.props.serviceContainer.openLink(exceptionDocURL);
+  },
+
   render() {
     const {
       messageId,
       open,
       collapsible,
       collapseTitle,
       source,
       type,
       level,
       indent,
       topLevelClasses,
       messageBody,
       frame,
       stacktrace,
       serviceContainer,
       dispatch,
+      exceptionDocURL,
     } = this.props;
 
     topLevelClasses.push("message", source, type, level);
     if (open) {
       topLevelClasses.push("open");
     }
 
     const icon = MessageIcon({level});
@@ -127,30 +135,40 @@ const Message = createClass({
       shouldRenderFrame ? FrameView({
         frame,
         onClick: serviceContainer.onViewSourceInDebugger,
         showEmptyPathAsHost: true,
         sourceMapService: serviceContainer.sourceMapService
       }) : null
     );
 
+    let learnMore;
+    if (exceptionDocURL) {
+      learnMore = dom.a({
+        className: "learn-more-link webconsole-learn-more-link",
+        title: exceptionDocURL.split("?")[0],
+        onClick: this.onLearnMoreClick,
+      }, `[${l10n.getStr("webConsoleMoreInfoLabel")}]`);
+    }
+
     return dom.div({
       className: topLevelClasses.join(" "),
       ref: node => {
         this.messageNode = node;
       }
     },
       // @TODO add timestamp
       MessageIndent({indent}),
       icon,
       collapse,
       dom.span({ className: "message-body-wrapper" },
         dom.span({ className: "message-flex-body" },
           dom.span({ className: "message-body devtools-monospace" },
-            messageBody
+            messageBody,
+            learnMore
           ),
           repeat,
           location
         ),
         attachment
       )
     );
   }
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -49,16 +49,17 @@ NewConsoleOutputWrapper.prototype = {
           frame.line
         ),
         openNetworkPanel: (requestId) => {
           return this.toolbox.selectTool("netmonitor").then(panel => {
             return panel.panelWin.NetMonitorController.inspectRequest(requestId);
           });
         },
         sourceMapService: this.toolbox ? this.toolbox._sourceMapService : null,
+        openLink: url => this.jsterm.hud.owner.openLink.call(this.jsterm.hud.owner, url)
       }
     });
     let filterBar = FilterBar({
       serviceContainer: {
         attachRefToHud
       }
     });
     let provider = React.createElement(
--- a/devtools/client/webconsole/new-console-output/test/components/evaluation-result.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/evaluation-result.test.js
@@ -1,46 +1,71 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test utils.
 const expect = require("expect");
-const { render } = require("enzyme");
+const { render, mount } = require("enzyme");
+const sinon = require("sinon");
 
 // React
 const { createFactory } = require("devtools/client/shared/vendor/react");
+const Provider = createFactory(require("react-redux").Provider);
+const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
 
 // Components under test.
 const EvaluationResult = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result"));
 const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
 
 // Test fakes.
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
+const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
 
 describe("EvaluationResult component:", () => {
   it("renders a grip result", () => {
     const message = stubPreparedMessages.get("new Date(0)");
     const wrapper = render(EvaluationResult({ message }));
 
     expect(wrapper.find(".message-body").text()).toBe("Date 1970-01-01T00:00:00.000Z");
 
     expect(wrapper.find(".message.log").length).toBe(1);
   });
 
   it("renders an error", () => {
     const message = stubPreparedMessages.get("asdf()");
     const wrapper = render(EvaluationResult({ message }));
 
     expect(wrapper.find(".message-body").text())
-      .toBe("ReferenceError: asdf is not defined");
+      .toBe("ReferenceError: asdf is not defined[Learn More]");
 
     expect(wrapper.find(".message.error").length).toBe(1);
   });
 
+  it("displays a [Learn more] link", () => {
+    const store = setupStore([]);
+
+    const message = stubPreparedMessages.get("asdf()");
+
+    serviceContainer.openLink = sinon.spy();
+    const wrapper = mount(Provider({store},
+      EvaluationResult({message, serviceContainer})
+    ));
+
+    const url =
+      "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined";
+    const learnMore = wrapper.find(".learn-more-link");
+    expect(learnMore.length).toBe(1);
+    expect(learnMore.prop("title")).toBe(url);
+
+    learnMore.simulate("click");
+    let call = serviceContainer.openLink.getCall(0);
+    expect(call.args[0]).toEqual(message.exceptionDocURL);
+  });
+
   it("has the expected indent", () => {
     const message = stubPreparedMessages.get("new Date(0)");
 
     const indent = 10;
     let wrapper = render(EvaluationResult({ message, indent}));
     expect(wrapper.find(".indent").prop("style").width)
         .toBe(`${indent * INDENT_WIDTH}px`);
 
--- a/devtools/client/webconsole/new-console-output/test/components/page-error.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/page-error.test.js
@@ -25,29 +25,51 @@ const { stubPreparedMessages } = require
 const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
 
 describe("PageError component:", () => {
   it("renders", () => {
     const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
     const wrapper = render(PageError({ message, serviceContainer }));
 
     expect(wrapper.find(".message-body").text())
-      .toBe("ReferenceError: asdf is not defined");
+      .toBe("ReferenceError: asdf is not defined[Learn More]");
 
     // The stacktrace should be closed by default.
     const frameLinks = wrapper.find(`.stack-trace`);
     expect(frameLinks.length).toBe(0);
 
-    // There should be the location
+    // There should be the location.
     const locationLink = wrapper.find(`.message-location`);
     expect(locationLink.length).toBe(1);
     // @TODO Will likely change. See https://github.com/devtools-html/gecko-dev/issues/285
     expect(locationLink.text()).toBe("test-tempfile.js:3:5");
   });
 
+  it("displays a [Learn more] link", () => {
+    const store = setupStore([]);
+
+    const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
+
+    serviceContainer.openLink = sinon.spy();
+    const wrapper = mount(Provider({store},
+      PageError({message, serviceContainer})
+    ));
+
+    // There should be a [Learn more] link.
+    const url =
+      "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined";
+    const learnMore = wrapper.find(".learn-more-link");
+    expect(learnMore.length).toBe(1);
+    expect(learnMore.prop("title")).toBe(url);
+
+    learnMore.simulate("click");
+    let call = serviceContainer.openLink.getCall(0);
+    expect(call.args[0]).toEqual(message.exceptionDocURL);
+  });
+
   it("has a stacktrace which can be openned", () => {
     const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
     const wrapper = render(PageError({ message, serviceContainer, open: true }));
 
     // There should be a collapse button.
     expect(wrapper.find(".theme-twisty.open").length).toBe(1);
 
     // There should be three stacktrace items.
--- a/devtools/client/webconsole/new-console-output/test/fixtures/L10n.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/L10n.js
@@ -8,16 +8,18 @@ class L10n {
   getStr(str) {
     switch (str) {
       case "level.error":
         return "Error";
       case "consoleCleared":
         return "Console was cleared.";
       case "webConsoleXhrIndicator":
         return "XHR";
+      case "webConsoleMoreInfoLabel":
+        return "Learn More";
     }
     return str;
   }
 
   getFormatStr(str) {
     return this.getStr(str);
   }
 }
--- a/devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer.js
@@ -7,9 +7,10 @@ module.exports = {
   attachRefToHud: () => {},
   emitNewMessage: () => {},
   hudProxyClient: {},
   onViewSourceInDebugger: () => {},
   openNetworkPanel: () => {},
   sourceMapService: {
     subscribe: () => {},
   },
-};
\ No newline at end of file
+  openLink: () => {},
+};
--- 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,175 +20,183 @@ stubPreparedMessages.set("console.log('f
 	"type": "log",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		"foobar",
 		"test"
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(%27foobar%27%2C%20%27test%27)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(%27foobar%27%2C%20%27test%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27foobar%27%2C%20%27test%27)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.log(undefined)", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		{
 			"type": "undefined"
 		}
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(undefined)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(undefined)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(undefined)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.warn('danger, will robinson!')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "warn",
 	"level": "warn",
 	"messageText": null,
 	"parameters": [
 		"danger, will robinson!"
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.warn(%27danger%2C%20will%20robinson!%27)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.warn(%27danger%2C%20will%20robinson!%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.warn(%27danger%2C%20will%20robinson!%27)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.log(NaN)", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		{
 			"type": "NaN"
 		}
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(NaN)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(NaN)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(NaN)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.log(null)", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		{
 			"type": "null"
 		}
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(null)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(null)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(null)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.log('鼬')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		"鼬"
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(%27%E9%BC%AC%27)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(%27%E9%BC%AC%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27%E9%BC%AC%27)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.clear()", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "clear",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		"Console was cleared."
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.clear()\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.clear()\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.clear()",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.count('bar')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "debug",
 	"messageText": "bar: 1",
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.count(%27bar%27)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.count(%27bar%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.count(%27bar%27)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.assert(false, {message: 'foobar'})", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "assert",
 	"level": "error",
@@ -213,74 +221,77 @@ stubPreparedMessages.set("console.assert
 					}
 				},
 				"ownPropertiesLength": 1,
 				"safeGetterValues": {}
 			}
 		}
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":1}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":1}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": [
 		{
 			"columnNumber": 27,
 			"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)",
 			"functionName": "triggerPacket",
 			"language": 2,
 			"lineNumber": 1
 		}
 	],
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.log('hello \nfrom \rthe \"string world!')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		"hello \nfrom \rthe \"string world!"
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(%27hello%20%5Cnfrom%20%5Crthe%20%5C%22string%20world!%27)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(%27hello%20%5Cnfrom%20%5Crthe%20%5C%22string%20world!%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27hello%20%5Cnfrom%20%5Crthe%20%5C%22string%20world!%27)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.log('úṇĩçödê țĕșť')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		"úṇĩçödê țĕșť"
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(%27%C3%BA%E1%B9%87%C4%A9%C3%A7%C3%B6d%C3%AA%20%C8%9B%C4%95%C8%99%C5%A5%27)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.log(%27%C3%BA%E1%B9%87%C4%A9%C3%A7%C3%B6d%C3%AA%20%C8%9B%C4%95%C8%99%C5%A5%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27%C3%BA%E1%B9%87%C4%A9%C3%A7%C3%B6d%C3%AA%20%C8%9B%C4%95%C8%99%C5%A5%27)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.dirxml(window)", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "log",
@@ -288,44 +299,45 @@ stubPreparedMessages.set("console.dirxml
 	"parameters": [
 		{
 			"type": "object",
 			"actor": "server1.conn11.child1/obj31",
 			"class": "Window",
 			"extensible": true,
 			"frozen": false,
 			"sealed": false,
-			"ownPropertyLength": 805,
+			"ownPropertyLength": 806,
 			"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\",\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn11.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":805,\"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-tempfile.js?key=console.dirxml(window)\",\"line\":1,\"column\":27},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn11.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":806,\"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-tempfile.js?key=console.dirxml(window)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.dirxml(window)",
 		"line": 1,
 		"column": 27
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.trace()", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "trace",
 	"level": "log",
 	"messageText": null,
 	"parameters": [],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.trace()\",\"functionName\":\"testStacktraceFiltering\",\"language\":2,\"lineNumber\":3},{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"functionName\":\"foo\",\"language\":2,\"lineNumber\":6},{\"columnNumber\":1,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":9}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"line\":3,\"column\":3},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.trace()\",\"functionName\":\"testStacktraceFiltering\",\"language\":2,\"lineNumber\":3},{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"functionName\":\"foo\",\"language\":2,\"lineNumber\":6},{\"columnNumber\":1,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":9}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"line\":3,\"column\":3},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": [
 		{
 			"columnNumber": 3,
 			"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
 			"functionName": "testStacktraceFiltering",
 			"language": 2,
 			"lineNumber": 3
 		},
@@ -344,76 +356,80 @@ stubPreparedMessages.set("console.trace(
 			"lineNumber": 9
 		}
 	],
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
 		"line": 3,
 		"column": 3
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.time('bar')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "nullMessage",
 	"level": "log",
 	"messageText": null,
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.time(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.time(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)",
 		"line": 2,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.timeEnd('bar')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "timeEnd",
 	"level": "log",
-	"messageText": "bar: 2ms",
+	"messageText": "bar: 1.8ms",
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 2ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)\",\"line\":3,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 1.8ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)",
 		"line": 3,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.table('bar')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "log",
 	"level": "log",
 	"messageText": null,
 	"parameters": [
 		"bar"
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.table(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.table(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%27bar%27)",
 		"line": 2,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.table(['a', 'b', 'c'])", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "table",
 	"level": "log",
@@ -434,138 +450,145 @@ stubPreparedMessages.set("console.table(
 					"a",
 					"b",
 					"c"
 				]
 			}
 		}
 	],
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn15.child1/obj31\",\"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-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)\",\"line\":2,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn15.child1/obj31\",\"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-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)",
 		"line": 2,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.group('bar')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "startGroup",
 	"level": "log",
 	"messageText": "bar",
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":\"bar\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":\"bar\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)",
 		"line": 2,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.groupEnd('bar')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "endGroup",
 	"level": "log",
 	"messageText": null,
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.group(%27bar%27)\",\"line\":3,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.group(%27bar%27)\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)",
 		"line": 3,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.groupCollapsed('foo')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "startGroupCollapsed",
 	"level": "log",
 	"messageText": "foo",
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"startGroupCollapsed\",\"level\":\"log\",\"messageText\":\"foo\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)\",\"line\":2,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"startGroupCollapsed\",\"level\":\"log\",\"messageText\":\"foo\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)",
 		"line": 2,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.groupEnd('foo')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "endGroup",
 	"level": "log",
 	"messageText": null,
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.groupCollapsed(%27foo%27)\",\"line\":3,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.groupCollapsed(%27foo%27)\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)",
 		"line": 3,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.group()", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "startGroup",
 	"level": "log",
 	"messageText": "<no group label>",
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":\"<no group label>\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()\",\"line\":2,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":\"<no group label>\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()",
 		"line": 2,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 stubPreparedMessages.set("console.groupEnd()", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "endGroup",
 	"level": "log",
 	"messageText": null,
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.group()\",\"line\":3,\"column\":1},\"groupId\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"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-tempfile.js?key=console.group()\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()",
 		"line": 3,
 		"column": 1
 	},
-	"groupId": null
+	"groupId": null,
+	"exceptionDocURL": null
 }));
 
 
 stubPackets.set("console.log('foobar', 'test')", {
 	"from": "server1.conn0.child1/consoleActor2",
 	"type": "consoleAPICall",
 	"message": {
 		"arguments": [
@@ -585,17 +608,17 @@ stubPackets.set("console.log('foobar', '
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932482736,
+		"timeStamp": 1476572494539,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log(undefined)", {
 	"from": "server1.conn1.child1/consoleActor2",
@@ -619,17 +642,17 @@ stubPackets.set("console.log(undefined)"
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932485201,
+		"timeStamp": 1476572496789,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.warn('danger, will robinson!')", {
 	"from": "server1.conn2.child1/consoleActor2",
@@ -651,17 +674,17 @@ stubPackets.set("console.warn('danger, w
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932487905,
+		"timeStamp": 1476572499458,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log(NaN)", {
 	"from": "server1.conn3.child1/consoleActor2",
@@ -685,17 +708,17 @@ stubPackets.set("console.log(NaN)", {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932490983,
+		"timeStamp": 1476572501339,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log(null)", {
 	"from": "server1.conn4.child1/consoleActor2",
@@ -719,17 +742,17 @@ stubPackets.set("console.log(null)", {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932493685,
+		"timeStamp": 1476572504208,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log('鼬')", {
 	"from": "server1.conn5.child1/consoleActor2",
@@ -751,17 +774,17 @@ stubPackets.set("console.log('鼬')", {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932496157,
+		"timeStamp": 1476572507048,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.clear()", {
 	"from": "server1.conn6.child1/consoleActor2",
@@ -780,17 +803,17 @@ stubPackets.set("console.clear()", {
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932498831,
+		"timeStamp": 1476572509784,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.count('bar')", {
@@ -815,17 +838,17 @@ stubPackets.set("console.count('bar')", 
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932502090,
+		"timeStamp": 1476572512209,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.assert(false, {message: 'foobar'})", {
@@ -869,17 +892,17 @@ stubPackets.set("console.assert(false, {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932504985,
+		"timeStamp": 1476572514941,
 		"timer": null,
 		"stacktrace": [
 			{
 				"columnNumber": 27,
 				"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)",
 				"functionName": "triggerPacket",
 				"language": 2,
 				"lineNumber": 1
@@ -910,17 +933,17 @@ stubPackets.set("console.log('hello \nfr
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932507548,
+		"timeStamp": 1476572517131,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log('úṇĩçödê țĕșť')", {
 	"from": "server1.conn10.child1/consoleActor2",
@@ -942,17 +965,17 @@ stubPackets.set("console.log('úṇĩçödê țĕșť')", {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1475932510208,
+		"timeStamp": 1476572519136,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.dirxml(window)", {
 	"from": "server1.conn11.child1/consoleActor2",
@@ -961,17 +984,17 @@ stubPackets.set("console.dirxml(window)"
 		"arguments": [
 			{
 				"type": "object",
 				"actor": "server1.conn11.child1/obj31",
 				"class": "Window",
 				"extensible": true,
 				"frozen": false,
 				"sealed": false,
-				"ownPropertyLength": 805,
+				"ownPropertyLength": 806,
 				"preview": {
 					"kind": "ObjectWithURL",
 					"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html"
 				}
 			}
 		],
 		"columnNumber": 27,
 		"counter": null,
@@ -985,17 +1008,17 @@ stubPackets.set("console.dirxml(window)"
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932512694,
+		"timeStamp": 1476572521656,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.trace()", {
@@ -1015,17 +1038,17 @@ stubPackets.set("console.trace()", {
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932515089,
+		"timeStamp": 1476572524381,
 		"timer": null,
 		"stacktrace": [
 			{
 				"columnNumber": 3,
 				"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
 				"functionName": "testStacktraceFiltering",
 				"language": 2,
 				"lineNumber": 3
@@ -1070,20 +1093,20 @@ stubPackets.set("console.time('bar')", {
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932517857,
+		"timeStamp": 1476572526421,
 		"timer": {
 			"name": "bar",
-			"started": 1855.08
+			"started": 1214.4750000000001
 		},
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.timeEnd('bar')", {
@@ -1105,19 +1128,19 @@ stubPackets.set("console.timeEnd('bar')"
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932517859,
+		"timeStamp": 1476572526423,
 		"timer": {
-			"duration": 2,
+			"duration": 1.8049999999998363,
 			"name": "bar"
 		},
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
@@ -1140,17 +1163,17 @@ stubPackets.set("console.table('bar')", 
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932520567,
+		"timeStamp": 1476572528374,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.table(['a', 'b', 'c'])", {
@@ -1189,17 +1212,17 @@ stubPackets.set("console.table(['a', 'b'
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932523995,
+		"timeStamp": 1476572530339,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.group('bar')", {
@@ -1221,17 +1244,17 @@ stubPackets.set("console.group('bar')", 
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932526918,
+		"timeStamp": 1476572532126,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.groupEnd('bar')", {
@@ -1253,17 +1276,17 @@ stubPackets.set("console.groupEnd('bar')
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932526919,
+		"timeStamp": 1476572532127,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.groupCollapsed('foo')", {
@@ -1285,17 +1308,17 @@ stubPackets.set("console.groupCollapsed(
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932529277,
+		"timeStamp": 1476572533893,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.groupEnd('foo')", {
@@ -1317,17 +1340,17 @@ stubPackets.set("console.groupEnd('foo')
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932529279,
+		"timeStamp": 1476572533897,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.group()", {
@@ -1347,17 +1370,17 @@ stubPackets.set("console.group()", {
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932531706,
+		"timeStamp": 1476572535552,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.groupEnd()", {
@@ -1377,17 +1400,17 @@ stubPackets.set("console.groupEnd()", {
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1475932531707,
+		"timeStamp": 1476572535554,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 
--- 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,35 +27,38 @@ stubPreparedMessages.set("new Date(0)", 
 		"frozen": false,
 		"sealed": false,
 		"ownPropertyLength": 0,
 		"preview": {
 			"timestamp": 0
 		}
 	},
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"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}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"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}",
 	"stacktrace": null,
-	"frame": null
+	"frame": null,
+	"groupId": null
 }));
 
 stubPreparedMessages.set("asdf()", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "javascript",
 	"type": "result",
 	"level": "error",
 	"messageText": "ReferenceError: asdf is not defined",
 	"parameters": {
 		"type": "undefined"
 	},
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"result\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":{\"type\":\"undefined\"},\"repeatId\":null,\"stacktrace\":null,\"frame\":null}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"result\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":{\"type\":\"undefined\"},\"repeatId\":null,\"stacktrace\":null,\"frame\":null,\"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\"}",
 	"stacktrace": null,
-	"frame": null
+	"frame": null,
+	"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"
 }));
 
 
 stubPackets.set("new Date(0)", {
 	"from": "server1.conn0.child1/consoleActor2",
 	"input": "new Date(0)",
 	"result": {
 		"type": "object",
@@ -64,28 +67,28 @@ stubPackets.set("new Date(0)", {
 		"extensible": true,
 		"frozen": false,
 		"sealed": false,
 		"ownPropertyLength": 0,
 		"preview": {
 			"timestamp": 0
 		}
 	},
-	"timestamp": 1474405330863,
+	"timestamp": 1476573073424,
 	"exception": null,
 	"helperResult": null
 });
 
 stubPackets.set("asdf()", {
 	"from": "server1.conn0.child1/consoleActor2",
 	"input": "asdf()",
 	"result": {
 		"type": "undefined"
 	},
-	"timestamp": 1474405330881,
+	"timestamp": 1476573073442,
 	"exception": {
 		"type": "object",
 		"actor": "server1.conn0.child1/obj32",
 		"class": "Error",
 		"extensible": true,
 		"frozen": false,
 		"sealed": false,
 		"ownPropertyLength": 4,
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js
@@ -19,55 +19,58 @@ stubPreparedMessages.set("GET request", 
 	"level": "log",
 	"isXHR": false,
 	"request": {
 		"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
 		"method": "GET"
 	},
 	"response": {},
 	"source": "network",
-	"type": "log"
+	"type": "log",
+	"groupId": null
 }));
 
 stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({
 	"id": "1",
 	"actor": "server1.conn1.child1/netEvent29",
 	"level": "log",
 	"isXHR": true,
 	"request": {
 		"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
 		"method": "GET"
 	},
 	"response": {},
 	"source": "network",
-	"type": "log"
+	"type": "log",
+	"groupId": null
 }));
 
 stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({
 	"id": "1",
 	"actor": "server1.conn2.child1/netEvent29",
 	"level": "log",
 	"isXHR": true,
 	"request": {
 		"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
 		"method": "POST"
 	},
 	"response": {},
 	"source": "network",
-	"type": "log"
+	"type": "log",
+	"groupId": null
 }));
 
 
 stubPackets.set("GET request", {
 	"from": "server1.conn0.child1/consoleActor2",
 	"type": "networkEvent",
 	"eventActor": {
 		"actor": "server1.conn0.child1/netEvent29",
-		"startedDateTime": "2016-09-14T02:38:18.046Z",
-		"timeStamp": 1473820698046,
+		"startedDateTime": "2016-10-15T23:12:04.196Z",
+		"timeStamp": 1476573124196,
 		"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
 		"method": "GET",
 		"isXHR": false,
 		"cause": {
 			"type": 3,
 			"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
 			"stacktrace": [
 				{
@@ -97,18 +100,18 @@ stubPackets.set("GET request", {
 	}
 });
 
 stubPackets.set("XHR GET request", {
 	"from": "server1.conn1.child1/consoleActor2",
 	"type": "networkEvent",
 	"eventActor": {
 		"actor": "server1.conn1.child1/netEvent29",
-		"startedDateTime": "2016-09-14T02:38:18.812Z",
-		"timeStamp": 1473820698812,
+		"startedDateTime": "2016-10-15T23:12:05.690Z",
+		"timeStamp": 1476573125690,
 		"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
 		"method": "GET",
 		"isXHR": true,
 		"cause": {
 			"type": 11,
 			"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
 			"stacktrace": [
 				{
@@ -138,18 +141,18 @@ stubPackets.set("XHR GET request", {
 	}
 });
 
 stubPackets.set("XHR POST request", {
 	"from": "server1.conn2.child1/consoleActor2",
 	"type": "networkEvent",
 	"eventActor": {
 		"actor": "server1.conn2.child1/netEvent29",
-		"startedDateTime": "2016-09-14T02:38:19.483Z",
-		"timeStamp": 1473820699483,
+		"startedDateTime": "2016-10-15T23:12:07.158Z",
+		"timeStamp": 1476573127158,
 		"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
 		"method": "POST",
 		"isXHR": true,
 		"cause": {
 			"type": 11,
 			"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
 			"stacktrace": [
 				{
--- 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,17 +17,17 @@ stubPreparedMessages.set("ReferenceError
 	"id": "1",
 	"allowRepeating": true,
 	"source": "javascript",
 	"type": "log",
 	"level": "error",
 	"messageText": "ReferenceError: asdf is not defined",
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"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-tempfile.js?key=Reference%20Error\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"line\":3,\"column\":5}}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"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-tempfile.js?key=Reference%20Error\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"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\"}",
 	"stacktrace": [
 		{
 			"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
 			"lineNumber": 3,
 			"columnNumber": 5,
 			"functionName": "bar"
 		},
 		{
@@ -42,33 +42,35 @@ stubPreparedMessages.set("ReferenceError
 			"columnNumber": 3,
 			"functionName": null
 		}
 	],
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
 		"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"
 }));
 
 
 stubPackets.set("ReferenceError: asdf is not defined", {
 	"from": "server1.conn0.child1/consoleActor2",
 	"type": "pageError",
 	"pageError": {
 		"errorMessage": "ReferenceError: asdf is not defined",
 		"errorMessageName": "JSMSG_NOT_DEFINED",
 		"exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
 		"sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
 		"lineText": "",
 		"lineNumber": 3,
 		"columnNumber": 5,
 		"category": "content javascript",
-		"timeStamp": 1473960366996,
+		"timeStamp": 1476573167137,
 		"warning": false,
 		"error": false,
 		"exception": true,
 		"strict": false,
 		"info": false,
 		"private": false,
 		"stacktrace": [
 			{
--- a/devtools/client/webconsole/new-console-output/types.js
+++ b/devtools/client/webconsole/new-console-output/types.js
@@ -31,16 +31,17 @@ exports.ConsoleMessage = Immutable.Recor
   level: null,
   messageText: null,
   parameters: null,
   repeat: 1,
   repeatId: null,
   stacktrace: null,
   frame: null,
   groupId: null,
+  exceptionDocURL: null,
 });
 
 exports.NetworkEventMessage = Immutable.Record({
   id: null,
   actor: null,
   level: MESSAGE_LEVEL.LOG,
   isXHR: false,
   request: null,
--- a/devtools/client/webconsole/new-console-output/utils/messages.js
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -158,16 +158,17 @@ function transformPacket(packet) {
 
       return new ConsoleMessage({
         source: MESSAGE_SOURCE.JAVASCRIPT,
         type: MESSAGE_TYPE.LOG,
         level,
         messageText: pageError.errorMessage,
         stacktrace: pageError.stacktrace ? pageError.stacktrace : null,
         frame,
+        exceptionDocURL: pageError.exceptionDocURL,
       });
     }
 
     case "networkEvent": {
       let { networkEvent } = packet;
 
       return new NetworkEventMessage({
         actor: networkEvent.actor,
@@ -176,26 +177,28 @@ function transformPacket(packet) {
         response: networkEvent.response,
       });
     }
 
     case "evaluationResult":
     default: {
       let {
         exceptionMessage: messageText,
+        exceptionDocURL,
         result: parameters
       } = packet;
 
       const level = messageText ? MESSAGE_LEVEL.ERROR : MESSAGE_LEVEL.LOG;
       return new ConsoleMessage({
         source: MESSAGE_SOURCE.JAVASCRIPT,
         type: MESSAGE_TYPE.RESULT,
         level,
         messageText,
         parameters,
+        exceptionDocURL,
       });
     }
   }
 }
 
 // Helpers
 function getRepeatId(message) {
   message = message.toJS();
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -31,16 +31,17 @@
 #include "DeviceStorageStatics.h"
 #include "GMPServiceParent.h"
 #include "HandlerServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #include "mozIApplication.h"
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 #include "mozilla/a11y/AccessibleWrap.h"
+#include "mozilla/WindowsVersion.h"
 #endif
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
@@ -1353,24 +1354,22 @@ ContentParent::Init()
     cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
     obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", cpId.get());
   }
 
 #ifdef ACCESSIBILITY
   // If accessibility is running in chrome process then start it in content
   // process.
   if (nsIPresShell::IsAccessibilityActive()) {
-#if !defined(XP_WIN)
-    Unused << SendActivateA11y();
-#else
-    // On Windows we currently only enable a11y in the content process
-    // for testing purposes.
-    if (Preferences::GetBool(kForceEnableE10sPref, false)) {
+#if defined(XP_WIN)
+    if (IsVistaOrLater()) {
       Unused << SendActivateA11y();
     }
+#else
+    Unused << SendActivateA11y();
 #endif
   }
 #endif
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
   nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
   bool profilerActive = false;
   DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
@@ -2784,24 +2783,22 @@ ContentParent::Observe(nsISupports* aSub
     Unused << SendVolumeRemoved(volName);
   }
 #endif
 #ifdef ACCESSIBILITY
   else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
     if (*aData == '1') {
       // Make sure accessibility is running in content process when
       // accessibility gets initiated in chrome process.
-#if !defined(XP_WIN)
-      Unused << SendActivateA11y();
-#else
-      // On Windows we currently only enable a11y in the content process
-      // for testing purposes.
-      if (Preferences::GetBool(kForceEnableE10sPref, false)) {
+#if defined(XP_WIN)
+      if (IsVistaOrLater()) {
         Unused << SendActivateA11y();
       }
+#else
+      Unused << SendActivateA11y();
 #endif
     } else {
       // If possible, shut down accessibility in content process when
       // accessibility gets shutdown in chrome process.
       Unused << SendShutdownA11y();
     }
   }
 #endif
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -210,16 +210,20 @@ public:
   virtual bool HandleWaitingForData() { return false; }
 
   virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) = 0;
 
   virtual bool HandleAudioCaptured() { return false; }
 
   virtual RefPtr<ShutdownPromise> HandleShutdown();
 
+  virtual void HandleVideoSuspendTimeout() = 0;
+
+  virtual void HandleResumeVideoDecoding();
+
   virtual void DumpDebugInfo() {}
 
 protected:
   using Master = MediaDecoderStateMachine;
   explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
   TaskQueue* OwnerThread() const { return mMaster->mTaskQueue; }
   MediaResource* Resource() const { return mMaster->mResource; }
   MediaDecoderReaderWrapper* Reader() const { return mMaster->mReader; }
@@ -267,16 +271,17 @@ protected:
 class MediaDecoderStateMachine::DecodeMetadataState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
   explicit DecodeMetadataState(Master* aPtr) : StateObject(aPtr) {}
 
   void Enter()
   {
+    MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
     MOZ_ASSERT(!mMetadataRequest.Exists());
     SLOG("Dispatching AsyncReadMetadata");
 
     // Set mode to METADATA since we are about to read metadata.
     Resource()->SetReadMode(MediaCacheStream::MODE_METADATA);
 
     // We disconnect mMetadataRequest in Exit() so it is fine to capture
     // a raw pointer here.
@@ -307,16 +312,27 @@ public:
   }
 
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
   {
     MOZ_DIAGNOSTIC_ASSERT(false, "Can't seek while decoding metadata.");
     return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
   }
 
+  void HandleVideoSuspendTimeout() override
+  {
+    // Do nothing since no decoders are created yet.
+  }
+
+  void HandleResumeVideoDecoding() override
+  {
+    // We never suspend video decoding in this state.
+    MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+  }
+
 private:
   void OnMetadataRead(MetadataHolder* aMetadata);
 
   void OnMetadataNotRead(const MediaResult& aError)
   {
     mMetadataRequest.Complete();
     SWARN("Decode metadata failed, shutting down decoder");
     mMaster->DecodeError(aError);
@@ -331,17 +347,21 @@ private:
 };
 
 class MediaDecoderStateMachine::WaitForCDMState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
   explicit WaitForCDMState(Master* aPtr) : StateObject(aPtr) {}
 
-  void Enter(bool aPendingDormant) { mPendingDormant = aPendingDormant; }
+  void Enter(bool aPendingDormant)
+  {
+    MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
+    mPendingDormant = aPendingDormant;
+  }
 
   State GetState() const override
   {
     return DECODER_STATE_WAIT_FOR_CDM;
   }
 
   bool HandleDormant(bool aDormant) override;
 
@@ -350,16 +370,27 @@ public:
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
   {
     SLOG("Not Enough Data to seek at this stage, queuing seek");
     mMaster->mQueuedSeek.RejectIfExists(__func__);
     mMaster->mQueuedSeek.mTarget = aTarget;
     return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
   }
 
+  void HandleVideoSuspendTimeout() override
+  {
+    // Do nothing since no decoders are created yet.
+  }
+
+  void HandleResumeVideoDecoding() override
+  {
+    // We never suspend video decoding in this state.
+    MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+  }
+
 private:
   bool mPendingDormant = false;
 };
 
 class MediaDecoderStateMachine::DormantState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
@@ -383,16 +414,26 @@ public:
 
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
   {
     SLOG("Not Enough Data to seek at this stage, queuing seek");
     mMaster->mQueuedSeek.RejectIfExists(__func__);
     mMaster->mQueuedSeek.mTarget = aTarget;
     return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
   }
+
+  void HandleVideoSuspendTimeout() override
+  {
+    // Do nothing since we've released decoders in Enter().
+  }
+
+  void HandleResumeVideoDecoding() override
+  {
+    // Do nothing since we won't resume decoding until exiting dormant.
+  }
 };
 
 class MediaDecoderStateMachine::DecodingFirstFrameState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
   explicit DecodingFirstFrameState(Master* aPtr) : StateObject(aPtr) {}
 
@@ -420,16 +461,27 @@ public:
   bool HandleEndOfStream() override
   {
     MaybeFinishDecodeFirstFrame();
     return true;
   }
 
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
 
+  void HandleVideoSuspendTimeout() override
+  {
+    // Do nothing for we need to decode the 1st video frame to get the dimensions.
+  }
+
+  void HandleResumeVideoDecoding() override
+  {
+    // We never suspend video decoding in this state.
+    MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+  }
+
 private:
   // Notify FirstFrameLoaded if having decoded first frames and
   // transition to SEEKING if there is any pending seek, or DECODING otherwise.
   void MaybeFinishDecodeFirstFrame();
 };
 
 class MediaDecoderStateMachine::DecodingState
   : public MediaDecoderStateMachine::StateObject
@@ -503,16 +555,25 @@ public:
   bool HandleAudioCaptured() override
   {
     MaybeStopPrerolling();
     // MediaSink is changed. Schedule Step() to check if we can start playback.
     mMaster->ScheduleStateMachine();
     return true;
   }
 
+  void HandleVideoSuspendTimeout() override
+  {
+    if (mMaster->HasVideo()) {
+      mMaster->mVideoDecodeSuspended = true;
+      mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend);
+      Reader()->SetVideoBlankDecode(true);
+    }
+  }
+
   void DumpDebugInfo() override
   {
     SDUMP("mIsPrerolling=%d", mIsPrerolling);
   }
 
 private:
   void MaybeStartBuffering();
 
@@ -590,16 +651,25 @@ class MediaDecoderStateMachine::SeekingS
 {
 public:
   explicit SeekingState(Master* aPtr) : StateObject(aPtr) {}
 
   RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob)
   {
     mSeekJob = Move(aSeekJob);
 
+    // Always switch off the blank decoder otherwise we might become visible
+    // in the middle of seeking and won't have a valid video frame to show
+    // when seek is done.
+    if (mMaster->mVideoDecodeSuspended) {
+      mMaster->mVideoDecodeSuspended = false;
+      mMaster->mOnPlaybackEvent.Notify(MediaEventType::ExitVideoSuspend);
+      Reader()->SetVideoBlankDecode(false);
+    }
+
     // SeekTask will register its callbacks to MediaDecoderReaderWrapper.
     mMaster->CancelMediaDecoderReaderWrapperCallback();
 
     // Create a new SeekTask instance for the incoming seek task.
     if (mSeekJob.mTarget.IsAccurate() ||
         mSeekJob.mTarget.IsFast()) {
       mSeekTask = new AccurateSeekTask(
         mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
@@ -679,16 +749,27 @@ public:
   bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
   {
     MOZ_ASSERT(false);
     return true;
   }
 
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
 
+  void HandleVideoSuspendTimeout() override
+  {
+    // Do nothing since we want a valid video frame to show when seek is done.
+  }
+
+  void HandleResumeVideoDecoding() override
+  {
+    // We set mVideoDecodeSuspended to false in Enter().
+    MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+  }
+
 private:
   void OnSeekTaskResolved(const SeekTaskResolveValue& aValue)
   {
     mSeekTaskRequest.Complete();
 
     if (aValue.mSeekedAudioData) {
       mMaster->Push(aValue.mSeekedAudioData, MediaData::AUDIO_DATA);
       mMaster->mDecodedAudioEndTime = std::max(
@@ -780,16 +861,25 @@ public:
     mMaster->ScheduleStateMachine();
     return true;
   }
 
   bool HandleEndOfStream() override;
 
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
 
+  void HandleVideoSuspendTimeout() override
+  {
+    if (mMaster->HasVideo()) {
+      mMaster->mVideoDecodeSuspended = true;
+      mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend);
+      Reader()->SetVideoBlankDecode(true);
+    }
+  }
+
 private:
   TimeStamp mBufferingStart;
 
   // The maximum number of second we spend buffering when we are short on
   // unbuffered data.
   const uint32_t mBufferingWait = 15;
 };
 
@@ -861,16 +951,21 @@ public:
 
   bool HandleAudioCaptured() override
   {
     // MediaSink is changed. Schedule Step() to check if we can start playback.
     mMaster->ScheduleStateMachine();
     return true;
   }
 
+  void HandleVideoSuspendTimeout() override
+  {
+    // Do nothing since no decoding is going on.
+  }
+
 private:
   bool mSentPlaybackEndedEvent = false;
 };
 
 class MediaDecoderStateMachine::ShutdownState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
@@ -899,16 +994,26 @@ public:
     return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
   }
 
   RefPtr<ShutdownPromise> HandleShutdown() override
   {
     MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
     return nullptr;
   }
+
+  void HandleVideoSuspendTimeout() override
+  {
+    MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
+  }
+
+  void HandleResumeVideoDecoding() override
+  {
+    MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
+  }
 };
 
 bool
 MediaDecoderStateMachine::
 StateObject::HandleDormant(bool aDormant)
 {
   if (!aDormant) {
     return true;
@@ -927,16 +1032,88 @@ StateObject::HandleDormant(bool aDormant
 
 RefPtr<ShutdownPromise>
 MediaDecoderStateMachine::
 StateObject::HandleShutdown()
 {
   return SetState<ShutdownState>();
 }
 
+static void
+ReportRecoveryTelemetry(const TimeStamp& aRecoveryStart,
+                        const MediaInfo& aMediaInfo,
+                        bool aIsHardwareAccelerated)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!aMediaInfo.HasVideo()) {
+    return;
+  }
+
+  // Keyed by audio+video or video alone, hardware acceleration,
+  // and by a resolution range.
+  nsCString key(aMediaInfo.HasAudio() ? "AV" : "V");
+  key.AppendASCII(aIsHardwareAccelerated ? "(hw)," : ",");
+  static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
+    {  240, "0-240" },
+    {  480, "241-480" },
+    {  720, "481-720" },
+    { 1080, "721-1080" },
+    { 2160, "1081-2160" }
+  };
+  const char* resolution = "2161+";
+  int32_t height = aMediaInfo.mVideo.mImage.height;
+  for (const auto& res : sResolutions) {
+    if (height <= res.mH) {
+      resolution = res.mRes;
+      break;
+    }
+  }
+  key.AppendASCII(resolution);
+
+  TimeDuration duration = TimeStamp::Now() - aRecoveryStart;
+  double duration_ms = duration.ToMilliseconds();
+  Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
+                        key,
+                        uint32_t(duration_ms + 0.5));
+  Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
+                        NS_LITERAL_CSTRING("All"),
+                        uint32_t(duration_ms + 0.5));
+}
+
+void
+MediaDecoderStateMachine::
+StateObject::HandleResumeVideoDecoding()
+{
+  MOZ_ASSERT(mMaster->mVideoDecodeSuspended);
+
+  // Start counting recovery time from right now.
+  TimeStamp start = TimeStamp::Now();
+
+  // Local reference to mInfo, so that it will be copied in the lambda below.
+  auto& info = Info();
+  bool hw = Reader()->VideoIsHardwareAccelerated();
+
+  // Start video-only seek to the current time.
+  SeekJob seekJob;
+
+  const SeekTarget::Type type = mMaster->HasAudio()
+                                ? SeekTarget::Type::Accurate
+                                : SeekTarget::Type::PrevSyncPoint;
+
+  seekJob.mTarget = SeekTarget(mMaster->GetMediaTime(),
+                               type,
+                               MediaDecoderEventVisibility::Suppressed,
+                               true /* aVideoOnly */);
+
+  SetState<SeekingState>(Move(seekJob))->Then(
+    AbstractThread::MainThread(), __func__,
+    [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
+    [](){});
+}
+
 void
 MediaDecoderStateMachine::
 DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
 {
   mMetadataRequest.Complete();
 
   // Set mode to PLAYBACK after reading metadata.
   Resource()->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
@@ -1039,16 +1216,18 @@ DecodingFirstFrameState::Enter()
   }
 
   // Transition to DECODING if we've decoded first frames.
   if (mMaster->mSentFirstFrameLoadedEvent) {
     SetState<DecodingState>();
     return;
   }
 
+  MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
+
   // Dispatch tasks to decode first frames.
   mMaster->DispatchDecodeTasksIfNeeded();
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::
 DecodingFirstFrameState::HandleSeek(SeekTarget aTarget)
 {
@@ -1097,16 +1276,24 @@ void
 MediaDecoderStateMachine::
 DecodingState::Enter()
 {
   MOZ_ASSERT(mMaster->mSentFirstFrameLoadedEvent);
   // Pending seek should've been handled by DECODING_FIRSTFRAME before
   // transitioning to DECODING.
   MOZ_ASSERT(!mMaster->mQueuedSeek.Exists());
 
+  if (!mMaster->mIsVisible &&
+      !mMaster->mVideoDecodeSuspendTimer.IsScheduled() &&
+      !mMaster->mVideoDecodeSuspended) {
+    // If we are not visible and the timer is not schedule, it means the timer
+    // has timed out and we should suspend video decoding now if necessary.
+    HandleVideoSuspendTimeout();
+  }
+
   if (mMaster->CheckIfDecodeComplete()) {
     SetState<CompletedState>();
     return;
   }
 
   mDecodeStartTime = TimeStamp::Now();
 
   MaybeStopPrerolling();
@@ -2195,68 +2382,23 @@ void MediaDecoderStateMachine::PlayState
   {
     DECODER_LOG("Unexpected state - Bailing out of PlayInternal()");
     return;
   }
 
   ScheduleStateMachine();
 }
 
-static void
-ReportRecoveryTelemetry(const TimeStamp& aRecoveryStart,
-                        const MediaInfo& aMediaInfo,
-                        bool aIsHardwareAccelerated)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!aMediaInfo.HasVideo()) {
-    return;
-  }
-
-  // Keyed by audio+video or video alone, hardware acceleration,
-  // and by a resolution range.
-  nsCString key(aMediaInfo.HasAudio() ? "AV" : "V");
-  key.AppendASCII(aIsHardwareAccelerated ? "(hw)," : ",");
-  static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
-    {  240, "0-240" },
-    {  480, "241-480" },
-    {  720, "481-720" },
-    { 1080, "721-1080" },
-    { 2160, "1081-2160" }
-  };
-  const char* resolution = "2161+";
-  int32_t height = aMediaInfo.mVideo.mImage.height;
-  for (const auto& res : sResolutions) {
-    if (height <= res.mH) {
-      resolution = res.mRes;
-      break;
-    }
-  }
-  key.AppendASCII(resolution);
-
-  TimeDuration duration = TimeStamp::Now() - aRecoveryStart;
-  double duration_ms = duration.ToMilliseconds();
-  Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
-                        key,
-                        uint32_t(duration_ms + 0.5));
-  Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
-                        NS_LITERAL_CSTRING("All"),
-                        uint32_t(duration_ms + 0.5));
-}
-
 void MediaDecoderStateMachine::VisibilityChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   DECODER_LOG("VisibilityChanged: mIsVisible=%d, "
               "mVideoDecodeSuspended=%c, mIsReaderSuspended=%d",
               mIsVisible.Ref(), mVideoDecodeSuspended ? 'T' : 'F', mIsReaderSuspended.Ref());
 
-  if (mInfo.isNothing() || !HasVideo()) {
-    return;
-  }
-
   // Start timer to trigger suspended decoding state when going invisible.
   if (!mIsVisible) {
     TimeStamp target = TimeStamp::Now() + SuspendBackgroundVideoDelay();
 
     RefPtr<MediaDecoderStateMachine> self = this;
     mVideoDecodeSuspendTimer.Ensure(target,
                                     [=]() { self->OnSuspendTimerResolved(); },
                                     [=]() { self->OnSuspendTimerRejected(); });
@@ -2264,52 +2406,17 @@ void MediaDecoderStateMachine::Visibilit
   }
 
   // Resuming from suspended decoding
 
   // If suspend timer exists, destroy it.
   mVideoDecodeSuspendTimer.Reset();
 
   if (mVideoDecodeSuspended) {
-    mVideoDecodeSuspended = false;
-    mOnPlaybackEvent.Notify(MediaEventType::ExitVideoSuspend);
-    mReader->SetVideoBlankDecode(false);
-
-    if (mIsReaderSuspended) {
-      return;
-    }
-
-    // If an existing seek is in flight don't bother creating a new
-    // one to catch up.
-    if (mState == DECODER_STATE_SEEKING || mQueuedSeek.Exists()) {
-      return;
-    }
-
-    // Start counting recovery time from right now.
-    TimeStamp start = TimeStamp::Now();
-    // Local reference to mInfo, so that it will be copied in the lambda below.
-    auto& info = Info();
-    bool hw = mReader->VideoIsHardwareAccelerated();
-
-    // Start video-only seek to the current time.
-    SeekJob seekJob;
-
-    const SeekTarget::Type type = HasAudio()
-                                  ? SeekTarget::Type::Accurate
-                                  : SeekTarget::Type::PrevSyncPoint;
-
-    seekJob.mTarget = SeekTarget(GetMediaTime(),
-                                 type,
-                                 MediaDecoderEventVisibility::Suppressed,
-                                 true /* aVideoOnly */);
-
-    mStateObj->SetState<SeekingState>(Move(seekJob))->Then(
-      AbstractThread::MainThread(), __func__,
-      [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
-      [](){});
+    mStateObj->HandleResumeVideoDecoding();
   }
 }
 
 void MediaDecoderStateMachine::BufferedRangeUpdated()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // While playing an unseekable stream of unknown duration, mObservedDuration
@@ -3228,19 +3335,17 @@ MediaDecoderStateMachine::VideoRequestSt
   return "idle";
 }
 
 void
 MediaDecoderStateMachine::OnSuspendTimerResolved()
 {
   DECODER_LOG("OnSuspendTimerResolved");
   mVideoDecodeSuspendTimer.CompleteRequest();
-  mVideoDecodeSuspended = true;
-  mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend);
-  mReader->SetVideoBlankDecode(true);
+  mStateObj->HandleVideoSuspendTimeout();
 }
 
 void
 MediaDecoderStateMachine::OnSuspendTimerRejected()
 {
   DECODER_LOG("OnSuspendTimerRejected");
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(!mVideoDecodeSuspended);
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -302,29 +302,31 @@ TrackBuffersManager::EvictData(const Tim
 
   return result;
 }
 
 TimeIntervals
 TrackBuffersManager::Buffered() const
 {
   MSE_DEBUG("");
+
   // http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
-  // 2. Let highest end time be the largest track buffer ranges end time across all the track buffers managed by this SourceBuffer object.
-  TimeUnit highestEndTime = HighestEndTime();
 
   MonitorAutoLock mon(mMonitor);
   nsTArray<const TimeIntervals*> tracks;
   if (HasVideo()) {
     tracks.AppendElement(&mVideoBufferedRanges);
   }
   if (HasAudio()) {
     tracks.AppendElement(&mAudioBufferedRanges);
   }
 
+  // 2. Let highest end time be the largest track buffer ranges end time across all the track buffers managed by this SourceBuffer object.
+  TimeUnit highestEndTime = HighestEndTime(tracks);
+
   // 3. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
   TimeIntervals intersection{TimeInterval(TimeUnit::FromSeconds(0), highestEndTime)};
 
   // 4. For each track buffer managed by this SourceBuffer, run the following steps:
   //   1. Let track ranges equal the track buffer ranges for the current track buffer.
   for (const TimeIntervals* trackRanges : tracks) {
     // 2. If readyState is "ended", then set the end time on the last range in track ranges to highest end time.
     // 3. Let new intersection ranges equal the intersection between the intersection ranges and the track ranges.
@@ -1974,26 +1976,36 @@ TrackBuffersManager::HighestStartTime() 
   }
   return highestStartTime;
 }
 
 TimeUnit
 TrackBuffersManager::HighestEndTime() const
 {
   MonitorAutoLock mon(mMonitor);
-  TimeUnit highestEndTime;
 
   nsTArray<const TimeIntervals*> tracks;
   if (HasVideo()) {
     tracks.AppendElement(&mVideoBufferedRanges);
   }
   if (HasAudio()) {
     tracks.AppendElement(&mAudioBufferedRanges);
   }
-  for (const auto& trackRanges : tracks) {
+  return HighestEndTime(tracks);
+}
+
+TimeUnit
+TrackBuffersManager::HighestEndTime(
+  nsTArray<const TimeIntervals*>& aTracks) const
+{
+  mMonitor.AssertCurrentThreadOwns();
+
+  TimeUnit highestEndTime;
+
+  for (const auto& trackRanges : aTracks) {
     highestEndTime = std::max(trackRanges->GetEnd(), highestEndTime);
   }
   return highestEndTime;
 }
 
 void
 TrackBuffersManager::ResetEvictionIndex(TrackData& aTrackData)
 {
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -462,16 +462,20 @@ private:
   UniquePtr<SourceBufferAttributes> mSourceBufferAttributes;
   // The current sourcebuffer append window. It's content is equivalent to
   // mSourceBufferAttributes.mAppendWindowStart/End
   media::TimeInterval mAppendWindow;
 
   // Strong references to external objects.
   nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder;
 
+  // Return public highest end time across all aTracks.
+  // Monitor must be held.
+  media::TimeUnit HighestEndTime(nsTArray<const media::TimeIntervals*>& aTracks) const;
+
   // Set to true if mediasource state changed to ended.
   Atomic<bool> mEnded;
 
   // Global size of this source buffer content.
   Atomic<int64_t> mSizeSourceBuffer;
   const int64_t mVideoEvictionThreshold;
   const int64_t mAudioEvictionThreshold;
   enum class EvictionState
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -681,27 +681,22 @@ PluginModuleParent::PluginModuleParent(b
     , mTaskFactory(this)
     , mSandboxLevel(0)
     , mIsFlashPlugin(false)
     , mIsStartingAsync(false)
     , mNPInitialized(false)
     , mIsNPShutdownPending(false)
     , mAsyncNewRv(NS_ERROR_NOT_INITIALIZED)
 {
-#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
-    mIsStartingAsync = aAllowAsyncInit &&
-                       Preferences::GetBool(kAsyncInitPref, false) &&
-                       !BrowserTabsRemoteAutostart();
 #if defined(MOZ_CRASHREPORTER)
     CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AsyncPluginInit"),
                                        mIsStartingAsync ?
                                            NS_LITERAL_CSTRING("1") :
                                            NS_LITERAL_CSTRING("0"));
 #endif
-#endif
 }
 
 PluginModuleParent::~PluginModuleParent()
 {
     if (!OkToCleanup()) {
         NS_RUNTIMEABORT("unsafe destruction");
     }
 
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -9,17 +9,16 @@
 #include "WindowsMessageLoop.h"
 #include "Neutering.h"
 #include "MessageChannel.h"
 
 #include "nsAutoPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsIXULAppInfo.h"
-#include "nsWindowsDllInterceptor.h"
 #include "WinUtils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/PaintTracker.h"
 #include "mozilla/WindowsVersion.h"
 
 using namespace mozilla;
 using namespace mozilla::ipc;
@@ -432,105 +431,16 @@ ProcessOrDeferMessage(HWND hwnd,
     NS_ASSERTION(gDeferredMessages, "Out of memory!");
   }
 
   // Save for later. The array takes ownership of |deferred|.
   gDeferredMessages->AppendElement(deferred);
   return res;
 }
 
-/*
- * It is bad to subclass a window when neutering is active because you'll end
- * up subclassing the *neutered* window procedure instead of the real window
- * procedure. Since CreateWindow* fires WM_CREATE (and could thus trigger
- * neutering), we intercept these calls and suppress neutering for the duration
- * of the call. This ensures that any subsequent subclassing replaces the
- * correct window procedure.
- */
-WindowsDllInterceptor sUser32Interceptor;
-typedef HWND (WINAPI *CreateWindowExWPtr)(DWORD,LPCWSTR,LPCWSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
-typedef HWND (WINAPI *CreateWindowExAPtr)(DWORD,LPCSTR,LPCSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
-typedef HWND (WINAPI *CreateWindowWPtr)(LPCWSTR,LPCWSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
-typedef HWND (WINAPI *CreateWindowAPtr)(LPCSTR,LPCSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID);
-
-CreateWindowExWPtr sCreateWindowExWStub = nullptr;
-CreateWindowExAPtr sCreateWindowExAStub = nullptr;
-CreateWindowWPtr sCreateWindowWStub = nullptr;
-CreateWindowAPtr sCreateWindowAStub = nullptr;
-
-HWND WINAPI
-CreateWindowExWHook(DWORD aExStyle, LPCWSTR aClassName, LPCWSTR aWindowName,
-                    DWORD aStyle, int aX, int aY, int aWidth, int aHeight,
-                    HWND aParent, HMENU aMenu, HINSTANCE aInstance,
-                    LPVOID aParam)
-{
-  SuppressedNeuteringRegion doNotNeuterThisWindowYet;
-  return sCreateWindowExWStub(aExStyle, aClassName, aWindowName, aStyle, aX, aY,
-                              aWidth, aHeight, aParent, aMenu, aInstance, aParam);
-}
-
-HWND WINAPI
-CreateWindowExAHook(DWORD aExStyle, LPCSTR aClassName, LPCSTR aWindowName,
-                    DWORD aStyle, int aX, int aY, int aWidth, int aHeight,
-                    HWND aParent, HMENU aMenu, HINSTANCE aInstance,
-                    LPVOID aParam)
-{
-  SuppressedNeuteringRegion doNotNeuterThisWindowYet;
-  return sCreateWindowExAStub(aExStyle, aClassName, aWindowName, aStyle, aX, aY,
-                              aWidth, aHeight, aParent, aMenu, aInstance, aParam);
-}
-
-HWND WINAPI
-CreateWindowWHook(LPCWSTR aClassName, LPCWSTR aWindowName, DWORD aStyle, int aX,
-                  int aY, int aWidth, int aHeight, HWND aParent, HMENU aMenu,
-                  HINSTANCE aInstance, LPVOID aParam)
-{
-  SuppressedNeuteringRegion doNotNeuterThisWindowYet;
-  return sCreateWindowWStub(aClassName, aWindowName, aStyle, aX, aY, aWidth,
-                            aHeight, aParent, aMenu, aInstance, aParam);
-}
-
-HWND WINAPI
-CreateWindowAHook(LPCSTR aClassName, LPCSTR aWindowName, DWORD aStyle, int aX,
-                  int aY, int aWidth, int aHeight, HWND aParent, HMENU aMenu,
-                  HINSTANCE aInstance, LPVOID aParam)
-{
-  SuppressedNeuteringRegion doNotNeuterThisWindowYet;
-  return sCreateWindowAStub(aClassName, aWindowName, aStyle, aX, aY, aWidth,
-                            aHeight, aParent, aMenu, aInstance, aParam);
-}
-
-void
-InitCreateWindowHook()
-{
-  // Forcing these interceptions to be detours due to conflicts with
-  // NVIDIA Optimus DLLs that are injected into our process.
-  sUser32Interceptor.Init("user32.dll");
-  if (!sCreateWindowExWStub) {
-    sUser32Interceptor.AddDetour("CreateWindowExW",
-                               reinterpret_cast<intptr_t>(CreateWindowExWHook),
-                               (void**) &sCreateWindowExWStub);
-  }
-  if (!sCreateWindowExAStub) {
-    sUser32Interceptor.AddDetour("CreateWindowExA",
-                               reinterpret_cast<intptr_t>(CreateWindowExAHook),
-                               (void**) &sCreateWindowExAStub);
-  }
-  if (!sCreateWindowWStub) {
-    sUser32Interceptor.AddDetour("CreateWindowW",
-                               reinterpret_cast<intptr_t>(CreateWindowWHook),
-                               (void**) &sCreateWindowWStub);
-  }
-  if (!sCreateWindowAStub) {
-    sUser32Interceptor.AddDetour("CreateWindowA",
-                               reinterpret_cast<intptr_t>(CreateWindowAHook),
-                               (void**) &sCreateWindowAStub);
-  }
-}
-
 } // namespace
 
 // We need the pointer value of this in PluginInstanceChild.
 LRESULT CALLBACK
 NeuteredWindowProc(HWND hwnd,
                    UINT uMsg,
                    WPARAM wParam,
                    LPARAM lParam)
@@ -797,18 +707,16 @@ InitUIThread()
                                     NULL, &WinEventHook, GetCurrentProcessId(),
                                     gUIThreadId, WINEVENT_OUTOFCONTEXT);
 
     // We need to execute this after setting the hook in case the OLE window
     // already existed.
     gCOMWindow = FindCOMWindow();
   }
   MOZ_ASSERT(gWinEventHook);
-
-  InitCreateWindowHook();
 }
 
 } // namespace windows
 } // namespace ipc
 } // namespace mozilla
 
 // See SpinInternalEventLoop below
 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1017,17 +1017,17 @@ fuzzy-if(skiaContent,1,5) == 421632-1.ht
 != 421710-1.html about:blank
 fails-if(Android) fails-if(usesRepeatResampling) == 421885-1.xml 421885-1-ref.xml
 == 421955-1.html 421955-1-ref.html
 == 422249-1.html 422249-1-ref.html
 == 422394-1.html 422394-1-ref.html
 == 422678-1.html 422678-1-ref.html
 == 423130-1.html 423130-1-ref.html
 == 423385-1.html 423385-1-ref.html
-== 423599-1.html 423599-1-ref.html
+random-if(gtkWidget) == 423599-1.html 423599-1-ref.html # bug 1309095
 == 423676-1.html 423676-1-ref.html
 fails == 423823-1.html 423823-1-ref.html # scrolling rowgroups were removed in bug 28800
 == 424074-1.xul 424074-1-ref.xul
 fails-if(Android) != 424074-1.xul 424074-1-ref2.xul
 random-if(gtkWidget) == 424074-1-ref2.xul 424074-1-ref3.xul
 == 424236-1.html 424236-1-ref.html
 == 424236-2.html 424236-2-ref.html
 == 424236-3.html 424236-3-ref.html
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -74,21 +74,21 @@ skip-if(Android) == grid-auto-min-sizing
 == grid-flex-min-sizing-002.html grid-flex-min-sizing-002-ref.html
 == grid-item-align-001.html grid-item-align-001-ref.html
 == grid-item-align-002.html grid-item-align-002-ref.html
 == grid-item-align-003.html grid-item-align-003-ref.html
 == grid-item-justify-001.html grid-item-justify-001-ref.html
 == grid-item-justify-002.html grid-item-justify-002-ref.html
 == grid-item-stretch-001.html grid-item-stretch-001-ref.html
 == grid-item-self-baseline-001.html grid-item-self-baseline-001-ref.html
-skip-if(!gtkWidget) == grid-item-content-baseline-001.html grid-item-content-baseline-001-ref.html # depends on exact Ahem baseline font metrics which seems to differ between platforms
-skip-if(!gtkWidget) == grid-item-content-baseline-002.html grid-item-content-baseline-002-ref.html # ditto
-skip-if(!gtkWidget) == grid-item-mixed-baseline-001.html grid-item-mixed-baseline-001-ref.html # ditto
-skip-if(!gtkWidget) == grid-item-mixed-baseline-002.html grid-item-mixed-baseline-002-ref.html # ditto
-skip-if(!gtkWidget) == grid-item-mixed-baseline-003.html grid-item-mixed-baseline-003-ref.html # ditto
+random-if(http.oscpu!="Linux\u0020i686") == grid-item-content-baseline-001.html grid-item-content-baseline-001-ref.html # depends on exact Ahem baseline font metrics which seems to differ between platforms: bug 1310792
+random-if(http.oscpu!="Linux\u0020i686") == grid-item-content-baseline-002.html grid-item-content-baseline-002-ref.html # ditto
+random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-001.html grid-item-mixed-baseline-001-ref.html # ditto
+random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-002.html grid-item-mixed-baseline-002-ref.html # ditto
+random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-003.html grid-item-mixed-baseline-003-ref.html # ditto
 skip-if(!gtkWidget) == grid-item-mixed-baseline-004.html grid-item-mixed-baseline-004-ref.html # ditto
 == grid-align-content-001.html grid-align-content-001-ref.html
 == grid-justify-content-001.html grid-justify-content-001-ref.html
 skip-if(Android&&isDebugBuild) == grid-justify-content-002.html grid-justify-content-002-ref.html # Bug 1245884 - slow
 skip-if(Android&&isDebugBuild) == grid-justify-content-003.html grid-justify-content-003-ref.html # Bug 1245884 - slow
 skip-if(!gtkWidget) == grid-container-baselines-001.html grid-container-baselines-001-ref.html
 skip-if(!gtkWidget) == grid-container-baselines-002.html grid-container-baselines-002-ref.html
 skip-if(!gtkWidget) == grid-container-baselines-003.html grid-container-baselines-003-ref.html
--- a/layout/reftests/first-letter/reftest.list
+++ b/layout/reftests/first-letter/reftest.list
@@ -59,16 +59,16 @@ HTTP(..) == 329069-5.html 329069-5-ref.h
 == 469227-1.html 469227-1-ref.html
 == 484400-1.html 484400-1-ref.html
 == 594303-1.html 594303-1-ref.html
 fails-if(winWidget||cocoaWidget) == 617869-1.html 617869-1-ref.html
 == 723509-1.html 723509-1-ref.html
 == 922550-1.html 922550-1-ref.html
 == 958249.html 958249-ref.html
 == font-text-styles.html font-text-styles-ref.html
-fails-if(gtkWidget) random-if(winWidget&&!d2d) == font-text-styles-floater.html font-text-styles-floater-ref.html # bug 992846
+random-if(gtkWidget) random-if(winWidget&&!d2d) == font-text-styles-floater.html font-text-styles-floater-ref.html # bug 992846
 == inline-height-empty.html inline-height-empty-ref.html
 HTTP(..) == indic-clusters-1.html indic-clusters-1-ref.html
 == overflow-float-nooverflow.html overflow-float-nooverflow-ref.html
 == overflow-float-overflow.html overflow-float-overflow-notref.html
 == overflow-inline-nooverflow.html overflow-inline-nooverflow-ref.html
 != overflow-inline-overflow.html overflow-inline-overflow-notref.html
 == overflow-inline-overflow.html overflow-inline-overflow-ref.html
--- a/layout/reftests/flexbox/reftest.list
+++ b/layout/reftests/flexbox/reftest.list
@@ -9,19 +9,20 @@
 # tests over to the w3c-css directory, so that they can become part of the
 # W3C's test suite.
 
 # SUBDIRECTORY: Reftests for paginated flex containers
 include pagination/reftest.list
 
 # Tests for cross-axis alignment (align-self / align-items properties)
 fails == flexbox-align-self-baseline-horiz-2.xhtml  flexbox-align-self-baseline-horiz-2-ref.xhtml # bug 793456, and possibly others
-# This one fails on windows R (but not Ru, strangely). On Windows R, the
-# single-line <label> flex item has a different background size in test vs. ref
-fuzzy-if(cocoaWidget,1,2) random-if(winWidget) == flexbox-align-self-baseline-horiz-3.xhtml  flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate
+# This one fails on windows R (but not Ru, strangely) and GTK.
+# On Windows R and GTK, the single-line <label> flex item has a different
+# background size in test vs. ref
+fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) == flexbox-align-self-baseline-horiz-3.xhtml  flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate
 == flexbox-align-self-baseline-horiz-4.xhtml flexbox-align-self-baseline-horiz-4-ref.xhtml
 
 # Tests for box-sizing on flex containers and flex items.
 == flexbox-box-sizing-on-container-horiz-1.html flexbox-box-sizing-on-container-horiz-1-ref.html
 == flexbox-box-sizing-on-container-vert-1.html flexbox-box-sizing-on-container-vert-1-ref.html
 == flexbox-box-sizing-on-items-horiz-1a.html flexbox-box-sizing-on-items-horiz-1-ref.html
 == flexbox-box-sizing-on-items-horiz-1b.html flexbox-box-sizing-on-items-horiz-1-ref.html
 == flexbox-box-sizing-on-items-vert-1a.html flexbox-box-sizing-on-items-vert-1-ref.html
--- a/layout/reftests/font-face/reftest.list
+++ b/layout/reftests/font-face/reftest.list
@@ -1,28 +1,28 @@
 # Tests using downloaded fonts use HTTP(..) because they use fonts in
 # ../fonts/.  We can't use file:/// URLs because of cross-directory access
 # restrictions on file: URLs.
 
 HTTP(..) != download-1.html download-1-notref.html
 HTTP(..) == download-2.html download-2-ref.html
 HTTP(..) != download-2.html about:blank
-random-if(winWidget) HTTP(..) == download-2-big.html download-2-big-otf.html # bug 470713
+random-if(winWidget||gtkWidget) HTTP(..) == download-2-big.html download-2-big-otf.html # bug 470713
 HTTP(..) != download-2-big-otf.html about:blank
 asserts-if(Android&&!asyncPan,1-8) HTTP(..) != download-3-notref.html download-3.html # bug 1019192
 asserts-if(Android,0-8) HTTP(..) == download-3-ref.html download-3.html # same bugs as above
 asserts-if(Android,0-8) HTTP(..) == fallback-to-system-1.html fallback-to-system-1-ref.html # just delayed assertions from above tests
 HTTP(..) == name-override-simple-1.html name-override-simple-1-ref.html
 HTTP(..) != name-override-simple-1.html download-1-notref.html
 fails HTTP(..) == name-override-1.html name-override-1-ref.html
 HTTP(..) == multiple-descriptor-1.html multiple-descriptor-1-ref.html
 HTTP(..) != multiple-descriptor-1.html multiple-descriptor-1-notref.html
 HTTP(..) == src-list-1.html src-list-1-ref.html
 HTTP(..) == src-list-2.html src-list-2-ref.html
-random-if(winWidget) HTTP(..) == src-list-2-big-otf.html src-list-2-big-ref.html # bug 470713
+random-if(winWidget||gtkWidget) HTTP(..) == src-list-2-big-otf.html src-list-2-big-ref.html # bug 470713
 HTTP(..) == src-list-format-1.html src-list-format-1-ref.html
 HTTP(..) == src-list-format-2.html src-list-format-2-ref.html
 HTTP(..) == src-list-format-3.html src-list-format-3-ref.html
 HTTP(..) == src-list-format-4.html src-list-format-1-ref.html
 HTTP(..) == src-list-format-5.html src-list-format-2-ref.html
 HTTP(..) == src-list-format-6.html src-list-format-3-ref.html
 # assumes AAT fonts are only supported on MacOS
 random-if(cocoaWidget) HTTP(..) == src-list-format-7.html src-list-format-2-ref.html
--- a/layout/reftests/font-matching/reftest.list
+++ b/layout/reftests/font-matching/reftest.list
@@ -45,17 +45,17 @@ random-if(cocoaWidget) != impact-bold.ht
 
 # localized font family names should always match just as English names do
 == localized-family-names-001.html localized-family-names-001-ref.html
 == localized-family-names-002.html localized-family-names-002-ref.html
 == localized-family-names-003.html localized-family-names-003-ref.html
 == localized-family-names-004.html localized-family-names-004-ref.html
 
 # family names with escaped spaces shouldn't match the names without the spaces
-== familyname-escapedidents.html familyname-escapedidents-ref.html
+fails-if(http.oscpu=="Linux\u0020x86_64") == familyname-escapedidents.html familyname-escapedidents-ref.html # bug 1309425
 
 # weight mapping tests
 HTTP(..) == normalmedium.html normalmedium-ref.html
 HTTP(..) != normalmedium.html normalmedium-notref.html
 
 # Linux fails due to bug 604815
 fuzzy-if(OSX==1010&&browserIsRemote,1,23) HTTP(..) == weightmapping-12.html weightmapping-12-ref.html
 HTTP(..) == weightmapping-25.html weightmapping-25-ref.html
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -1,11 +1,11 @@
 == dir-1.html dir-1-ref.html
 == dir-2.html dir-2-ref.html
-== dir-3.html dir-3-ref.html
+random-if(gtkWidget) == dir-3.html dir-3-ref.html # bug 1309426
 == dir-4.html dir-4-ref.html
 == dir-5.html dir-5-ref.html
 == dir-6.html dir-6-ref.html
 == dir-6a.html dir-6a-ref.html
 == dir-7.html dir-7-ref.html
 fails == dir-8.html dir-8-ref.html
 fails == dir-9.html dir-9-ref.html # Bug 787215
 == dir-10.html dir-10-ref.html
@@ -37,18 +37,18 @@ random-if(smallScreen&&Android) fuzzy(25
 == mfenced-5b.xhtml mfenced-5-ref.xhtml
 == mfenced-5c.xhtml mfenced-5-ref.xhtml
 == mfenced-5d.xhtml mfenced-5-ref.xhtml
 == mfenced-6.html mfenced-6-ref.html
 == mfenced-7.html mfenced-7-ref.html
 != mfenced-8.html mfenced-8-ref.html
 == mfenced-9.html mfenced-9-ref.html
 fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == mfenced-10.html mfenced-10-ref.html # Windows versions without Cambria Math, see bug 670592
-== mfenced-11.html mfenced-11-ref.html
-== mfenced-12.html mfenced-12-ref.html
+fails-if(http.oscpu=="Linux\u0020x86_64") == mfenced-11.html mfenced-11-ref.html # bug 670592
+fails-if(http.oscpu=="Linux\u0020x86_64") == mfenced-12.html mfenced-12-ref.html # bug 670592
 == mi-mathvariant-1.xhtml mi-mathvariant-1-ref.xhtml
 == mi-mathvariant-2.xhtml mi-mathvariant-2-ref.xhtml
 != mi-mathvariant-3.html mi-mathvariant-3-ref.html
 != non-spacing-accent-1.xhtml non-spacing-accent-1-ref.xhtml
 == overbar-width-1.xhtml overbar-width-1-ref.xhtml
 == quotes-1.xhtml quotes-1-ref.xhtml
 != stretchy-underbar-1.xhtml stretchy-underbar-1-ref.xhtml
 != stretchy-munderover-1a.html stretchy-munderover-1-ref.html
@@ -108,17 +108,17 @@ fails == stretchy-mover-2a.html stretchy
 != embellished-op-3-3.html embellished-op-3-3-ref.html
 != embellished-op-3-4.html embellished-op-3-4-ref.html
 != embellished-op-3-5.html embellished-op-3-5-ref.html
 == embellished-op-4-1.html embellished-op-4-1-ref.html
 == embellished-op-4-2.html embellished-op-4-2-ref.html
 == embellished-op-4-3.html embellished-op-4-3-ref.html
 == embellished-op-5-1.html embellished-op-5-ref.html
 == embellished-op-5-2.html embellished-op-5-ref.html
-random-if(winWidget&&!/^Windows\x20NT\x205\.1/.test(http.oscpu)) == semantics-1.xhtml semantics-1-ref.xhtml # Windows versions with Cambria Math
+fails-if(http.oscpu=="Linux\u0020x86_64") random-if(winWidget&&!/^Windows\x20NT\x205\.1/.test(http.oscpu)) == semantics-1.xhtml semantics-1-ref.xhtml # Windows versions with Cambria Math, Linux x86_64:bug 1309429
 == semantics-2.html semantics-2-ref.html
 == semantics-3.html semantics-3-ref.html
 == semantics-4.html semantics-4-ref.html
 != mathcolor-1.xml mathcolor-1-ref.xml
 != mathcolor-2.xml mathcolor-2-ref.xml
 != mathcolor-3.xml mathcolor-3-ref.xml
 == mathcolor-4.xml mathcolor-4-ref.xml
 != mathbackground-1.xml mathbackground-1-ref.xml
@@ -139,19 +139,19 @@ fails-if(skiaContent&&OSX>=1010) == scal
 == mspace-1.html mspace-1-ref.html
 == mpadded-1.html mpadded-1-ref.html
 == mpadded-2.html mpadded-2-ref.html
 == mpadded-3.html mpadded-3-ref.html
 == mpadded-4.html mpadded-4-ref.html
 == mpadded-5.html mpadded-5-ref.html
 == mpadded-1-2.html mpadded-1-2-ref.html
 == mpadded-6.html mpadded-6-ref.html
-== mpadded-7.html mpadded-7-ref.html
-== mpadded-8.html mpadded-8-ref.html
-== mpadded-9.html mpadded-9-ref.html
+random-if(gtkWidget) == mpadded-7.html mpadded-7-ref.html # bug 1309430
+random-if(gtkWidget) == mpadded-8.html mpadded-8-ref.html # bug 1309430
+random-if(gtkWidget) == mpadded-9.html mpadded-9-ref.html # bug 1309430
 == math-display.html math-display-ref.html
 == scriptlevel-1.html scriptlevel-1-ref.html
 == scriptlevel-movablelimits-1.html scriptlevel-movablelimits-1-ref.html
 == munderover-align-accent-false.html munderover-align-accent-false-ref.html
 == munderover-align-accent-true.html munderover-align-accent-true-ref.html
 == munder-mover-align-accent-true.html munder-mover-align-accent-true-ref.html
 == munder-mover-align-accent-false.html munder-mover-align-accent-false-ref.html
 == mfrac-linethickness-1.xhtml mfrac-linethickness-1-ref.xhtml
@@ -187,17 +187,17 @@ fails-if(skiaContent&&OSX>=1010) == scal
 == maction-dynamic-1.html maction-dynamic-1-ref.html # bug 773482
 == maction-dynamic-2.html maction-dynamic-2-ref.html
 == mo-lspace-rspace.html mo-lspace-rspace-ref.html
 == mo-lspace-rspace-2.html mo-lspace-rspace-2-ref.html
 == mo-lspace-rspace-3.html mo-lspace-rspace-3-ref.html
 == mo-lspace-rspace-4.html mo-lspace-rspace-4-ref.html
 == mo-invisibleoperators.html mo-invisibleoperators-ref.html
 == mo-invisibleoperators-2.html mo-invisibleoperators-2-ref.html
-== mo-glyph-size.html mo-glyph-size-ref.html
+random-if(gtkWidget) == mo-glyph-size.html mo-glyph-size-ref.html # bug 1309426
 == maction-dynamic-3.html maction-dynamic-3-ref.html # bug 773482
 == whitespace-trim-1.html whitespace-trim-1-ref.html
 == whitespace-trim-2.html whitespace-trim-2-ref.html
 == whitespace-trim-3.html whitespace-trim-3-ref.html
 fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 787215
 == whitespace-trim-5.html whitespace-trim-5-ref.html
 random-if(winWidget&&!d2d) == opentype-stretchy.html opentype-stretchy-ref.html
 == opentype-fraction-dynamic-linethickness.html opentype-fraction-dynamic-linethickness-ref.html
@@ -219,31 +219,31 @@ fuzzy-if(skiaContent,1,80) fails-if(winW
 != menclose-1j.html menclose-1-ref.html
 != menclose-1k.html menclose-1-ref.html
 != menclose-1l.html menclose-1-ref.html
 != menclose-1m.html menclose-1-ref.html
 != menclose-1n.html menclose-1-ref.html
 != menclose-1o.html menclose-1-ref.html
 != menclose-1p.html menclose-1-ref.html
 != menclose-1q.html menclose-1-ref.html
-== menclose-2-actuarial.html menclose-2-actuarial-ref.html
-== menclose-2-bottom.html menclose-2-bottom-ref.html
-== menclose-2-box.html menclose-2-box-ref.html
+random-if(gtkWidget) == menclose-2-actuarial.html menclose-2-actuarial-ref.html # bug 1309426
+random-if(gtkWidget) == menclose-2-bottom.html menclose-2-bottom-ref.html # bug 1309426
+random-if(gtkWidget) == menclose-2-box.html menclose-2-box-ref.html # bug 1309426
 == menclose-2-circle.html menclose-2-circle-ref.html
 == menclose-2-downdiagonalstrike.html menclose-2-downdiagonalstrike-ref.html
 == menclose-2-horizontalstrike.html menclose-2-horizontalstrike-ref.html
 == menclose-2-left.html menclose-2-left-ref.html
 fuzzy-if(skiaContent,80,5) == menclose-2-longdiv.html menclose-2-longdiv-ref.html
-== menclose-2-right.html menclose-2-right-ref.html
-== menclose-2-roundedbox.html menclose-2-roundedbox-ref.html
+random-if(gtkWidget) == menclose-2-right.html menclose-2-right-ref.html # bug 1309426
+random-if(gtkWidget) == menclose-2-roundedbox.html menclose-2-roundedbox-ref.html # bug 1309426
 == menclose-2-top.html menclose-2-top-ref.html
-== menclose-2-updiagonalarrow.html menclose-2-updiagonalarrow-ref.html
+random-if(gtkWidget) == menclose-2-updiagonalarrow.html menclose-2-updiagonalarrow-ref.html # bug 1309426
 == menclose-2-updiagonalstrike.html menclose-2-updiagonalstrike-ref.html
 == menclose-2-verticalstrike.html menclose-2-verticalstrike-ref.html
-== menclose-2-roundedbox.html menclose-2-roundedbox-ref.html
+random-if(gtkWidget) == menclose-2-roundedbox.html menclose-2-roundedbox-ref.html # bug 1309426
 == menclose-2-phasorangle.html menclose-2-phasorangle-ref.html
 == menclose-3-box.html menclose-3-box-ref.html
 == menclose-3-madruwb.html menclose-3-madruwb-ref.html
 fails-if(Android||OSX==1006) == menclose-3-radical.html menclose-3-radical-ref.html # Bug 973917
 == menclose-3-default.html menclose-3-default-ref.html
 == menclose-3-invalid.html menclose-3-invalid-ref.html
 == menclose-3-multiple.html menclose-3-multiple-ref.html
 == menclose-3-unknown.html menclose-3-unknown.html
@@ -313,17 +313,17 @@ fails-if(Android||/^Windows\x20NT\x205\.
 != columnlines-3-1.html columnlines-3-1-ref.html
 == columnlines-3-2.html columnlines-3-2-ref.html
 == rowlines-1a.html rowlines-1-ref.html
 != rowlines-1b.html rowlines-1-ref.html
 != rowlines-1c.html rowlines-1-ref.html
 == rowlines-2a.html rowlines-2-ref.html
 == rowlines-2b.html rowlines-2-ref.html
 != rowlines-3-1.html rowlines-3-1-ref.html
-== rowlines-3-2.html rowlines-3-2-ref.html
+random-if(gtkWidget) == rowlines-3-2.html rowlines-3-2-ref.html # bug 1309426
 == tablespacing-1.html tablespacing-1-ref.html
 == tablespacing-2.html tablespacing-2-ref.html
 == tablespacing-3.html tablespacing-3-ref.html
 == tablespacing-4.html tablespacing-4-ref.html
 == tablespacing-5.html tablespacing-5-ref.html
 == tablespacing-5a.html tablespacing-5a-ref.html
 == tablespacing-6.html tablespacing-6-ref.html
 == tablespacing-7.html tablespacing-7-ref.html
--- a/layout/reftests/text-overflow/reftest.list
+++ b/layout/reftests/text-overflow/reftest.list
@@ -1,33 +1,33 @@
 == ellipsis-font-fallback.html ellipsis-font-fallback-ref.html
 == line-clipping.html line-clipping-ref.html
 fuzzy-if(Android,16,244) HTTP(..) == marker-basic.html marker-basic-ref.html  # Bug 1128229
 HTTP(..) == marker-string.html marker-string-ref.html
 skip-if(Android) HTTP(..) == bidi-simple.html bidi-simple-ref.html # Fails on Android due to anti-aliasing
 skip-if(!gtkWidget) fuzzy-if(gtkWidget,2,289) HTTP(..) == bidi-simple-scrolled.html bidi-simple-scrolled-ref.html # Fails on Windows and OSX due to anti-aliasing
 fuzzy-if(Android,24,4000) fuzzy-if(cocoaWidget,1,40) fuzzy-if(asyncPan&&!layersGPUAccelerated,121,1836) HTTP(..) == scroll-rounding.html scroll-rounding-ref.html # bug 760264
-fuzzy(2,453) fuzzy-if(skiaContent,9,2100) HTTP(..) == anonymous-block.html anonymous-block-ref.html
+fuzzy(2,453) fuzzy-if(skiaContent,9,2100) fails-if(gtkWidget) HTTP(..) == anonymous-block.html anonymous-block-ref.html # gtkWidget:bug 1309103
 HTTP(..) == false-marker-overlap.html false-marker-overlap-ref.html
 HTTP(..) == visibility-hidden.html visibility-hidden-ref.html
 fuzzy-if(asyncPan&&!layersGPUAccelerated,102,1724) fuzzy-if(gtkWidget,10,8) HTTP(..) == block-padding.html block-padding-ref.html
 HTTP(..) == quirks-decorations.html quirks-decorations-ref.html
 HTTP(..) == quirks-line-height.html quirks-line-height-ref.html
 HTTP(..) == standards-decorations.html standards-decorations-ref.html
 HTTP(..) == standards-line-height.html standards-line-height-ref.html
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) fuzzy-if(skiaContent,1,4200) HTTP(..) == selection.html selection-ref.html # bug 668849
 HTTP(..) == marker-shadow.html marker-shadow-ref.html
 == aligned-baseline.html aligned-baseline-ref.html
 skip-if(Android) fuzzy-if(skiaContent,1,5) == clipped-elements.html clipped-elements-ref.html
 HTTP(..) == theme-overflow.html theme-overflow-ref.html
 HTTP(..) == table-cell.html table-cell-ref.html
 fuzzy-if(gtkWidget,10,32) HTTP(..) == two-value-syntax.html two-value-syntax-ref.html
 HTTP(..) == single-value.html single-value-ref.html
 fuzzy-if(gtkWidget,10,2) HTTP(..) == atomic-under-marker.html atomic-under-marker-ref.html
-fuzzy(1,2616) skip-if(Android) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,12352) HTTP(..) == xulscroll.html xulscroll-ref.html
+fuzzy(1,2616) skip-if(Android) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,12352) fails-if(http.oscpu=="Linux\u0020x86_64") HTTP(..) == xulscroll.html xulscroll-ref.html # Linux x86_64:bug 1309107
 HTTP(..) == combobox-zoom.html combobox-zoom-ref.html
 
 # The vertical-text pref setting can be removed after bug 1138384 lands
 == vertical-decorations-1.html vertical-decorations-1-ref.html
 == vertical-decorations-2.html vertical-decorations-2-ref.html
 != vertical-decorations-1.html vertical-decorations-1-2-notref.html
 != vertical-decorations-2.html vertical-decorations-1-2-notref.html
 == vertical-decorations-3.html vertical-decorations-3-ref.html
--- a/layout/reftests/text/reftest.list
+++ b/layout/reftests/text/reftest.list
@@ -139,17 +139,17 @@ fuzzy-if(gtkWidget,1,177) fuzzy-if(skiaC
 != overflowwrap-09.html wordwrap-01-ref.html
 == wordwrap-09.html wordwrap-09-ref.html
 == overflowwrap-09.html wordwrap-09-ref.html
 == wordwrap-10.html wordwrap-10-ref.html # bug 752459
 == overflowwrap-10.html wordwrap-10-ref.html
 == word-spacing-01.html word-spacing-01-ref.html
 # the following will fail when rendering with Core Text (see bug 389074) due to what appears to be
 # an Apple bug: the presence of ZWNJ disturbs the positioning of an adjacent glyph. rdar://6427865
-random-if(cocoaWidget) HTTP(..) == zwnj-01.xhtml zwnj-01-ref.xhtml
+random-if(cocoaWidget) random-if(gtkWidget) HTTP(..) == zwnj-01.xhtml zwnj-01-ref.xhtml # gtkWidget:bug 1309113
 HTTP(..) == zwnj-02.xhtml zwnj-02-ref.xhtml # HTTP(..) for ../filters.svg
 != zwnj-01.html zwnj-01-notref.html
 == initial-zwj-1.html initial-zwj-1-ref.html
 == cgj-01.html cgj-01-ref.html
 == 444656.html 444656-ref.html
 == 449555-1.html 449555-1-ref.html
 == 467722.html 467722-ref.html
 fuzzy-if(skiaContent,1,600) HTTP(..) == 475092-sub.html 475092-ref.html
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -864,16 +864,29 @@ Gecko_DestroyClipPath(mozilla::StyleClip
 
 mozilla::StyleBasicShape*
 Gecko_NewBasicShape(mozilla::StyleBasicShapeType aType)
 {
   RefPtr<StyleBasicShape> ptr = new mozilla::StyleBasicShape(aType);
   return ptr.forget().take();
 }
 
+void
+Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len)
+{
+  effects->mFilters.Clear();
+  effects->mFilters.SetLength(new_len);
+}
+
+void
+Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest)
+{
+  aDest->mFilters = aSrc->mFilters;
+}
+
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
 
 nsCSSShadowArray*
 Gecko_NewCSSShadowArray(uint32_t aLen)
 {
   RefPtr<nsCSSShadowArray> arr = new(aLen) nsCSSShadowArray(aLen);
   return arr.forget().take();
 }
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -313,16 +313,19 @@ void Gecko_ResetStyleCoord(nsStyleUnit* 
 // Set an nsStyleCoord to a computed `calc()` value
 void Gecko_SetStyleCoordCalcValue(nsStyleUnit* unit, nsStyleUnion* value, nsStyleCoord::CalcValue calc);
 
 void Gecko_CopyClipPathValueFrom(mozilla::StyleClipPath* dst, const mozilla::StyleClipPath* src);
 
 void Gecko_DestroyClipPath(mozilla::StyleClipPath* clip);
 mozilla::StyleBasicShape* Gecko_NewBasicShape(mozilla::StyleBasicShapeType type);
 
+void Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len);
+void Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest);
+
 void Gecko_FillAllBackgroundLists(nsStyleImageLayers* layers, uint32_t max_len);
 void Gecko_FillAllMaskLists(nsStyleImageLayers* layers, uint32_t max_len);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
 
 nsCSSShadowArray* Gecko_NewCSSShadowArray(uint32_t len);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray);
 
 nsStyleQuoteValues* Gecko_NewStyleQuoteValues(uint32_t len);
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -3600,17 +3600,17 @@ struct nsStyleFilter
 
   nsStyleFilter& operator=(const nsStyleFilter& aOther);
 
   bool operator==(const nsStyleFilter& aOther) const;
   bool operator!=(const nsStyleFilter& aOther) const {
     return !(*this == aOther);
   }
 
-  int32_t GetType() const {
+  uint32_t GetType() const {
     return mType;
   }
 
   const nsStyleCoord& GetFilterParameter() const {
     NS_ASSERTION(mType != NS_STYLE_FILTER_DROP_SHADOW &&
                  mType != NS_STYLE_FILTER_URL &&
                  mType != NS_STYLE_FILTER_NONE, "wrong filter type");
     return mFilterParameter;
@@ -3629,17 +3629,17 @@ struct nsStyleFilter
     NS_ASSERTION(mType == NS_STYLE_FILTER_DROP_SHADOW, "wrong filter type");
     return mDropShadow;
   }
   void SetDropShadow(nsCSSShadowArray* aDropShadow);
 
 private:
   void ReleaseRef();
 
-  int32_t mType; // see NS_STYLE_FILTER_* constants in nsStyleConsts.h
+  uint32_t mType; // see NS_STYLE_FILTER_* constants in nsStyleConsts.h
   nsStyleCoord mFilterParameter; // coord, percent, factor, angle
   union {
     mozilla::css::URLValue* mURL;
     nsCSSShadowArray* mDropShadow;
   };
 };
 
 template<>
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -1105,17 +1105,18 @@ BookmarksTracker.prototype = {
     }
   },
 
   // This method is oddly structured, but the idea is to return as quickly as
   // possible -- this handler gets called *every time* a bookmark changes, for
   // *each change*.
   onItemChanged: function BMT_onItemChanged(itemId, property, isAnno, value,
                                             lastModified, itemType, parentId,
-                                            guid, parentGuid, source) {
+                                            guid, parentGuid, oldValue,
+                                            source) {
     if (IGNORED_SOURCES.includes(source)) {
       return;
     }
 
     if (isAnno && (ANNOS_TO_TRACK.indexOf(property) == -1))
       // Ignore annotations except for the ones that we sync.
       return;
 
--- a/services/sync/tests/unit/test_bookmark_engine.js
+++ b/services/sync/tests/unit/test_bookmark_engine.js
@@ -1,36 +1,44 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 Cu.import("resource://gre/modules/PlacesSyncUtils.jsm");
 Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 initTestLogging("Trace");
 
 Service.engineManager.register(BookmarksEngine);
 
 add_task(function* test_change_during_sync() {
   _("Ensure that we track changes made during a sync.");
 
-  let engine = new BookmarksEngine(Service);
-  let store  = engine._store;
+  let engine  = new BookmarksEngine(Service);
+  let store   = engine._store;
+  let tracker = engine._tracker;
   let server = serverForFoo(engine);
   new SyncTestingInfrastructure(server.server);
 
   let collection = server.user("foo").collection("bookmarks");
 
+  let bz_id = PlacesUtils.bookmarks.insertBookmark(
+    PlacesUtils.bookmarksMenuFolderId, Utils.makeURI("https://bugzilla.mozilla.org/"),
+    PlacesUtils.bookmarks.DEFAULT_INDEX, "Bugzilla");
+  let bz_guid = yield PlacesUtils.promiseItemGuid(bz_id);
+    _(`Bugzilla GUID: ${bz_guid}`);
+
   Svc.Obs.notify("weave:engine:start-tracking");
 
   try {
     let folder1_id = PlacesUtils.bookmarks.createFolder(
       PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0);
     let folder1_guid = store.GUIDForId(folder1_id);
     _(`Folder GUID: ${folder1_guid}`);
 
@@ -40,16 +48,27 @@ add_task(function* test_change_during_sy
     let bmk1_guid = store.GUIDForId(bmk1_id);
     _(`Thunderbird GUID: ${bmk1_guid}`);
 
     // Sync is synchronous, so, to simulate a bookmark change made during a
     // sync, we create a server record that adds a bookmark as a side effect.
     let bmk2_guid = "get-firefox1";
     let bmk3_id = -1;
     {
+      // An existing record changed on the server that should not trigger
+      // another sync when applied.
+      let changedRecord = new Bookmark("bookmarks", bz_guid);
+      changedRecord.bmkUri = "https://bugzilla.mozilla.org/";
+      changedRecord.description = "New description";
+      changedRecord.title = "Bugzilla";
+      changedRecord.tags = ["new", "tags"];
+      changedRecord.parentName = "Bookmarks Toolbar";
+      changedRecord.parentid = PlacesUtils.bookmarks.toolbarGuid;
+      collection.insert(bz_guid, encryptPayload(changedRecord.cleartext));
+
       let localRecord = new Bookmark("bookmarks", bmk2_guid);
       localRecord.bmkUri        = "http://getfirefox.com/";
       localRecord.description   = "Firefox is awesome.";
       localRecord.title         = "Get Firefox!";
       localRecord.tags          = ["firefox", "awesome", "browser"];
       localRecord.keyword       = "awesome";
       localRecord.loadInSidebar = false;
       localRecord.parentName    = "Folder 1";
@@ -68,17 +87,22 @@ add_task(function* test_change_during_sy
 
     {
       let tree = yield PlacesUtils.promiseBookmarksTree(folder1_guid);
       let childGuids = tree.children.map(child => child.guid);
       deepEqual(childGuids, [bmk1_guid], "Folder should have 1 child before first sync");
     }
 
     _("Perform first sync");
-    yield sync_engine_and_validate_telem(engine, false);
+    {
+      let changes = engine.pullNewChanges();
+      deepEqual(changes.ids().sort(), [folder1_guid, bmk1_guid, "toolbar"].sort(),
+        "Should track bookmark and folder created before first sync");
+      yield sync_engine_and_validate_telem(engine, false);
+    }
 
     let bmk2_id = store.idForGUID(bmk2_guid);
     let bmk3_guid = store.GUIDForId(bmk3_id);
     _(`Mozilla GUID: ${bmk3_guid}`);
     {
       equal(store.GUIDForId(bmk2_id), bmk2_guid,
         "Remote bookmark should be applied during first sync");
       ok(bmk3_id > -1,
@@ -88,19 +112,22 @@ add_task(function* test_change_during_sy
 
       let tree = yield PlacesUtils.promiseBookmarksTree(folder1_guid);
       let childGuids = tree.children.map(child => child.guid);
       deepEqual(childGuids, [bmk1_guid, bmk3_guid, bmk2_guid],
         "Folder should have 3 children after first sync");
     }
 
     _("Perform second sync");
-    yield sync_engine_and_validate_telem(engine, false);
+    {
+      let changes = engine.pullNewChanges();
+      deepEqual(changes.ids().sort(), [bmk3_guid, folder1_guid].sort(),
+        "Should track bookmark added during last sync and its parent");
+      yield sync_engine_and_validate_telem(engine, false);
 
-    {
       ok(collection.wbo(bmk3_guid),
         "Bookmark created during first sync should be uploaded during second sync");
 
       let tree = yield PlacesUtils.promiseBookmarksTree(folder1_guid);
       let childGuids = tree.children.map(child => child.guid);
       deepEqual(childGuids, [bmk1_guid, bmk3_guid, bmk2_guid],
         "Folder should have same children after second sync");
     }
--- a/taskcluster/ci/desktop-test/tests.yml
+++ b/taskcluster/ci/desktop-test/tests.yml
@@ -25,16 +25,17 @@ cppunit:
                     - remove_executables.py
         extra-options:
             - --cppunittest-suite=cppunittest
 
 crashtest:
     description: "Crashtest run"
     suite: reftest/crashtest
     treeherder-symbol: tc-R(C)
+    docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
         script: desktop_unittest.py
         chunked: true
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 default:
                     - unittests/linux_unittest.py
@@ -383,32 +384,34 @@ mochitest-webgl:
     # Bug 1296733: llvmpipe with mesa 9.2.1 lacks thread safety
     allow-software-gl-layers: false
 
 reftest:
     description: "Reftest run"
     suite: reftest/reftest
     treeherder-symbol: tc-R(R)
     chunks: 8
+    docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --reftest-suite=reftest
 
 reftest-no-accel:
     description: "Reftest not accelerated run"
     suite: reftest/reftest-no-accel
     treeherder-symbol: tc-R(Ru)
     chunks: 8
+    docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -969,16 +969,17 @@ GeckoDriver.prototype.get = function*(cm
       // TODO(ato): Bug 1242595
       let id = this.listener.activeMessageId;
 
       // If a remoteness update interrupts our page load, this will never return
       // We need to re-issue this request to correctly poll for readyState and
       // send errors.
       this.curBrowser.pendingCommands.push(() => {
         cmd.parameters.command_id = id;
+        cmd.parameters.pageTimeout = this.pageTimeout;
         this.mm.broadcastAsyncMessage(
             "Marionette:pollForReadyState" + this.curBrowser.curFrameId,
             cmd.parameters);
       });
 
       yield get;
       break;
 
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -62,16 +62,22 @@ ifndef MOZ_THUNDERBIRD
 		$(ABS_DIST)/$(PKG_PATH)$(MOZHARNESS_PACKAGE))
 endif # MOZ_THUNDERBIRD
 ifdef MOZ_PACKAGE_JSSHELL
 	# Package JavaScript Shell
 	@echo 'Packaging JavaScript Shell...'
 	$(RM) $(PKG_JSSHELL)
 	$(MAKE_JSSHELL)
 endif # MOZ_PACKAGE_JSSHELL
+ifdef MOZ_ARTIFACT_BUILD_SYMBOLS
+	@echo 'Packaging existing crashreporter symbols from artifact build...'
+	$(NSINSTALL) -D $(DIST)/$(PKG_PATH)
+	cd $(DIST)/crashreporter-symbols && \
+          zip -r5D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym' -i '*.txt'
+endif # MOZ_ARTIFACT_BUILD_SYMBOLS
 ifdef MOZ_CODE_COVERAGE
 	# Package code coverage gcno tree
 	@echo 'Packaging code coverage data...'
 	$(RM) $(CODE_COVERAGE_ARCHIVE_BASENAME).zip
 	$(PYTHON) -mmozbuild.codecoverage.packager \
 		--output-file='$(DIST)/$(PKG_PATH)$(CODE_COVERAGE_ARCHIVE_BASENAME).zip'
 endif
 ifeq (Darwin, $(OS_ARCH))
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -4862,21 +4862,24 @@ MultiprocessBlockPolicy() {
     uint32_t difference = now - a11yRunDate;
     if (difference > ONE_WEEK_IN_SECONDS || !a11yRunDate) {
       Preferences::ClearUser(kAccessibilityLastRunDatePref);
     } else {
       disabledForA11y = true;
     }
   }
 
+  bool doAccessibilityCheck = true;
+#if defined(MOZ_WIDGET_GTK) && !defined(RELEASE_OR_BETA)
   // For linux nightly and aurora builds skip accessibility
   // checks.
-  bool doAccessibilityCheck = true;
-#if defined(MOZ_WIDGET_GTK) && !defined(RELEASE_OR_BETA)
   doAccessibilityCheck = false;
+#elif defined(XP_WIN)
+  // For Windows Vista and up, skip accessibility checks.
+  doAccessibilityCheck = !IsVistaOrLater();
 #endif
 
   if (doAccessibilityCheck && disabledForA11y) {
     gMultiprocessBlockPolicy = kE10sDisabledForAccessibility;
     return gMultiprocessBlockPolicy;
   }
 
   /**
--- a/widget/gtk/WidgetStyleCache.cpp
+++ b/widget/gtk/WidgetStyleCache.cpp
@@ -206,16 +206,245 @@ static GtkWidget*
 CreateEntryWidget()
 {
   GtkWidget* widget = gtk_entry_new();
   AddToWindowContainer(widget);
   return widget;
 }
 
 static GtkWidget*
+CreateComboBoxWidget()
+{
+  GtkWidget* widget = gtk_combo_box_new();
+  AddToWindowContainer(widget);
+  return widget;
+}
+
+typedef struct
+{
+  GType       type;
+  GtkWidget** widget;
+} GtkInnerWidgetInfo;
+
+static void
+GetInnerWidget(GtkWidget* widget, gpointer client_data)
+{
+  auto info = static_cast<GtkInnerWidgetInfo*>(client_data);
+
+  if (G_TYPE_CHECK_INSTANCE_TYPE(widget, info->type)) {
+    *info->widget = widget;
+  }
+
+  gtk_widget_realize(widget);
+}
+
+static GtkWidget*
+CreateComboBoxButtonWidget()
+{
+  GtkWidget* comboBox = GetWidget(MOZ_GTK_COMBOBOX);
+  GtkWidget* comboBoxButton = nullptr;
+
+  /* Get its inner Button */
+  GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON,
+                              &comboBoxButton };
+  gtk_container_forall(GTK_CONTAINER(comboBox),
+                       GetInnerWidget, &info);
+
+  if (!comboBoxButton) {
+    /* Shouldn't be reached with current internal gtk implementation; we
+     * use a generic toggle button as last resort fallback to avoid
+     * crashing. */
+    comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
+  } else {
+    /* We need to have pointers to the inner widgets (button, separator, arrow)
+     * of the ComboBox to get the correct rendering from theme engines which
+     * special cases their look. Since the inner layout can change, we ask GTK
+     * to NULL our pointers when they are about to become invalid because the
+     * corresponding widgets don't exist anymore. It's the role of
+     * g_object_add_weak_pointer().
+     * Note that if we don't find the inner widgets (which shouldn't happen), we
+     * fallback to use generic "non-inner" widgets, and they don't need that kind
+     * of weak pointer since they are explicit children of gProtoLayout and as
+     * such GTK holds a strong reference to them. */
+    g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
+                              reinterpret_cast<gpointer *>(sWidgetStorage) +
+                              MOZ_GTK_COMBOBOX_BUTTON);
+  }
+
+  return comboBoxButton;
+}
+
+static GtkWidget*
+CreateComboBoxArrowWidget()
+{
+  GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON);
+  GtkWidget* comboBoxArrow = nullptr;
+
+  /* Get the widgets inside the Button */
+  GtkWidget* buttonChild = gtk_bin_get_child(GTK_BIN(comboBoxButton));
+  if (GTK_IS_BOX(buttonChild)) {
+    /* appears-as-list = FALSE, cell-view = TRUE; the button
+     * contains an hbox. This hbox is there because the ComboBox
+     * needs to place a cell renderer, a separator, and an arrow in
+     * the button when appears-as-list is FALSE. */
+    GtkInnerWidgetInfo info = { GTK_TYPE_ARROW,
+                                &comboBoxArrow };
+    gtk_container_forall(GTK_CONTAINER(buttonChild),
+                         GetInnerWidget, &info);
+  } else if (GTK_IS_ARROW(buttonChild)) {
+    /* appears-as-list = TRUE, or cell-view = FALSE;
+     * the button only contains an arrow */
+    comboBoxArrow = buttonChild;
+    gtk_widget_realize(comboBoxArrow);
+  }
+
+  if (!comboBoxArrow) {
+    /* Shouldn't be reached with current internal gtk implementation;
+     * we gButtonArrowWidget as last resort fallback to avoid
+     * crashing. */
+    comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
+  } else {
+    g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
+                              reinterpret_cast<gpointer *>(sWidgetStorage) +
+                              MOZ_GTK_COMBOBOX_ARROW);
+  }
+
+  return comboBoxArrow;
+}
+
+static GtkWidget*
+CreateComboBoxSeparatorWidget()
+{
+  // Ensure to search for separator only once as it can fail
+  // TODO - it won't initialize after ResetWidgetCache() call
+  static bool isMissingSeparator = false;
+  if (isMissingSeparator)
+    return nullptr;
+
+  /* Get the widgets inside the Button */
+  GtkWidget* comboBoxSeparator = nullptr;
+  GtkWidget* buttonChild =
+    gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
+  if (GTK_IS_BOX(buttonChild)) {
+    /* appears-as-list = FALSE, cell-view = TRUE; the button
+     * contains an hbox. This hbox is there because the ComboBox
+     * needs to place a cell renderer, a separator, and an arrow in
+     * the button when appears-as-list is FALSE. */
+    GtkInnerWidgetInfo info = { GTK_TYPE_SEPARATOR,
+                                &comboBoxSeparator };
+    gtk_container_forall(GTK_CONTAINER(buttonChild),
+                         GetInnerWidget, &info);
+  }
+
+  if (comboBoxSeparator) {
+    g_object_add_weak_pointer(G_OBJECT(comboBoxSeparator),
+                              reinterpret_cast<gpointer *>(sWidgetStorage) +
+                              MOZ_GTK_COMBOBOX_SEPARATOR);
+  } else {
+    /* comboBoxSeparator may be NULL
+     * when "appears-as-list" = TRUE or "cell-view" = FALSE;
+     * if there is no separator, then we just won't paint it. */
+    isMissingSeparator = true;
+  }
+
+  return comboBoxSeparator;
+}
+
+static GtkWidget*
+CreateComboBoxEntryWidget()
+{
+  GtkWidget* widget = gtk_combo_box_new_with_entry();
+  AddToWindowContainer(widget);
+  return widget;
+}
+
+static GtkWidget*
+CreateComboBoxEntryTextareaWidget()
+{
+  GtkWidget* comboBoxTextarea = nullptr;
+
+  /* Get its inner Entry and Button */
+  GtkInnerWidgetInfo info = { GTK_TYPE_ENTRY,
+                              &comboBoxTextarea };
+  gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
+                       GetInnerWidget, &info);
+
+  if (!comboBoxTextarea) {
+    comboBoxTextarea = GetWidget(MOZ_GTK_ENTRY);
+  } else {
+    g_object_add_weak_pointer(G_OBJECT(comboBoxTextarea),
+                              reinterpret_cast<gpointer *>(sWidgetStorage) +
+                              MOZ_GTK_COMBOBOX_ENTRY);
+  }
+
+  return comboBoxTextarea;
+}
+
+static GtkWidget*
+CreateComboBoxEntryButtonWidget()
+{
+  GtkWidget* comboBoxButton = nullptr;
+
+  /* Get its inner Entry and Button */
+  GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON,
+                              &comboBoxButton };
+  gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
+                       GetInnerWidget, &info);
+
+  if (!comboBoxButton) {
+    comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
+  } else {
+    g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
+                              reinterpret_cast<gpointer *>(sWidgetStorage) +
+                              MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
+  }
+
+  return comboBoxButton;
+}
+
+static GtkWidget*
+CreateComboBoxEntryArrowWidget()
+{
+  GtkWidget* comboBoxArrow = nullptr;
+
+  /* Get the Arrow inside the Button */
+  GtkWidget* buttonChild =
+    gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON)));
+
+  if (GTK_IS_BOX(buttonChild)) {
+   /* appears-as-list = FALSE, cell-view = TRUE; the button
+     * contains an hbox. This hbox is there because the ComboBox
+     * needs to place a cell renderer, a separator, and an arrow in
+     * the button when appears-as-list is FALSE. */
+    GtkInnerWidgetInfo info = { GTK_TYPE_ARROW,
+                                &comboBoxArrow };
+    gtk_container_forall(GTK_CONTAINER(buttonChild),
+                         GetInnerWidget, &info);
+  } else if (GTK_IS_ARROW(buttonChild)) {
+    /* appears-as-list = TRUE, or cell-view = FALSE;
+     * the button only contains an arrow */
+    comboBoxArrow = buttonChild;
+    gtk_widget_realize(comboBoxArrow);
+  }
+
+  if (!comboBoxArrow) {
+    /* Shouldn't be reached with current internal gtk implementation;
+     * we gButtonArrowWidget as last resort fallback to avoid
+     * crashing. */
+    comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
+  } else {
+    g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
+                              reinterpret_cast<gpointer *>(sWidgetStorage) +
+                              MOZ_GTK_COMBOBOX_ENTRY_ARROW);
+  }
+
+  return comboBoxArrow;
+}
+
+static GtkWidget*
 CreateScrolledWindowWidget()
 {
   GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr);
   AddToWindowContainer(widget);
   return widget;
 }
 
 static GtkWidget*
@@ -427,16 +656,32 @@ CreateWidget(WidgetNodeType aWidgetType)
     case MOZ_GTK_RADIOMENUITEM_CONTAINER:
       return CreateRadioMenuItemWidget();
     case MOZ_GTK_SCALE_HORIZONTAL:
       return CreateScaleWidget(GTK_ORIENTATION_HORIZONTAL);
     case MOZ_GTK_SCALE_VERTICAL:
       return CreateScaleWidget(GTK_ORIENTATION_VERTICAL);
     case MOZ_GTK_NOTEBOOK:
       return CreateNotebookWidget();
+    case MOZ_GTK_COMBOBOX:
+      return CreateComboBoxWidget();
+    case MOZ_GTK_COMBOBOX_BUTTON:
+      return CreateComboBoxButtonWidget();
+    case MOZ_GTK_COMBOBOX_ARROW:
+      return CreateComboBoxArrowWidget();
+    case MOZ_GTK_COMBOBOX_SEPARATOR:
+      return CreateComboBoxSeparatorWidget();
+    case MOZ_GTK_COMBOBOX_ENTRY:
+      return CreateComboBoxEntryWidget();
+    case MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA:
+      return CreateComboBoxEntryTextareaWidget();
+    case MOZ_GTK_COMBOBOX_ENTRY_BUTTON:
+      return CreateComboBoxEntryButtonWidget();
+    case MOZ_GTK_COMBOBOX_ENTRY_ARROW:
+      return CreateComboBoxEntryArrowWidget();
     default:
       /* Not implemented */
       return nullptr;
   }
 }
 
 GtkWidget*
 GetWidget(WidgetNodeType aWidgetType)
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -13,26 +13,16 @@
 #include <string.h>
 #include "gtkdrawing.h"
 #include "mozilla/Assertions.h"
 #include "prinrval.h"
 #include "WidgetStyleCache.h"
 
 #include <math.h>
 
-static GtkWidget* gProtoLayout;
-static GtkWidget* gComboBoxWidget;
-static GtkWidget* gComboBoxButtonWidget;
-static GtkWidget* gComboBoxArrowWidget;
-static GtkWidget* gComboBoxSeparatorWidget;
-static GtkWidget* gComboBoxEntryWidget;
-static GtkWidget* gComboBoxEntryTextareaWidget;
-static GtkWidget* gComboBoxEntryButtonWidget;
-static GtkWidget* gComboBoxEntryArrowWidget;
-
 static style_prop_t style_prop_func;
 static gboolean have_arrow_scaling;
 static gboolean checkbox_check_state;
 static gboolean notebook_has_tab_gap;
 static gboolean is_initialized;
 
 #define ARROW_UP      0
 #define ARROW_DOWN    G_PI
@@ -74,222 +64,16 @@ GetStateFlagsFromGtkTabFlags(GtkTabFlags
 
 gint
 moz_gtk_enable_style_props(style_prop_t styleGetProp)
 {
     style_prop_func = styleGetProp;
     return MOZ_GTK_SUCCESS;
 }
 
-static gint
-setup_widget_prototype(GtkWidget* widget)
-{
-    if (!gProtoLayout) {
-        gProtoLayout = GetWidget(MOZ_GTK_WINDOW_CONTAINER);
-    }
-    gtk_container_add(GTK_CONTAINER(gProtoLayout), widget);
-    return MOZ_GTK_SUCCESS;
-}
-
-/* We need to have pointers to the inner widgets (button, separator, arrow)
- * of the ComboBox to get the correct rendering from theme engines which
- * special cases their look. Since the inner layout can change, we ask GTK
- * to NULL our pointers when they are about to become invalid because the
- * corresponding widgets don't exist anymore. It's the role of
- * g_object_add_weak_pointer().
- * Note that if we don't find the inner widgets (which shouldn't happen), we
- * fallback to use generic "non-inner" widgets, and they don't need that kind
- * of weak pointer since they are explicit children of gProtoLayout and as
- * such GTK holds a strong reference to them. */
-static void
-moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data)
-{
-    if (GTK_IS_TOGGLE_BUTTON(widget)) {
-        gComboBoxButtonWidget = widget;
-        g_object_add_weak_pointer(G_OBJECT(widget),
-                                  (gpointer *) &gComboBoxButtonWidget);
-        gtk_widget_realize(widget);
-    }
-}
-
-static void
-moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget,
-                                           gpointer client_data)
-{
-    if (GTK_IS_SEPARATOR(widget)) {
-        gComboBoxSeparatorWidget = widget;
-        g_object_add_weak_pointer(G_OBJECT(widget),
-                                  (gpointer *) &gComboBoxSeparatorWidget);
-    } else if (GTK_IS_ARROW(widget)) {
-        gComboBoxArrowWidget = widget;
-        g_object_add_weak_pointer(G_OBJECT(widget),
-                                  (gpointer *) &gComboBoxArrowWidget);
-    } else
-        return;
-    gtk_widget_realize(widget);
-}
-
-static gint
-ensure_combo_box_widgets()
-{
-    GtkWidget* buttonChild;
-
-    if (gComboBoxButtonWidget && gComboBoxArrowWidget)
-        return MOZ_GTK_SUCCESS;
-
-    /* Create a ComboBox if needed */
-    if (!gComboBoxWidget) {
-        gComboBoxWidget = gtk_combo_box_new();
-        setup_widget_prototype(gComboBoxWidget);
-    }
-
-    /* Get its inner Button */
-    gtk_container_forall(GTK_CONTAINER(gComboBoxWidget),
-                         moz_gtk_get_combo_box_inner_button,
-                         NULL);
-
-    if (gComboBoxButtonWidget) {
-        /* Get the widgets inside the Button */
-        buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxButtonWidget));
-        if (GTK_IS_BOX(buttonChild)) {
-            /* appears-as-list = FALSE, cell-view = TRUE; the button
-             * contains an hbox. This hbox is there because the ComboBox
-             * needs to place a cell renderer, a separator, and an arrow in
-             * the button when appears-as-list is FALSE. */
-            gtk_container_forall(GTK_CONTAINER(buttonChild),
-                                 moz_gtk_get_combo_box_button_inner_widgets,
-                                 NULL);
-        } else if(GTK_IS_ARROW(buttonChild)) {
-            /* appears-as-list = TRUE, or cell-view = FALSE;
-             * the button only contains an arrow */
-            gComboBoxArrowWidget = buttonChild;
-            g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer *)
-                                      &gComboBoxArrowWidget);
-            gtk_widget_realize(gComboBoxArrowWidget);
-        }
-    } else {
-        /* Shouldn't be reached with current internal gtk implementation; we
-         * use a generic toggle button as last resort fallback to avoid
-         * crashing. */
-        gComboBoxButtonWidget = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
-    }
-
-    if (!gComboBoxArrowWidget) {
-        /* Shouldn't be reached with current internal gtk implementation;
-         * we gButtonArrowWidget as last resort fallback to avoid
-         * crashing. */
-        gComboBoxArrowWidget = GetWidget(MOZ_GTK_BUTTON_ARROW);
-    }
-
-    /* We don't test the validity of gComboBoxSeparatorWidget since there
-     * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it
-     * is invalid we just won't paint it. */
-
-    return MOZ_GTK_SUCCESS;
-}
-
-/* We need to have pointers to the inner widgets (entry, button, arrow) of
- * the ComboBoxEntry to get the correct rendering from theme engines which
- * special cases their look. Since the inner layout can change, we ask GTK
- * to NULL our pointers when they are about to become invalid because the
- * corresponding widgets don't exist anymore. It's the role of
- * g_object_add_weak_pointer().
- * Note that if we don't find the inner widgets (which shouldn't happen), we
- * fallback to use generic "non-inner" widgets, and they don't need that kind
- * of weak pointer since they are explicit children of gProtoLayout and as
- * such GTK holds a strong reference to them. */
-static void
-moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget,
-                                          gpointer client_data)
-{
-    if (GTK_IS_TOGGLE_BUTTON(widget)) {
-        gComboBoxEntryButtonWidget = widget;
-        g_object_add_weak_pointer(G_OBJECT(widget),
-                                  (gpointer *) &gComboBoxEntryButtonWidget);
-    } else if (GTK_IS_ENTRY(widget)) {
-        gComboBoxEntryTextareaWidget = widget;
-        g_object_add_weak_pointer(G_OBJECT(widget),
-                                  (gpointer *) &gComboBoxEntryTextareaWidget);
-    } else
-        return;
-    gtk_widget_realize(widget);
-}
-
-static void
-moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data)
-{
-    if (GTK_IS_ARROW(widget)) {
-        gComboBoxEntryArrowWidget = widget;
-        g_object_add_weak_pointer(G_OBJECT(widget),
-                                  (gpointer *) &gComboBoxEntryArrowWidget);
-        gtk_widget_realize(widget);
-    }
-}
-
-static gint
-ensure_combo_box_entry_widgets()
-{
-    GtkWidget* buttonChild;
-
-    if (gComboBoxEntryTextareaWidget &&
-            gComboBoxEntryButtonWidget &&
-            gComboBoxEntryArrowWidget)
-        return MOZ_GTK_SUCCESS;
-
-    /* Create a ComboBoxEntry if needed */
-    if (!gComboBoxEntryWidget) {
-        gComboBoxEntryWidget = gtk_combo_box_new_with_entry();
-        setup_widget_prototype(gComboBoxEntryWidget);
-    }
-
-    /* Get its inner Entry and Button */
-    gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget),
-                         moz_gtk_get_combo_box_entry_inner_widgets,
-                         NULL);
-
-    if (!gComboBoxEntryTextareaWidget) {
-        gComboBoxEntryTextareaWidget = GetWidget(MOZ_GTK_ENTRY);
-    }
-
-    if (gComboBoxEntryButtonWidget) {
-        /* Get the Arrow inside the Button */
-        buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxEntryButtonWidget));
-        if (GTK_IS_BOX(buttonChild)) {
-           /* appears-as-list = FALSE, cell-view = TRUE; the button
-             * contains an hbox. This hbox is there because the ComboBox
-             * needs to place a cell renderer, a separator, and an arrow in
-             * the button when appears-as-list is FALSE. */
-            gtk_container_forall(GTK_CONTAINER(buttonChild),
-                                 moz_gtk_get_combo_box_entry_arrow,
-                                 NULL);
-        } else if(GTK_IS_ARROW(buttonChild)) {
-            /* appears-as-list = TRUE, or cell-view = FALSE;
-             * the button only contains an arrow */
-            gComboBoxEntryArrowWidget = buttonChild;
-            g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer *)
-                                      &gComboBoxEntryArrowWidget);
-            gtk_widget_realize(gComboBoxEntryArrowWidget);
-        }
-    } else {
-        /* Shouldn't be reached with current internal gtk implementation;
-         * we use a generic toggle button as last resort fallback to avoid
-         * crashing. */
-        gComboBoxEntryButtonWidget = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
-    }
-
-    if (!gComboBoxEntryArrowWidget) {
-        /* Shouldn't be reached with current internal gtk implementation;
-         * we gButtonArrowWidget as last resort fallback to avoid
-         * crashing. */
-        gComboBoxEntryArrowWidget = GetWidget(MOZ_GTK_BUTTON_ARROW);
-    }
-
-    return MOZ_GTK_SUCCESS;
-}
-
 gint
 moz_gtk_init()
 {
     if (is_initialized)
         return MOZ_GTK_SUCCESS;
 
     is_initialized = TRUE;
     have_arrow_scaling = (gtk_major_version > 2 ||
@@ -331,26 +115,34 @@ moz_gtk_radio_get_metrics(gint* indicato
 {
     gtk_widget_style_get(GetWidget(MOZ_GTK_RADIOBUTTON_CONTAINER),
                          "indicator_size", indicator_size,
                          "indicator_spacing", indicator_spacing,
                           NULL);
     return MOZ_GTK_SUCCESS;
 }
 
-gint
-moz_gtk_get_focus_outline_size(gint* focus_h_width, gint* focus_v_width)
+static gint
+moz_gtk_get_focus_outline_size(GtkStyleContext* style,
+                               gint* focus_h_width, gint* focus_v_width)
 {
     GtkBorder border;
     GtkBorder padding;
-    GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_ENTRY);
     gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border);
     gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding);
     *focus_h_width = border.left + padding.left;
     *focus_v_width = border.top + padding.top;
+    return MOZ_GTK_SUCCESS;
+}
+
+gint
+moz_gtk_get_focus_outline_size(gint* focus_h_width, gint* focus_v_width)
+{
+    GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_ENTRY);
+    moz_gtk_get_focus_outline_size(style, focus_h_width, focus_v_width);
     ReleaseStyleContext(style);
     return MOZ_GTK_SUCCESS;
 }
 
 gint
 moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding)
 {
     gtk_widget_style_get(GetWidget(MOZ_GTK_MENUITEM),
@@ -1025,70 +817,36 @@ moz_gtk_vpaned_paint(cairo_t *cr, GdkRec
     ReleaseStyleContext(style);
     return MOZ_GTK_SUCCESS;
 }
 
 // See gtk_entry_draw() for reference.
 static gint
 moz_gtk_entry_paint(cairo_t *cr, GdkRectangle* rect,
                     GtkWidgetState* state,
-                    GtkWidget* widget, GtkTextDirection direction)
+                    GtkStyleContext* style)
 {
     gint x = rect->x, y = rect->y, width = rect->width, height = rect->height;
-    GtkStyleContext* style;
     int draw_focus_outline_only = state->depressed; // NS_THEME_FOCUS_OUTLINE
 
-    gtk_widget_set_direction(widget, direction);
-
-    style = gtk_widget_get_style_context(widget);
-
     if (draw_focus_outline_only) {
         // Inflate the given 'rect' with the focus outline size.
         gint h, v;
-        moz_gtk_get_focus_outline_size(&h, &v);
+        moz_gtk_get_focus_outline_size(style, &h, &v);
         rect->x -= h;
         rect->width += 2 * h;
         rect->y -= v;
         rect->height += 2 * v;
         width = rect->width;
         height = rect->height;
-    }
-
-    /* gtkentry.c uses two windows, one for the entire widget and one for the
-     * text area inside it. The background of both windows is set to the "base"
-     * color of the new state in gtk_entry_state_changed, but only the inner
-     * textarea window uses gtk_paint_flat_box when exposed */
-
-    /* This gets us a lovely greyish disabledish look */
-    gtk_widget_set_sensitive(widget, !state->disabled);
-
-    gtk_style_context_save(style);
-    gtk_style_context_add_class(style, GTK_STYLE_CLASS_ENTRY);
-  
-    /* Now paint the shadow and focus border.
-     * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad
-     * smaller when focused if the focus is not interior, then the focus. */
-
-    if (state->focused && !state->disabled) {
-        /* This will get us the lit borders that focused textboxes enjoy on
-         * some themes. */
-        gtk_style_context_set_state(style, GTK_STATE_FLAG_FOCUSED);
-    }
-
-    if (state->disabled) {
-        gtk_style_context_set_state(style, GTK_STATE_FLAG_INSENSITIVE);
-    }
-
-    if (!draw_focus_outline_only) {
+    } else {
         gtk_render_background(style, cr, x, y, width, height);
     }
     gtk_render_frame(style, cr, x, y, width, height);
 
-    gtk_style_context_restore(style);
-
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_text_view_paint(cairo_t *cr, GdkRectangle* rect,
                         GtkWidgetState* state,
                         GtkTextDirection direction)
 {
@@ -1243,44 +1001,47 @@ moz_gtk_combo_box_paint(cairo_t *cr, Gdk
                         GtkTextDirection direction)
 {
     GdkRectangle arrow_rect, real_arrow_rect;
     gint separator_width;
     gboolean wide_separators;
     GtkStyleContext* style;
     GtkRequisition arrow_req;
 
-    ensure_combo_box_widgets();
+    GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON);
+    GtkWidget* comboBoxArrow = GetWidget(MOZ_GTK_COMBOBOX_ARROW);
 
     /* Also sets the direction on gComboBoxButtonWidget, which is then
      * inherited by the separator and arrow */
     moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL,
-                         gComboBoxButtonWidget, direction);
+                         comboBoxButton, direction);
 
-    calculate_button_inner_rect(gComboBoxButtonWidget,
-                                rect, &arrow_rect, direction);
+    calculate_button_inner_rect(comboBoxButton, rect, &arrow_rect, direction);
     /* Now arrow_rect contains the inner rect ; we want to correct the width
      * to what the arrow needs (see gtk_combo_box_size_allocate) */
-    gtk_widget_get_preferred_size(gComboBoxArrowWidget, NULL, &arrow_req);
+    gtk_widget_get_preferred_size(comboBoxArrow, NULL, &arrow_req);
+
     if (direction == GTK_TEXT_DIR_LTR)
         arrow_rect.x += arrow_rect.width - arrow_req.width;
     arrow_rect.width = arrow_req.width;
 
-    calculate_arrow_rect(gComboBoxArrowWidget,
+    calculate_arrow_rect(comboBoxArrow,
                          &arrow_rect, &real_arrow_rect, direction);
 
-    style = gtk_widget_get_style_context(gComboBoxArrowWidget);
+    style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ARROW);
     gtk_render_arrow(style, cr, ARROW_DOWN,
                      real_arrow_rect.x, real_arrow_rect.y,
                      real_arrow_rect.width);
+    ReleaseStyleContext(style);
 
     /* If there is no separator in the theme, there's nothing left to do. */
-    if (!gComboBoxSeparatorWidget)
+    GtkWidget* widget = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR);
+    if (!widget)
         return MOZ_GTK_SUCCESS;
-    style = gtk_widget_get_style_context(gComboBoxSeparatorWidget);
+    style = gtk_widget_get_style_context(widget);
     gtk_style_context_get_style(style,
                                 "wide-separators", &wide_separators,
                                 "separator-width", &separator_width,
                                 NULL);
 
     if (wide_separators) {
         if (direction == GTK_TEXT_DIR_LTR)
             arrow_rect.x -= separator_width;
@@ -1354,41 +1115,39 @@ moz_gtk_combo_box_entry_button_paint(cai
                                      gboolean input_focus,
                                      GtkTextDirection direction)
 {
     gint x_displacement, y_displacement;
     GdkRectangle arrow_rect, real_arrow_rect;
     GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
     GtkStyleContext* style;
 
-    ensure_combo_box_entry_widgets();
-
+    GtkWidget* comboBoxEntry = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
     moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL,
-                         gComboBoxEntryButtonWidget, direction);
+                         comboBoxEntry, direction);
+    calculate_button_inner_rect(comboBoxEntry, rect, &arrow_rect, direction);
 
-    calculate_button_inner_rect(gComboBoxEntryButtonWidget,
-                                rect, &arrow_rect, direction);
     if (state_flags & GTK_STATE_FLAG_ACTIVE) {
-        gtk_style_context_get_style(gtk_widget_get_style_context(gComboBoxEntryButtonWidget),
+        style = gtk_widget_get_style_context(comboBoxEntry);
+        gtk_style_context_get_style(style,
                                     "child-displacement-x", &x_displacement,
                                     "child-displacement-y", &y_displacement,
                                     NULL);
         arrow_rect.x += x_displacement;
         arrow_rect.y += y_displacement;
     }
 
-    calculate_arrow_rect(gComboBoxEntryArrowWidget,
+    calculate_arrow_rect(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_ARROW),
                          &arrow_rect, &real_arrow_rect, direction);
 
-    style = gtk_widget_get_style_context(gComboBoxEntryArrowWidget);
-
+    style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ENTRY_ARROW);
     gtk_render_arrow(style, cr, ARROW_DOWN,
                     real_arrow_rect.x, real_arrow_rect.y,
                     real_arrow_rect.width);
-
+    ReleaseStyleContext(style);
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_container_paint(cairo_t *cr, GdkRectangle* rect,
                         GtkWidgetState* state, 
                         WidgetNodeType  widget_type,
                         GtkTextDirection direction)
@@ -1398,17 +1157,16 @@ moz_gtk_container_paint(cairo_t *cr, Gdk
                                                state_flags);
     /* this is for drawing a prelight box */
     if (state_flags & GTK_STATE_FLAG_PRELIGHT) {
         gtk_render_background(style, cr,
                               rect->x, rect->y, rect->width, rect->height);
     }
 
     ReleaseStyleContext(style);
-
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_toggle_label_paint(cairo_t *cr, GdkRectangle* rect,
                            GtkWidgetState* state, 
                            gboolean isradio, GtkTextDirection direction)
 {
@@ -2256,59 +2014,58 @@ moz_gtk_get_widget_border(WidgetNodeType
             moz_gtk_add_style_padding(style, left, top, right, bottom);
             ReleaseStyleContext(style);
             return MOZ_GTK_SUCCESS;
         }
     case MOZ_GTK_TREE_HEADER_SORTARROW:
         w = GetWidget(MOZ_GTK_TREE_HEADER_SORTARROW);
         break;
     case MOZ_GTK_DROPDOWN_ENTRY:
-        ensure_combo_box_entry_widgets();
-        w = gComboBoxEntryTextareaWidget;
+        w = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA);
         break;
     case MOZ_GTK_DROPDOWN_ARROW:
-        ensure_combo_box_entry_widgets();
-        w = gComboBoxEntryButtonWidget;
+        w = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
         break;
     case MOZ_GTK_DROPDOWN:
         {
             /* We need to account for the arrow on the dropdown, so text
              * doesn't come too close to the arrow, or in some cases spill
              * into the arrow. */
             gboolean wide_separators;
             gint separator_width;
             GtkRequisition arrow_req;
             GtkBorder border;
 
-            ensure_combo_box_widgets();
-
-            *left = *top = *right = *bottom = 
-                gtk_container_get_border_width(GTK_CONTAINER(gComboBoxButtonWidget));
-
-            style = gtk_widget_get_style_context(gComboBoxButtonWidget);
-
+            *left = *top = *right = *bottom =
+                gtk_container_get_border_width(GTK_CONTAINER(
+                                               GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
+            style = ClaimStyleContext(MOZ_GTK_COMBOBOX_BUTTON);
             moz_gtk_add_style_padding(style, left, top, right, bottom);
             moz_gtk_add_style_border(style, left, top, right, bottom);
+            ReleaseStyleContext(style);
 
             /* If there is no separator, don't try to count its width. */
             separator_width = 0;
-            if (gComboBoxSeparatorWidget) {
-                style = gtk_widget_get_style_context(gComboBoxSeparatorWidget);
+            GtkWidget* comboBoxSeparator = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR);
+            if (comboBoxSeparator) {
+                style = gtk_widget_get_style_context(comboBoxSeparator);
                 gtk_style_context_get_style(style,
                                             "wide-separators", &wide_separators,
                                             "separator-width", &separator_width,
                                             NULL);
 
                 if (!wide_separators) {
-                    gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border);
+                    gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL,
+                                                 &border);
                     separator_width = border.left;
                 }
             }
 
-            gtk_widget_get_preferred_size(gComboBoxArrowWidget, NULL, &arrow_req);
+            gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ARROW),
+                                          NULL, &arrow_req);
 
             if (direction == GTK_TEXT_DIR_RTL)
                 *left += separator_width + arrow_req.width;
             else
                 *right += separator_width + arrow_req.width;
 
             return MOZ_GTK_SUCCESS;
         }
@@ -2519,19 +2276,19 @@ gint
 moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height)
 {
     /*
      * We get the requisition of the drop down button, which includes
      * all padding, border and focus line widths the button uses,
      * as well as the minimum arrow size and its padding
      * */
     GtkRequisition requisition;
-    ensure_combo_box_entry_widgets();
 
-    gtk_widget_get_preferred_size(gComboBoxEntryButtonWidget, NULL, &requisition);
+    gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON),
+                                  NULL, &requisition);
     *width = requisition.width;
     *height = requisition.height;
 
     return MOZ_GTK_SUCCESS;
 }
 
 gint
 moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height)
@@ -2550,18 +2307,17 @@ moz_gtk_get_tab_scroll_arrow_size(gint* 
 }
 
 void
 moz_gtk_get_arrow_size(WidgetNodeType widgetType, gint* width, gint* height)
 {
     GtkWidget* widget;
     switch (widgetType) {
         case MOZ_GTK_DROPDOWN:
-            ensure_combo_box_widgets();
-            widget = gComboBoxArrowWidget;
+            widget = GetWidget(MOZ_GTK_COMBOBOX_ARROW);
             break;
         default:
             widget = GetWidget(MOZ_GTK_BUTTON_ARROW);
             break;
     }
 
     GtkRequisition requisition;
     gtk_widget_get_preferred_size(widget, NULL, &requisition);
@@ -2825,19 +2581,23 @@ moz_gtk_widget_paint(WidgetNodeType widg
         break;
     case MOZ_GTK_SPINBUTTON_UP:
     case MOZ_GTK_SPINBUTTON_DOWN:
         return moz_gtk_spin_updown_paint(cr, rect,
                                          (widget == MOZ_GTK_SPINBUTTON_DOWN),
                                          state, direction);
         break;
     case MOZ_GTK_SPINBUTTON_ENTRY:
-        // TODO - use MOZ_GTK_SPINBUTTON_ENTRY style directly
-        return moz_gtk_entry_paint(cr, rect, state,
-                                   GetWidget(MOZ_GTK_SPINBUTTON), direction);
+        {
+            GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_SPINBUTTON_ENTRY,
+                                     direction, GetStateFlagsFromGtkWidgetState(state));
+            gint ret = moz_gtk_entry_paint(cr, rect, state, style);
+            ReleaseStyleContext(style);
+            return ret;
+        }
         break;
     case MOZ_GTK_GRIPPER:
         return moz_gtk_gripper_paint(cr, rect, state,
                                      direction);
         break;
     case MOZ_GTK_TREEVIEW:
         return moz_gtk_treeview_paint(cr, rect, state,
                                       direction);
@@ -2852,33 +2612,41 @@ moz_gtk_widget_paint(WidgetNodeType widg
                                                     (GtkArrowType) flags,
                                                     direction);
         break;
     case MOZ_GTK_TREEVIEW_EXPANDER:
         return moz_gtk_treeview_expander_paint(cr, rect, state,
                                                (GtkExpanderStyle) flags, direction);
         break;
     case MOZ_GTK_ENTRY:
-        return moz_gtk_entry_paint(cr, rect, state, GetWidget(MOZ_GTK_ENTRY),
-                                   direction);
-        break;
+        {
+            GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_ENTRY,
+                                     direction, GetStateFlagsFromGtkWidgetState(state));
+            gint ret = moz_gtk_entry_paint(cr, rect, state, style);
+            ReleaseStyleContext(style);
+            return ret;
+        }
     case MOZ_GTK_TEXT_VIEW:
         return moz_gtk_text_view_paint(cr, rect, state, direction);
         break;
     case MOZ_GTK_DROPDOWN:
         return moz_gtk_combo_box_paint(cr, rect, state, direction);
         break;
     case MOZ_GTK_DROPDOWN_ARROW:
         return moz_gtk_combo_box_entry_button_paint(cr, rect,
                                                     state, flags, direction);
         break;
     case MOZ_GTK_DROPDOWN_ENTRY:
-        ensure_combo_box_entry_widgets();
-        return moz_gtk_entry_paint(cr, rect, state,
-                                   gComboBoxEntryTextareaWidget, direction);
+        {
+            GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA,
+                                     direction, GetStateFlagsFromGtkWidgetState(state));
+            gint ret = moz_gtk_entry_paint(cr, rect, state, style);
+            ReleaseStyleContext(style);
+            return ret;
+        }
         break;
     case MOZ_GTK_CHECKBUTTON_CONTAINER:
     case MOZ_GTK_RADIOBUTTON_CONTAINER:
         return moz_gtk_container_paint(cr, rect, state, widget, direction);
         break;
     case MOZ_GTK_CHECKBUTTON_LABEL:
     case MOZ_GTK_RADIOBUTTON_LABEL:
         return moz_gtk_toggle_label_paint(cr, rect, state,
@@ -2993,22 +2761,12 @@ gboolean moz_gtk_has_scrollbar_buttons(v
 }
 
 gint
 moz_gtk_shutdown()
 {
     /* This will destroy all of our widgets */
     ResetWidgetCache();
 
-    gProtoLayout = NULL;
-    gComboBoxWidget = NULL;
-    gComboBoxButtonWidget = NULL;
-    gComboBoxSeparatorWidget = NULL;
-    gComboBoxArrowWidget = NULL;
-    gComboBoxEntryWidget = NULL;
-    gComboBoxEntryButtonWidget = NULL;
-    gComboBoxEntryArrowWidget = NULL;
-    gComboBoxEntryTextareaWidget = NULL;
-
     is_initialized = FALSE;
 
     return MOZ_GTK_SUCCESS;
 }
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -225,16 +225,32 @@ typedef enum {
   /* Paints a GtkHPaned separator */
   MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL,
   /* Paints the background of a window, dialog or page. */
   MOZ_GTK_WINDOW,
   /* Window container for all widgets */
   MOZ_GTK_WINDOW_CONTAINER,
   /* Paints a GtkInfoBar, for notifications. */
   MOZ_GTK_INFO_BAR,
+  /* Used for widget tree construction. */
+  MOZ_GTK_COMBOBOX,
+  /* Paints a GtkComboBox button widget. */
+  MOZ_GTK_COMBOBOX_BUTTON,
+  /* Paints a GtkComboBox arrow widget. */
+  MOZ_GTK_COMBOBOX_ARROW,
+  /* Paints a GtkComboBox separator widget. */
+  MOZ_GTK_COMBOBOX_SEPARATOR,
+  /* Used for widget tree construction. */
+  MOZ_GTK_COMBOBOX_ENTRY,
+  /* Paints a GtkComboBox entry widget. */
+  MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA,
+  /* Paints a GtkComboBox entry button widget. */
+  MOZ_GTK_COMBOBOX_ENTRY_BUTTON,
+  /* Paints a GtkComboBox entry arrow widget. */
+  MOZ_GTK_COMBOBOX_ENTRY_ARROW,
   /* Used for scrolled window shell. */
   MOZ_GTK_SCROLLED_WINDOW,
 
   MOZ_GTK_WIDGET_NODE_COUNT
 } WidgetNodeType;
 
 /*** General library functions ***/
 /**