JSTests:
[WebKit-https.git] / Source / WebCore / bindings / js / JSHTMLElementCustom.cpp
1 /*
2  * Copyright (C) 2007, 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "JSHTMLElement.h"
28
29 #include "CustomElementRegistry.h"
30 #include "DOMWindow.h"
31 #include "Document.h"
32 #include "HTMLFormElement.h"
33 #include "JSCustomElementInterface.h"
34 #include "JSDOMConstructorBase.h"
35 #include "JSNodeCustom.h"
36 #include "ScriptExecutionContext.h"
37 #include <runtime/InternalFunction.h>
38 #include <runtime/JSWithScope.h>
39
40 namespace WebCore {
41
42 using namespace JSC;
43
44 EncodedJSValue JSC_HOST_CALL constructJSHTMLElement(ExecState& exec)
45 {
46     VM& vm = exec.vm();
47     auto scope = DECLARE_THROW_SCOPE(vm);
48
49     auto* jsConstructor = jsCast<JSDOMConstructorBase*>(exec.jsCallee());
50     ASSERT(jsConstructor);
51
52     auto* context = jsConstructor->scriptExecutionContext();
53     if (!context)
54         return throwConstructorScriptExecutionContextUnavailableError(exec, scope, "HTMLElement");
55     ASSERT(context->isDocument());
56
57     JSValue newTargetValue = exec.thisValue();
58     auto* globalObject = jsConstructor->globalObject();
59     JSValue htmlElementConstructorValue = JSHTMLElement::getConstructor(vm, globalObject);
60     if (newTargetValue == htmlElementConstructorValue)
61         return throwVMTypeError(&exec, scope, ASCIILiteral("new.target is not a valid custom element constructor"));
62
63     auto& document = downcast<Document>(*context);
64
65     auto* window = document.domWindow();
66     if (!window)
67         return throwVMTypeError(&exec, scope, ASCIILiteral("new.target is not a valid custom element constructor"));
68
69     auto* registry = window->customElementRegistry();
70     if (!registry)
71         return throwVMTypeError(&exec, scope, ASCIILiteral("new.target is not a valid custom element constructor"));
72
73     JSObject* newTarget = newTargetValue.getObject();
74     auto* elementInterface = registry->findInterface(newTarget);
75     if (!elementInterface)
76         return throwVMTypeError(&exec, scope, ASCIILiteral("new.target does not define a custom element"));
77
78     if (!elementInterface->isUpgradingElement()) {
79         Structure* baseStructure = getDOMStructure<JSHTMLElement>(vm, *globalObject);
80         auto* newElementStructure = InternalFunction::createSubclassStructure(&exec, newTargetValue, baseStructure);
81         RETURN_IF_EXCEPTION(scope, encodedJSValue());
82
83         Ref<HTMLElement> element = HTMLElement::create(elementInterface->name(), document);
84         element->setIsDefinedCustomElement(*elementInterface);
85         auto* jsElement = JSHTMLElement::create(newElementStructure, globalObject, element.get());
86         cacheWrapper(globalObject->world(), element.ptr(), jsElement);
87         return JSValue::encode(jsElement);
88     }
89
90     Element* elementToUpgrade = elementInterface->lastElementInConstructionStack();
91     if (!elementToUpgrade) {
92         throwInvalidStateError(exec, scope, ASCIILiteral("Cannot instantiate a custom element inside its own constrcutor during upgrades"));
93         return JSValue::encode(jsUndefined());
94     }
95
96     JSValue elementWrapperValue = toJS(&exec, jsConstructor->globalObject(), *elementToUpgrade);
97     ASSERT(elementWrapperValue.isObject());
98
99     JSValue newPrototype = newTarget->get(&exec, vm.propertyNames->prototype);
100     RETURN_IF_EXCEPTION(scope, encodedJSValue());
101
102     JSObject* elementWrapperObject = asObject(elementWrapperValue);
103     JSObject::setPrototype(elementWrapperObject, &exec, newPrototype, true /* shouldThrowIfCantSet */);
104     RETURN_IF_EXCEPTION(scope, encodedJSValue());
105
106     elementInterface->didUpgradeLastElementInConstructionStack();
107
108     return JSValue::encode(elementWrapperValue);
109 }
110
111 JSScope* JSHTMLElement::pushEventHandlerScope(ExecState* exec, JSScope* scope) const
112 {
113     HTMLElement& element = wrapped();
114
115     // The document is put on first, fall back to searching it only after the element and form.
116     // FIXME: This probably may use the wrong global object. If this is called from a native
117     // function, then it would be correct but not optimal since the native function would *know*
118     // the global object. But, it may be that globalObject() is more correct.
119     // https://bugs.webkit.org/show_bug.cgi?id=134932
120     VM& vm = exec->vm();
121     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
122     
123     scope = JSWithScope::create(vm, lexicalGlobalObject, scope, asObject(toJS(exec, globalObject(), element.document())));
124
125     // The form is next, searched before the document, but after the element itself.
126     if (HTMLFormElement* form = element.form())
127         scope = JSWithScope::create(vm, lexicalGlobalObject, scope, asObject(toJS(exec, globalObject(), *form)));
128
129     // The element is on top, searched first.
130     return JSWithScope::create(vm, lexicalGlobalObject, scope, asObject(toJS(exec, globalObject(), element)));
131 }
132
133 } // namespace WebCore