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 <runtime/FunctionConstructor.h>
27 #include <runtime/JSFunction.h>
28 #include <runtime/JSLock.h>
29 #include <wtf/RefCountedLeakCounter.h>
30 
31 using namespace JSC;
32 
33 namespace WebCore {
34 
35 #ifndef NDEBUG
36 static WTF::RefCountedLeakCounter eventListenerCounter("JSLazyEventListener");
37 #endif
38 
JSLazyEventListener(const String & functionName,const String & eventParameterName,const String & code,Node * node,const String & sourceURL,int lineNumber,JSObject * wrapper,DOMWrapperWorld * isolatedWorld)39 JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, Node* node, const String& sourceURL, int lineNumber, JSObject* wrapper, DOMWrapperWorld* isolatedWorld)
40     : JSEventListener(0, wrapper, true, isolatedWorld)
41     , m_functionName(functionName)
42     , m_eventParameterName(eventParameterName)
43     , m_code(code)
44     , m_sourceURL(sourceURL)
45     , m_lineNumber(lineNumber)
46     , m_originalNode(node)
47 {
48     // We don't retain the original node because we assume it
49     // will stay alive as long as this handler object is around
50     // and we need to avoid a reference cycle. If JS transfers
51     // this handler to another node, initializeJSFunction will
52     // be called and then originalNode is no longer needed.
53 
54     // A JSLazyEventListener can be created with a line number of zero when it is created with
55     // a setAttribute call from JavaScript, so make the line number 1 in that case.
56     if (m_lineNumber == 0)
57         m_lineNumber = 1;
58 
59 #ifndef NDEBUG
60     eventListenerCounter.increment();
61 #endif
62 }
63 
~JSLazyEventListener()64 JSLazyEventListener::~JSLazyEventListener()
65 {
66 #ifndef NDEBUG
67     eventListenerCounter.decrement();
68 #endif
69 }
70 
initializeJSFunction(ScriptExecutionContext * executionContext) const71 JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* executionContext) const
72 {
73     ASSERT(executionContext);
74     ASSERT(executionContext->isDocument());
75     if (!executionContext)
76         return 0;
77 
78     Document* document = static_cast<Document*>(executionContext);
79 
80     if (!document->frame())
81         return 0;
82 
83     if (!document->contentSecurityPolicy()->allowInlineEventHandlers())
84         return 0;
85 
86     ScriptController* script = document->frame()->script();
87     if (!script->canExecuteScripts(AboutToExecuteScript) || script->isPaused())
88         return 0;
89 
90     JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext, isolatedWorld());
91     if (!globalObject)
92         return 0;
93 
94     ExecState* exec = globalObject->globalExec();
95 
96     MarkedArgumentBuffer args;
97     args.append(jsNontrivialString(exec, stringToUString(m_eventParameterName)));
98     args.append(jsString(exec, m_code));
99 
100     JSObject* jsFunction = constructFunction(exec, exec->lexicalGlobalObject(), args, Identifier(exec, stringToUString(m_functionName)), stringToUString(m_sourceURL), m_lineNumber); // FIXME: is globalExec ok?
101     if (exec->hadException()) {
102         exec->clearException();
103         return 0;
104     }
105 
106     JSFunction* listenerAsFunction = static_cast<JSFunction*>(jsFunction);
107     if (m_originalNode) {
108         if (!wrapper()) {
109             // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating.
110             JSLock lock(SilenceAssertionsOnly);
111             // FIXME: Should pass the global object associated with the node
112             setWrapper(exec->globalData(), asObject(toJS(exec, globalObject, m_originalNode)));
113         }
114 
115         // Add the event's home element to the scope
116         // (and the document, and the form - see JSHTMLElement::eventHandlerScope)
117         listenerAsFunction->setScope(exec->globalData(), static_cast<JSNode*>(wrapper())->pushEventHandlerScope(exec, listenerAsFunction->scope()));
118     }
119 
120     // Since we only parse once, there's no need to keep data used for parsing around anymore.
121     m_functionName = String();
122     m_code = String();
123     m_eventParameterName = String();
124     m_sourceURL = String();
125     return jsFunction;
126 }
127 
128 } // namespace WebCore
129