Add basic support for attributeChanged lifecycle callback
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Mar 2016 07:50:54 +0000 (07:50 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Mar 2016 07:50:54 +0000 (07:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155011

Reviewed by Antti Koivisto.

Source/WebCore:

Add basic support for attributeChangedCallback in setAttribute, removeAttribute, setAttributeNS,
remoteAttributeNS, setAttributeNode, and removeAttributeNS. There are many other DOM APIs that
could modify attributes but we would annotate those APIs in a separate patch to limit the scope
of this change.

In order to invoke the lifecycle callback right before returning to the author script, allocate
an instance of CustomElementLifecycleProcessingStack in each of these functions' binding code.
The stack object's destructor invokes all callbacks enqueued by the DOM API if there are any.

Spec: https://w3c.github.io/webcomponents/spec/custom/#dfn-attribute-changed-callback

Tests: fast/custom-elements/attribute-changed-callback.html
       fast/custom-elements/lifecycle-callback-timing.html

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::attributeChanged): Added. Invokes attributeChangedCallback.
* bindings/js/JSCustomElementInterface.h:
* bindings/js/JSMainThreadExecState.h:
(JSMainThreadNullState): Allocate an instance of CustomElementLifecycleProcessingStack in GObject
and Objective-C binding code for consistency with JavaScript. We can't do this in JavaScript
because there is no RAII object all functions, getters, and setters allocate (for a good reason).

* bindings/scripts/CodeGeneratorJS.pm:
(GenerateImplementation): Generate an instance of CustomElementLifecycleProcessingStack when
NeedsLifecycleProcessingStack is specified as an extended IDL attribute.
* bindings/scripts/IDLAttributes.txt: Added NeedsLifecycleProcessingStack.
* bindings/scripts/test/JS/JSTestObj.cpp:
(WebCore::jsTestObjPrototypeFunctionMethodWithNeedsLifecycleProcessingStack):
* bindings/scripts/test/TestObj.idl: Added a test for NeedsLifecycleProcessingStack.

* dom/DOMAllInOne.cpp:
* dom/Element.cpp:
(WebCore::Element::attributeChanged): Enqueue attributeChanged callback if the context object
is a custom element and there is a CustomElementLifecycleProcessingStack allocated in the stack.
* dom/Element.idl:

* dom/LifecycleCallbackQueue.cpp: Added.
(WebCore::LifecycleQueueItem): Added.
(WebCore::LifecycleQueueItem::LifecycleQueueItem): Added.
(WebCore::LifecycleQueueItem::invoke): Added.
(WebCore::LifecycleCallbackQueue::LifecycleCallbackQueue): Added.
(WebCore::LifecycleCallbackQueue::~LifecycleCallbackQueue): Added.
(WebCore::LifecycleCallbackQueue::enqueueAttributeChangedCallback): Added.
(WebCore::LifecycleCallbackQueue::invokeAll): Added.
(WebCore::CustomElementLifecycleProcessingStack::ensureCurrentQueue): Added. As noted in FIXME,
the early exit in the code is necessary only because we haven't added NeedsLifecycleProcessingStack
in all places. It should go away in a follow up patch.
(WebCore::CustomElementLifecycleProcessingStack::processQueue): Added.
* dom/LifecycleCallbackQueue.h: Added.
(WebCore::CustomElementLifecycleProcessingStack): This is a light weight RAII object the binding
code will allocate in order to queue up lifecycle callbacks. We don't use Ref or std::unique_ptr
in m_queue to avoid generating the code to destruct LifecycleCallbackQueue everywhere.
(WebCore::CustomElementLifecycleProcessingStack::CustomElementLifecycleProcessingStack): Added.
(WebCore::CustomElementLifecycleProcessingStack::~CustomElementLifecycleProcessingStack): Added.
(WebCore::CustomElementLifecycleProcessingStack::hasCurrentProcessingStack): Added.

LayoutTests:

Added a test for attributeChangedCallback and a test for the timing of lifecycle callbacks in general.

* fast/custom-elements/attribute-changed-callback-expected.txt: Added.
* fast/custom-elements/attribute-changed-callback.html: Added.
* fast/custom-elements/lifecycle-callback-timing-expected.txt: Added.
* fast/custom-elements/lifecycle-callback-timing.html: Added.

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/custom-elements/attribute-changed-callback-expected.txt [new file with mode: 0644]
LayoutTests/fast/custom-elements/attribute-changed-callback.html [new file with mode: 0644]
LayoutTests/fast/custom-elements/lifecycle-callback-timing-expected.txt [new file with mode: 0644]
LayoutTests/fast/custom-elements/lifecycle-callback-timing.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/JSCustomElementInterface.cpp
Source/WebCore/bindings/js/JSCustomElementInterface.h
Source/WebCore/bindings/js/JSMainThreadExecState.h
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/bindings/scripts/IDLAttributes.txt
Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp
Source/WebCore/bindings/scripts/test/TestObj.idl
Source/WebCore/dom/DOMAllInOne.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.idl
Source/WebCore/dom/LifecycleCallbackQueue.cpp [new file with mode: 0644]
Source/WebCore/dom/LifecycleCallbackQueue.h [new file with mode: 0644]

index 743c0ea..05b4544 100644 (file)
@@ -1,3 +1,17 @@
+2016-03-04  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Add basic support for attributeChanged lifecycle callback
+        https://bugs.webkit.org/show_bug.cgi?id=155011
+
+        Reviewed by Antti Koivisto.
+
+        Added a test for attributeChangedCallback and a test for the timing of lifecycle callbacks in general.
+
+        * fast/custom-elements/attribute-changed-callback-expected.txt: Added.
+        * fast/custom-elements/attribute-changed-callback.html: Added.
+        * fast/custom-elements/lifecycle-callback-timing-expected.txt: Added.
+        * fast/custom-elements/lifecycle-callback-timing.html: Added.
+
 2016-03-04  Tim Horton  <timothy_horton@apple.com>
 
         Temporarily skip attachment tests on iOS
diff --git a/LayoutTests/fast/custom-elements/attribute-changed-callback-expected.txt b/LayoutTests/fast/custom-elements/attribute-changed-callback-expected.txt
new file mode 100644 (file)
index 0000000..465a97b
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback 
+PASS setAttributeNS and removeAttributeNS must enqueue and invoke attributeChangedCallback 
+PASS setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback 
+PASS setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback 
+
diff --git a/LayoutTests/fast/custom-elements/attribute-changed-callback.html b/LayoutTests/fast/custom-elements/attribute-changed-callback.html
new file mode 100644 (file)
index 0000000..5199461
--- /dev/null
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: attributeChangedCallback</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="attributeChangedCallback must be enqueued whenever custom element's attribute is added, changed or removed">
+<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#dfn-attribute-changed-callback">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+var argumentList = [];
+class MyCustomElement extends HTMLElement {
+    attributeChangedCallback(name, oldValue, newValue, namespace) {
+        argumentList.push({arguments: arguments, value: this.getAttributeNS(namespace, name)});
+    }
+}
+document.defineElement('my-custom-element', MyCustomElement);
+
+test(function () {
+    var instance = document.createElement('my-custom-element');
+    argumentList = [];
+
+    instance.setAttribute('title', 'foo');
+    assert_equals(instance.getAttribute('title'), 'foo');
+    assert_equals(argumentList.length, 1);
+    assert_equals(argumentList[0].value, 'foo');
+    assert_array_equals(argumentList[0].arguments, ['title', null, 'foo', null]);
+
+    instance.removeAttribute('title');
+    assert_equals(instance.getAttribute('title'), null);
+    assert_equals(argumentList.length, 2);
+    assert_equals(argumentList[1].value, null);
+    assert_array_equals(argumentList[1].arguments, ['title', 'foo', null, null]);
+
+}, 'setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback');
+
+test(function () {
+    var instance = document.createElement('my-custom-element');
+    argumentList = [];
+
+    instance.setAttributeNS('http://www.w3.org/2000/svg', 'title', 'hello');
+    assert_equals(instance.getAttribute('title'), 'hello');
+    assert_equals(argumentList.length, 1);
+    assert_equals(argumentList[0].value, 'hello');
+    assert_array_equals(argumentList[0].arguments, ['title', null, 'hello', 'http://www.w3.org/2000/svg']);
+
+    instance.removeAttributeNS('http://www.w3.org/2000/svg', 'title');
+    assert_equals(instance.getAttribute('title'), null);
+    assert_equals(argumentList.length, 2);
+    assert_equals(argumentList[1].value, null);
+    assert_array_equals(argumentList[1].arguments, ['title', 'hello', null, 'http://www.w3.org/2000/svg']);
+
+}, 'setAttributeNS and removeAttributeNS must enqueue and invoke attributeChangedCallback');
+
+test(function () {
+    var instance = document.createElement('my-custom-element');
+    argumentList = [];
+
+    var attr = document.createAttribute('id');
+    attr.value = 'bar';
+    instance.setAttributeNode(attr);
+
+    assert_equals(instance.getAttribute('id'), 'bar');
+    assert_equals(argumentList.length, 1);
+    assert_equals(argumentList[0].value, 'bar');
+    assert_array_equals(argumentList[0].arguments, ['id', null, 'bar', null]);
+
+    instance.removeAttributeNode(attr);
+    assert_equals(instance.getAttribute('id'), null);
+    assert_equals(argumentList.length, 2);
+    assert_equals(argumentList[1].value, null);
+    assert_array_equals(argumentList[1].arguments, ['id', 'bar', null, null]);
+
+}, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback');
+
+test(function () {
+    var instance = document.createElement('my-custom-element');
+    argumentList = [];
+
+    var attr = document.createAttributeNS('http://www.w3.org/2000/svg', 'r');
+    attr.value = '100';
+    instance.setAttributeNode(attr);
+
+    assert_equals(instance.getAttribute('r'), '100');
+    assert_equals(argumentList.length, 1);
+    assert_equals(argumentList[0].value, '100');
+    assert_array_equals(argumentList[0].arguments, ['r', null, '100', 'http://www.w3.org/2000/svg']);
+
+    instance.removeAttributeNode(attr);
+    assert_equals(instance.getAttribute('r'), null);
+    assert_equals(argumentList.length, 2);
+    assert_equals(argumentList[1].value, null);
+    assert_array_equals(argumentList[1].arguments, ['r', '100', null, 'http://www.w3.org/2000/svg']);
+
+}, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback');
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/custom-elements/lifecycle-callback-timing-expected.txt b/LayoutTests/fast/custom-elements/lifecycle-callback-timing-expected.txt
new file mode 100644 (file)
index 0000000..19ef673
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback 
+
diff --git a/LayoutTests/fast/custom-elements/lifecycle-callback-timing.html b/LayoutTests/fast/custom-elements/lifecycle-callback-timing.html
new file mode 100644 (file)
index 0000000..a3e987a
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Lifecycle callback must be invoked before returning to author scripts</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="Lifecycle callbacks must be invoked before returning to author scripts">
+<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#enqueuing-and-invoking-callbacks">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+class MyCustomElement extends HTMLElement {
+    attributeChangedCallback(...args) {
+        this.handler(...args);
+    }
+
+    handler() { }
+}
+document.defineElement('my-custom-element', MyCustomElement);
+
+test(function () {
+    var instance = document.createElement('my-custom-element');
+    var anotherInstance = document.createElement('my-custom-element');
+
+    var callbackOrder = [];
+    instance.handler = function () {
+        callbackOrder.push([this, 'begin']);
+        anotherInstance.setAttribute('data-title', 'baz');
+        callbackOrder.push([this, 'end']);
+    }
+    anotherInstance.handler = function () {
+        callbackOrder.push([this, 'begin']);
+        callbackOrder.push([this, 'end']);
+    }
+
+    instance.setAttribute('title', 'foo');
+    assert_equals(callbackOrder.length, 4);
+
+    assert_array_equals(callbackOrder[0], [instance, 'begin']);
+    assert_array_equals(callbackOrder[1], [anotherInstance, 'begin']);
+    assert_array_equals(callbackOrder[2], [anotherInstance, 'end']);
+    assert_array_equals(callbackOrder[3], [instance, 'end']);
+
+}, 'setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback');
+
+</script>
+</body>
+</html>
index dee52aa..5658897 100644 (file)
@@ -1453,6 +1453,7 @@ set(WebCore_SOURCES
     dom/IdTargetObserverRegistry.cpp
     dom/InlineStyleSheetOwner.cpp
     dom/KeyboardEvent.cpp
+    dom/LifecycleCallbackQueue.cpp
     dom/LiveNodeList.cpp
     dom/MessageChannel.cpp
     dom/MessageEvent.cpp
index 9f00d45..68c47b1 100644 (file)
@@ -1,3 +1,68 @@
+2016-03-04  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Add basic support for attributeChanged lifecycle callback
+        https://bugs.webkit.org/show_bug.cgi?id=155011
+
+        Reviewed by Antti Koivisto.
+
+        Add basic support for attributeChangedCallback in setAttribute, removeAttribute, setAttributeNS,
+        remoteAttributeNS, setAttributeNode, and removeAttributeNS. There are many other DOM APIs that
+        could modify attributes but we would annotate those APIs in a separate patch to limit the scope
+        of this change.
+
+        In order to invoke the lifecycle callback right before returning to the author script, allocate
+        an instance of CustomElementLifecycleProcessingStack in each of these functions' binding code.
+        The stack object's destructor invokes all callbacks enqueued by the DOM API if there are any.
+
+        Spec: https://w3c.github.io/webcomponents/spec/custom/#dfn-attribute-changed-callback
+
+        Tests: fast/custom-elements/attribute-changed-callback.html
+               fast/custom-elements/lifecycle-callback-timing.html
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/JSCustomElementInterface.cpp:
+        (WebCore::JSCustomElementInterface::attributeChanged): Added. Invokes attributeChangedCallback.
+        * bindings/js/JSCustomElementInterface.h:
+        * bindings/js/JSMainThreadExecState.h:
+        (JSMainThreadNullState): Allocate an instance of CustomElementLifecycleProcessingStack in GObject
+        and Objective-C binding code for consistency with JavaScript. We can't do this in JavaScript
+        because there is no RAII object all functions, getters, and setters allocate (for a good reason).
+
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GenerateImplementation): Generate an instance of CustomElementLifecycleProcessingStack when
+        NeedsLifecycleProcessingStack is specified as an extended IDL attribute.
+        * bindings/scripts/IDLAttributes.txt: Added NeedsLifecycleProcessingStack.
+        * bindings/scripts/test/JS/JSTestObj.cpp:
+        (WebCore::jsTestObjPrototypeFunctionMethodWithNeedsLifecycleProcessingStack):
+        * bindings/scripts/test/TestObj.idl: Added a test for NeedsLifecycleProcessingStack.
+
+        * dom/DOMAllInOne.cpp:
+        * dom/Element.cpp:
+        (WebCore::Element::attributeChanged): Enqueue attributeChanged callback if the context object
+        is a custom element and there is a CustomElementLifecycleProcessingStack allocated in the stack.
+        * dom/Element.idl:
+
+        * dom/LifecycleCallbackQueue.cpp: Added.
+        (WebCore::LifecycleQueueItem): Added.
+        (WebCore::LifecycleQueueItem::LifecycleQueueItem): Added.
+        (WebCore::LifecycleQueueItem::invoke): Added.
+        (WebCore::LifecycleCallbackQueue::LifecycleCallbackQueue): Added.
+        (WebCore::LifecycleCallbackQueue::~LifecycleCallbackQueue): Added.
+        (WebCore::LifecycleCallbackQueue::enqueueAttributeChangedCallback): Added.
+        (WebCore::LifecycleCallbackQueue::invokeAll): Added.
+        (WebCore::CustomElementLifecycleProcessingStack::ensureCurrentQueue): Added. As noted in FIXME,
+        the early exit in the code is necessary only because we haven't added NeedsLifecycleProcessingStack
+        in all places. It should go away in a follow up patch.
+        (WebCore::CustomElementLifecycleProcessingStack::processQueue): Added.
+        * dom/LifecycleCallbackQueue.h: Added.
+        (WebCore::CustomElementLifecycleProcessingStack): This is a light weight RAII object the binding
+        code will allocate in order to queue up lifecycle callbacks. We don't use Ref or std::unique_ptr
+        in m_queue to avoid generating the code to destruct LifecycleCallbackQueue everywhere.
+        (WebCore::CustomElementLifecycleProcessingStack::CustomElementLifecycleProcessingStack): Added.
+        (WebCore::CustomElementLifecycleProcessingStack::~CustomElementLifecycleProcessingStack): Added.
+        (WebCore::CustomElementLifecycleProcessingStack::hasCurrentProcessingStack): Added.
+
 2016-03-04  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [GTK] Scrollbars are broken again with GTK+ >= 3.19.11
index cb74f56..3db9b77 100644 (file)
                9B532EA31BA928570038A827 /* SlotAssignment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B532EA11BA928570038A827 /* SlotAssignment.cpp */; };
                9B532EA41BA928570038A827 /* SlotAssignment.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B532EA21BA928570038A827 /* SlotAssignment.h */; };
                9B55EEE91B3E8898005342BC /* EditorCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B55EEE81B3E8898005342BC /* EditorCocoa.mm */; };
