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
31using namespace JSC;
32
33namespace WebCore {
34
35#ifndef NDEBUG
36static WTF::RefCountedLeakCounter eventListenerCounter("JSLazyEventListener");
37#endif
38
39JSLazyEventListener::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
64JSLazyEventListener::~JSLazyEventListener()
65{
66#ifndef NDEBUG
67    eventListenerCounter.decrement();
68#endif
69}
70
71JSObject* 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