Skip to content
Permalink
Browse files
Source/WebCore:
Add adopted callback for custom elements
https://bugs.webkit.org/show_bug.cgi?id=161284

Reviewed by Antti Koivisto.

Added the support for adoptedCallback: https://dom.spec.whatwg.org/#concept-node-adopt
For now, we only support this callback on appendChild.

Test: fast/custom-elements/adopted-callback.html

* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::invokeCallback): Added JSDOMGlobalObject* as an argument to the callback so that
we can invoke toJS on Document in invokeAdoptedCallback.
(WebCore::JSCustomElementInterface::setAdoptedCallback): Added.
(WebCore::JSCustomElementInterface::invokeAdoptedCallback): Added.
(WebCore::JSCustomElementInterface::setAttributeChangedCallback):
* bindings/js/JSCustomElementInterface.h:
(WebCore::JSCustomElementInterface::hasConnectedCallback): Added.
(WebCore::JSCustomElementInterface::hasDisconnectedCallback): Added.
(WebCore::JSCustomElementInterface::hasAdoptedCallback): Added.
* bindings/js/JSCustomElementRegistryCustom.cpp:
(WebCore::JSCustomElementRegistry::define):
* dom/CustomElementReactionQueue.cpp:
(WebCore::CustomElementReactionQueueItem::CustomElementReactionQueueItem): Added a variant that takes two documents.
(WebCore::CustomElementReactionQueueItem::invoke):
(WebCore::CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded): Fixed a bug that this function was always
enqueuing a callback even when the interface didn't have connectedCallback. Also, there is no need to check
the nullity of the interface since it should never be null.
(WebCore::CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded): Ditto.
(WebCore::CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded): Added.
(WebCore::CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded): Assert that the interface is never
null instead of exiting early.
* dom/CustomElementReactionQueue.h:
* dom/Element.cpp:
(WebCore::Element::didMoveToNewDocument): Added a call to enqueueAdoptedCallbackIfNeeded.

LayoutTests:
adoptcallback

Add adopted callback for custom elements
https://bugs.webkit.org/show_bug.cgi?id=161284

Reviewed by Antti Koivisto.

* fast/custom-elements/adopted-callback-expected.txt: Added.
* fast/custom-elements/adopted-callback.html: Added.
* fast/custom-elements/resources/document-types.js:
(const.DocumentTypes.create):


Canonical link: https://commits.webkit.org/179459@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@205085 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
rniwa committed Aug 27, 2016
1 parent c2bfc22 commit e65d27e33a8817990bc6754eadd82a0b9a0a77a4
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 10 deletions.
@@ -1,3 +1,17 @@
2016-08-27 Ryosuke Niwa <rniwa@webkit.org>

adoptcallback

Add adopted callback for custom elements
https://bugs.webkit.org/show_bug.cgi?id=161284

Reviewed by Antti Koivisto.

* fast/custom-elements/adopted-callback-expected.txt: Added.
* fast/custom-elements/adopted-callback.html: Added.
* fast/custom-elements/resources/document-types.js:
(const.DocumentTypes.create):

2016-08-27 Youenn Fablet <youenn@apple.com>

html/dom/interfaces.html is flaky due to WebSocket test
@@ -0,0 +1,59 @@

