CSS Painting API should pass 'this' correctly to paint callback, and repaint when...
authorjustin_michaud@apple.com <justin_michaud@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Nov 2018 21:01:15 +0000 (21:01 +0000)
committerjustin_michaud@apple.com <justin_michaud@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Nov 2018 21:01:15 +0000 (21:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191443

Reviewed by Dean Jackson.

Source/JavaScriptCore:

Export the simpler construct() method for use in WebCore.

* runtime/ConstructData.h:

Source/WebCore:

Instantiate a new instance of the paint class, and pass it as 'this' object when the paint callback is called.
Also, this patch makes sure that custom paint elements get repainted when properties that they care about get changed.
Finally, we fix two reference cycles that caused WorkletGlobalScope to never be destroyed.

Tests: fast/css-custom-paint/animate-repaint.html
       fast/css-custom-paint/animate.html

* bindings/js/JSDOMWrapper.cpp:
* bindings/js/JSPaintWorkletGlobalScopeCustom.cpp:
(WebCore::JSPaintWorkletGlobalScope::visitAdditionalChildren):
* bindings/js/JSWorkletGlobalScopeBase.cpp:
(WebCore::toJS):
* bindings/js/ScriptState.cpp:
(WebCore::execStateFromWorkletGlobalScope):
* css/CSSPaintCallback.h:
* css/CSSPaintCallback.idl:
* css/CSSPaintImageValue.h:
* css/StyleResolver.cpp:
(WebCore::StyleResolver::applyProperty):
* dom/Document.cpp:
(WebCore::Document::prepareForDestruction):
* dom/ScriptExecutionContext.cpp:
(WebCore::ScriptExecutionContext::vm):
* platform/graphics/CustomPaintImage.cpp:
(WebCore::CustomPaintImage::CustomPaintImage):
(WebCore::CustomPaintImage::doCustomPaint):
* platform/graphics/CustomPaintImage.h:
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::addCustomPaintWatchProperty):
(WebCore::RenderStyle::changeRequiresRepaint const):
* rendering/style/RenderStyle.h:
* rendering/style/StyleRareNonInheritedData.cpp:
(WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData):
(WebCore::StyleRareNonInheritedData::operator== const):
* rendering/style/StyleRareNonInheritedData.h:
* testing/Internals.cpp:
(WebCore::Internals::isAnyWorkletGlobalScopeAlive const):
* testing/Internals.h:
* testing/Internals.idl:
* worklets/PaintWorkletGlobalScope.cpp:
(WebCore::PaintWorkletGlobalScope::devicePixelRatio const):
(WebCore::PaintWorkletGlobalScope::PaintDefinition::PaintDefinition):
(WebCore::PaintWorkletGlobalScope::registerPaint):
* worklets/PaintWorkletGlobalScope.h:
(WebCore::PaintWorkletGlobalScope::~PaintWorkletGlobalScope):
* worklets/WorkletGlobalScope.cpp:
(WebCore::WorkletGlobalScope::WorkletGlobalScope):
(WebCore::WorkletGlobalScope::~WorkletGlobalScope):
(WebCore::WorkletGlobalScope::prepareForDestruction):
(WebCore::WorkletGlobalScope::allWorkletGlobalScopesSet):
(WebCore::WorkletGlobalScope::isJSExecutionForbidden const):
(WebCore::WorkletGlobalScope::logExceptionToConsole):
(WebCore::WorkletGlobalScope::addConsoleMessage):
(WebCore::WorkletGlobalScope::addMessage):
* worklets/WorkletGlobalScope.h:
(WebCore::WorkletGlobalScope::script):
(WebCore::WorkletGlobalScope::responsibleDocument):
(WebCore::WorkletGlobalScope::responsibleDocument const):
(WebCore::WorkletGlobalScope::identifier const): Deleted.
(WebCore::WorkletGlobalScope::responsableDocument): Deleted.
(WebCore::WorkletGlobalScope::responsableDocument const): Deleted.
* worklets/WorkletScriptController.cpp:
(WebCore::WorkletScriptController::~WorkletScriptController):
(WebCore::WorkletScriptController::disableEval):
(WebCore::WorkletScriptController::disableWebAssembly):
(WebCore::WorkletScriptController::initScript):

LayoutTests:

* fast/css-custom-paint/animate-expected.html: Added.
* fast/css-custom-paint/animate-repaint-expected.txt: Added.
* fast/css-custom-paint/animate-repaint.html: Added.
* fast/css-custom-paint/animate.html: Added.
* fast/css-custom-paint/constructor-expected.html: Added.
* fast/css-custom-paint/constructor.html: Added.
* fast/css-custom-paint/leaks-expected.txt: Added.
* fast/css-custom-paint/leaks.html: Added.
* fast/css-custom-paint/properties.html:
* fast/css-custom-paint/resources/leaks-frame.html: Added.
* platform/mac/fast/css-custom-paint/raf-leak-expected.txt: Added.

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