+               9B56C9AA1C89329A00C456DF /* LifecycleCallbackQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B56C9A91C89329A00C456DF /* LifecycleCallbackQueue.cpp */; };
                9B69D3B41B98FFE900E3512B /* HTMLSlotElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B69D3B21B98FFE900E3512B /* HTMLSlotElement.cpp */; };
                9B69D3B51B98FFE900E3512B /* HTMLSlotElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B69D3B31B98FFE900E3512B /* HTMLSlotElement.h */; };
                9B69D3B81B99100700E3512B /* JSHTMLSlotElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B69D3B61B99100700E3512B /* JSHTMLSlotElement.cpp */; };
                9B532EA21BA928570038A827 /* SlotAssignment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SlotAssignment.h; sourceTree = "<group>"; };
                9B55EEE81B3E8898005342BC /* EditorCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EditorCocoa.mm; sourceTree = "<group>"; };
                9B55EEEA1B3F3FEF005342BC /* EditorCocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EditorCocoa.h; sourceTree = "<group>"; };
+               9B56C9A81C89312800C456DF /* LifecycleCallbackQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LifecycleCallbackQueue.h; sourceTree = "<group>"; };
+               9B56C9A91C89329A00C456DF /* LifecycleCallbackQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LifecycleCallbackQueue.cpp; sourceTree = "<group>"; };
                9B69D3B11B98FF0A00E3512B /* HTMLSlotElement.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLSlotElement.idl; sourceTree = "<group>"; };
                9B69D3B21B98FFE900E3512B /* HTMLSlotElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLSlotElement.cpp; sourceTree = "<group>"; };
                9B69D3B31B98FFE900E3512B /* HTMLSlotElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLSlotElement.h; sourceTree = "<group>"; };
                                85031B2D0A44EFC700F992E0 /* KeyboardEvent.cpp */,
                                85031B2E0A44EFC700F992E0 /* KeyboardEvent.h */,
                                14CF7C2009F7110600EB3665 /* KeyboardEvent.idl */,