PASS Inserting a custom element into the owner document must not enqueue and invoke adoptedCallback
PASS Inserting a custom element into a document of a template element must enqueue and invoke adoptedCallback
PASS Moving a custom element from the owner document into a document of a template element must enqueue and invoke adoptedCallback
PASS Inserting an ancestor of custom element into a document of a template element must enqueue and invoke adoptedCallback
PASS Moving an ancestor of custom element from the owner document into a document of a template element must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a shadow tree in a document of a template element must enqueue and invoke adoptedCallback
PASS Inserting the shadow host of a custom element into a document of a template element must enqueue and invoke adoptedCallback
PASS Moving the shadow host of a custom element from the owner document into a document of a template element must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a detached shadow tree that belongs to a document of a template element must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a new document must enqueue and invoke adoptedCallback
PASS Moving a custom element from the owner document into a new document must enqueue and invoke adoptedCallback
PASS Inserting an ancestor of custom element into a new document must enqueue and invoke adoptedCallback
PASS Moving an ancestor of custom element from the owner document into a new document must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a shadow tree in a new document must enqueue and invoke adoptedCallback
PASS Inserting the shadow host of a custom element into a new document must enqueue and invoke adoptedCallback
PASS Moving the shadow host of a custom element from the owner document into a new document must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a detached shadow tree that belongs to a new document must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a cloned document must enqueue and invoke adoptedCallback
PASS Moving a custom element from the owner document into a cloned document must enqueue and invoke adoptedCallback
PASS Inserting an ancestor of custom element into a cloned document must enqueue and invoke adoptedCallback
PASS Moving an ancestor of custom element from the owner document into a cloned document must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a shadow tree in a cloned document must enqueue and invoke adoptedCallback
PASS Inserting the shadow host of a custom element into a cloned document must enqueue and invoke adoptedCallback
PASS Moving the shadow host of a custom element from the owner document into a cloned document must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a detached shadow tree that belongs to a cloned document must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a document created by createHTMLDocument must enqueue and invoke adoptedCallback
PASS Moving a custom element from the owner document into a document created by createHTMLDocument must enqueue and invoke adoptedCallback
PASS Inserting an ancestor of custom element into a document created by createHTMLDocument must enqueue and invoke adoptedCallback
PASS Moving an ancestor of custom element from the owner document into a document created by createHTMLDocument must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a shadow tree in a document created by createHTMLDocument must enqueue and invoke adoptedCallback
PASS Inserting the shadow host of a custom element into a document created by createHTMLDocument must enqueue and invoke adoptedCallback
PASS Moving the shadow host of a custom element from the owner document into a document created by createHTMLDocument must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a detached shadow tree that belongs to a document created by createHTMLDocument must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a HTML document created by createDocument must enqueue and invoke adoptedCallback
PASS Moving a custom element from the owner document into a HTML document created by createDocument must enqueue and invoke adoptedCallback
PASS Inserting an ancestor of custom element into a HTML document created by createDocument must enqueue and invoke adoptedCallback
PASS Moving an ancestor of custom element from the owner document into a HTML document created by createDocument must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a shadow tree in a HTML document created by createDocument must enqueue and invoke adoptedCallback
PASS Inserting the shadow host of a custom element into a HTML document created by createDocument must enqueue and invoke adoptedCallback
PASS Moving the shadow host of a custom element from the owner document into a HTML document created by createDocument must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a detached shadow tree that belongs to a HTML document created by createDocument must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a document in an iframe must enqueue and invoke adoptedCallback
PASS Moving a custom element from the owner document into a document in an iframe must enqueue and invoke adoptedCallback
PASS Inserting an ancestor of custom element into a document in an iframe must enqueue and invoke adoptedCallback
PASS Moving an ancestor of custom element from the owner document into a document in an iframe must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a shadow tree in a document in an iframe must enqueue and invoke adoptedCallback
PASS Inserting the shadow host of a custom element into a document in an iframe must enqueue and invoke adoptedCallback
PASS Moving the shadow host of a custom element from the owner document into a document in an iframe must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a detached shadow tree that belongs to a document in an iframe must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a HTML document fetched by XHR must enqueue and invoke adoptedCallback
PASS Moving a custom element from the owner document into a HTML document fetched by XHR must enqueue and invoke adoptedCallback
PASS Inserting an ancestor of custom element into a HTML document fetched by XHR must enqueue and invoke adoptedCallback
PASS Moving an ancestor of custom element from the owner document into a HTML document fetched by XHR must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a shadow tree in a HTML document fetched by XHR must enqueue and invoke adoptedCallback
PASS Inserting the shadow host of a custom element into a HTML document fetched by XHR must enqueue and invoke adoptedCallback
PASS Moving the shadow host of a custom element from the owner document into a HTML document fetched by XHR must enqueue and invoke adoptedCallback
PASS Inserting a custom element into a detached shadow tree that belongs to a HTML document fetched by XHR must enqueue and invoke adoptedCallback

@@ -0,0 +1,136 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom Elements: adoptedCallback</title>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<meta name="assert" content="adoptedCallback must be enqueued whenever custom element is adopted into a new document">
<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#dfn-connected-callback">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="./resources/document-types.js"></script>
<link rel='stylesheet' href='../../resources/testharness.css'>
</head>
<body>
<div id="log"></div>
<script>

var calls = [];
class MyCustomElement extends HTMLElement {
connectedCallback() { calls.push('connected'); }
adoptedCallback(oldDocument, newDocument) { calls.push('adopted'); calls.push(oldDocument); calls.push(newDocument); }
disconnectedCallback() { calls.push('disconnected'); }
}
customElements.define('my-custom-element', MyCustomElement);

