Restrict the total combined size of backdrop filters
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Sep 2018 05:37:01 +0000 (05:37 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Sep 2018 05:37:01 +0000 (05:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189812
<rdar://problem/44532782>

Reviewed by Simon Fraser.

Source/WebCore:

If the total area of all backdrop filters on the page gets
too large, the universe collapses in on itself and we enter
the Quantum Realm (i.e. crash horribly).

Put a hard limit on the total coverage, and ignore any backdrop
filters after the limit. This might break some content, but
such content is likely not doing things in the most optimal manner.
There isn't any reason to have a backdrop larger than the size of
the screen, because you'd be better off applying a foreground
filter to the main content and showing something above it.

Tests: css3/filters/backdrop/resource-use-add-more-layers.html
       css3/filters/backdrop/resource-use-excessive.html
       css3/filters/backdrop/resource-use-ok.html
       css3/filters/backdrop/resource-use-remove-some-layers.html

* platform/graphics/ca/GraphicsLayerCA.cpp: Pick a fairly small maximum size. We
can consider increasing this if necessary, and as devices with less RAM are
upgraded.
(WebCore::GraphicsLayerCA::recursiveCommitChanges): Gather the accumulated size
of backdrop filters into the commit state as we are recursing through the tree.
(WebCore::GraphicsLayerCA::commitLayerChangesBeforeSublayers): Force any layer
with backdrop filters, or any that is removing backdrop filters, into an update.
(WebCore::GraphicsLayerCA::updateBackdropFilters): Update the logic to first
check if this backdrop layer causes us to exceed the total allowed size, and if
it does, forbid it from getting the GraphicsLayer that composits the backdrop.

* platform/graphics/ca/GraphicsLayerCA.h: Remove const from some parameters so
that we can use the CommitState to hold the accumulated size.

LayoutTests:

Tests that have an acceptable number of backdrops, an excessive
number of backdrops, and then some that add and remove backdrops
at various points in the tree to confirm we do recursive checks
correctly.

* css3/filters/backdrop/layer-tree-as-text.js: Added.
* css3/filters/backdrop/resource-use-add-more-layers-expected.txt: Added.
* css3/filters/backdrop/resource-use-add-more-layers.html: Added.
* css3/filters/backdrop/resource-use-excessive-expected.txt: Added.
* css3/filters/backdrop/resource-use-excessive.html: Added.
* css3/filters/backdrop/resource-use-ok-expected.txt: Added.
* css3/filters/backdrop/resource-use-ok.html: Added.
* css3/filters/backdrop/resource-use-remove-some-layers-expected.txt: Added.
* css3/filters/backdrop/resource-use-remove-some-layers.html: Added.
* css3/filters/backdrop/resource-use.css: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@236306 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/css3/filters/backdrop/layer-tree-as-text.js [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use-add-more-layers-expected.txt [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use-add-more-layers.html [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use-excessive-expected.txt [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use-excessive.html [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use-ok-expected.txt [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use-ok.html [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use-remove-some-layers-expected.txt [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use-remove-some-layers.html [new file with mode: 0644]
LayoutTests/css3/filters/backdrop/resource-use.css [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h

index f30500b..09493bf 100644 (file)
@@ -1,3 +1,27 @@
+2018-09-20  Dean Jackson  <dino@apple.com>
+
+        Restrict the total combined size of backdrop filters
+        https://bugs.webkit.org/show_bug.cgi?id=189812
+        <rdar://problem/44532782>
+
+        Reviewed by Simon Fraser.
+
+        Tests that have an acceptable number of backdrops, an excessive
+        number of backdrops, and then some that add and remove backdrops
+        at various points in the tree to confirm we do recursive checks
+        correctly.
+
+        * css3/filters/backdrop/layer-tree-as-text.js: Added.
+        * css3/filters/backdrop/resource-use-add-more-layers-expected.txt: Added.
+        * css3/filters/backdrop/resource-use-add-more-layers.html: Added.
+        * css3/filters/backdrop/resource-use-excessive-expected.txt: Added.
+        * css3/filters/backdrop/resource-use-excessive.html: Added.
+        * css3/filters/backdrop/resource-use-ok-expected.txt: Added.
+        * css3/filters/backdrop/resource-use-ok.html: Added.
+        * css3/filters/backdrop/resource-use-remove-some-layers-expected.txt: Added.
+        * css3/filters/backdrop/resource-use-remove-some-layers.html: Added.
+        * css3/filters/backdrop/resource-use.css: Added.
+
 2018-09-20  Truitt Savell  <tsavell@apple.com>
 
         Continued test gardening after r236236
diff --git a/LayoutTests/css3/filters/backdrop/layer-tree-as-text.js b/LayoutTests/css3/filters/backdrop/layer-tree-as-text.js
new file mode 100644 (file)
index 0000000..10a80cd
--- /dev/null
@@ -0,0 +1,25 @@
+const addLayerTree = () => {
+    if (!window.internals)
+        return;
+    const layerTree = internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_CONTENT_LAYERS);
+    const output = document.createElement("pre");
+    output.textContent = layerTree;
+    document.body.insertBefore(output, document.body.firstChild);
+};
+
+const addLayerTreeAndFinish = () => {
+    addLayerTree();
+    testRunner.notifyDone();
+};
+
+if (window.internals) {
+    window.addEventListener("load", () => {
+        if (window.doNotAutomaticallyCallLayerTree != undefined && window.doNotAutomaticallyCallLayerTree) {
+            testRunner.waitUntilDone();
+            return;
+        }
+        addLayerTree();
+    }, false);
+
+    testRunner.dumpAsText();
+}
diff --git a/LayoutTests/css3/filters/backdrop/resource-use-add-more-layers-expected.txt b/LayoutTests/css3/filters/backdrop/resource-use-add-more-layers-expected.txt
new file mode 100644 (file)
index 0000000..f93b316
--- /dev/null
@@ -0,0 +1,113 @@
+(GraphicsLayer
+  (anchor 0.00 0.00)
+  (bounds 1243.00 2209.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 1243.00 2209.00)
+      (contentsOpaque 1)
+      (children 3
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+          (children 10
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+            )
+          )
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/css3/filters/backdrop/resource-use-add-more-layers.html b/LayoutTests/css3/filters/backdrop/resource-use-add-more-layers.html
new file mode 100644 (file)
index 0000000..57e0aa8
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="stylesheet" href="resource-use.css">
+<!-- start with a small number of backdrop filters, so each should get a backdrop layer at first -->
+<div></div>
+<div></div>
+<div></div>
+<!-- then add a bunch via script, as children to the first div, eventually pushing us over the limit -->
+<script>
+const doNotAutomaticallyCallLayerTree = true;
+
+window.addEventListener("load", () => {
+    const firstDiv = document.querySelector("div");
+    for (let i = 0; i < 10; i++) {
+        const newDiv = document.createElement("div");
+        firstDiv.appendChild(newDiv);
+    }
+    setTimeout(() => {
+        addLayerTreeAndFinish();
+    }, 0);
+}, false);
+</script>
+<script src="layer-tree-as-text.js"></script>
diff --git a/LayoutTests/css3/filters/backdrop/resource-use-excessive-expected.txt b/LayoutTests/css3/filters/backdrop/resource-use-excessive-expected.txt
new file mode 100644 (file)
index 0000000..a76b6f8
--- /dev/null
@@ -0,0 +1,116 @@
+(GraphicsLayer
+  (anchor 0.00 0.00)
+  (bounds 1245.00 2211.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 1245.00 2211.00)
+      (contentsOpaque 1)
+      (children 8
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+          (children 1
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+          )
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+          (children 1
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+              (children 1
+                (GraphicsLayer
+                  (position 1.00 1.00)
+                  (bounds 1242.00 2208.00)
+                  (usingTiledLayer 1)
+                  (drawsContent 1)
+                  (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+                  (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+                  (children 1
+                    (GraphicsLayer
+                      (position 1.00 1.00)
+                      (bounds 1242.00 2208.00)
+                      (usingTiledLayer 1)
+                      (drawsContent 1)
+                      (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+                      (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+                    )
+                  )
+                )
+              )
+            )
+          )
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (children 1
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+            )
+          )
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/css3/filters/backdrop/resource-use-excessive.html b/LayoutTests/css3/filters/backdrop/resource-use-excessive.html
new file mode 100644 (file)
index 0000000..6f85ee2
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="stylesheet" href="resource-use.css">
+<!-- these first few elements will get a backdrop layer -->
+<div></div>
+<div><div></div></div>
+<div></div>
+<div><div><div><div></div></div></div></div>
+<div></div>
+<div></div>
+<!-- and by here we're exceeding the limits, so they won't get a backdrop layer -->
+<div><div></div></div>
+<div></div>
+<script src="layer-tree-as-text.js"></script>
diff --git a/LayoutTests/css3/filters/backdrop/resource-use-ok-expected.txt b/LayoutTests/css3/filters/backdrop/resource-use-ok-expected.txt
new file mode 100644 (file)
index 0000000..81ccaa4
--- /dev/null
@@ -0,0 +1,44 @@
+(GraphicsLayer
+  (anchor 0.00 0.00)
+  (bounds 1243.00 2209.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 1243.00 2209.00)
+      (contentsOpaque 1)
+      (children 3
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+          (children 1
+            (GraphicsLayer
+              (position 1.00 1.00)
+              (bounds 1242.00 2208.00)
+              (usingTiledLayer 1)
+              (drawsContent 1)
+              (structural layer 622.00, 1105.00 1242.00 x 2208.00)
+              (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+            )
+          )
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/css3/filters/backdrop/resource-use-ok.html b/LayoutTests/css3/filters/backdrop/resource-use-ok.html
new file mode 100644 (file)
index 0000000..51cdb9b
--- /dev/null
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<link rel="stylesheet" href="resource-use.css">
+<!-- this is a small number of backdrop filters, so each should get a backdrop layer -->
+<div></div>
+<div><div></div></div>
+<div></div>
+<script src="layer-tree-as-text.js"></script>
diff --git a/LayoutTests/css3/filters/backdrop/resource-use-remove-some-layers-expected.txt b/LayoutTests/css3/filters/backdrop/resource-use-remove-some-layers-expected.txt
new file mode 100644 (file)
index 0000000..3f89ce1
--- /dev/null
@@ -0,0 +1,62 @@
+(GraphicsLayer
+  (anchor 0.00 0.00)
+  (bounds 1242.00 2208.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 1242.00 2208.00)
+      (contentsOpaque 1)
+      (children 7
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+        (GraphicsLayer
+          (bounds 1242.00 2208.00)
+          (usingTiledLayer 1)
+          (drawsContent 1)
+          (structural layer 621.00, 1104.00 1242.00 x 2208.00)
+          (backdrop layer 0.00, 0.00 1242.00 x 2208.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/css3/filters/backdrop/resource-use-remove-some-layers.html b/LayoutTests/css3/filters/backdrop/resource-use-remove-some-layers.html
new file mode 100644 (file)
index 0000000..9c4395d
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="stylesheet" href="resource-use.css">
+<!-- start with a large number of backdrop filters, so we exceed the limit -->
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<!-- then remove some from the start, getting us back under the limit -->
+<script>
+const doNotAutomaticallyCallLayerTree = true;
+
+window.addEventListener("load", () => {
+    for (let i = 0; i < 10; i++) {
+        const firstDiv = document.querySelector("div");
+        firstDiv.remove();
+    }
+    setTimeout(() => {
+        addLayerTreeAndFinish();
+    }, 0);
+}, false);
+</script>
+<script src="layer-tree-as-text.js"></script>
diff --git a/LayoutTests/css3/filters/backdrop/resource-use.css b/LayoutTests/css3/filters/backdrop/resource-use.css
new file mode 100644 (file)
index 0000000..b03fc75
--- /dev/null
@@ -0,0 +1,11 @@
+/* The total backdrop filter limit is 1242 * 2208 * 10, so make each div 1/10th of that */
+div {
+      -webkit-backdrop-filter: blur(1px);
+      width: 1242px;
+      height: 2208px;
+      position: absolute;
+      top: 0;
+      left: 0;
+      border: 1px solid blue;
+      box-sizing: border-box;
+}
index 93cfa02..042968e 100644 (file)
@@ -1,3 +1,41 @@
+2018-09-20  Dean Jackson  <dino@apple.com>
+
+        Restrict the total combined size of backdrop filters
+        https://bugs.webkit.org/show_bug.cgi?id=189812
+        <rdar://problem/44532782>
+
+        Reviewed by Simon Fraser.
+
+        If the total area of all backdrop filters on the page gets
+        too large, the universe collapses in on itself and we enter
+        the Quantum Realm (i.e. crash horribly).
+
+        Put a hard limit on the total coverage, and ignore any backdrop
+        filters after the limit. This might break some content, but
+        such content is likely not doing things in the most optimal manner.
+        There isn't any reason to have a backdrop larger than the size of
+        the screen, because you'd be better off applying a foreground
+        filter to the main content and showing something above it.
+
+        Tests: css3/filters/backdrop/resource-use-add-more-layers.html
+               css3/filters/backdrop/resource-use-excessive.html
+               css3/filters/backdrop/resource-use-ok.html
+               css3/filters/backdrop/resource-use-remove-some-layers.html
+
+        * platform/graphics/ca/GraphicsLayerCA.cpp: Pick a fairly small maximum size. We
+        can consider increasing this if necessary, and as devices with less RAM are
+        upgraded.
+        (WebCore::GraphicsLayerCA::recursiveCommitChanges): Gather the accumulated size
+        of backdrop filters into the commit state as we are recursing through the tree.
+        (WebCore::GraphicsLayerCA::commitLayerChangesBeforeSublayers): Force any layer
+        with backdrop filters, or any that is removing backdrop filters, into an update.
+        (WebCore::GraphicsLayerCA::updateBackdropFilters): Update the logic to first
+        check if this backdrop layer causes us to exceed the total allowed size, and if
+        it does, forbid it from getting the GraphicsLayer that composits the backdrop.
+
+        * platform/graphics/ca/GraphicsLayerCA.h: Remove const from some parameters so
+        that we can use the CommitState to hold the accumulated size.
+
 2018-09-20  Benjamin Poulain  <benjamin@webkit.org>
 
         Adopt safe-area-insets on ImageDocument
index d8a4372..76484cf 100644 (file)
@@ -47,6 +47,7 @@
 #include <QuartzCore/CATransform3D.h>
 #include <limits.h>
 #include <pal/spi/cf/CFUtilitiesSPI.h>
+#include <wtf/CheckedArithmetic.h>
 #include <wtf/MathExtras.h>
 #include <wtf/NeverDestroyed.h>
 #include <wtf/SetForScope.h>
@@ -88,7 +89,10 @@ static const int cMaxPixelDimension = 2048;
 #endif
 
 // Derived empirically: <rdar://problem/13401861>
-static const int cMaxLayerTreeDepth = 250;
+static const unsigned cMaxLayerTreeDepth = 250;
+
+// About 10 screens of an iPhone 6 Plus. <rdar://problem/44532782>
+static const unsigned cMaxTotalBackdropFilterArea = 1242 * 2208 * 10;
 
 // If we send a duration of 0 to CA, then it will use the default duration
 // of 250ms. So send a very small value instead.
@@ -1487,7 +1491,7 @@ bool GraphicsLayerCA::needsCommit(const CommitState& commitState)
 
 // rootRelativeTransformForScaling is a transform from the root, but for layers with transform animations, it cherry-picked the state of the
 // animation that contributes maximally to the scale (on every layer with animations down the hierarchy).
-void GraphicsLayerCA::recursiveCommitChanges(const CommitState& commitState, const TransformState& state, float pageScaleFactor, const FloatPoint& positionRelativeToBase, bool affectedByPageScale)
+void GraphicsLayerCA::recursiveCommitChanges(CommitState& commitState, const TransformState& state, float pageScaleFactor, const FloatPoint& positionRelativeToBase, bool affectedByPageScale)
 {
     if (!needsCommit(commitState))
         return;
@@ -1584,6 +1588,8 @@ void GraphicsLayerCA::recursiveCommitChanges(const CommitState& commitState, con
             hasDescendantsWithRunningTransformAnimations = true;
     }
 
+    commitState.totalBackdropFilterArea = childCommitState.totalBackdropFilterArea;
+
     if (GraphicsLayerCA* replicaLayer = downcast<GraphicsLayerCA>(m_replicaLayer.get()))
         replicaLayer->recursiveCommitChanges(childCommitState, localState, pageScaleFactor, baseRelativePosition, affectedByPageScale);
 
@@ -1778,8 +1784,10 @@ void GraphicsLayerCA::commitLayerChangesBeforeSublayers(CommitState& commitState
     if (m_uncommittedChanges & FiltersChanged)
         updateFilters();
 
-    if (m_uncommittedChanges & BackdropFiltersChanged)
-        updateBackdropFilters();
+    // If there are backdrop filters, we need to always check the resource usage
+    // because something up the tree may have changed its usage.
+    if (m_uncommittedChanges & BackdropFiltersChanged || needsBackdrop())
+        updateBackdropFilters(commitState);
 
     if (m_uncommittedChanges & BackdropFiltersRectChanged)
         updateBackdropFiltersRect();
@@ -2126,9 +2134,24 @@ void GraphicsLayerCA::updateFilters()
     }
 }
 
-void GraphicsLayerCA::updateBackdropFilters()
+void GraphicsLayerCA::updateBackdropFilters(CommitState& commitState)
 {
-    if (m_backdropFilters.isEmpty()) {
+    bool canHaveBackdropFilters = needsBackdrop();
+
+    if (canHaveBackdropFilters) {
+        Checked<unsigned, RecordOverflow> backdropFilterArea = Checked<unsigned>(static_cast<int>(m_backdropFiltersRect.rect().width())) * Checked<unsigned>(static_cast<int>(m_backdropFiltersRect.rect().height()));
+        if (backdropFilterArea.hasOverflowed())
+            canHaveBackdropFilters = false;
+        else {
+            Checked<unsigned, RecordOverflow> newTotalBackdropFilterArea = Checked<unsigned, RecordOverflow>(commitState.totalBackdropFilterArea) + backdropFilterArea;
+            if (newTotalBackdropFilterArea.hasOverflowed() || newTotalBackdropFilterArea.unsafeGet() > cMaxTotalBackdropFilterArea)
+                canHaveBackdropFilters = false;
+            else
+                commitState.totalBackdropFilterArea = newTotalBackdropFilterArea.unsafeGet();
+        }
+    }
+
+    if (!canHaveBackdropFilters) {
         if (m_backdropLayer) {
             m_backdropLayer->removeFromSuperlayer();
             m_backdropLayer->setOwner(nullptr);
@@ -2137,6 +2160,10 @@ void GraphicsLayerCA::updateBackdropFilters()
         return;
     }
 
+    // If nothing actually changed, no need to touch the layer properties.
+    if (!(m_uncommittedChanges & BackdropFiltersChanged))
+        return;
+
     bool madeLayer = !m_backdropLayer;
     if (!m_backdropLayer) {
         m_backdropLayer = createPlatformCALayer(PlatformCALayer::LayerTypeBackdropLayer, this);
index 3de602e..55bf516 100644 (file)
@@ -154,7 +154,8 @@ public:
     FloatSize pixelAlignmentOffset() const override { return m_pixelAlignmentOffset; }
 
     struct CommitState {
-        int treeDepth { 0 };
+        unsigned treeDepth { 0 };
+        unsigned totalBackdropFilterArea { 0 };
         bool ancestorHadChanges { false };
         bool ancestorHasTransformAnimation { false };
         bool ancestorStartedOrEndedTransformAnimation { false };
@@ -162,7 +163,7 @@ public:
         bool ancestorIsViewportConstrained { false };
     };
     bool needsCommit(const CommitState&);
-    void recursiveCommitChanges(const CommitState&, const TransformState&, float pageScaleFactor = 1, const FloatPoint& positionRelativeToBase = FloatPoint(), bool affectedByPageScale = false);
+    void recursiveCommitChanges(CommitState&, const TransformState&, float pageScaleFactor = 1, const FloatPoint& positionRelativeToBase = FloatPoint(), bool affectedByPageScale = false);
 
     WEBCORE_EXPORT void flushCompositingState(const FloatRect&) override;
     WEBCORE_EXPORT void flushCompositingStateForThisLayerOnly() override;
@@ -424,7 +425,7 @@ private:
 
     void updateOpacityOnLayer();
     void updateFilters();
-    void updateBackdropFilters();
+    void updateBackdropFilters(CommitState&);
     void updateBackdropFiltersRect();
 
 #if ENABLE(CSS_COMPOSITING)