+                               9B56C9A91C89329A00C456DF /* LifecycleCallbackQueue.cpp */,
+                               9B56C9A81C89312800C456DF /* LifecycleCallbackQueue.h */,
                                BC7FA61E0D1F0CBD00DB22A9 /* LiveNodeList.cpp */,
                                BC7FA61F0D1F0CBD00DB22A9 /* LiveNodeList.h */,
                                BC9A6144146859D9006057FD /* make_dom_exceptions.pl */,
                                A8EA7CAD0A192B9C00A8EF5F /* HTMLPreElement.cpp in Sources */,
                                977B3872122883E900B81FF8 /* HTMLPreloadScanner.cpp in Sources */,
                                A43BF5981149290A00C643CA /* HTMLProgressElement.cpp in Sources */,
+                               9B56C9AA1C89329A00C456DF /* LifecycleCallbackQueue.cpp in Sources */,
                                A8CFF7A50A156978000A4234 /* HTMLQuoteElement.cpp in Sources */,
                                A8D223FD16B52E4E00157288 /* HTMLResourcePreloader.cpp in Sources */,
                                A871DC220A15205700B12A68 /* HTMLScriptElement.cpp in Sources */,
index ebeb565..5a19bc1 100644 (file)
@@ -101,6 +101,50 @@ RefPtr<Element> JSCustomElementInterface::constructElement(const AtomicString& t
     return wrappedElement;
 }
 
