blob: e5a5cf465e51f7c85e007c787269dca245e9244b [file] [log] [blame]
/*
* Copyright (C) 2007, 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 "JSHTMLElement.h"
#include "CustomElementRegistry.h"
#include "DOMWindow.h"
#include "Document.h"
#include "HTMLFormElement.h"
#include "JSCustomElementInterface.h"
#include "JSDOMConstructorBase.h"
#include "JSNodeCustom.h"
#include "ScriptExecutionContext.h"
#include <JavaScriptCore/InternalFunction.h>
#include <JavaScriptCore/JSWithScope.h>
namespace WebCore {
using namespace JSC;
EncodedJSValue constructJSHTMLElement(JSGlobalObject* globalObject, ExecState& exec)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* jsConstructor = jsCast<JSDOMConstructorBase*>(exec.jsCallee());
ASSERT(jsConstructor);
auto* context = jsConstructor->scriptExecutionContext();
if (!context)
return throwConstructorScriptExecutionContextUnavailableError(exec, scope, "HTMLElement");
ASSERT(context->isDocument());
JSValue newTargetValue = exec.thisValue();
auto* newTarget = newTargetValue.getObject();
auto* newTargetGlobalObject = jsCast<JSDOMGlobalObject*>(newTarget->globalObject(vm));
JSValue htmlElementConstructorValue = JSHTMLElement::getConstructor(vm, newTargetGlobalObject);
if (newTargetValue == htmlElementConstructorValue)
return throwVMTypeError(&exec, scope, "new.target is not a valid custom element constructor"_s);
auto& document = downcast<Document>(*context);
auto* window = document.domWindow();
if (!window)
return throwVMTypeError(&exec, scope, "new.target is not a valid custom element constructor"_s);
auto* registry = window->customElementRegistry();
if (!registry)
return throwVMTypeError(&exec, scope, "new.target is not a valid custom element constructor"_s);
auto* elementInterface = registry->findInterface(newTarget);
if (!elementInterface)
return throwVMTypeError(&exec, scope, "new.target does not define a custom element"_s);
if (!elementInterface->isUpgradingElement()) {
Structure* baseStructure = getDOMStructure<JSHTMLElement>(vm, *newTargetGlobalObject);
auto* newElementStructure = InternalFunction::createSubclassStructure(&exec, newTargetValue, baseStructure);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
Ref<HTMLElement> element = HTMLElement::create(elementInterface->name(), document);
element->setIsDefinedCustomElement(*elementInterface);
auto* jsElement = JSHTMLElement::create(newElementStructure, newTargetGlobalObject, element.get());
cacheWrapper(newTargetGlobalObject->world(), element.ptr(), jsElement);
return JSValue::encode(jsElement);
}
Element* elementToUpgrade = elementInterface->lastElementInConstructionStack();
if (!elementToUpgrade) {
throwTypeError(&exec, scope, "Cannot instantiate a custom element inside its own constructor during upgrades"_s);
return JSValue::encode(jsUndefined());
}
JSValue elementWrapperValue = toJS(&exec, jsConstructor->globalObject(), *elementToUpgrade);
ASSERT(elementWrapperValue.isObject());
JSValue newPrototype = newTarget->get(&exec, vm.propertyNames->prototype);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
JSObject* elementWrapperObject = asObject(elementWrapperValue);
JSObject::setPrototype(elementWrapperObject, &exec, newPrototype, true /* shouldThrowIfCantSet */);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
elementInterface->didUpgradeLastElementInConstructionStack();
return JSValue::encode(elementWrapperValue);
}
JSScope* JSHTMLElement::pushEventHandlerScope(ExecState* exec, JSScope* scope) const
{
HTMLElement& element = wrapped();
// The document is put on first, fall back to searching it only after the element and form.
// FIXME: This probably may use the wrong global object. If this is called from a native
// function, then it would be correct but not optimal since the native function would *know*
// the global object. But, it may be that globalObject() is more correct.
// https://bugs.webkit.org/show_bug.cgi?id=134932
VM& vm = exec->vm();
JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
scope = JSWithScope::create(vm, lexicalGlobalObject, scope, asObject(toJS(exec, globalObject(), element.document())));
// The form is next, searched before the document, but after the element itself.
if (HTMLFormElement* form = element.form())
scope = JSWithScope::create(vm, lexicalGlobalObject, scope, asObject(toJS(exec, globalObject(), *form)));
// The element is on top, searched first.
return JSWithScope::create(vm, lexicalGlobalObject, scope, asObject(toJS(exec, globalObject(), element)));
}
} // namespace WebCore