JSLazyEventListener.cpp [plain text]
#include "config.h"
#include "JSLazyEventListener.h"
#include "ContentSecurityPolicy.h"
#include "Frame.h"
#include "JSNode.h"
#include "ScriptController.h"
#include <runtime/FunctionConstructor.h>
#include <runtime/IdentifierInlines.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/StdLibExtras.h>
using namespace JSC;
namespace WebCore {
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, eventListenerCounter, ("JSLazyEventListener"));
JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, ContainerNode* node, const String& sourceURL, const TextPosition& sourcePosition, JSObject* wrapper, DOMWrapperWorld& isolatedWorld)
: JSEventListener(0, wrapper, true, isolatedWorld)
, m_functionName(functionName)
, m_eventParameterName(eventParameterName)
, m_code(code)
, m_sourceURL(sourceURL)
, m_sourcePosition(sourcePosition)
, m_originalNode(node)
{
if (m_sourcePosition == TextPosition::belowRangePosition())
m_sourcePosition = TextPosition();
ASSERT(m_eventParameterName == "evt" || m_eventParameterName == "event");
#ifndef NDEBUG
eventListenerCounter.increment();
#endif
}
JSLazyEventListener::~JSLazyEventListener()
{
#ifndef NDEBUG
eventListenerCounter.decrement();
#endif
}
JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* executionContext) const
{
ASSERT(is<Document>(executionContext));
if (!executionContext)
return nullptr;
ASSERT(!m_code.isNull());
ASSERT(!m_eventParameterName.isNull());
if (m_code.isNull() || m_eventParameterName.isNull())
return nullptr;
Document& document = downcast<Document>(*executionContext);
if (!document.frame())
return nullptr;
if (!document.contentSecurityPolicy()->allowInlineEventHandlers(m_sourceURL, m_sourcePosition.m_line))
return nullptr;
ScriptController& script = document.frame()->script();
if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused())
return nullptr;
JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext, isolatedWorld());
if (!globalObject)
return nullptr;
VM& vm = globalObject->vm();
JSLockHolder lock(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
ExecState* exec = globalObject->globalExec();
MarkedArgumentBuffer args;
args.append(jsNontrivialString(exec, m_eventParameterName));
args.append(jsStringWithCache(exec, m_code));
int overrideLineNumber = m_sourcePosition.m_line.oneBasedInt();
JSObject* jsFunction = constructFunctionSkippingEvalEnabledCheck(
exec, exec->lexicalGlobalObject(), args, Identifier::fromString(exec, m_functionName),
m_sourceURL, m_sourcePosition, overrideLineNumber);
if (UNLIKELY(scope.exception())) {
reportCurrentException(exec);
scope.clearException();
return nullptr;
}
JSFunction* listenerAsFunction = jsCast<JSFunction*>(jsFunction);
if (m_originalNode) {
if (!wrapper()) {
setWrapper(vm, asObject(toJS(exec, globalObject, *m_originalNode)));
}
listenerAsFunction->setScope(vm, jsCast<JSNode*>(wrapper())->pushEventHandlerScope(exec, listenerAsFunction->scope()));
}
return jsFunction;
}
static const String& eventParameterName(bool isSVGEvent)
{
static NeverDestroyed<const String> eventString(ASCIILiteral("event"));
static NeverDestroyed<const String> evtString(ASCIILiteral("evt"));
return isSVGEvent ? evtString : eventString;
}
struct JSLazyEventListener::CreationArguments {
const QualifiedName& attributeName;
const AtomicString& attributeValue;
Document& document;
ContainerNode* node;
JSC::JSObject* wrapper;
bool shouldUseSVGEventName;
};
RefPtr<JSLazyEventListener> JSLazyEventListener::create(const CreationArguments& arguments)
{
if (arguments.attributeValue.isNull())
return nullptr;
TextPosition position;
String sourceURL;
if (Frame* frame = arguments.document.frame()) {
if (!frame->script().canExecuteScripts(AboutToExecuteScript))
return nullptr;
position = frame->script().eventHandlerPosition();
sourceURL = arguments.document.url().string();
}
return adoptRef(*new JSLazyEventListener(arguments.attributeName.localName().string(),
eventParameterName(arguments.shouldUseSVGEventName), arguments.attributeValue,
arguments.node, sourceURL, position, arguments.wrapper, mainThreadNormalWorld()));
}
RefPtr<JSLazyEventListener> JSLazyEventListener::create(Element& element, const QualifiedName& attributeName, const AtomicString& attributeValue)
{
return create({ attributeName, attributeValue, element.document(), &element, nullptr, element.isSVGElement() });
}
RefPtr<JSLazyEventListener> JSLazyEventListener::create(Document& document, const QualifiedName& attributeName, const AtomicString& attributeValue)
{
return create({ attributeName, attributeValue, document, &document, nullptr, false });
}
RefPtr<JSLazyEventListener> JSLazyEventListener::create(DOMWindow& window, const QualifiedName& attributeName, const AtomicString& attributeValue)
{
ASSERT(window.document());
auto& document = *window.document();
ASSERT(document.frame());
return create({ attributeName, attributeValue, document, nullptr, toJSDOMWindow(document.frame(), mainThreadNormalWorld()), document.isSVGDocument() });
}
}