Bug 1511381 - Prevent forced layout flush when click-to-play UI loads on page load r=mconley

This patch attempts to bail out of probing into the layout when it is not ready.

It abuses the overflow event from layout a bit so that it could size and do the elementFromPoint() tests when there is a layout.

Differential Revision: https://phabricator.services.mozilla.com/D15905

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Timothy Guan-tin Chien 2019-01-31 17:56:03 +00:00
parent 4a9308c0a7
commit a2c8d49b09
3 changed files with 47 additions and 11 deletions

View File

@ -244,9 +244,14 @@ class PluginChild extends ActorChild {
* not handle hiding it. That is done by setVisibility with the return value
* from this function.
*
* @param {Element} plugin The plug-in element
* @param {Element} overlay The overlay element inside the UA Shadow DOM of
* the plug-in element
* @param {boolean} flushLayout Allow flush layout during computation and
* adjustment.
* @returns A value from OVERLAY_DISPLAY.
*/
computeAndAdjustOverlayDisplay(plugin, overlay) {
computeAndAdjustOverlayDisplay(plugin, overlay, flushLayout) {
let fallbackType = plugin.pluginFallbackType;
if (plugin.pluginFallbackTypeOverride !== undefined) {
fallbackType = plugin.pluginFallbackTypeOverride;
@ -257,24 +262,38 @@ class PluginChild extends ActorChild {
// If the overlay size is 0, we haven't done layout yet. Presume that
// plugins are visible until we know otherwise.
if (overlay.scrollWidth == 0) {
if (flushLayout && overlay.scrollWidth == 0) {
return OVERLAY_DISPLAY.FULL;
}
let overlayDisplay = OVERLAY_DISPLAY.FULL;
let contentWindow = plugin.ownerGlobal;
let cwu = contentWindow.windowUtils;
// Is the <object>'s size too small to hold what we want to show?
let pluginRect = plugin.getBoundingClientRect();
let pluginRect = flushLayout ? plugin.getBoundingClientRect() :
cwu.getBoundsWithoutFlushing(plugin);
let pluginWidth = Math.ceil(pluginRect.width);
let pluginHeight = Math.ceil(pluginRect.height);
let layoutNeedsFlush = !flushLayout &&
cwu.needsFlush(cwu.FLUSH_STYLE) &&
cwu.needsFlush(cwu.FLUSH_LAYOUT);
// We must set the attributes while here inside this function in order
// for a possible re-style to occur, which will make the scrollWidth/Height
// checks below correct. Otherwise, we would be requesting e.g. a TINY
// overlay here, but the default styling would be used, and that would make
// it overflow, causing it to change to BLANK instead of remaining as TINY.
if (pluginWidth <= 32 || pluginHeight <= 32) {
if (layoutNeedsFlush) {
// Set the content to be oversized when we the overlay size is 0,
// so that we could receive an overflow event afterwards when there is
// a layout.
overlayDisplay = OVERLAY_DISPLAY.FULL;
overlay.setAttribute("sizing", "oversized");
overlay.removeAttribute("notext");
} else if (pluginWidth <= 32 || pluginHeight <= 32) {
overlay.setAttribute("sizing", "blank");
overlayDisplay = OVERLAY_DISPLAY.BLANK;
} else if (pluginWidth <= 80 || pluginHeight <= 60) {
@ -295,6 +314,13 @@ class PluginChild extends ActorChild {
overlay.removeAttribute("notext");
}
// The hit test below only works with correct layout information,
// don't do it if layout needs flush.
// We also don't want to access scrollWidth/scrollHeight if
// the layout needs flush.
if (layoutNeedsFlush) {
return overlayDisplay;
}
// XXX bug 446693. The text-shadow on the submitted-report text at
// the bottom causes scrollHeight to be larger than it should be.
@ -319,9 +345,6 @@ class PluginChild extends ActorChild {
[right, bottom],
[centerX, centerY]];
let contentWindow = plugin.ownerGlobal;
let cwu = contentWindow.windowUtils;
for (let [x, y] of points) {
if (x < 0 || y < 0) {
continue;
@ -505,10 +528,11 @@ class PluginChild extends ActorChild {
if (eventType != "PluginCrashed") {
if (overlay != null) {
this.setVisibility(plugin, overlay,
this.computeAndAdjustOverlayDisplay(plugin, overlay));
this.computeAndAdjustOverlayDisplay(plugin, overlay, false));
let resizeListener = () => {
this.setVisibility(plugin, overlay,
this.computeAndAdjustOverlayDisplay(plugin, overlay));
this.computeAndAdjustOverlayDisplay(plugin, overlay, true));
};
plugin.addEventListener("overflow", resizeListener);
plugin.addEventListener("underflow", resizeListener);
@ -926,14 +950,15 @@ class PluginChild extends ActorChild {
let link = this.getPluginUI(plugin, "reloadLink");
this.addLinkClickCallback(link, "reloadPage");
let overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);
// This might trigger force reflow, but plug-in crashing code path shouldn't be hot.
let overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay, true);
// Is the <object>'s size too small to hold what we want to show?
if (overlayDisplayState != OVERLAY_DISPLAY.FULL) {
// First try hiding the crash report submission UI.
statusDiv.removeAttribute("status");
overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);
overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay, true);
}
this.setVisibility(plugin, overlay, overlayDisplayState);

View File

@ -349,6 +349,10 @@ add_task(async function() {
});
await ContentTask.spawn(gTestBrowser, null, async function() {
// Waiting for layout to flush and the overlay layout to compute
await new Promise(resolve => content.requestAnimationFrame(resolve));
await new Promise(resolve => content.requestAnimationFrame(resolve));
let doc = content.document;
let plugin = doc.getElementById("test");
let mainBox = plugin.openOrClosedShadowRoot.getElementById("main");

View File

@ -214,9 +214,16 @@ html|a {
}
:host(:-moz-handler-clicktoplay) .msgClickToPlay {
/* Triggers overflow event if the container is too small */
min-width: 240px;
font-size: 13px;
}
:host(:-moz-handler-clicktoplay) .mainBox[sizing=oversized] {
/* Triggers overflow event when there is a layout */
width: 101%;
}
:host(:-moz-handler-clicktoplay) .mainBox[sizing=blank] .hoverBox {
visibility: hidden;
}