test(function () {
var instance = document.createElement('my-custom-element');
calls = [];
document.body.appendChild(instance);
assert_array_equals(calls, ['connected']);
}, 'Inserting a custom element into the owner document must not enqueue and invoke adoptedCallback');

DocumentTypes.forEach(function (entry) {
if (entry.isOwner)
return;

var documentName = entry.name;
var getDocument = entry.create;

promise_test(function () {
return getDocument().then(function (doc) {
var instance = document.createElement('my-custom-element');
calls = [];
doc.documentElement.appendChild(instance);
assert_array_equals(calls, ['adopted', document, doc, 'connected']);
});
}, 'Inserting a custom element into a ' + documentName + ' must enqueue and invoke adoptedCallback');

promise_test(function () {
return getDocument().then(function (doc) {
var instance = document.createElement('my-custom-element');
document.body.appendChild(instance);
calls = [];
doc.documentElement.appendChild(instance);
assert_array_equals(calls, ['disconnected', 'adopted', document, doc, 'connected']);
});
}, 'Moving a custom element from the owner document into a ' + documentName + ' must enqueue and invoke adoptedCallback');

promise_test(function () {
return getDocument().then(function (doc) {
var instance = document.createElement('my-custom-element');
var parent = document.createElement('div');
parent.appendChild(instance);
calls = [];
doc.documentElement.appendChild(parent);
assert_array_equals(calls, ['adopted', document, doc, 'connected']);
});
}, 'Inserting an ancestor of custom element into a ' + documentName + ' must enqueue and invoke adoptedCallback');

promise_test(function () {
return getDocument().then(function (doc) {
var instance = document.createElement('my-custom-element');
var parent = document.createElement('div');
parent.appendChild(instance);
document.body.appendChild(parent);
calls = [];
doc.documentElement.appendChild(parent);
assert_array_equals(calls, ['disconnected', 'adopted', document, doc, 'connected']);
});
}, 'Moving an ancestor of custom element from the owner document into a ' + documentName + ' must enqueue and invoke adoptedCallback');

promise_test(function () {
return getDocument().then(function (doc) {
var instance = document.createElement('my-custom-element');
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
var shadowRoot = host.attachShadow({mode: 'closed'});
doc.documentElement.appendChild(host);

calls = [];
shadowRoot.appendChild(instance);
assert_array_equals(calls, ['adopted', document, doc, 'connected']);
});
}, 'Inserting a custom element into a shadow tree in a ' + documentName + ' must enqueue and invoke adoptedCallback');

promise_test(function () {
return getDocument().then(function (doc) {
var instance = document.createElement('my-custom-element');
var host = document.createElement('div');
var shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.appendChild(instance);

calls = [];
doc.documentElement.appendChild(host);
assert_array_equals(calls, ['adopted', document, doc, 'connected']);
});
}, 'Inserting the shadow host of a custom element into a ' + documentName + ' must enqueue and invoke adoptedCallback');

promise_test(function () {
return getDocument().then(function (doc) {
var instance = document.createElement('my-custom-element');
var host = document.createElement('div');
var shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.appendChild(instance);
document.body.appendChild(host);

calls = [];
doc.documentElement.appendChild(host);
assert_array_equals(calls, ['disconnected', 'adopted', document, doc, 'connected']);
});
}, 'Moving the shadow host of a custom element from the owner document into a ' + documentName + ' must enqueue and invoke adoptedCallback');

promise_test(function () {
return getDocument().then(function (doc) {
var instance = document.createElement('my-custom-element');
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
var shadowRoot = host.attachShadow({mode: 'closed'});

calls = [];
shadowRoot.appendChild(instance);
assert_array_equals(calls, ['adopted', document, doc]);
});
}, 'Inserting a custom element into a detached shadow tree that belongs to a ' + documentName + ' must enqueue and invoke adoptedCallback');
});

