Bug 1543482 - Add the irregular-area flag for frames with masks and clip paths. r=mattwoodrow,botond
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 15 Apr 2019 16:02:02 +0000
changeset 528337 7d6cd4e207055a667e1136ba200603f2420c671c
parent 528336 7d9507696389b81ea47798b0528769422bd3ec96
child 528338 ca2c4053e33c353f361daa7036ebf7a9a960c50a
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, botond
bugs1543482
milestone68.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
Bug 1543482 - Add the irregular-area flag for frames with masks and clip paths. r=mattwoodrow,botond This will force APZ to do a main-thread hit-test for the cases where the shapes are too complex to easily send to the compositor. Differential Revision: https://phabricator.services.mozilla.com/D27455
gfx/layers/apz/test/mochitest/helper_hittest_clippath.html
gfx/layers/apz/test/mochitest/test_group_hittest.html
layout/generic/nsFrame.cpp
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_hittest_clippath.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Hit-testing an iframe covered by an element with a clip-path</title>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <meta name="viewport" content="width=device-width"/>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<style>
+    html, body { margin: 0; }
+    #clipped {
+        width: 400px;
+        height: 400px;
+        background-color: green;
+        position: absolute;
+        top: 100px;
+        left: 100px;
+        clip-path: circle(150px);
+    }
+    iframe {
+        width: 400px;
+        height: 300px;
+        border: 0px solid black;
+    }
+</style>
+</head>
+<body style="height: 5000px">
+<iframe id="sub" srcdoc="<!DOCTYPE html><body style='height: 5000px'><div style='position: absolute; top: 150px; left: 150px; width: 300px; height: 300px; background-color: blue;'></div>
+when this page loads, the blue rect should be behind the green circle. mousing over the area with the blue rect and scrolling with the wheel or trackpad should cause the iframe to scroll."></iframe>
+<div id="clipped"></div>
+<script>
+
+function* test(testDriver) {
+  var config = getHitTestConfig();
+  var utils = config.utils;
+
+  // layerize the iframe
+  var subwindow = document.getElementById("sub").contentWindow;
+  var subscroller = subwindow.document.scrollingElement;
+  var subutils = SpecialPowers.getDOMWindowUtils(subwindow);
+  subutils.setDisplayPortForElement(0, 0, 400, 1000, subscroller, 1);
+  yield waitForApzFlushedRepaints(testDriver);
+
+  var rootViewId = utils.getViewId(document.scrollingElement);
+  var iframeViewId = subutils.getViewId(subscroller);
+
+  checkHitResult(hitTest({ x: 10, y: 10 }),
+      APZHitResultFlags.VISIBLE,
+      iframeViewId,
+      "(simple) uninteresting point inside the iframe");
+  checkHitResult(hitTest({ x: 500, y: 10 }),
+      APZHitResultFlags.VISIBLE,
+      rootViewId,
+      "(simple) uninteresting point in the root scroller");
+  checkHitResult(hitTest({ x: 110, y: 110 }),
+      APZHitResultFlags.VISIBLE,
+      iframeViewId,
+      "(simple) point in the iframe behind overlaying div, but outside the bounding box of the clip path");
+  checkHitResult(hitTest({ x: 160, y: 160 }),
+      config.isWebRender ? APZHitResultFlags.VISIBLE
+                         : APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
+      config.isWebRender ? iframeViewId : rootViewId,
+      "(simple) point in the iframe behind overlaying div, inside the bounding box of the clip path, but outside the actual clip shape");
+  checkHitResult(hitTest({ x: 300, y: 200 }),
+      config.isWebRender ? APZHitResultFlags.VISIBLE
+                         : APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
+      rootViewId,
+      "(simple) point inside the clip shape of the overlaying div");
+
+  // Now we turn the "simple" clip-path that WR can handle into a more complex
+  // one that needs a mask. Then run the checks again; the expected results for
+  // WR are slightly different
+  document.getElementById("clipped").style.clipPath = "polygon(50px 200px, 75px 75px, 200px 50px, 350px 200px, 200px 350px)";
+  yield waitForApzFlushedRepaints(testDriver);
+
+  checkHitResult(hitTest({ x: 10, y: 10 }),
+      APZHitResultFlags.VISIBLE,
+      iframeViewId,
+      "(complex) uninteresting point inside the iframe");
+  checkHitResult(hitTest({ x: 500, y: 10 }),
+      APZHitResultFlags.VISIBLE,
+      rootViewId,
+      "(complex) uninteresting point in the root scroller");
+  checkHitResult(hitTest({ x: 110, y: 110 }),
+      APZHitResultFlags.VISIBLE,
+      iframeViewId,
+      "(complex) point in the iframe behind overlaying div, but outside the bounding box of the clip path");
+  checkHitResult(hitTest({ x: 160, y: 160 }),
+      APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
+      rootViewId,
+      "(complex) point in the iframe behind overlaying div, inside the bounding box of the clip path, but outside the actual clip shape");
+  checkHitResult(hitTest({ x: 300, y: 200 }),
+      APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
+      rootViewId,
+      "(complex) point inside the clip shape of the overlaying div");
+}
+
+waitUntilApzStable()
+    .then(runContinuation(test))
+    .then(subtestDone);
+</script>
+</body></html>
--- a/gfx/layers/apz/test/mochitest/test_group_hittest.html
+++ b/gfx/layers/apz/test/mochitest/test_group_hittest.html
@@ -33,16 +33,17 @@ var subtests = [
   {"file": "helper_hittest_float_bug1443518.html", "prefs": prefs},
   {"file": "helper_hittest_checkerboard.html", "prefs": prefs},
   {"file": "helper_hittest_backface_hidden.html", "prefs": prefs},
   {"file": "helper_hittest_touchaction.html", "prefs": prefs},
   {"file": "helper_hittest_nested_transforms_bug1459696.html", "prefs": prefs},
   {"file": "helper_hittest_sticky_bug1478304.html", "prefs": prefs},
   {"file": "helper_hittest_clipped_fixed_modal.html", "prefs": prefs},
   {"file": "helper_hittest_pointerevents_svg.html", "prefs": prefs},
+  {"file": "helper_hittest_clippath.html", "prefs": prefs},
 ];
 
 if (isApzEnabled()) {
   SimpleTest.waitForExplicitFinish();
   window.onload = function() {
     runSubtestsSeriallyInFreshWindows(subtests)
     .then(SimpleTest.finish, SimpleTest.finish);
   };
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10899,16 +10899,25 @@ CompositorHitTestInfo nsIFrame::GetCompo
     return result;
   }
   if (!StyleVisibility()->IsVisible()) {
     return result;
   }
 
   // Anything that didn't match the above conditions is visible to hit-testing.
   result = CompositorHitTestFlags::eVisibleToHitTest;
+  if (nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) {
+    // If WebRender is enabled, simple clip-paths can be converted into WR
+    // clips that WR knows how to hit-test against, so we don't need to mark
+    // it as an irregular area.
+    if (!gfxVars::UseWebRender() ||
+        !nsSVGIntegrationUtils::UsingSimpleClipPathForFrame(this)) {
+      result += CompositorHitTestFlags::eIrregularArea;
+    }
+  }
 
   if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
     // Scrollbars may be painted into a layer below the actual layer they will
     // scroll, and therefore wheel events may be dispatched to the outer frame
     // instead of the intended scrollframe. To address this, we force a d-t-c
     // region on scrollbar frames that won't be placed in their own layer. See
     // bug 1213324 for details.
     result += CompositorHitTestFlags::eInactiveScrollframe;