<https://webkit.org/b/119852> Frame::scriptController() should return a reference
[WebKit-https.git] / Source / WebCore / bindings / js / JSLazyEventListener.cpp
1 /*
2  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
3  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All Rights Reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "JSLazyEventListener.h"
22
23 #include "ContentSecurityPolicy.h"
24 #include "Frame.h"
25 #include "JSNode.h"
26 #include "ScriptController.h"
27 #include <runtime/FunctionConstructor.h>
28 #include <runtime/JSFunction.h>
29 #include <runtime/JSLock.h>
30 #include <wtf/RefCountedLeakCounter.h>
31 #include <wtf/StdLibExtras.h>
32 #include <wtf/text/TextPosition.h>
33
34 using namespace JSC;
35
36 namespace WebCore {
37
38 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, eventListenerCounter, ("JSLazyEventListener"));
39
40 JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, Node* node, const String& sourceURL, const TextPosition& position, JSObject* wrapper, DOMWrapperWorld* isolatedWorld)
41     : JSEventListener(0, wrapper, true, isolatedWorld)
42     , m_functionName(functionName)
43     , m_eventParameterName(eventParameterName)
44     , m_code(code)
45     , m_sourceURL(sourceURL)
46     , m_position(position)
47     , m_originalNode(node)
48 {
49     // We don't retain the original node because we assume it
50     // will stay alive as long as this handler object is around
51     // and we need to avoid a reference cycle. If JS transfers
52     // this handler to another node, initializeJSFunction will
53     // be called and then originalNode is no longer needed.
54
55     // A JSLazyEventListener can be created with a line number of zero when it is created with
56     // a setAttribute call from JavaScript, so make the line number 1 in that case.
57     if (m_position == TextPosition::belowRangePosition())
58         m_position = TextPosition::minimumPosition();
59
60     ASSERT(m_eventParameterName == "evt" || m_eventParameterName == "event");
61
62 #ifndef NDEBUG
63     eventListenerCounter.increment();
64 #endif
65 }
66
67 JSLazyEventListener::~JSLazyEventListener()
68 {
69 #ifndef NDEBUG
70     eventListenerCounter.decrement();
71 #endif
72 }
73
74 JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* executionContext) const
75 {
76     ASSERT(executionContext);
77     ASSERT(executionContext->isDocument());
78     if (!executionContext)
79         return 0;
80
81     ASSERT(!m_code.isNull());
82     ASSERT(!m_eventParameterName.isNull());
83     if (m_code.isNull() || m_eventParameterName.isNull())
84         return 0;
85
86     Document* document = toDocument(executionContext);
87
88     if (!document->frame())
89         return 0;
90
91     if (!document->contentSecurityPolicy()->allowInlineEventHandlers(m_sourceURL, m_position.m_line))
92         return 0;
93
94     ScriptController& script = document->frame()->script();
95     if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused())
96         return 0;
97
98     JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext, isolatedWorld());
99     if (!globalObject)
100         return 0;
101
102     ExecState* exec = globalObject->globalExec();
103
104     MarkedArgumentBuffer args;
105     args.append(jsNontrivialString(exec, m_eventParameterName));
106     args.append(jsStringWithCache(exec, m_code));
107
108     JSObject* jsFunction = constructFunctionSkippingEvalEnabledCheck(exec, exec->lexicalGlobalObject(), args, Identifier(exec, m_functionName), m_sourceURL, m_position); // FIXME: is globalExec ok?
109     if (exec->hadException()) {
110         reportCurrentException(exec);
111         exec->clearException();
112         return 0;
113     }
114
115     JSFunction* listenerAsFunction = jsCast<JSFunction*>(jsFunction);
116     if (m_originalNode) {
117         if (!wrapper()) {
118             // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating.
119             JSLockHolder lock(exec);
120             // FIXME: Should pass the global object associated with the node
121             setWrapper(exec->vm(), asObject(toJS(exec, globalObject, m_originalNode)));
122         }
123
124         // Add the event's home element to the scope
125         // (and the document, and the form - see JSHTMLElement::eventHandlerScope)
126         listenerAsFunction->setScope(exec->vm(), jsCast<JSNode*>(wrapper())->pushEventHandlerScope(exec, listenerAsFunction->scope()));
127     }
128     return jsFunction;
129 }
130
131 } // namespace WebCore