+void JSCustomElementInterface::attributeChanged(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
+{
+    if (!canInvokeCallback())
+        return;
+
+    Ref<JSCustomElementInterface> protect(*this);
+
+    JSLockHolder lock(m_isolatedWorld->vm());
+
+    ScriptExecutionContext* context = scriptExecutionContext();
+    if (!context)
+        return;
+
+    ASSERT(context->isDocument());
+    JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, *m_isolatedWorld);
+    ExecState* state = globalObject->globalExec();
+
+    JSObject* jsElement = asObject(toJS(state, globalObject, &element));
+
+    PropertyName attributeChanged(Identifier::fromString(state, "attributeChangedCallback"));
+    JSValue callback = jsElement->get(state, attributeChanged);
+    CallData callData;
+    CallType callType = getCallData(callback, callData);
+    if (callType == CallTypeNone)
+        return;
+
+    const AtomicString& namespaceURI = attributeName.namespaceURI();
+    MarkedArgumentBuffer args;
+    args.append(jsStringWithCache(state, attributeName.localName()));
+    args.append(oldValue == nullAtom ? jsNull() : jsStringWithCache(state, oldValue));
+    args.append(newValue == nullAtom ? jsNull() : jsStringWithCache(state, newValue));
+    args.append(namespaceURI == nullAtom ? jsNull() : jsStringWithCache(state, attributeName.namespaceURI()));
+
+    InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(context, callType, callData);
+
+    NakedPtr<Exception> exception;
+    JSMainThreadExecState::call(state, callback, callType, callData, jsElement, args, exception);
+
+    InspectorInstrumentation::didCallFunction(cookie, context);
+
+    if (exception)
+        reportException(state, exception);
+}
+
 } // namespace WebCore
 
 #endif
