2020-11-04 15:56:26 +00:00
|
|
|
<!DOCTYPE html><!-- webkit-test-runner [ CSSPaintingAPIEnabled=true ] -->
|
2018-10-16 18:04:43 +00:00
|
|
|
<meta name="author" title="Justin Michaud" href="mailto:justin_michaud@webkit.org">
|
|
|
|
<meta name="assert" content="registerPaint accesses arguments in the correct order">
|
|
|
|
<link rel="help" content="https://drafts.css-houdini.org/css-paint-api-1/">
|
2018-11-03 04:01:29 +00:00
|
|
|
<script src="resources/testharness.js"></script>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
#paint {
|
|
|
|
background-image: paint(my-paint);
|
|
|
|
width: 150px;
|
|
|
|
height: 150px;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
|
|
|
|
<div id="paint"></div>
|
|
|
|
|
|
|
|
<script id="code" type="text/worklet">
|
2018-10-16 18:04:43 +00:00
|
|
|
class MyPaint {
|
|
|
|
paint() { }
|
|
|
|
}
|
|
|
|
|
2018-11-03 04:01:29 +00:00
|
|
|
test(function() {
|
|
|
|
registerPaint('basic1', class {
|
|
|
|
static get inputProperties() { return ["--test"]; }
|
|
|
|
paint() { }
|
|
|
|
});
|
|
|
|
}, 'test that registerPaint runs');
|
|
|
|
|
[WebIDL] Remove [ImplicitThis] and [CustomProxyToJSObject] extended attributes
https://bugs.webkit.org/show_bug.cgi?id=223758
Reviewed by Sam Weinig.
LayoutTests/imported/w3c:
* web-platform-tests/WebIDL/ecmascript-binding/global-object-implicit-this-value.any-expected.txt: Added.
* web-platform-tests/WebIDL/ecmascript-binding/global-object-implicit-this-value.any.html: Added.
* web-platform-tests/WebIDL/ecmascript-binding/global-object-implicit-this-value.any.js: Added.
* web-platform-tests/WebIDL/ecmascript-binding/global-object-implicit-this-value.any.worker-expected.txt: Added.
* web-platform-tests/WebIDL/ecmascript-binding/global-object-implicit-this-value.any.worker.html: Added.
* web-platform-tests/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-registerprocessor-called-on-globalthis.https-expected.txt: Added.
* web-platform-tests/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-registerprocessor-called-on-globalthis.https.html: Added.
* web-platform-tests/webaudio/the-audio-api/the-audioworklet-interface/processors/dummy-processor-globalthis.js: Added.
Source/WebCore:
This change introduces castThisValue<JSClass>, taking a step towards unification of |this|
value casting between IDLAttribute and IDLOperation. The helper uses compile-time inheritance
check to provide implicit |this| value for DOM global objects [1], replacing [ImplicitThis]
extended attribute, which was removed from the spec [2] a while ago.
IDLAttribute can't perform toThis() with ECMAMode::strict(), like IDLOperation now does,
because CustomValue getters are called with |this| value of JSGlobalObject type, which gets
tainted by JSScope::toThis(). #225397 will remove the need for toThis(), finally making |this|
value casting consistent between attributes and operations.
Also, this patch fixes `Object.create(window).location` to throw as per spec [1] by removing
prototype chain traversal from toJSDOMWindow(), which aligns WebKit with Blink and Gecko.
As DOM global objects are wrapped in proxies and require special casting, toJSDOMWindow() and
friends are merged into toJSDOMGlobalObject<JSClass>, which is aware of inheritance / JSProxy.
It replaces [CustomProxyToJSObject] extended attribute, which could be missed when adding new
DOM global objects, fixing worklets' global functions not to throw when called on `globalThis`.
This change reduces WebCore --release binary size by 0.2% (147 KB).
[1] https://heycam.github.io/webidl/#dfn-attribute-getter (step 1.1.2.3)
[2] https://github.com/heycam/webidl/pull/155
Tests: imported/w3c/web-platform-tests/WebIDL/ecmascript-binding/global-object-implicit-this-value.any.js
imported/w3c/web-platform-tests/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-registerprocessor-called-on-globalthis.https.html
fast/css-custom-paint/registerPaintBindings.html
http/tests/security/listener/*.html
* Headers.cmake:
* Modules/webaudio/AudioWorkletGlobalScope.idl:
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/JSDOMAttribute.h:
(WebCore::IDLAttribute::set):
(WebCore::IDLAttribute::setPassingPropertyName):
(WebCore::IDLAttribute::get):
(WebCore::IDLAttribute::getPassingPropertyName):
* bindings/js/JSDOMCastThisValue.h: Added.
(WebCore::castThisValue):
* bindings/js/JSDOMCastedThisErrorBehavior.h: Removed.
* bindings/js/JSDOMGlobalObject.h:
(WebCore::toJSDOMGlobalObject):
* bindings/js/JSDOMOperation.h:
(WebCore::IDLOperation::cast):
* bindings/js/JSDOMWindowBase.cpp:
* bindings/js/JSDOMWindowBase.h:
(WebCore::toJSDOMWindow):
* bindings/js/JSDOMWindowCustom.cpp:
(WebCore::JSC_DEFINE_CUSTOM_GETTER):
(WebCore::IDLOperation<JSDOMWindow>::cast): Deleted.
* bindings/js/JSDocumentCustom.cpp:
(WebCore::cachedDocumentWrapper):
* bindings/js/JSEventTargetCustom.cpp:
(WebCore::jsEventTargetCast):
* bindings/js/JSEventTargetCustom.h:
(WebCore::IDLOperation<JSEventTarget>::call):
* bindings/js/JSRemoteDOMWindowBase.cpp:
(WebCore::toJSRemoteDOMWindow): Deleted.
* bindings/js/JSRemoteDOMWindowBase.h:
* bindings/js/JSWorkerGlobalScopeBase.cpp:
(WebCore::toJSDedicatedWorkerGlobalScope): Deleted.
(WebCore::toJSWorkerGlobalScope): Deleted.
(WebCore::toJSServiceWorkerGlobalScope): Deleted.
* bindings/js/JSWorkerGlobalScopeBase.h:
* bindings/js/JSWorkletGlobalScopeBase.cpp:
(WebCore::toJSWorkletGlobalScope): Deleted.
* bindings/js/JSWorkletGlobalScopeBase.h:
* bindings/scripts/CodeGeneratorJS.pm:
(ShouldGenerateToJSDeclaration):
(IsAcceleratedDOMAttribute):
(GenerateImplementation):
* bindings/scripts/IDLAttributes.json:
* bindings/scripts/test/JS/*: Updated.
* inspector/InspectorController.cpp:
(WebCore::InspectorController::canAccessInspectedScriptState const):
* page/DOMWindow.idl:
* page/RemoteDOMWindow.idl:
* workers/DedicatedWorkerGlobalScope.idl:
* workers/WorkerGlobalScope.idl:
* workers/service/ServiceWorkerGlobalScope.idl:
* worklets/PaintWorkletGlobalScope.idl:
* worklets/WorkletGlobalScope.idl:
Source/WebKit:
Use inherits<T> instead of toJSDOMWindow() if the value is never a JSProxy.
* WebProcess/InjectedBundle/InjectedBundle.cpp:
(WebKit::InjectedBundle::reportException):
Source/WebKitLegacy/mac:
Use inherits<T> instead of toJSDOMWindow() if the value is never a JSProxy.
* WebView/WebView.mm:
(+[WebView _reportException:inContext:]):
Source/WebKitLegacy/win:
Use inherits<T> instead of toJSDOMWindow() if the value is never a JSProxy.
* WebView.cpp:
(WebView::reportException):
LayoutTests:
* fast/css-custom-paint/registerPaintBindings.html:
* http/tests/security/listener/*:
This is a progression: Blink and Gecko don't call event listeners belonging to destroyed frames.
* js/property-of-window-as-prototype-expected.txt: Removed.
* js/property-of-window-as-prototype.html: Removed.
Canonical link: https://commits.webkit.org/237976@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@277830 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2021-05-20 22:25:01 +00:00
|
|
|
test(function() {
|
|
|
|
globalThis.registerPaint('basic1-globalThis', class {
|
|
|
|
static get inputProperties() { return ["--test"]; }
|
|
|
|
paint() { }
|
|
|
|
});
|
|
|
|
}, 'test that registerPaint runs (called on globalThis)');
|
|
|
|
|
2018-11-03 04:01:29 +00:00
|
|
|
test(function() {
|
|
|
|
registerPaint('basic2', class {
|
|
|
|
paint() { }
|
|
|
|
});
|
|
|
|
}, 'test that registerPaint runs without inputProperties');
|
|
|
|
|
|
|
|
test(function() {
|
|
|
|
registerPaint('basic3', class {
|
|
|
|
static get inputArguments() { return ["<length>"]; }
|
|
|
|
paint() { }
|
|
|
|
});
|
|
|
|
}, 'test that registerPaint runs with inputArguments');
|
|
|
|
|
|
|
|
test(function() {
|
|
|
|
registerPaint('basic4', class {
|
|
|
|
static get contextOptions() { return { alpha: true }; }
|
|
|
|
paint() { }
|
|
|
|
});
|
|
|
|
}, 'test that registerPaint runs with contextOptions');
|
|
|
|
|
|
|
|
test(function() {
|
|
|
|
registerPaint('basic5', MyPaint);
|
|
|
|
assert_throws({'name': 'InvalidModificationError'}, () => registerPaint('basic5', MyPaint));
|
|
|
|
registerPaint('basic6', class extends MyPaint { });
|
|
|
|
}, 'test that registerPaint runs with predefined class');
|
|
|
|
|
|
|
|
test(function() {
|
|
|
|
assert_throws({'name': 'TypeError'}, () => registerPaint('basic6', 'test'));
|
|
|
|
assert_throws(new TypeError(), () => registerPaint('basic8'));
|
|
|
|
assert_throws(new TypeError(), () => registerPaint('basic9', []));
|
|
|
|
assert_throws(new TypeError(), () => registerPaint('basic10', {}));
|
|
|
|
assert_throws(new TypeError(), () => registerPaint('basic11', 5));
|
|
|
|
assert_throws(new TypeError(), () => registerPaint('', 5));
|
|
|
|
}, 'test that registerPaint accepts only a string and a class constructor');
|
2018-10-16 18:04:43 +00:00
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const calls = [];
|
|
|
|
const proxy = new Proxy(class extends MyPaint { }, {
|
|
|
|
get: function (target, name) {
|
|
|
|
calls.push(name);
|
|
|
|
return target[name];
|
|
|
|
}
|
|
|
|
});
|
2018-11-03 04:01:29 +00:00
|
|
|
registerPaint('test-proto', proxy);
|
2018-10-16 18:04:43 +00:00
|
|
|
assert_array_equals(calls, ["inputProperties", "inputArguments", "contextOptions", "prototype"]);
|
|
|
|
}, 'must get "prototype" property of the constructor');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const calls = [];
|
|
|
|
const proxy = new Proxy(class extends MyPaint { }, {
|
|
|
|
get: function (target, name) {
|
|
|
|
calls.push(name);
|
|
|
|
if (name == 'inputProperties')
|
|
|
|
throw {name: 'expectedError'};
|
|
|
|
return target[name];
|
|
|
|
}
|
|
|
|
});
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'expectedError'}, function () { registerPaint('test-rethrow-inputProperties0', proxy); });
|
2018-10-16 18:04:43 +00:00
|
|
|
}, 'must rethrow an exception thrown while getting "inputProperties" property of the constructor');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const calls = [];
|
|
|
|
const proxy = new Proxy(class extends MyPaint { }, {
|
|
|
|
get: function (target, name) {
|
|
|
|
calls.push(name);
|
|
|
|
if (name == 'prototype')
|
|
|
|
throw {name: 'expectedError'};
|
|
|
|
return target[name];
|
|
|
|
}
|
|
|
|
});
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'expectedError'}, function () { registerPaint('test-rethrow-proto', proxy); });
|
2018-10-16 18:04:43 +00:00
|
|
|
}, 'must rethrow an exception thrown while getting "prototype" property of the constructor');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
let returnedValue;
|
|
|
|
const proxy = new Proxy(class extends MyPaint { }, {
|
|
|
|
get: function (target, name) {
|
|
|
|
if (name == 'prototype')
|
|
|
|
return returnedValue;
|
|
|
|
return target[name];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
returnedValue = null;
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'TypeError'}, function () { registerPaint('test-rethrow-proto-noobj1', proxy); },
|
2018-10-16 18:04:43 +00:00
|
|
|
'must throw when "prototype" property of the constructor is null');
|
|
|
|
returnedValue = undefined;
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'TypeError'}, function () { registerPaint('test-rethrow-proto-noobj2', proxy); },
|
2018-10-16 18:04:43 +00:00
|
|
|
'must throw when "prototype" property of the constructor is undefined');
|
|
|
|
returnedValue = 'hello';
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'TypeError'}, function () { registerPaint('test-rethrow-proto-noobj3', proxy);; },
|
2018-10-16 18:04:43 +00:00
|
|
|
'must throw when "prototype" property of the constructor is a string');
|
|
|
|
returnedValue = 1;
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'TypeError'}, function () { registerPaint('test-rethrow-proto-noobj4', proxy);; },
|
2018-10-16 18:04:43 +00:00
|
|
|
'must throw when "prototype" property of the constructor is a number');
|
|
|
|
|
|
|
|
}, 'must throw when "prototype" property of the constructor is not an object');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const constructor = function () {}
|
|
|
|
constructor.prototype.paint = function() {}
|
|
|
|
const calls = [];
|
|
|
|
constructor.prototype = new Proxy(constructor.prototype, {
|
|
|
|
get: function (target, name) {
|
|
|
|
calls.push(name);
|
|
|
|
return target[name];
|
|
|
|
}
|
|
|
|
});
|
2018-11-03 04:01:29 +00:00
|
|
|
registerPaint('callbacks', constructor);
|
2018-10-16 18:04:43 +00:00
|
|
|
assert_array_equals(calls, ['paint']);
|
|
|
|
}, 'must get paint callback of the constructor prototype');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const constructor = function () {}
|
|
|
|
const calls = [];
|
|
|
|
constructor.prototype = new Proxy(constructor.prototype, {
|
|
|
|
get: function (target, name) {
|
|
|
|
calls.push(name);
|
|
|
|
if (name == 'paint')
|
|
|
|
throw {name: 'expectedError'};
|
|
|
|
return target[name];
|
|
|
|
}
|
|
|
|
});
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'expectedError'}, function () { registerPaint('callbacks-throw', constructor); });
|
2018-10-16 18:04:43 +00:00
|
|
|
assert_array_equals(calls, ['paint']);
|
|
|
|
}, 'must rethrow an exception thrown while getting paint callback on the constructor prototype');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const constructor = function () {}
|
|
|
|
const calls = [];
|
|
|
|
constructor.prototype = new Proxy(constructor.prototype, {
|
|
|
|
get: function (target, name) {
|
|
|
|
calls.push(name);
|
|
|
|
if (name == 'paint')
|
|
|
|
return 1;
|
|
|
|
return target[name];
|
|
|
|
}
|
|
|
|
});
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'TypeError'}, function () { registerPaint('callbacks-throw2', constructor); });
|
2018-10-16 18:04:43 +00:00
|
|
|
assert_array_equals(calls, ['paint']);
|
|
|
|
}, 'must rethrow an exception thrown while converting paint callback value to Function callback type');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const calls = [];
|
|
|
|
const proxy = new Proxy(class extends MyPaint { }, {
|
|
|
|
get: function (target, name) {
|
|
|
|
calls.push(name);
|
|
|
|
if (name == 'inputArguments')
|
|
|
|
return 1;
|
|
|
|
return target[name];
|
|
|
|
}
|
|
|
|
});
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'TypeError'}, function () { registerPaint('sequence-throw', proxy); });
|
2018-10-16 18:04:43 +00:00
|
|
|
assert_array_equals(calls, ["inputProperties", "inputArguments"]);
|
|
|
|
}, 'must rethrow an exception thrown while converting the value of inputArguments to sequence<DOMString>');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const constructor = function () {}
|
|
|
|
constructor.inputArguments = {[Symbol.iterator]: function *() {
|
|
|
|
yield '<length>';
|
|
|
|
throw {name: 'SomeError'};
|
|
|
|
}};
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'SomeError'}, function () { registerPaint('sequence-throw2', constructor); });
|
2018-10-16 18:04:43 +00:00
|
|
|
}, 'must rethrow an exception thrown while iterating over inputArguments to sequence<DOMString>');
|
|
|
|
|
|
|
|
test(function () {
|
|
|
|
const constructor = function () {}
|
|
|
|
constructor.inputArguments = {[Symbol.iterator]: 1};
|
2018-11-03 04:01:29 +00:00
|
|
|
assert_throws({'name': 'TypeError'}, function () { registerPaint('symbol-iterator-throw2', constructor); });
|
2018-10-16 18:04:43 +00:00
|
|
|
}, 'must rethrow an exception thrown while retrieving Symbol.iterator on inputArguments');
|
2018-11-03 04:01:29 +00:00
|
|
|
|
|
|
|
assert_true(PaintWorkletGlobalScope.hasRunATest);
|
|
|
|
if (PaintWorkletGlobalScope.errors) {
|
|
|
|
console.log("The following (" + PaintWorkletGlobalScope.errors.length + ") tests failed: ")
|
|
|
|
console.log(JSON.stringify(PaintWorkletGlobalScope.errors));
|
|
|
|
assert_true(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
class MyPaint2 {
|
|
|
|
paint(ctx, geom, properties) {
|
|
|
|
for (var i = 0; i < 6; i++){
|
|
|
|
for (var j = 0; j < 6; j++){
|
|
|
|
ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ',' +
|
|
|
|
Math.floor(255 - 42.5 * j) + ',0)';
|
|
|
|
ctx.fillRect(j * 25, i * 25, 25, 25);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
registerPaint('my-paint', MyPaint2);
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
importWorklet(CSS.paintWorklet, document.getElementById('code').textContent);
|
2018-10-16 18:04:43 +00:00
|
|
|
</script>
|