Bug 1257937 - Make _delayedStartup run after both the outer window and the web content in the initial browser has painted. r?Gijs
MozReview-Commit-ID: 7tGLtErFKVO
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -903,16 +903,23 @@ function RedirectLoad({ target: browser,
};
Services.obs.addObserver(delayedStartupFinished,
"browser-delayed-startup-finished",
false);
}
}
var gBrowserInit = {
+ /**
+ * We normally wait until both the parent and the content process have
+ * painted before running _delayedStartup, but if the content process is
+ * hung, we should make sure that _delayedStartup runs anyways. After
+ * contentPaintTimeout ms, we'll stop waiting for the content paint message.
+ */
+ contentPaintTimeout: 1000, // ms
delayedStartupFinished: false,
onLoad: function() {
gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
Services.obs.addObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed", false);
window.addEventListener("AppCommand", HandleAppCommandEvent, true);
@@ -1024,25 +1031,48 @@ var gBrowserInit = {
let contrastRatio = (backgroundLuminance + 0.05) / (foregroundLuminance + 0.05);
if (contrastRatio < 3) {
document.documentElement.setAttribute("darkwindowframe", "true");
}
}
ToolbarIconColor.init();
- // Wait until chrome is painted before executing code not critical to making the window visible
+ // Wait until chrome and content is painted before executing code not critical
+ // to making the window visible
this._boundDelayedStartup = this._delayedStartup.bind(this);
- window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
+
+ this._parentPaintPromise = new Promise((resolve) => {
+ window.addEventListener("MozAfterPaint", function onParentPaint() {
+ window.removeEventListener("MozAfterPaint", onParentPaint);
+ resolve();
+ });
+ });
+
+ this._contentPaintPromise = new Promise((resolve) => {
+ this._contentPaintTimeoutID = setTimeout(resolve, this.contentPaintTimeout);
+ let mm = window.messageManager;
+ mm.addMessageListener("Browser:FirstContentPaint", function onContentPaint() {
+ mm.removeMessageListener("Browser:FirstContentPaint", onContentPaint);
+ clearTimeout(this._contentPaintTimeoutID);
+ resolve();
+ });
+ });
+
+ Promise.all([this._parentPaintPromise,
+ this._contentPaintPromise]).then(() => {
+ if (this._boundDelayedStartup) {
+ this._boundDelayedStartup();
+ }
+ })
this._loadHandled = true;
},
_cancelDelayedStartup: function () {
- window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
this._boundDelayedStartup = null;
},
_delayedStartup: function() {
let tmp = {};
Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", tmp);
let TelemetryTimestamps = tmp.TelemetryTimestamps;
TelemetryTimestamps.add("delayedStartupStarted");
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -1125,8 +1125,21 @@ var ViewSelectionSource = {
// replace chars > 0x7f via nsIEntityConverter
str = str.replace(/[^\0-\u007f]/g, convertEntity);
return str;
}
};
ViewSelectionSource.init();
+
+// Some initialization work in the parent for a new browser window is
+// keyed off of the first paint of its content after load has started.
+// Note that we need to wait for the load event to occur first, to avoid
+// MozAfterPaint's that might occur for the blank document that's loaded
+// before the actual load occurs.
+addEventListener("load", function onLoad(e) {
+ removeEventListener("load", onLoad);
+ addEventListener("MozAfterPaint", function onFirstPaint(e) {
+ removeEventListener("MozAfterPaint", onFirstPaint);
+ sendAsyncMessage("Browser:FirstPaint");
+ });
+});