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 "Frame.h"
24 #include "JSNode.h"
25 #include <runtime/FunctionConstructor.h>
26 #include <runtime/JSLock.h>
27 #include <wtf/RefCountedLeakCounter.h>
28
29 using namespace JSC;
30
31 namespace WebCore {
32
33 #ifndef NDEBUG
34 static WTF::RefCountedLeakCounter eventListenerCounter("JSLazyEventListener");
35 #endif
36
JSLazyEventListener(const String & functionName,const String & eventParameterName,const String & code,JSDOMGlobalObject * globalObject,Node * node,int lineNumber)37 JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, JSDOMGlobalObject* globalObject, Node* node, int lineNumber)
38 : JSEventListener(0, globalObject, true)
39 , m_functionName(functionName)
40 , m_eventParameterName(eventParameterName)
41 , m_code(code)
42 , m_parsed(false)
43 , m_lineNumber(lineNumber)
44 , m_originalNode(node)
45 {
46 // We don't retain the original node because we assume it
47 // will stay alive as long as this handler object is around
48 // and we need to avoid a reference cycle. If JS transfers
49 // this handler to another node, parseCode will be called and
50 // then originalNode is no longer needed.
51
52 // A JSLazyEventListener can be created with a line number of zero when it is created with
53 // a setAttribute call from JavaScript, so make the line number 1 in that case.
54 if (m_lineNumber == 0)
55 m_lineNumber = 1;
56
57 #ifndef NDEBUG
58 eventListenerCounter.increment();
59 #endif
60 }
61
~JSLazyEventListener()62 JSLazyEventListener::~JSLazyEventListener()
63 {
64 #ifndef NDEBUG
65 eventListenerCounter.decrement();
66 #endif
67 }
68
jsFunction() const69 JSObject* JSLazyEventListener::jsFunction() const
70 {
71 parseCode();
72 return m_jsFunction;
73 }
74
parseCode() const75 void JSLazyEventListener::parseCode() const
76 {
77 if (m_parsed)
78 return;
79
80 ScriptExecutionContext* executionContext = m_globalObject->scriptExecutionContext();
81 ASSERT(executionContext);
82 if (!executionContext)
83 return;
84 if (executionContext->isDocument()) {
85 JSDOMWindow* window = static_cast<JSDOMWindow*>(m_globalObject);
86 Frame* frame = window->impl()->frame();
87 if (!frame)
88 return;
89 // FIXME: Is this check needed for non-Document contexts?
90 ScriptController* script = frame->script();
91 if (!script->isEnabled() || script->isPaused())
92 return;
93 }
94
95 m_parsed = true;
96
97 ExecState* exec = m_globalObject->globalExec();
98
99 MarkedArgumentBuffer args;
100 UString sourceURL(executionContext->url().string());
101 args.append(jsNontrivialString(exec, m_eventParameterName));
102 args.append(jsString(exec, m_code));
103
104 // FIXME: Passing the document's URL to construct is not always correct, since this event listener might
105 // have been added with setAttribute from a script, and we should pass String() in that case.
106 m_jsFunction = constructFunction(exec, args, Identifier(exec, m_functionName), sourceURL, m_lineNumber); // FIXME: is globalExec ok?
107
108 JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_jsFunction);
109
110 if (exec->hadException()) {
111 exec->clearException();
112
113 // failed to parse, so let's just make this listener a no-op
114 m_jsFunction = 0;
115 } else if (m_originalNode) {
116 // Add the event's home element to the scope
117 // (and the document, and the form - see JSHTMLElement::eventHandlerScope)
118 ScopeChain scope = listenerAsFunction->scope();
119
120 JSValue thisObj = toJS(exec, m_globalObject, m_originalNode);
121 if (thisObj.isObject()) {
122 static_cast<JSNode*>(asObject(thisObj))->pushEventHandlerScope(exec, scope);
123 listenerAsFunction->setScope(scope);
124 }
125 }
126
127 // Since we only parse once, there's no need to keep data used for parsing around anymore.
128 m_functionName = String();
129 m_code = String();
130 m_eventParameterName = String();
131 }
132
133 } // namespace WebCore
134