1dd84cd62ec2f07ea2780a85d0e97f2cd50cc050
[WebKit-https.git] / WebCore / bindings / js / JSLazyEventListener.cpp
1 /*
2  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
3  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 "Frame.h"
24 #include "JSNode.h"
25 #include <runtime/FunctionConstructor.h>
26
27 using namespace JSC;
28
29 namespace WebCore {
30
31 JSLazyEventListener::JSLazyEventListener(LazyEventListenerType type, const String& functionName, const String& code, JSDOMGlobalObject* globalObject, Node* node, int lineNumber)
32     : JSProtectedEventListener(0, globalObject, true)
33     , m_functionName(functionName)
34     , m_code(code)
35     , m_parsed(false)
36     , m_lineNumber(lineNumber)
37     , m_originalNode(node)
38     , m_type(type)
39 {
40     // We don't retain the original node because we assume it
41     // will stay alive as long as this handler object is around
42     // and we need to avoid a reference cycle. If JS transfers
43     // this handler to another node, parseCode will be called and
44     // then originalNode is no longer needed.
45
46     // A JSLazyEventListener can be created with a line number of zero when it is created with
47     // a setAttribute call from JavaScript, so make the line number 1 in that case.
48     if (m_lineNumber == 0)
49         m_lineNumber = 1;
50 }
51
52 JSObject* JSLazyEventListener::listenerObj() const
53 {
54     parseCode();
55     return m_listener;
56 }
57
58 static inline JSValuePtr eventParameterName(JSLazyEventListener::LazyEventListenerType type, ExecState* exec)
59 {
60     switch (type) {
61         case JSLazyEventListener::HTMLLazyEventListener:
62             return jsNontrivialString(exec, "event");
63 #if ENABLE(SVG)
64         case JSLazyEventListener::SVGLazyEventListener:
65             return jsNontrivialString(exec, "evt");
66 #endif
67     }
68     ASSERT_NOT_REACHED();
69     return jsUndefined();
70 }
71
72 void JSLazyEventListener::parseCode() const
73 {
74     if (m_parsed)
75         return;
76
77     if (globalObject()->scriptExecutionContext()->isDocument()) {
78         JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject());
79         Frame* frame = window->impl()->frame();
80         if (!frame)
81             return;
82         // FIXME: Is this check needed for non-Document contexts?
83         ScriptController* script = frame->script();
84         if (!script->isEnabled() || script->isPaused())
85             return;
86     }
87
88     m_parsed = true;
89
90     ExecState* exec = globalObject()->globalExec();
91
92     ArgList args;
93     UString sourceURL(globalObject()->scriptExecutionContext()->url().string());
94     args.append(eventParameterName(m_type, exec));
95     args.append(jsString(exec, m_code));
96
97     // FIXME: Passing the document's URL to construct is not always correct, since this event listener might
98     // have been added with setAttribute from a script, and we should pass String() in that case.
99     m_listener = constructFunction(exec, args, Identifier(exec, m_functionName), sourceURL, m_lineNumber); // FIXME: is globalExec ok?
100
101     JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_listener.get());
102
103     if (exec->hadException()) {
104         exec->clearException();
105
106         // failed to parse, so let's just make this listener a no-op
107         m_listener = 0;
108     } else if (m_originalNode) {
109         // Add the event's home element to the scope
110         // (and the document, and the form - see JSHTMLElement::eventHandlerScope)
111         ScopeChain scope = listenerAsFunction->scope();
112
113         JSValuePtr thisObj = toJS(exec, m_originalNode);
114         if (thisObj.isObject()) {
115             static_cast<JSNode*>(asObject(thisObj))->pushEventHandlerScope(exec, scope);
116             listenerAsFunction->setScope(scope);
117         }
118     }
119
120     // no more need to keep the unparsed code around
121     m_functionName = String();
122     m_code = String();
123
124     if (m_listener) {
125         ASSERT(isInline());
126         JSDOMWindow::ProtectedListenersMap& listeners = globalObject()->jsProtectedInlineEventListeners();
127         listeners.set(m_listener, const_cast<JSLazyEventListener*>(this));
128     }
129 }
130
131 } // namespace WebCore