</script>
</body>
</html>
@@ -1,7 +1,8 @@
const DocumentTypes = [
{
name: 'document',
create: function () { return Promise.resolve(document); }
create: function () { return Promise.resolve(document); },
isOwner: true,
},
{
name: 'document of a template element',
@@ -1,3 +1,41 @@
2016-08-27 Ryosuke Niwa <rniwa@webkit.org>

Add adopted callback for custom elements
https://bugs.webkit.org/show_bug.cgi?id=161284

Reviewed by Antti Koivisto.

Added the support for adoptedCallback: https://dom.spec.whatwg.org/#concept-node-adopt
For now, we only support this callback on appendChild.

Test: fast/custom-elements/adopted-callback.html

* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::invokeCallback): Added JSDOMGlobalObject* as an argument to the callback so that
we can invoke toJS on Document in invokeAdoptedCallback.
(WebCore::JSCustomElementInterface::setAdoptedCallback): Added.
(WebCore::JSCustomElementInterface::invokeAdoptedCallback): Added.
(WebCore::JSCustomElementInterface::setAttributeChangedCallback):
* bindings/js/JSCustomElementInterface.h:
(WebCore::JSCustomElementInterface::hasConnectedCallback): Added.
(WebCore::JSCustomElementInterface::hasDisconnectedCallback): Added.
(WebCore::JSCustomElementInterface::hasAdoptedCallback): Added.
* bindings/js/JSCustomElementRegistryCustom.cpp:
(WebCore::JSCustomElementRegistry::define):
* dom/CustomElementReactionQueue.cpp:
(WebCore::CustomElementReactionQueueItem::CustomElementReactionQueueItem): Added a variant that takes two documents.
(WebCore::CustomElementReactionQueueItem::invoke):
(WebCore::CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded): Fixed a bug that this function was always
enqueuing a callback even when the interface didn't have connectedCallback. Also, there is no need to check
the nullity of the interface since it should never be null.
(WebCore::CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded): Ditto.
(WebCore::CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded): Added.
(WebCore::CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded): Assert that the interface is never
null instead of exiting early.
* dom/CustomElementReactionQueue.h:
* dom/Element.cpp:
(WebCore::Element::didMoveToNewDocument): Added a call to enqueueAdoptedCallbackIfNeeded.

2016-08-27 Yoshiaki Jitsukawa <Yoshiaki.Jitsukawa@sony.com>

Fix the !PLATFORM(WIN) && USE(CURL) build.
@@ -150,7 +150,7 @@ void JSCustomElementInterface::upgradeElement(Element& element)
wrappedElement->setCustomElementIsResolved(*this);
}

void JSCustomElementInterface::invokeCallback(Element& element, JSObject* callback, const WTF::Function<void(ExecState*, MarkedArgumentBuffer&)>& addArguments)
void JSCustomElementInterface::invokeCallback(Element& element, JSObject* callback, const WTF::Function<void(ExecState*, JSDOMGlobalObject*, MarkedArgumentBuffer&)>& addArguments)
{
if (!canInvokeCallback())
return;
@@ -174,7 +174,7 @@ void JSCustomElementInterface::invokeCallback(Element& element, JSObject* callba
ASSERT(callType != CallType::None);

MarkedArgumentBuffer args;
addArguments(state, args);
addArguments(state, globalObject, args);

InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(context, callType, callData);

@@ -207,6 +207,19 @@ void JSCustomElementInterface::invokeDisconnectedCallback(Element& element)
invokeCallback(element, m_disconnectedCallback.get());
}

void JSCustomElementInterface::setAdoptedCallback(JSC::JSObject* callback)
{
m_adoptedCallback = callback;
}

void JSCustomElementInterface::invokeAdoptedCallback(Element& element, Document& oldDocument, Document& newDocument)
{
invokeCallback(element, m_adoptedCallback.get(), [&](ExecState* state, JSDOMGlobalObject* globalObject, MarkedArgumentBuffer& args) {
args.append(toJS(state, globalObject, oldDocument));
args.append(toJS(state, globalObject, newDocument));
});
}

void JSCustomElementInterface::setAttributeChangedCallback(JSC::JSObject* callback, const Vector<String>& observedAttributes)
{
m_attributeChangedCallback = callback;
@@ -217,7 +230,7 @@ void JSCustomElementInterface::setAttributeChangedCallback(JSC::JSObject* callba

void JSCustomElementInterface::invokeAttributeChangedCallback(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
{
invokeCallback(element, m_attributeChangedCallback.get(), [&](ExecState* state, MarkedArgumentBuffer& args) {
invokeCallback(element, m_attributeChangedCallback.get(), [&](ExecState* state, JSDOMGlobalObject*, MarkedArgumentBuffer& args) {
args.append(jsStringWithCache(state, attributeName.localName()));
args.append(jsStringOrNull(state, oldValue));
args.append(jsStringOrNull(state, newValue));

0 comments on commit e65d27e

Please sign in to comment.