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 <JavaScriptCore/InternalFunction.h>
38#include <JavaScriptCore/JSWithScope.h>
39
40namespace WebCore {
41
42using namespace JSC;
43
44EncodedJSValue 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* newTarget = newTargetValue.getObject();
59 auto* globalObject = jsCast<JSDOMGlobalObject*>(newTarget->globalObject(vm));
60 JSValue htmlElementConstructorValue = JSHTMLElement::getConstructor(vm, globalObject);
61 if (newTargetValue == htmlElementConstructorValue)
62 return throwVMTypeError(&exec, scope, "new.target is not a valid custom element constructor"_s);
63
64 auto& document = downcast<Document>(*context);
65
66 auto* window = document.domWindow();
67 if (!window)
68 return throwVMTypeError(&exec, scope, "new.target is not a valid custom element constructor"_s);
69
70 auto* registry = window->customElementRegistry();
71 if (!registry)
72 return throwVMTypeError(&exec, scope, "new.target is not a valid custom element constructor"_s);
73
74 auto* elementInterface = registry->findInterface(newTarget);
75 if (!elementInterface)
76 return throwVMTypeError(&exec, scope, "new.target does not define a custom element"_s);
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 throwTypeError(&exec, scope, "Cannot instantiate a custom element inside its own constructor during upgrades"_s);
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
111JSScope* 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
134