39 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css-custom-paint/animate-expected.html [new file with mode: 0644]
LayoutTests/fast/css-custom-paint/animate-repaint-expected.txt [new file with mode: 0644]
LayoutTests/fast/css-custom-paint/animate-repaint.html [new file with mode: 0644]
LayoutTests/fast/css-custom-paint/animate.html [new file with mode: 0644]
LayoutTests/fast/css-custom-paint/constructor-expected.html [new file with mode: 0644]
LayoutTests/fast/css-custom-paint/constructor.html [new file with mode: 0644]
LayoutTests/fast/css-custom-paint/leaks-expected.txt [new file with mode: 0644]
LayoutTests/fast/css-custom-paint/leaks.html [new file with mode: 0644]
LayoutTests/fast/css-custom-paint/properties.html
LayoutTests/fast/css-custom-paint/resources/leaks-frame.html [new file with mode: 0644]
LayoutTests/platform/mac/fast/css-custom-paint/raf-leak-expected.txt [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/ConstructData.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMWrapper.cpp
Source/WebCore/bindings/js/JSPaintWorkletGlobalScopeCustom.cpp
Source/WebCore/bindings/js/JSWorkletGlobalScopeBase.cpp
Source/WebCore/bindings/js/ScriptState.cpp
Source/WebCore/css/CSSPaintCallback.h
Source/WebCore/css/CSSPaintCallback.idl
Source/WebCore/css/CSSPaintImageValue.h
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/ScriptExecutionContext.cpp
Source/WebCore/platform/graphics/CustomPaintImage.cpp
Source/WebCore/platform/graphics/CustomPaintImage.h
Source/WebCore/rendering/style/RenderStyle.cpp
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp
Source/WebCore/rendering/style/StyleRareNonInheritedData.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebCore/worklets/PaintWorkletGlobalScope.cpp
Source/WebCore/worklets/PaintWorkletGlobalScope.h
Source/WebCore/worklets/WorkletGlobalScope.cpp
Source/WebCore/worklets/WorkletGlobalScope.h
Source/WebCore/worklets/WorkletScriptController.cpp

index 2d87950..32c274e 100644 (file)
@@ -1,3 +1,22 @@
+2018-11-29  Justin Michaud  <justin_michaud@apple.com>
+
+        CSS Painting API should pass 'this' correctly to paint callback, and repaint when properties change.
+        https://bugs.webkit.org/show_bug.cgi?id=191443
+
+        Reviewed by Dean Jackson.
+
+        * fast/css-custom-paint/animate-expected.html: Added.
+        * fast/css-custom-paint/animate-repaint-expected.txt: Added.
+        * fast/css-custom-paint/animate-repaint.html: Added.
+        * fast/css-custom-paint/animate.html: Added.
+        * fast/css-custom-paint/constructor-expected.html: Added.
+        * fast/css-custom-paint/constructor.html: Added.
+        * fast/css-custom-paint/leaks-expected.txt: Added.
+        * fast/css-custom-paint/leaks.html: Added.
+        * fast/css-custom-paint/properties.html:
+        * fast/css-custom-paint/resources/leaks-frame.html: Added.
+        * platform/mac/fast/css-custom-paint/raf-leak-expected.txt: Added.
+
 2018-11-29  Youenn Fablet  <youenn@apple.com>
 
         A sender created through addTransceiver and populated using addTrack should have its source set
diff --git a/LayoutTests/fast/css-custom-paint/animate-expected.html b/LayoutTests/fast/css-custom-paint/animate-expected.html
new file mode 100644 (file)
index 0000000..f4ab88d
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<style>
+  #paint {
+    background: green;
+    width: 150px;
+    height: 150px;
+  }
+</style>
+
+<body>
+  <div id="paint"></div>
+</body>
diff --git a/LayoutTests/fast/css-custom-paint/animate-repaint-expected.txt b/LayoutTests/fast/css-custom-paint/animate-repaint-expected.txt
new file mode 100644 (file)
index 0000000..c410ce3
--- /dev/null
@@ -0,0 +1,11 @@
+(repaint rects
+  (rect 0 0 800 600)
+  (rect 0 0 150 150)
+  (rect 8 8 784 150)
+  (rect 0 0 800 166)
+  (rect 0 0 800 600)
+  (rect 8 8 150 150)
+  (rect 8 8 150 150)
+  (rect 8 8 150 150)
+)
+
diff --git a/LayoutTests/fast/css-custom-paint/animate-repaint.html b/LayoutTests/fast/css-custom-paint/animate-repaint.html
new file mode 100644 (file)
index 0000000..3a326a5
--- /dev/null
@@ -0,0 +1,66 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSPaintingAPIEnabled=true ] -->
+<meta name="author" title="Justin Michaud" href="mailto:justin_michaud@webkit.org">
+<meta name="assert" content="Test that paint worklets repaint when properties change">
+<link rel="help" content="https://drafts.css-houdini.org/css-paint-api-1/">
+<script src="resources/testharness.js"></script>
+
+<script id="code" type="text/worklet">
+  class MyPaint {
+    static get inputProperties() { return ['--my-prop']; }
+
+    paint(ctx, geom, properties) {
+      if (properties.get('--my-prop').toString() != "goodbye") {
+        ctx.fillStyle = "red";
+      } else {
+        ctx.fillStyle = "green";
+      }
+
+      ctx.fillRect(0, 0, geom.width, geom.height);
+    }
+  }
+  registerPaint('my-paint', MyPaint);
+</script>
+
+<script type="text/javascript">
+  if (window.testRunner && window.internals) {
+    window.testRunner.dumpAsText(false);
+    window.internals.startTrackingRepaints();
+  }
+  importWorklet(CSS.paintWorklet, document.getElementById('code').textContent);
+
+  // FIXME: Once importWorklet returns a promise, these setTimeouts should go away.
+  setTimeout(function() {
+    document.getElementById('paint').style.setProperty('--my-prop', 'goodbye');
+  }, 500);
+
+  setTimeout(function() {
+    var repaintRects = "No test runner";
+    if (window.testRunner && window.internals) {
+      window.internals.startTrackingRepaints();
+
+      // force a style recalc.
+      var dummy = document.body.offsetTop;
+
+      repaintRects = window.internals.repaintRectsAsText();
+
+      window.internals.stopTrackingRepaints();
+    }
+
+    var pre = document.createElement('pre');
+    document.body.appendChild(pre);
+    pre.innerHTML = repaintRects;
+  }, 1000);
+</script>
+
+<style>
+  #paint {
+    background-image: paint(my-paint);
+    --my-prop: hello;
+    width: 150px;
+    height: 150px;
+  }
+</style>
+
+<body>
+  <div id="paint"></div>
+</body>
diff --git a/LayoutTests/fast/css-custom-paint/animate.html b/LayoutTests/fast/css-custom-paint/animate.html
new file mode 100644 (file)
index 0000000..ce171b8
--- /dev/null
@@ -0,0 +1,44 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSPaintingAPIEnabled=true ] -->
+<meta name="author" title="Justin Michaud" href="mailto:justin_michaud@webkit.org">
+<meta name="assert" content="Test that paint worklets repaint when properties change">
+<link rel="help" content="https://drafts.css-houdini.org/css-paint-api-1/">
+<script src="resources/testharness.js"></script>
+
+<script id="code" type="text/worklet">
+  class MyPaint {
+    static get inputProperties() { return ['--my-prop']; }
+
+    paint(ctx, geom, properties) {
+      if (properties.get('--my-prop').toString() != "goodbye") {
+        ctx.fillStyle = "red";
+      } else {
+        ctx.fillStyle = "green";
+      }
+
+      ctx.fillRect(0, 0, geom.width, geom.height);
+    }
+  }
+  registerPaint('my-paint', MyPaint);
+</script>
+
+<script type="text/javascript">
+  importWorklet(CSS.paintWorklet, document.getElementById('code').textContent);
+
+  // FIXME: Once importWorklet returns a promise, these setTimeouts should go away.
+  setTimeout(function() {
+    document.getElementById('paint').style.setProperty('--my-prop', 'goodbye');
+  }, 500);
+</script>
+
+<style>
+  #paint {
+    background-image: paint(my-paint);
+    --my-prop: hello;
+    width: 150px;
+    height: 150px;
+  }
+</style>
+
+<body>
+  <div id="paint"></div>
+</body>
diff --git a/LayoutTests/fast/css-custom-paint/constructor-expected.html b/LayoutTests/fast/css-custom-paint/constructor-expected.html
new file mode 100644 (file)
index 0000000..4445792
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+  .paint {
+    width: 150px;
+    height: 150px;
+  }
+</style>
+
+<body>
+  <div class="paint" style="background: white;"></div>
+  <div class="paint" style="background: green;"></div>
+  <div class="paint" style="background: green;"></div>
+</body>
diff --git a/LayoutTests/fast/css-custom-paint/constructor.html b/LayoutTests/fast/css-custom-paint/constructor.html
new file mode 100644 (file)
index 0000000..0f5ac2d
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSPaintingAPIEnabled=true ] -->
+<meta name="author" title="Justin Michaud" href="mailto:justin_michaud@webkit.org">
+<meta name="assert" content="Test that paint worklets do not crash if the constructor throws">
+<link rel="help" content="https://drafts.css-houdini.org/css-paint-api-1/">
+<script src="resources/testharness.js"></script>
+
+<script id="code" type="text/worklet">
+  class MyPaint {
+    constructor() {
+      throw "Hello!";
+    }
+
+    paint(ctx, geom) {
+      ctx.fillStyle = "green";
+      ctx.fillRect(0, 0, geom.width, geom.height);
+    }
+  }
+
+  class MyPaint2 {
+    constructor() {
+    }
+
+    paint(ctx, geom) {
+      ctx.fillStyle = "green";
+      ctx.fillRect(0, 0, geom.width, geom.height);
+    }
+  }
+  registerPaint('my-paint', MyPaint);
+  registerPaint('my-paint2', MyPaint2);
+</script>
+
+<script type="text/javascript">
+  importWorklet(CSS.paintWorklet, document.getElementById('code').textContent);
+  setTimeout(function() {
+    if (window.internals && window.internals.isAnyWorkletGlobalScopeAlive()) {
+      document.getElementById('leaks').style.background = "green";
+    }
+  }, 500);
+</script>
+
+<style>
+  .paint {
+    width: 150px;
+    height: 150px;
+  }
+</style>
+
+<body>
+  <div class="paint" style="background-image: paint(my-paint);"></div>
+  <div class="paint" style="background-image: paint(my-paint2);"></div>
+  <div id="leaks" style="background: red; width: 150px; height: 150px;"></div>
+</body>
diff --git a/LayoutTests/fast/css-custom-paint/leaks-expected.txt b/LayoutTests/fast/css-custom-paint/leaks-expected.txt
new file mode 100644 (file)
index 0000000..d3ce777
--- /dev/null
@@ -0,0 +1,10 @@
+Tests that using custom paint does not cause the paint worklet global scope to get leaked.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Worklet global scope did not leak
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/css-custom-paint/leaks.html b/LayoutTests/fast/css-custom-paint/leaks.html
new file mode 100644 (file)
index 0000000..8c0c529
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSPaintingAPIEnabled=true ] -->
+<meta name="author" title="Justin Michaud" href="mailto:justin_michaud@webkit.org">
+<meta name="assert" content="Test that paint worklets don't leak">
+<link rel="help" content="https://drafts.css-houdini.org/css-paint-api-1/">
+<script src="../../resources/js-test-pre.js"></script>
+
+<iframe id="testFrame" src="resources/leaks-frame.html"></iframe>
+
+<script>
+description("Tests that using custom paint does not cause the paint worklet global scope to get leaked.");
+window.jsTestIsAsync = true;
+
+function paintShouldDie()
+{
+    return new Promise(function(resolve, reject) {
+        handle = setInterval(function() {
+            gc();
+            if (!internals.isAnyWorkletGlobalScopeAlive()) {
+                clearInterval(handle);
+                resolve();
+            }
+        }, 10);
+    });
+}
+
+var testFrame = document.getElementById("testFrame");
+testFrame.onload = function() {
+    setTimeout(function() {
+        testFrame.remove();
+        paintShouldDie().then(function() {
+            testPassed("Worklet global scope did not leak");
+            finishJSTest();
+        });
+    }, 10);
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
index 0ae688b..d9a4655 100644 (file)
 class MyPaint {
   static get inputProperties() { return ['height', '--my-prop', '--my-registered-prop', "--never-specified"]; }
   static get inputArguments() { return ['*', '*', '*']; }
+
+  constructor() { this.myAttribute = 42; }
+
+  testThis() { return this.myAttribute; }
+
   paint(ctx, geom, properties, args) {
     assert_equals(properties.get('height').toString(), args[0].toString());
     assert_equals(properties.get('height').value, 150);
@@ -30,6 +35,8 @@ class MyPaint {
     assert_equals(properties.get('width'), null);
     assert_equals(properties.get('--never-specified').toString(), '');
 
+    assert_equals(this.testThis(), 42);
+
     for (var i = 0; i < 6; i++){
       for (var j = 0; j < 6; j++){
         ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ',' +
diff --git a/LayoutTests/fast/css-custom-paint/resources/leaks-frame.html b/LayoutTests/fast/css-custom-paint/resources/leaks-frame.html
new file mode 100644 (file)
index 0000000..82757dd
--- /dev/null
@@ -0,0 +1,31 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSPaintingAPIEnabled=true ] -->
+<meta name="author" title="Justin Michaud" href="mailto:justin_michaud@webkit.org">
+<meta name="assert" content="Test that paint worklets don't leak">
+<link rel="help" content="https://drafts.css-houdini.org/css-paint-api-1/">
+
+<style>
+  .paint {
+    background-image: paint(my-paint);
+    width: 150px;
+    height: 150px;
+  }
+</style>
+
+<div class="paint"></div>
+
+<script id="code" type="text/worklet">
+const globalScope = this;
+class MyPaint {
+  paint(ctx, geom, properties, args) {
+    const dummy = globalScope; // Leak!
+    ctx.fillStyle = "green";
+    ctx.fillRect(0, 0, geom.width, geom.height);
+  }
+}
+MyPaint.createALeak = this;
+registerPaint('my-paint', MyPaint);
+</script>
+
+<script>
+CSS.paintWorklet.addModule(document.getElementById('code').textContent);
+</script>
diff --git a/LayoutTests/platform/mac/fast/css-custom-paint/raf-leak-expected.txt b/LayoutTests/platform/mac/fast/css-custom-paint/raf-leak-expected.txt
new file mode 100644 (file)
index 0000000..db63a5f
--- /dev/null
@@ -0,0 +1,5 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x8
+  RenderBlock {HTML} at (0,0) size 800x8
+    RenderBody {BODY} at (8,8) size 784x0
index 22559e4..676d21b 100644 (file)
@@ -1,3 +1,14 @@
+2018-11-29  Justin Michaud  <justin_michaud@apple.com>
+
+        CSS Painting API should pass 'this' correctly to paint callback, and repaint when properties change.
+        https://bugs.webkit.org/show_bug.cgi?id=191443
+
+        Reviewed by Dean Jackson.
+
+        Export the simpler construct() method for use in WebCore.
+
+        * runtime/ConstructData.h:
+
 2018-11-28  Mark Lam  <mark.lam@apple.com>
 
         ENABLE_SEPARATED_WX_HEAP needs to be defined in Platform.h.
index 097cf48..5938a90 100644 (file)
@@ -59,7 +59,7 @@ struct ConstructData {
 };
 
 // Convenience wrapper so you don't need to deal with CallData and CallType unless you are going to use them.
-JSObject* construct(ExecState*, JSValue functionObject, const ArgList&, const char* errorMessage);
+JS_EXPORT_PRIVATE JSObject* construct(ExecState*, JSValue functionObject, const ArgList&, const char* errorMessage);
 JS_EXPORT_PRIVATE JSObject* construct(ExecState*, JSValue constructor, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget);
 
 ALWAYS_INLINE JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args)
index 574e050..08bc032 100644 (file)
@@ -1,3 +1,77 @@
+2018-11-29  Justin Michaud  <justin_michaud@apple.com>
+
+        CSS Painting API should pass 'this' correctly to paint callback, and repaint when properties change.
+        https://bugs.webkit.org/show_bug.cgi?id=191443
+
+        Reviewed by Dean Jackson.
+
+        Instantiate a new instance of the paint class, and pass it as 'this' object when the paint callback is called. 
+        Also, this patch makes sure that custom paint elements get repainted when properties that they care about get changed.
+        Finally, we fix two reference cycles that caused WorkletGlobalScope to never be destroyed.
+
+        Tests: fast/css-custom-paint/animate-repaint.html
+               fast/css-custom-paint/animate.html
+
+        * bindings/js/JSDOMWrapper.cpp:
+        * bindings/js/JSPaintWorkletGlobalScopeCustom.cpp:
+        (WebCore::JSPaintWorkletGlobalScope::visitAdditionalChildren):
+        * bindings/js/JSWorkletGlobalScopeBase.cpp:
+        (WebCore::toJS):
+        * bindings/js/ScriptState.cpp:
+        (WebCore::execStateFromWorkletGlobalScope):
+        * css/CSSPaintCallback.h:
+        * css/CSSPaintCallback.idl:
+        * css/CSSPaintImageValue.h:
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::applyProperty):
+        * dom/Document.cpp:
+        (WebCore::Document::prepareForDestruction):
+        * dom/ScriptExecutionContext.cpp:
+        (WebCore::ScriptExecutionContext::vm):
+        * platform/graphics/CustomPaintImage.cpp:
+        (WebCore::CustomPaintImage::CustomPaintImage):
+        (WebCore::CustomPaintImage::doCustomPaint):
+        * platform/graphics/CustomPaintImage.h:
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::addCustomPaintWatchProperty):
+        (WebCore::RenderStyle::changeRequiresRepaint const):
+        * rendering/style/RenderStyle.h:
+        * rendering/style/StyleRareNonInheritedData.cpp:
+        (WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData):
+        (WebCore::StyleRareNonInheritedData::operator== const):
+        * rendering/style/StyleRareNonInheritedData.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::isAnyWorkletGlobalScopeAlive const):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        * worklets/PaintWorkletGlobalScope.cpp:
+        (WebCore::PaintWorkletGlobalScope::devicePixelRatio const):
+        (WebCore::PaintWorkletGlobalScope::PaintDefinition::PaintDefinition):
+        (WebCore::PaintWorkletGlobalScope::registerPaint):
+        * worklets/PaintWorkletGlobalScope.h:
+        (WebCore::PaintWorkletGlobalScope::~PaintWorkletGlobalScope):
+        * worklets/WorkletGlobalScope.cpp:
+        (WebCore::WorkletGlobalScope::WorkletGlobalScope):
+        (WebCore::WorkletGlobalScope::~WorkletGlobalScope):
+        (WebCore::WorkletGlobalScope::prepareForDestruction):
+        (WebCore::WorkletGlobalScope::allWorkletGlobalScopesSet):
+        (WebCore::WorkletGlobalScope::isJSExecutionForbidden const):
+        (WebCore::WorkletGlobalScope::logExceptionToConsole):
+        (WebCore::WorkletGlobalScope::addConsoleMessage):
+        (WebCore::WorkletGlobalScope::addMessage):
+        * worklets/WorkletGlobalScope.h:
+        (WebCore::WorkletGlobalScope::script):
+        (WebCore::WorkletGlobalScope::responsibleDocument):
+        (WebCore::WorkletGlobalScope::responsibleDocument const):
+        (WebCore::WorkletGlobalScope::identifier const): Deleted.
+        (WebCore::WorkletGlobalScope::responsableDocument): Deleted.
+        (WebCore::WorkletGlobalScope::responsableDocument const): Deleted.
+        * worklets/WorkletScriptController.cpp:
+        (WebCore::WorkletScriptController::~WorkletScriptController):
+        (WebCore::WorkletScriptController::disableEval):
+        (WebCore::WorkletScriptController::disableWebAssembly):
+        (WebCore::WorkletScriptController::initScript):
+
 2018-11-29  Alexey Proskuryakov  <ap@apple.com>
 
         Modernize the check for kCFURLRequestContentDecoderSkipURLCheck existence
index 19fcd77..9bf0b1c 100644 (file)
@@ -35,6 +35,7 @@
 #include <JavaScriptCore/Error.h>
 
 namespace WebCore {
+using namespace JSC;
 
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSDOMObject);
 
index a8ceae6..01184a8 100644 (file)
@@ -34,8 +34,10 @@ using namespace JSC;
 void JSPaintWorkletGlobalScope::visitAdditionalChildren(JSC::SlotVisitor& visitor)
 {
     auto locker = holdLock(wrapped().paintDefinitionLock());
-    for (auto& registered : wrapped().paintDefinitionMap().values())
+    for (auto& registered : wrapped().paintDefinitionMap().values()) {
         registered->paintCallback->visitJSFunction(visitor);
+        visitor.appendUnbarriered(registered->paintConstructor);
+    }
 }
 
 }