index 53bc23d..175c3b8 100644 (file)
@@ -50,6 +50,7 @@ class DOMWrapperWorld;
 class Element;
 class JSDOMGlobalObject;
 class MathMLElement;
+class QualifiedName;
 class SVGElement;
 
 class JSCustomElementInterface : public RefCounted<JSCustomElementInterface>, public ActiveDOMCallback {
@@ -62,6 +63,8 @@ public:
     enum class ShouldClearException { Clear, DoNotClear };
     RefPtr<Element> constructElement(const AtomicString&, ShouldClearException);
 
+    void attributeChanged(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);
+
     ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); }
     JSC::JSObject* constructor() { return m_constructor.get(); }
 
index dffc8f1..92bdaa4 100644 (file)
@@ -27,6 +27,7 @@
 #define JSMainThreadExecState_h
 
 #include "JSDOMBinding.h"
+#include "LifecycleCallbackQueue.h"
 #include <runtime/Completion.h>
 #include <runtime/Microtask.h>
 #include <wtf/MainThread.h>
@@ -146,6 +147,9 @@ public:
 
 private:
     JSC::ExecState* m_previousState;
+#if ENABLE(CUSTOM_ELEMENTS)
+    CustomElementLifecycleProcessingStack m_lifecycleProcessingStack;
+#endif
 };
 
 JSC::JSValue functionCallHandlerFromAnyThread(JSC::ExecState*, JSC::JSValue functionObject, JSC::CallType, const JSC::CallData&, JSC::JSValue thisValue, const JSC::ArgList& args, NakedPtr<JSC::Exception>& returnedException);
index 27b4bdb..2f72404 100644 (file)
@@ -2913,6 +2913,13 @@ sub GenerateImplementation
 
             $implIncludes{"<runtime/Error.h>"} = 1;
 
