NeverDestroyed<String>(ASCIILiteral(...)) is not thread safe.
[WebKit-https.git] / Source / WebCore / bindings / js / JSLazyEventListener.cpp
index 6e27685..9e051a9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
- *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All Rights Reserved.
+ *  Copyright (C) 2003-2017 Apple Inc. All Rights Reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
 #include "config.h"
 #include "JSLazyEventListener.h"
 
+#include "CachedScriptFetcher.h"
 #include "ContentSecurityPolicy.h"
 #include "Frame.h"
 #include "JSNode.h"
 #include "ScriptController.h"
-#include <runtime/Executable.h>
 #include <runtime/FunctionConstructor.h>
 #include <runtime/IdentifierInlines.h>
 #include <wtf/NeverDestroyed.h>
@@ -37,13 +37,13 @@ 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& position, JSObject* wrapper, DOMWrapperWorld& isolatedWorld)
+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_position(position)
+    , m_sourcePosition(sourcePosition)
     , m_originalNode(node)
 {
     // We don't retain the original node because we assume it
@@ -54,8 +54,8 @@ JSLazyEventListener::JSLazyEventListener(const String& functionName, const Strin
 
     // A JSLazyEventListener can be created with a line number of zero when it is created with
     // a setAttribute call from JavaScript, so make the line number 1 in that case.
-    if (m_position == TextPosition::belowRangePosition())
-        m_position = TextPosition::minimumPosition();
+    if (m_sourcePosition == TextPosition::belowRangePosition())
+        m_sourcePosition = TextPosition();
 
     ASSERT(m_eventParameterName == "evt" || m_eventParameterName == "event");
 
@@ -87,7 +87,7 @@ JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* exec
     if (!document.frame())
         return nullptr;
 
-    if (!document.contentSecurityPolicy()->allowInlineEventHandlers(m_sourceURL, m_position.m_line))
+    if (!document.contentSecurityPolicy()->allowInlineEventHandlers(m_sourceURL, m_sourcePosition.m_line))
         return nullptr;
 
     ScriptController& script = document.frame()->script();
@@ -98,6 +98,9 @@ JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* exec
     if (!globalObject)
         return nullptr;
 
+    VM& vm = globalObject->vm();
+    JSLockHolder lock(vm);
+    auto scope = DECLARE_CATCH_SCOPE(vm);
     ExecState* exec = globalObject->globalExec();
 
     MarkedArgumentBuffer args;
@@ -106,15 +109,15 @@ JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* exec
 
     // We want all errors to refer back to the line on which our attribute was
     // declared, regardless of any newlines in our JavaScript source text.
-    int overrideLineNo = m_position.m_line.oneBasedInt();
+    int overrideLineNumber = m_sourcePosition.m_line.oneBasedInt();
 
     JSObject* jsFunction = constructFunctionSkippingEvalEnabledCheck(
-        exec, exec->lexicalGlobalObject(), args, Identifier(exec, m_functionName), 
-        m_sourceURL, m_position, overrideLineNo);
+        exec, exec->lexicalGlobalObject(), args, Identifier::fromString(exec, m_functionName),
+        SourceOrigin { m_sourceURL, CachedScriptFetcher::create(document.charset()) }, m_sourceURL, m_sourcePosition, overrideLineNumber);
 
-    if (exec->hadException()) {
+    if (UNLIKELY(scope.exception())) {
         reportCurrentException(exec);
-        exec->clearException();
+        scope.clearException();
         return nullptr;
     }
 
@@ -123,59 +126,71 @@ JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* exec
     if (m_originalNode) {
         if (!wrapper()) {
             // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating.
-            JSLockHolder lock(exec);
             // FIXME: Should pass the global object associated with the node
-            setWrapper(exec->vm(), asObject(toJS(exec, globalObject, m_originalNode)));
+            setWrapper(vm, asObject(toJS(exec, globalObject, *m_originalNode)));
         }
 
         // Add the event's home element to the scope
         // (and the document, and the form - see JSHTMLElement::eventHandlerScope)
-        listenerAsFunction->setScope(exec->vm(), jsCast<JSNode*>(wrapper())->pushEventHandlerScope(exec, listenerAsFunction->scope()));
+        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"));
+    static NeverDestroyed<const String> eventString(MAKE_STATIC_STRING_IMPL("event"));
+    static NeverDestroyed<const String> evtString(MAKE_STATIC_STRING_IMPL("evt"));
     return isSVGEvent ? evtString : eventString;
 }
 
-PassRefPtr<JSLazyEventListener> JSLazyEventListener::createForNode(ContainerNode& node, const QualifiedName& attributeName, const AtomicString& attributeValue)
+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 (attributeValue.isNull())
+    if (arguments.attributeValue.isNull())
         return nullptr;
 
-    TextPosition position = TextPosition::minimumPosition();
-    String sourceURL;
-
     // FIXME: We should be able to provide source information for frameless documents too (e.g. for importing nodes from XMLHttpRequest.responseXML).
-    if (Frame* frame = node.document().frame()) {
+    TextPosition position;
+    String sourceURL;
+    if (Frame* frame = arguments.document.frame()) {
         if (!frame->script().canExecuteScripts(AboutToExecuteScript))
             return nullptr;
-
         position = frame->script().eventHandlerPosition();
-        sourceURL = node.document().url().string();
+        sourceURL = arguments.document.url().string();
     }
 
-    return adoptRef(new JSLazyEventListener(attributeName.localName().string(),
-        eventParameterName(node.isSVGElement()), attributeValue,
-        &node, sourceURL, position, nullptr, mainThreadNormalWorld()));
+    return adoptRef(*new JSLazyEventListener(arguments.attributeName.localName().string(),
+        eventParameterName(arguments.shouldUseSVGEventName), arguments.attributeValue,
+        arguments.node, sourceURL, position, arguments.wrapper, mainThreadNormalWorld()));
 }
 
-PassRefPtr<JSLazyEventListener> JSLazyEventListener::createForDOMWindow(Frame& frame, const QualifiedName& attributeName, const AtomicString& attributeValue)
+RefPtr<JSLazyEventListener> JSLazyEventListener::create(Element& element, const QualifiedName& attributeName, const AtomicString& attributeValue)
 {
-    if (attributeValue.isNull())
-        return nullptr;
+    return create({ attributeName, attributeValue, element.document(), &element, nullptr, element.isSVGElement() });
+}
 
-    if (!frame.script().canExecuteScripts(AboutToExecuteScript))
-        return nullptr;
+RefPtr<JSLazyEventListener> JSLazyEventListener::create(Document& document, const QualifiedName& attributeName, const AtomicString& attributeValue)
+{
+    // FIXME: This always passes false for "shouldUseSVGEventName". Is that correct for events dispatched to SVG documents?
+    // This has been this way for a long time, but became more obvious when refactoring to separate the Element and Document code paths.
+    return create({ attributeName, attributeValue, document, &document, nullptr, false });
+}
 
-    return adoptRef(new JSLazyEventListener(attributeName.localName().string(),
-        eventParameterName(frame.document()->isSVGDocument()), attributeValue,
-        nullptr, frame.document()->url().string(), frame.script().eventHandlerPosition(),
-        toJSDOMWindow(&frame, mainThreadNormalWorld()), mainThreadNormalWorld()));
+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() });
 }
 
 } // namespace WebCore