index fb5a755..7cd80e7 100644 (file)
@@ -128,7 +128,8 @@ JSValue toJS(ExecState* exec, JSDOMGlobalObject*, WorkletGlobalScope& workletGlo
 
 JSValue toJS(ExecState*, WorkletGlobalScope& workletGlobalScope)
 {
-    auto* contextWrapper = workletGlobalScope.script().workletGlobalScopeWrapper();
+    ASSERT(workletGlobalScope.script());
+    auto* contextWrapper = workletGlobalScope.script()->workletGlobalScopeWrapper();
     ASSERT(contextWrapper);
     return contextWrapper->proxy();
 }
index 37b0cde..e98410b 100644 (file)
@@ -106,7 +106,9 @@ JSC::ExecState* execStateFromWorkerGlobalScope(WorkerGlobalScope& workerGlobalSc
 #if ENABLE(CSS_PAINTING_API)
 JSC::ExecState* execStateFromWorkletGlobalScope(WorkletGlobalScope& workletGlobalScope)
 {
-    return workletGlobalScope.script().workletGlobalScopeWrapper()->globalExec();
+    if (!workletGlobalScope.script())
+        return nullptr;
+    return workletGlobalScope.script()->workletGlobalScopeWrapper()->globalExec();
 }
 #endif
 
index 756563c..0c03f0a 100644 (file)
 #include "CSSPaintSize.h"
 #include "CallbackResult.h"
 #include "StylePropertyMapReadOnly.h"
+#include <JavaScriptCore/JSCJSValue.h>
 #include <wtf/RefCounted.h>
+#include <wtf/WeakPtr.h>
 
 namespace WebCore {
 class PaintRenderingContext2D;
 
-class CSSPaintCallback : public RefCounted<CSSPaintCallback>, public ActiveDOMCallback {
+class CSSPaintCallback : public RefCounted<CSSPaintCallback>, public CanMakeWeakPtr<CSSPaintCallback>, public ActiveDOMCallback {
 public:
     using ActiveDOMCallback::ActiveDOMCallback;
 
-    virtual CallbackResult<void> handleEvent(PaintRenderingContext2D&, CSSPaintSize&, StylePropertyMapReadOnly&, const Vector<String>&) = 0;
+    virtual CallbackResult<void> handleEvent(JSC::JSValue, PaintRenderingContext2D&, CSSPaintSize&, StylePropertyMapReadOnly&, const Vector<String>&) = 0;
 
     virtual ~CSSPaintCallback()
     {
index 86756b5..72b523d 100644 (file)
@@ -26,4 +26,5 @@
 [
     EnabledAtRuntime=CSSPaintingAPI,
     Conditional=CSS_PAINTING_API,
+    CallbackThisObject=any
 ] callback CSSPaintCallback = void (PaintRenderingContext2D context, CSSPaintSize size, StylePropertyMapReadOnly styleMap, sequence<USVString> arguments);
index b6b69c8..43bcc1c 100644 (file)
@@ -40,6 +40,8 @@ public:
         return adoptRef(*new CSSPaintImageValue(name, WTFMove(arguments)));
     }
 
+    const String& name() const { return m_name; }
+
     RefPtr<Image> image(RenderElement&, const FloatSize&);
 
     bool equals(const CSSPaintImageValue& other) const { return m_name == other.m_name; }
index c7ab3bb..6a1ce49 100644 (file)
@@ -42,6 +42,7 @@
 #include "CSSImageValue.h"
 #include "CSSKeyframeRule.h"
 #include "CSSKeyframesRule.h"
+#include "CSSPaintImageValue.h"
 #include "CSSParser.h"
 #include "CSSPrimitiveValueMappings.h"
 #include "CSSPropertyNames.h"
@@ -73,6 +74,7 @@
 #include "MediaQueryEvaluator.h"
 #include "NodeRenderStyle.h"
 #include "PageRuleCollector.h"
+#include "PaintWorkletGlobalScope.h"
 #include "Pair.h"
 #include "RenderScrollbar.h"
 #include "RenderStyleConstants.h"
@@ -1717,6 +1719,18 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, ApplyCascad
     if (isInherit && !CSSProperty::isInheritedProperty(id))
         state.style()->setHasExplicitlyInheritedProperties();
 
+#if ENABLE(CSS_PAINTING_API)
+    if (is<CSSPaintImageValue>(*valueToApply) && document().paintWorkletGlobalScope()) {
+        // FIXME: This should use the "document paint registration map" from the spec, once it is implemented.
+        auto& paintWorklet = *document().paintWorkletGlobalScope();
+        auto locker = holdLock(paintWorklet.paintDefinitionLock());
+        if (auto* registration = paintWorklet.paintDefinitionMap().get(downcast<CSSPaintImageValue>(*valueToApply).name())) {
+            for (auto& property : registration->inputProperties)
+                state.style()->addCustomPaintWatchProperty(property);
+        }
+    }
+#endif
+
     // Use the generated StyleBuilder.
     StyleBuilder::applyProperty(id, *this, *valueToApply, isInitial, isInherit, customPropertyRegistered);
 }
index 2ac5bbe..de08181 100644 (file)
@@ -2531,6 +2531,13 @@ void Document::prepareForDestruction()
     }
 #endif
 
+#if ENABLE(CSS_PAINTING_API)
+    if (m_paintWorkletGlobalScope) {
+        m_paintWorkletGlobalScope->prepareForDestruction();
+        m_paintWorkletGlobalScope = nullptr;
+    }
+#endif
+
     m_hasPreparedForDestruction = true;
 
     // Note that m_pageCacheState can be Document::AboutToEnterPageCache if our frame
index 760dbb6..b3cfba9 100644 (file)
@@ -482,7 +482,7 @@ JSC::VM& ScriptExecutionContext::vm()
         return downcast<WorkerGlobalScope>(*this).script()->vm();
 #if ENABLE(CSS_PAINTING_API)
     if (is<WorkletGlobalScope>(*this))
-        return downcast<WorkletGlobalScope>(*this).script().vm();
+        return downcast<WorkletGlobalScope>(*this).script()->vm();
 #endif
 
     RELEASE_ASSERT_NOT_REACHED();
index 43962c5..034da56 100644 (file)
 #include "JSCSSPaintCallback.h"
 #include "PaintRenderingContext2D.h"
 #include "RenderElement.h"
+#include <JavaScriptCore/ConstructData.h>
 
 namespace WebCore {
 
-CustomPaintImage::CustomPaintImage(const PaintWorkletGlobalScope::PaintDefinition& definition, const FloatSize& size, RenderElement& element, const Vector<String>& arguments)
-    : m_paintCallback(definition.paintCallback.get())
+CustomPaintImage::CustomPaintImage(PaintWorkletGlobalScope::PaintDefinition& definition, const FloatSize& size, RenderElement& element, const Vector<String>& arguments)
+    : m_paintDefinition(makeWeakPtr(definition))
     , m_inputProperties(definition.inputProperties)
     , m_element(makeWeakPtr(element))
     , m_arguments(arguments)
@@ -56,13 +57,20 @@ CustomPaintImage::~CustomPaintImage() = default;
 
 ImageDrawResult CustomPaintImage::doCustomPaint(GraphicsContext& destContext, const FloatSize& destSize)
 {
-    if (!m_element || !m_element->element())
+    if (!m_element || !m_element->element() || !m_paintDefinition)
         return ImageDrawResult::DidNothing;
 
+    JSC::JSValue paintConstructor(m_paintDefinition->paintConstructor);
+
+    if (!paintConstructor)
+        return ImageDrawResult::DidNothing;
+
+    auto& paintCallback = m_paintDefinition->paintCallback.get();
+
     ASSERT(!m_element->needsLayout());
     ASSERT(!m_element->element()->document().needsStyleRecalc());
 
-    JSCSSPaintCallback& callback = static_cast<JSCSSPaintCallback&>(m_paintCallback.get());
+    JSCSSPaintCallback& callback = static_cast<JSCSSPaintCallback&>(paintCallback);
     auto* scriptExecutionContext = callback.scriptExecutionContext();
     if (!scriptExecutionContext)
         return ImageDrawResult::DidNothing;
@@ -103,7 +111,21 @@ ImageDrawResult CustomPaintImage::doCustomPaint(GraphicsContext& destContext, co
     auto size = CSSPaintSize::create(destSize.width(), destSize.height());
     auto propertyMap = StylePropertyMapReadOnly::create(WTFMove(propertyValues));
 
-    auto result = m_paintCallback->handleEvent(*context, size, propertyMap, m_arguments);
+    auto& vm = *paintConstructor.getObject()->vm();
+    JSC::JSLockHolder lock(vm);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    auto& globalObject = *paintConstructor.getObject()->globalObject();
+
+    auto& state = *globalObject.globalExec();
+    JSC::ArgList noArgs;
+    JSC::JSValue thisObject = { JSC::construct(&state, WTFMove(paintConstructor), noArgs, "Failed to construct paint class") };
+
+    if (UNLIKELY(scope.exception())) {
+        reportException(&state, scope.exception());
+        return ImageDrawResult::DidNothing;
+    }
+
+    auto result = paintCallback.handleEvent(WTFMove(thisObject), *context, size, propertyMap, m_arguments);
     if (result.type() != CallbackResultType::Success)
         return ImageDrawResult::DidNothing;
 
index d306793..68b40c9 100644 (file)
@@ -29,6 +29,8 @@
 
 #include "GeneratedImage.h"
 #include "PaintWorkletGlobalScope.h"
+#include <JavaScriptCore/JSObject.h>
+#include <JavaScriptCore/Weak.h>
 #include <wtf/WeakPtr.h>
 
 namespace WebCore {
@@ -38,7 +40,7 @@ class RenderElement;
 
 class CustomPaintImage final : public GeneratedImage {
 public:
-    static Ref<CustomPaintImage> create(const PaintWorkletGlobalScope::PaintDefinition& definition, const FloatSize& size, RenderElement& element, const Vector<String>& arguments)
+    static Ref<CustomPaintImage> create(PaintWorkletGlobalScope::PaintDefinition& definition, const FloatSize& size, RenderElement& element, const Vector<String>& arguments)
     {
         return adoptRef(*new CustomPaintImage(definition, size, element, arguments));
     }
@@ -47,14 +49,14 @@ public:
     bool isCustomPaintImage() const override { return true; }
 
 private:
-    CustomPaintImage(const PaintWorkletGlobalScope::PaintDefinition&, const FloatSize&, RenderElement&, const Vector<String>& arguments);
+    CustomPaintImage(PaintWorkletGlobalScope::PaintDefinition&, const FloatSize&, RenderElement&, const Vector<String>& arguments);
 
     ImageDrawResult doCustomPaint(GraphicsContext&, const FloatSize&);
 
     ImageDrawResult draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) final;
     void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) final;
 
-    Ref<CSSPaintCallback> m_paintCallback;
+    WeakPtr<PaintWorkletGlobalScope::PaintDefinition> m_paintDefinition;
     Vector<String> m_inputProperties;
     WeakPtr<RenderElement> m_element;
     Vector<String> m_arguments;
index a57d1c5..faf9ca0 100644 (file)
 #include "config.h"
 #include "RenderStyle.h"
 
-#include "ContentData.h"
+#include "CSSComputedStyleDeclaration.h"
 #include "CSSCustomPropertyValue.h"
 #include "CSSParser.h"
 #include "CSSPropertyNames.h"
+#include "CSSPropertyParser.h"
+#include "ContentData.h"
 #include "CursorList.h"
 #include "FloatRoundedRect.h"
 #include "FontCascade.h"
@@ -962,6 +964,16 @@ static bool rareInheritedDataChangeRequiresRepaint(const StyleRareInheritedData&
     ;
 }
 
+#if ENABLE(CSS_PAINTING_API)
+void RenderStyle::addCustomPaintWatchProperty(const String& name)
+{
+    auto& data = m_rareNonInheritedData.access();
+    if (!data.customPaintWatchedProperties)
+        data.customPaintWatchedProperties = std::make_unique<HashSet<String>>();
+    data.customPaintWatchedProperties->add(name);
+}
+#endif
+
 bool RenderStyle::changeRequiresRepaint(const RenderStyle& other, OptionSet<StyleDifferenceContextSensitiveProperty>& changedContextSensitiveProperties) const
 {
     if (!requiresPainting(*this) && !requiresPainting(other))
@@ -983,6 +995,45 @@ bool RenderStyle::changeRequiresRepaint(const RenderStyle& other, OptionSet<Styl
         && rareInheritedDataChangeRequiresRepaint(*m_rareInheritedData, *other.m_rareInheritedData))
         return true;
 
+#if ENABLE(CSS_PAINTING_API)
+    auto* propertiesA = m_rareNonInheritedData.ptr()->customPaintWatchedProperties.get();
+    auto* propertiesB = other.m_rareNonInheritedData.ptr()->customPaintWatchedProperties.get();
+
+    if (UNLIKELY(propertiesA || propertiesB)) {
+        // FIXME: We should not need to use ComputedStyleExtractor here.
+        ComputedStyleExtractor extractor((Element*) nullptr);
+
+        for (auto* watchPropertiesMap : { propertiesA, propertiesB }) {
+            if (!watchPropertiesMap)
+                continue;
+
+            for (auto& name : *watchPropertiesMap) {
+                RefPtr<CSSValue> valueA;
+                RefPtr<CSSValue> valueB;
+                if (isCustomPropertyName(name) && getCustomProperty(name) && other.getCustomProperty(name)) {
+                    valueA = CSSCustomPropertyValue::create(*getCustomProperty(name));
+                    valueB = CSSCustomPropertyValue::create(*other.getCustomProperty(name));
+                } else {
+                    CSSPropertyID propertyID = cssPropertyID(name);
+                    if (!propertyID)
+                        continue;
+                    valueA = extractor.valueForPropertyinStyle(*this, propertyID);
+                    valueB = extractor.valueForPropertyinStyle(other, propertyID);
+                }
+
+                if ((valueA && !valueB) || (!valueA && valueB))
+                    return true;
+
+                if (!valueA)
+                    continue;
+
+                if (!(*valueA == *valueB))
+                    return true;
+            }
+        }
+    }
+#endif
+
     return false;
 }
 
index 3acb1c9..dbaaf0c 100644 (file)
@@ -1267,6 +1267,10 @@ public:
     void setApplePayButtonType(ApplePayButtonType type) { SET_VAR(m_rareNonInheritedData, applePayButtonType, static_cast<unsigned>(type)); }
 #endif
 
+#if ENABLE(CSS_PAINTING_API)
+    void addCustomPaintWatchProperty(const String& name);
+#endif
+
     // Support for paint-order, stroke-linecap, stroke-linejoin, and stroke-miterlimit from https://drafts.fxtf.org/paint/.
     void setPaintOrder(PaintOrder order) { SET_VAR(m_rareInheritedData, paintOrder, static_cast<unsigned>(order)); }
     PaintOrder paintOrder() const { return static_cast<PaintOrder>(m_rareInheritedData->paintOrder); }
index 080667b..a312bda 100644 (file)
@@ -169,6 +169,7 @@ inline StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonIn
     , justifyItems(o.justifyItems)
     , justifySelf(o.justifySelf)
     , customProperties(o.customProperties)
+    , customPaintWatchedProperties(o.customPaintWatchedProperties ? std::make_unique<HashSet<String>>(*o.customPaintWatchedProperties) : nullptr)
 #if ENABLE(TOUCH_EVENTS)
     , touchAction(o.touchAction)
 #endif
@@ -271,6 +272,8 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c
         && justifyItems == o.justifyItems
         && justifySelf == o.justifySelf
         && customProperties == o.customProperties
+        && ((customPaintWatchedProperties && o.customPaintWatchedProperties && *customPaintWatchedProperties == *o.customPaintWatchedProperties)
+            || (!customPaintWatchedProperties && !o.customPaintWatchedProperties))
         && pageSizeType == o.pageSizeType
         && transformStyle3D == o.transformStyle3D
         && backfaceVisibility == o.backfaceVisibility
index d411cb2..f9baa14 100644 (file)
@@ -172,6 +172,7 @@ public:
     StyleSelfAlignmentData justifySelf;
 
     DataRef<StyleCustomPropertyData> customProperties;
+    std::unique_ptr<HashSet<String>> customPaintWatchedProperties;
 
 #if ENABLE(TOUCH_EVENTS)
     unsigned touchAction : 1; // TouchAction
index 2fb676d..b11a325 100644 (file)
 #include "WebCoreJSClientData.h"
 #include "WindowProxy.h"
 #include "WorkerThread.h"
+#include "WorkletGlobalScope.h"
 #include "WritingDirection.h"
 #include "XMLHttpRequest.h"
 #include <JavaScriptCore/CodeBlock.h>
@@ -2387,6 +2388,15 @@ bool Internals::isDocumentAlive(uint64_t documentIdentifier) const
     return Document::allDocumentsMap().contains(makeObjectIdentifier<DocumentIdentifierType>(documentIdentifier));
 }
 
+bool Internals::isAnyWorkletGlobalScopeAlive() const
+{
+#if ENABLE(CSS_PAINTING_API)
+    return !WorkletGlobalScope::allWorkletGlobalScopesSet().isEmpty();
+#else
+    return false;
+#endif
+}
+
 String Internals::serviceWorkerClientIdentifier(const Document& document) const
 {
 #if ENABLE(SERVICE_WORKER)
index 943967f..8ae3443 100644 (file)
@@ -388,6 +388,8 @@ public:
     uint64_t documentIdentifier(const Document&) const;
     bool isDocumentAlive(uint64_t documentIdentifier) const;
 
+    bool isAnyWorkletGlobalScopeAlive() const;
+
     String serviceWorkerClientIdentifier(const Document&) const;
 
     RefPtr<WindowProxy> openDummyInspectorFrontend(const String& url);
index a1705ed..2719346 100644 (file)
@@ -675,6 +675,8 @@ enum CompositingPolicy {
     unsigned long long documentIdentifier(Document document);
     boolean isDocumentAlive(unsigned long long documentIdentifier);
 
+    boolean isAnyWorkletGlobalScopeAlive();
+
     DOMString serviceWorkerClientIdentifier(Document document);
 
     Promise<void> clearCacheStorageMemoryRepresentation();
index a29a200..3e3d956 100644 (file)
@@ -33,6 +33,7 @@
 #include "JSCSSPaintCallback.h"
 #include "JSDOMConvertCallbacks.h"
 #include "JSDOMConvertSequences.h"
+#include "RenderView.h"
 #include <wtf/SetForScope.h>
 
 namespace WebCore {
@@ -50,15 +51,25 @@ PaintWorkletGlobalScope::PaintWorkletGlobalScope(Document& document, ScriptSourc
 
 double PaintWorkletGlobalScope::devicePixelRatio() const
 {
-    if (!responsableDocument() || !responsableDocument()->domWindow())
+    if (!responsibleDocument() || !responsibleDocument()->domWindow())
         return 1.0;
-    return responsableDocument()->domWindow()->devicePixelRatio();
+    return responsibleDocument()->domWindow()->devicePixelRatio();
+}
+
+PaintWorkletGlobalScope::PaintDefinition::PaintDefinition(const AtomicString& name, JSC::JSObject* paintConstructor, Ref<CSSPaintCallback>&& paintCallback, Vector<String>&& inputProperties, Vector<String>&& inputArguments)
+    : name(name)
+    , paintConstructor(paintConstructor)
+    , paintCallback(WTFMove(paintCallback))
+    , inputProperties(WTFMove(inputProperties))
+    , inputArguments(WTFMove(inputArguments))
+{
 }
 
 // https://drafts.css-houdini.org/css-paint-api/#registering-custom-paint
 ExceptionOr<void> PaintWorkletGlobalScope::registerPaint(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const String& name, Strong<JSObject> paintConstructor)
 {
     auto& vm = *paintConstructor->vm();
+    JSC::JSLockHolder lock(vm);
     auto scope = DECLARE_THROW_SCOPE(vm);
 
     // Validate that paintConstructor is a VoidFunction
@@ -69,62 +80,70 @@ ExceptionOr<void> PaintWorkletGlobalScope::registerPaint(JSC::ExecState& state,
     if (name.isEmpty())
         return Exception { TypeError, "The first argument must not be the empty string" };
 
-    auto locker = holdLock(paintDefinitionLock());
+    {
+        auto locker = holdLock(paintDefinitionLock());
 
-    if (paintDefinitionMap().contains(name))
-        return Exception { InvalidModificationError, "This name has already been registered" };
+        if (paintDefinitionMap().contains(name))
+            return Exception { InvalidModificationError, "This name has already been registered" };
 
-    Vector<String> inputProperties;
+        Vector<String> inputProperties;
 
-    JSValue inputPropertiesIterableValue = paintConstructor->get(&state, Identifier::fromString(&vm, "inputProperties"));
-    RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
+        JSValue inputPropertiesIterableValue = paintConstructor->get(&state, Identifier::fromString(&vm, "inputProperties"));
+        RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
 
-    if (!inputPropertiesIterableValue.isUndefined())
-        inputProperties = convert<IDLSequence<IDLDOMString>>(state, inputPropertiesIterableValue);
-    RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
+        if (!inputPropertiesIterableValue.isUndefined())
+            inputProperties = convert<IDLSequence<IDLDOMString>>(state, inputPropertiesIterableValue);
+        RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
 
-    // FIXME: Validate input properties here (step 7).
+        // FIXME: Validate input properties here (step 7).
 
-    Vector<String> inputArguments;
+        Vector<String> inputArguments;
 
-    JSValue inputArgumentsIterableValue = paintConstructor->get(&state, Identifier::fromString(&vm, "inputArguments"));
-    RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
+        JSValue inputArgumentsIterableValue = paintConstructor->get(&state, Identifier::fromString(&vm, "inputArguments"));
+        RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
 
-    if (!inputArgumentsIterableValue.isUndefined())
-        inputArguments = convert<IDLSequence<IDLDOMString>>(state, inputArgumentsIterableValue);
-    RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
+        if (!inputArgumentsIterableValue.isUndefined())
+            inputArguments = convert<IDLSequence<IDLDOMString>>(state, inputArgumentsIterableValue);
+        RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
 
-    // FIXME: Parse syntax for inputArguments here (steps 11 and 12).
+        // FIXME: Parse syntax for inputArguments here (steps 11 and 12).
 
-    JSValue contextOptionsValue = paintConstructor->get(&state, Identifier::fromString(&vm, "contextOptions"));
-    RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
-    UNUSED_PARAM(contextOptionsValue);
+        JSValue contextOptionsValue = paintConstructor->get(&state, Identifier::fromString(&vm, "contextOptions"));
+        RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
+        UNUSED_PARAM(contextOptionsValue);
 
-    // FIXME: Convert to PaintRenderingContext2DSettings here (step 14).
+        // FIXME: Convert to PaintRenderingContext2DSettings here (step 14).
 
-    if (!paintConstructor->isConstructor(vm))
-        return Exception { TypeError, "The second argument must be a constructor" };
+        if (!paintConstructor->isConstructor(vm))
+            return Exception { TypeError, "The second argument must be a constructor" };
 
-    JSValue prototypeValue = paintConstructor->get(&state, vm.propertyNames->prototype);
-    RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
+        JSValue prototypeValue = paintConstructor->get(&state, vm.propertyNames->prototype);
+        RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
 
-    if (!prototypeValue.isObject())
-        return Exception { TypeError, "The second argument must have a prototype that is an object" };
+        if (!prototypeValue.isObject())
+            return Exception { TypeError, "The second argument must have a prototype that is an object" };
 
-    JSValue paintValue = prototypeValue.get(&state, Identifier::fromString(&vm, "paint"));
-    RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
+        JSValue paintValue = prototypeValue.get(&state, Identifier::fromString(&vm, "paint"));
+        RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
 
-    if (paintValue.isUndefined())
-        return Exception { TypeError, "The class must have a paint method" };
+        if (paintValue.isUndefined())
+            return Exception { TypeError, "The class must have a paint method" };
 
-    RefPtr<JSCSSPaintCallback> paint = convert<IDLCallbackFunction<JSCSSPaintCallback>>(state, paintValue, globalObject);
-    RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
+        RefPtr<JSCSSPaintCallback> paint = convert<IDLCallbackFunction<JSCSSPaintCallback>>(state, paintValue, globalObject);
+        RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
 
-    auto paintDefinition = std::unique_ptr<PaintDefinition>(new PaintDefinition { name, paint.releaseNonNull(), inputProperties, inputArguments });
-    paintDefinitionMap().add(name, WTFMove(paintDefinition));
+        auto paintDefinition = std::make_unique<PaintDefinition>(name, paintConstructor.get(), paint.releaseNonNull(), WTFMove(inputProperties), WTFMove(inputArguments));
+        paintDefinitionMap().add(name, WTFMove(paintDefinition));
+    }
+
+    // This is for the case when we have already visited the paint definition map, and the GC is currently running in the background.
+    vm.heap.writeBarrier(&globalObject);
 
     // FIXME: construct documentDefinition (step 22).
 
+    if (responsibleDocument() && responsibleDocument()->renderView())
+        responsibleDocument()->renderView()->repaintRootContents();
+
     return { };
 }
 
index 23271f1..f98e577 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "CSSPaintCallback.h"
 #include "WorkletGlobalScope.h"
+#include <JavaScriptCore/JSObject.h>
 #include <JavaScriptCore/Strong.h>
 
 namespace JSC {
@@ -45,8 +46,12 @@ public:
     ExceptionOr<void> registerPaint(JSC::ExecState&, JSDOMGlobalObject&, const String& name, JSC::Strong<JSC::JSObject> paintConstructor);
     double devicePixelRatio() const;
 
-    struct PaintDefinition {
+    struct PaintDefinition : public CanMakeWeakPtr<PaintDefinition> {
+        PaintDefinition(const AtomicString& name, JSC::JSObject* paintConstructor, Ref<CSSPaintCallback>&&, Vector<String>&& inputProperties, Vector<String>&& inputArguments);
+
         const AtomicString name;
+        // This map must be cleared before the vm is destroyed!
+        JSC::JSObject* paintConstructor { nullptr };
         const Ref<CSSPaintCallback> paintCallback;
         const Vector<String> inputProperties;
         const Vector<String> inputArguments;
@@ -55,9 +60,26 @@ public:
     HashMap<String, std::unique_ptr<PaintDefinition>>& paintDefinitionMap() { ASSERT(m_paintDefinitionLock.isLocked()); return m_paintDefinitionMap; }
     Lock& paintDefinitionLock() { return m_paintDefinitionLock; }
 
+    void prepareForDestruction() final
+    {
+        {
+            auto locker = holdLock(paintDefinitionLock());
+            paintDefinitionMap().clear();
+        }
+        WorkletGlobalScope::prepareForDestruction();
+    }
+
 private:
     PaintWorkletGlobalScope(Document&, ScriptSourceCode&&);
 
+    ~PaintWorkletGlobalScope()
+    {
+#if !ASSERT_DISABLED
+        auto locker = holdLock(paintDefinitionLock());
+        ASSERT(paintDefinitionMap().isEmpty());
+#endif
+    }
+
     bool isPaintWorkletGlobalScope() const final { return true; }
 
     HashMap<String, std::unique_ptr<PaintDefinition>> m_paintDefinitionMap;
index a0d3009..3c56196 100644 (file)
@@ -39,7 +39,6 @@
 #include "WorkletScriptController.h"
 
 #include <JavaScriptCore/Exception.h>
-#include <JavaScriptCore/IdentifiersFactory.h>
 #include <JavaScriptCore/JSLock.h>
 #include <JavaScriptCore/ScriptCallStack.h>
 
@@ -49,12 +48,14 @@ using namespace Inspector;
 WorkletGlobalScope::WorkletGlobalScope(Document& document, ScriptSourceCode&& code)
     : m_document(makeWeakPtr(document))
     , m_sessionID(m_document->sessionID())
-    , m_script(makeUniqueRef<WorkletScriptController>(this))
+    , m_script(std::make_unique<WorkletScriptController>(this))
     , m_topOrigin(SecurityOrigin::createUnique())
-    , m_identifier(makeString("WorkletGlobalScope:"_s, Inspector::IdentifiersFactory::createIdentifier()))
     , m_eventQueue(*this)
     , m_code(WTFMove(code))
 {
+    auto addResult = allWorkletGlobalScopesSet().add(this);
+    ASSERT_UNUSED(addResult, addResult);
+
     auto* frame = document.frame();
     m_jsRuntimeFlags = frame ? frame->settings().javaScriptRuntimeFlags() : JSC::RuntimeFlags();
     ASSERT(document.page());
@@ -65,8 +66,26 @@ WorkletGlobalScope::WorkletGlobalScope(Document& document, ScriptSourceCode&& co
 
 WorkletGlobalScope::~WorkletGlobalScope()
 {
+    ASSERT(!m_script);
     removeFromContextsMap();
-    m_script->workletGlobalScopeWrapper()->setConsoleClient(nullptr);
+    auto removeResult = allWorkletGlobalScopesSet().remove(this);
+    ASSERT_UNUSED(removeResult, removeResult);
+}
+
+void WorkletGlobalScope::prepareForDestruction()
+{
+    ASSERT(m_script);
+    stopActiveDOMObjects();
+    removeRejectedPromiseTracker();
+    removeAllEventListeners();
+    m_script->vm().notifyNeedTermination();
+    m_script = nullptr;
+}
+
+auto WorkletGlobalScope::allWorkletGlobalScopesSet() -> WorkletGlobalScopesSet&
+{
+    static NeverDestroyed<WorkletGlobalScopesSet> scopes;
+    return scopes;
 }
 
 String WorkletGlobalScope::origin() const
@@ -88,7 +107,7 @@ void WorkletGlobalScope::evaluate()
 
 bool WorkletGlobalScope::isJSExecutionForbidden() const
 {
-    return m_script->isExecutionForbidden();
+    return !m_script || m_script->isExecutionForbidden();
 }
 
 void WorkletGlobalScope::disableEval(const String& errorMessage)
@@ -110,30 +129,30 @@ URL WorkletGlobalScope::completeURL(const String& url) const
 
 void WorkletGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<ScriptCallStack>&& stack)
 {
-    if (!m_document)
+    if (!m_document || isJSExecutionForbidden())
         return;
     m_document->logExceptionToConsole(errorMessage, sourceURL, lineNumber, columnNumber, WTFMove(stack));
 }
 
 void WorkletGlobalScope::addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&& message)
 {
-    if (!m_document)
+    if (!m_document || isJSExecutionForbidden() || !message)
         return;
-    m_document->addConsoleMessage(WTFMove(message));
+    m_document->addConsoleMessage(std::make_unique<Inspector::ConsoleMessage>(message->source(), message->type(), message->level(), message->message(), 0));
 }
 
 void WorkletGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
 {
-    if (!m_document)
+    if (!m_document || isJSExecutionForbidden())
         return;
     m_document->addConsoleMessage(source, level, message, requestIdentifier);
 }
 
-void WorkletGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& messageText, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<ScriptCallStack>&& callStack, JSC::ExecState* state, unsigned long requestIdentifier)
+void WorkletGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& messageText, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<ScriptCallStack>&& callStack, JSC::ExecState*, unsigned long requestIdentifier)
 {
-    if (!m_document)
+    if (!m_document || isJSExecutionForbidden())
         return;
-    m_document->addMessage(source, level, messageText, sourceURL, lineNumber, columnNumber, WTFMove(callStack), state, requestIdentifier);
+    m_document->addMessage(source, level, messageText, sourceURL, lineNumber, columnNumber, WTFMove(callStack), nullptr, requestIdentifier);
 }
 
 } // namespace WebCore
index f01564d..ccec0dc 100644 (file)
 #include <JavaScriptCore/ConsoleMessage.h>
 #include <JavaScriptCore/RuntimeFlags.h>
 #include <pal/SessionID.h>
-#include <wtf/UniqueRef.h>
+#include <wtf/ObjectIdentifier.h>
 #include <wtf/WeakPtr.h>
 
 namespace WebCore {
 class WorkletScriptController;
 class ScriptSourceCode;
 
+enum WorkletGlobalScopeIdentifierType { };
+using WorkletGlobalScopeIdentifier = ObjectIdentifier<WorkletGlobalScopeIdentifierType>;
+
 class WorkletGlobalScope : public RefCounted<WorkletGlobalScope>, public ScriptExecutionContext, public EventTargetWithInlineData {
 public:
-    ~WorkletGlobalScope();
+    virtual ~WorkletGlobalScope();
+
+    using WorkletGlobalScopesSet = HashSet<const WorkletGlobalScope*>;
+    WEBCORE_EXPORT static WorkletGlobalScopesSet& allWorkletGlobalScopesSet();
 
     virtual bool isPaintWorkletGlobalScope() const { return false; }
 
     const URL& url() const final { return m_code.url(); }
     String origin() const final;
-    const String& identifier() const { return m_identifier; }
 
     void evaluate();
 
     using RefCounted::ref;
     using RefCounted::deref;
 
-    WorkletScriptController& script() { return m_script.get(); }
+    WorkletScriptController* script() { return m_script.get(); }
 
     void addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&&) final;
 
@@ -74,11 +79,15 @@ public:
 
     JSC::RuntimeFlags jsRuntimeFlags() const { return m_jsRuntimeFlags; }
 
+    virtual void prepareForDestruction();
+
 protected:
     WorkletGlobalScope(Document&, ScriptSourceCode&&);
+    WorkletGlobalScope(const WorkletGlobalScope&) = delete;
+    WorkletGlobalScope(WorkletGlobalScope&&) = delete;
 
-    Document* responsableDocument() { return m_document.get(); }
-    const Document* responsableDocument() const { return m_document.get(); }
+    Document* responsibleDocument() { return m_document.get(); }
+    const Document* responsibleDocument() const { return m_document.get(); }
 
 private:
 #if ENABLE(INDEXED_DATABASE)
@@ -118,10 +127,9 @@ private:
     WeakPtr<Document> m_document;
 
     PAL::SessionID m_sessionID;
-    UniqueRef<WorkletScriptController> m_script;
+    std::unique_ptr<WorkletScriptController> m_script;
 
     Ref<SecurityOrigin> m_topOrigin;
-    String m_identifier;
 
     // FIXME: This is not implemented properly, it just satisfies the compiler.
     // https://bugs.webkit.org/show_bug.cgi?id=191136
index 055d7f1..1168978 100644 (file)
@@ -59,13 +59,15 @@ WorkletScriptController::WorkletScriptController(WorkletGlobalScope* workletGlob
 WorkletScriptController::~WorkletScriptController()
 {
     JSLockHolder lock(vm());
-    m_workletGlobalScopeWrapper.clear();
-    m_vm = nullptr;
+    forbidExecution();
+
     if (m_workletGlobalScopeWrapper) {
         m_workletGlobalScopeWrapper->clearDOMGuardedObjects();
         m_workletGlobalScopeWrapper->setConsoleClient(nullptr);
         m_consoleClient = nullptr;
     }
+    m_workletGlobalScopeWrapper.clear();
+    m_vm = nullptr;
 }
 
 void WorkletScriptController::forbidExecution()
@@ -82,6 +84,9 @@ bool WorkletScriptController::isExecutionForbidden() const
 
 void WorkletScriptController::disableEval(const String& errorMessage)
 {
+    if (isExecutionForbidden())
+        return;
+
     initScriptIfNeeded();
     JSLockHolder lock { vm() };
 
@@ -90,6 +95,9 @@ void WorkletScriptController::disableEval(const String& errorMessage)
 
 void WorkletScriptController::disableWebAssembly(const String& errorMessage)
 {
+    if (isExecutionForbidden())
+        return;
+
     initScriptIfNeeded();
     JSLockHolder lock { vm() };
 
@@ -132,6 +140,9 @@ void WorkletScriptController::initScriptWithSubclass()
 
 void WorkletScriptController::initScript()
 {
+    if (isExecutionForbidden())
+        return;
+
     if (is<PaintWorkletGlobalScope>(m_workletGlobalScope)) {
         initScriptWithSubclass<JSPaintWorkletGlobalScopePrototype, JSPaintWorkletGlobalScope, PaintWorkletGlobalScope>();
         return;