+            if ($function->signature->extendedAttributes->{"InvokesCustomElementLifecycleCallbacks"}) {
+                push(@implContent, "#if ENABLE(CUSTOM_ELEMENTS)\n");
+                push(@implContent, "    CustomElementLifecycleProcessingStack customElementLifecycleProcessingStack;\n");
+                push(@implContent, "#endif\n");
+                $implIncludes{"LifecycleCallbackQueue.h"} = 1;
+            }
+
             if ($function->isStatic) {
                 if ($isCustom) {
                     GenerateArgumentsCountCheck(\@implContent, $function, $interface);
index f17bd81..b922bd7 100644 (file)
@@ -91,6 +91,7 @@ JSLegacyParent=*
 LenientThis
 MasqueradesAsUndefined
 NamedConstructor=*
+InvokesCustomElementLifecycleCallbacks
 NewImpurePropertyFiresWatchpoints
 NewObject
 NoInterfaceObject
index 379fbf8..5d43996 100644 (file)
@@ -45,6 +45,7 @@
 #include "JSTestObj.h"
 #include "JSTestSubObj.h"
 #include "JSbool.h"
+#include "LifecycleCallbackQueue.h"
 #include "SVGDocument.h"
 #include "SVGPoint.h"
 #include "SVGStaticPropertyTearOff.h"
@@ -185,6 +186,7 @@ JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunction(
 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgument(JSC::ExecState*);
 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunctionWithException(JSC::ExecState*);
 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunctionWithOptionalIntArgument(JSC::ExecState*);
+JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionMethodWithNeedsLifecycleProcessingStack(JSC::ExecState*);
 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionSymbolIterator(JSC::ExecState*);
 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionEntries(JSC::ExecState*);
 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionKeys(JSC::ExecState*);
@@ -713,6 +715,7 @@ static const HashTableValue JSTestObjPrototypeTableValues[] =
     { "testPromiseFunctionWithFloatArgument", JSC::Function, NoIntrinsic, { (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgument), (intptr_t) (1) } },
     { "testPromiseFunctionWithException", JSC::Function, NoIntrinsic, { (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionTestPromiseFunctionWithException), (intptr_t) (0) } },
     { "testPromiseFunctionWithOptionalIntArgument", JSC::Function, NoIntrinsic, { (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionTestPromiseFunctionWithOptionalIntArgument), (intptr_t) (0) } },
+    { "methodWithNeedsLifecycleProcessingStack", JSC::Function, NoIntrinsic, { (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionMethodWithNeedsLifecycleProcessingStack), (intptr_t) (0) } },
     { "entries", JSC::Function, NoIntrinsic, { (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionEntries), (intptr_t) (0) } },
     { "keys", JSC::Function, NoIntrinsic, { (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionKeys), (intptr_t) (0) } },
     { "values", JSC::Function, NoIntrinsic, { (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionValues), (intptr_t) (0) } },
@@ -5021,6 +5024,21 @@ static inline EncodedJSValue jsTestObjPrototypeFunctionTestPromiseFunctionWithOp
     return JSValue::encode(jsUndefined());
 }
 
+EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionMethodWithNeedsLifecycleProcessingStack(ExecState* state)
+{
+#if ENABLE(CUSTOM_ELEMENTS)
+    CustomElementLifecycleProcessingStack customElementLifecycleProcessingStack;
+#endif
+    JSValue thisValue = state->thisValue();
+    auto castedThis = jsDynamicCast<JSTestObj*>(thisValue);
+    if (UNLIKELY(!castedThis))
+        return throwThisTypeError(*state, "TestObj", "methodWithNeedsLifecycleProcessingStack");
+    ASSERT_GC_OBJECT_INHERITS(castedThis, JSTestObj::info());
+    auto& impl = castedThis->wrapped();
+    impl.methodWithNeedsLifecycleProcessingStack();
+    return JSValue::encode(jsUndefined());
+}
+
 using TestObjIterator = JSKeyValueIterator<JSTestObj>;
 using TestObjIteratorPrototype = JSKeyValueIteratorPrototype<JSTestObj>;
 
index 00da128..c9da3a2 100644 (file)
@@ -332,6 +332,10 @@ enum _optional { "", "OptionalValue1", "OptionalValue2", "OptionalValue3" };
     // PutForwards
     [PutForwards=name] readonly attribute TestNode putForwardsAttribute;
     [PutForwards=name] readonly attribute TestNode? putForwardsNullableAttribute;
+
+#if defined(TESTING_JS)
+    [NeedsLifecycleProcessingStack] void methodWithNeedsLifecycleProcessingStack();
+#endif
 };
 
 // The following comment should not generate any code.
index 4d2a532..7dada31 100644 (file)
@@ -93,6 +93,7 @@
 #include "IdTargetObserverRegistry.cpp"
 #include "InlineStyleSheetOwner.cpp"
 #include "KeyboardEvent.cpp"
+#include "LifecycleCallbackQueue.cpp"
 #include "LiveNodeList.cpp"
 #include "MessageChannel.cpp"
 #include "MessageEvent.cpp"
index 14c70bc..8ca6f37 100644 (file)
@@ -37,6 +37,7 @@
 #include "ClientRectList.h"
 #include "ComposedTreeAncestorIterator.h"
 #include "ContainerNodeAlgorithms.h"
+#include "CustomElementDefinitions.h"
 #include "DOMTokenList.h"
 #include "Dictionary.h"
 #include "DocumentSharedObjectPool.h"
@@ -61,6 +62,7 @@
 #include "IdTargetObserverRegistry.h"
 #include "JSLazyEventListener.h"
 #include "KeyboardEvent.h"
+#include "LifecycleCallbackQueue.h"
 #include "MainFrame.h"
 #include "MutationObserverInterestGroup.h"
 #include "MutationRecord.h"
@@ -1262,6 +1264,15 @@ void Element::attributeChanged(const QualifiedName& name, const AtomicString& ol
 
     document().incDOMTreeVersion();
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    if (UNLIKELY(isCustomElement())) {
+        auto* definitions = document().customElementDefinitions();
+        auto* interface = definitions->findInterface(tagQName());
+        RELEASE_ASSERT(interface);
+        LifecycleCallbackQueue::enqueueAttributeChangedCallback(*this, *interface, name, oldValue, newValue);
+    }
+#endif
+
     if (valueIsSameAsBefore)
         return;
 
index eb33aa6..99ce3da 100644 (file)
     readonly attribute DOMString? tagName;
 
     DOMString? getAttribute([Default=Undefined] optional DOMString name);
-    [ObjCLegacyUnnamedParameters, RaisesException] void setAttribute([Default=Undefined] optional DOMString name,
-                                     [Default=Undefined] optional DOMString value);
-    void removeAttribute([Default=Undefined] optional DOMString name);
+
+    [ObjCLegacyUnnamedParameters, RaisesException, InvokesCustomElementLifecycleCallbacks]
+    void setAttribute([Default=Undefined] optional DOMString name, [Default=Undefined] optional DOMString value);
+
+    [InvokesCustomElementLifecycleCallbacks] void removeAttribute([Default=Undefined] optional DOMString name);
     Attr getAttributeNode([Default=Undefined] optional DOMString name);
-    [RaisesException] Attr setAttributeNode([Default=Undefined] optional Attr newAttr);
-    [RaisesException] Attr removeAttributeNode([Default=Undefined] optional Attr oldAttr);
+    [RaisesException, InvokesCustomElementLifecycleCallbacks] Attr setAttributeNode([Default=Undefined] optional Attr newAttr);
+    [RaisesException, InvokesCustomElementLifecycleCallbacks] Attr removeAttributeNode([Default=Undefined] optional Attr oldAttr);
 
 #if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C
     [ImplementedAs=getElementsByTagNameForObjC] NodeList getElementsByTagName([Default=Undefined] optional DOMString name);
     // DOM Level 2 Core
 
     [ObjCLegacyUnnamedParameters] DOMString? getAttributeNS([Default=Undefined] optional DOMString? namespaceURI, [Default=Undefined] optional DOMString localName);
-    [ObjCLegacyUnnamedParameters, RaisesException] void setAttributeNS([Default=Undefined] optional DOMString? namespaceURI,
-        [Default=Undefined] optional DOMString qualifiedName,
-        [Default=Undefined] optional DOMString value);
-    [ObjCLegacyUnnamedParameters] void removeAttributeNS(DOMString? namespaceURI, DOMString localName);
+
+    [ObjCLegacyUnnamedParameters, RaisesException, InvokesCustomElementLifecycleCallbacks]
+    void setAttributeNS([Default=Undefined] optional DOMString? namespaceURI,
+        [Default=Undefined] optional DOMString qualifiedName, [Default=Undefined] optional DOMString value);
+
+    [ObjCLegacyUnnamedParameters, InvokesCustomElementLifecycleCallbacks] void removeAttributeNS(DOMString? namespaceURI, DOMString localName);
 
 #if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C
     [ObjCLegacyUnnamedParameters, ImplementedAs=getElementsByTagNameNSForObjC] NodeList getElementsByTagNameNS(optional DOMString namespaceURI, optional DOMString localName);
@@ -61,7 +65,7 @@
     HTMLCollection getElementsByTagNameNS([Default=Undefined] optional DOMString? namespaceURI, [Default=Undefined] optional DOMString localName);
 #endif
     [ObjCLegacyUnnamedParameters] Attr getAttributeNodeNS([Default=Undefined] optional DOMString? namespaceURI, [Default=Undefined] optional DOMString localName);
-    [RaisesException] Attr setAttributeNodeNS([Default=Undefined] optional Attr newAttr);
+    [RaisesException, InvokesCustomElementLifecycleCallbacks] Attr setAttributeNodeNS([Default=Undefined] optional Attr newAttr);
     boolean hasAttribute(DOMString name);
     [ObjCLegacyUnnamedParameters] boolean hasAttributeNS([Default=Undefined] optional DOMString? namespaceURI, [Default=Undefined] optional DOMString localName);
 
diff --git a/Source/WebCore/dom/LifecycleCallbackQueue.cpp b/Source/WebCore/dom/LifecycleCallbackQueue.cpp
new file mode 100644 (file)
index 0000000..385a9f6
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LifecycleCallbackQueue.h"
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+#include "Document.h"
+#include "Element.h"
+#include "JSCustomElementInterface.h"
+#include "JSDOMBinding.h"
+#include <heap/Heap.h>
+#include <wtf/Ref.h>
+
+namespace WebCore {
+
+class LifecycleQueueItem {
+public:
+    enum class Type {
+        AttributeChanged,
+    };
+
+    LifecycleQueueItem(Element& element, JSCustomElementInterface& interface, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
+        : m_type(Type::AttributeChanged)
+        , m_element(element)
+        , m_interface(interface)
+        , m_attributeName(attributeName)
+        , m_oldValue(oldValue)
+        , m_newValue(newValue)
+    { }
+
+    void invoke()
+    {
+        m_interface->attributeChanged(m_element.get(), m_attributeName, m_oldValue, m_newValue);
+    }
+
+private:
+    Type m_type;
+    Ref<Element> m_element;
+    Ref<JSCustomElementInterface> m_interface;
+    QualifiedName m_attributeName;
+    AtomicString m_oldValue;
+    AtomicString m_newValue;
+};
+
+LifecycleCallbackQueue::LifecycleCallbackQueue()
+{ }
+
+LifecycleCallbackQueue::~LifecycleCallbackQueue()
+{
+    ASSERT(m_items.isEmpty());
+}
+
+void LifecycleCallbackQueue::enqueueAttributeChangedCallback(Element& element, JSCustomElementInterface& interface,
+    const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
+{
+    if (auto* queue = CustomElementLifecycleProcessingStack::ensureCurrentQueue())
+        queue->m_items.append(LifecycleQueueItem(element, interface, attributeName, oldValue, newValue));
+}
+
+void LifecycleCallbackQueue::invokeAll()
+{
+    Vector<LifecycleQueueItem> items;
+    items.swap(m_items);
+    for (auto& item : items)
+        item.invoke();
+}
+
+LifecycleCallbackQueue* CustomElementLifecycleProcessingStack::ensureCurrentQueue()
+{
+    // FIXME: This early exit indicates a bug that some DOM API is missing InvokesCustomElementLifecycleCallbacks
+    if (!s_currentProcessingStack)
+        return nullptr;
+
+    auto*& queue = s_currentProcessingStack->m_queue;
+    if (!queue) // We use a raw pointer to avoid genearing code to delete it in ~CustomElementLifecycleProcessingStack.
+        queue = new LifecycleCallbackQueue;
+    return queue;
+}
+
+CustomElementLifecycleProcessingStack* CustomElementLifecycleProcessingStack::s_currentProcessingStack = nullptr;
+
+void CustomElementLifecycleProcessingStack::processQueue()
+{
+    ASSERT(m_queue);
+    m_queue->invokeAll();
+    delete m_queue;
+    m_queue = nullptr;
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/dom/LifecycleCallbackQueue.h b/Source/WebCore/dom/LifecycleCallbackQueue.h
new file mode 100644 (file)
index 0000000..0e9ca18
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LifecycleCallbackQueue_h
+#define LifecycleCallbackQueue_h
+
+#if ENABLE(CUSTOM_ELEMENTS)
+
+#include <wtf/Noncopyable.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class JSCustomElementInterface;
+class Document;
+class Element;
+class QualifiedName;
+class LifecycleQueueItem;
+
+class LifecycleCallbackQueue {
+    WTF_MAKE_NONCOPYABLE(LifecycleCallbackQueue);
+public:
+    LifecycleCallbackQueue();
+    ~LifecycleCallbackQueue();
+
+    static void enqueueAttributeChangedCallback(Element&, JSCustomElementInterface&,
+        const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);
+
+    void invokeAll();
+
+private:
+    Vector<LifecycleQueueItem> m_items;
+};
+
+class CustomElementLifecycleProcessingStack {
+public:
+    CustomElementLifecycleProcessingStack()
+        : m_previousProcessingStack(s_currentProcessingStack)
+    {
+        s_currentProcessingStack = this;
+    }
+
+    ~CustomElementLifecycleProcessingStack()
+    {
+        if (UNLIKELY(m_queue))
+            processQueue();
+        s_currentProcessingStack = m_previousProcessingStack;
+    }
+
+    // FIXME: This should be a reference once "ensure" starts to work.
+    static LifecycleCallbackQueue* ensureCurrentQueue();
+
+    static bool hasCurrentProcessingStack() { return s_currentProcessingStack; }
+
+private:
+    void processQueue();
+
+    LifecycleCallbackQueue* m_queue { nullptr };
+    CustomElementLifecycleProcessingStack* m_previousProcessingStack;
+
+    static CustomElementLifecycleProcessingStack* s_currentProcessingStack;
+};
+
+}
+
+#endif
+
+#endif // LifecycleCallbackQueue_h