kjs_events.cpp 10.1 KB
Newer Older
kocienda's avatar
kocienda committed
1 2
/*
 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
weinig@apple.com's avatar
weinig@apple.com committed
3
 *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All Rights Reserved.
kocienda's avatar
kocienda committed
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
ddkilzer's avatar
ddkilzer committed
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
kocienda's avatar
kocienda committed
18
 */
darin's avatar
darin committed
19

mjs's avatar
mjs committed
20
#include "config.h"
kocienda's avatar
kocienda committed
21
#include "kjs_events.h"
darin's avatar
darin committed
22

darin's avatar
darin committed
23
#include "CString.h"
weinig's avatar
weinig committed
24
#include "Chrome.h"
weinig's avatar
weinig committed
25
#include "DOMWindow.h"
darin's avatar
darin committed
26
#include "Document.h"
weinig's avatar
 
weinig committed
27
#include "Event.h"
darin's avatar
darin committed
28 29
#include "EventNames.h"
#include "Frame.h"
darin's avatar
darin committed
30
#include "FrameLoader.h"
weinig@apple.com's avatar
weinig@apple.com committed
31
#include "JSDOMWindow.h"
ggaren's avatar
ggaren committed
32
#include "JSEvent.h"
weinig's avatar
weinig committed
33
#include "JSEventTargetNode.h"
andersca's avatar
andersca committed
34
#include "Page.h"
weinig@apple.com's avatar
weinig@apple.com committed
35
#include "kjs_dom.h"
darin's avatar
darin committed
36 37
#include "kjs_proxy.h"
#include "kjs_window.h"
ggaren@apple.com's avatar
ggaren@apple.com committed
38
#include <kjs/function_object.h>
kocienda's avatar
kocienda committed
39

darin's avatar
darin committed
40
#include "kjs_events.lut.h"
41

weinig@apple.com's avatar
weinig@apple.com committed
42 43
using namespace KJS;

weinig's avatar
weinig committed
44 45
namespace WebCore {

darin's avatar
darin committed
46
using namespace EventNames;
darin's avatar
darin committed
47

weinig's avatar
weinig committed
48 49
JSAbstractEventListener::JSAbstractEventListener(bool html)
    : m_html(html)
kocienda's avatar
kocienda committed
50 51 52
{
}

darin's avatar
darin committed
53
void JSAbstractEventListener::handleEvent(Event* ele, bool isWindowEvent)
kocienda's avatar
kocienda committed
54
{
55
#ifdef KJS_DEBUGGER
darin's avatar
darin committed
56 57
    if (KJSDebugWin::instance() && KJSDebugWin::instance()->inSession())
        return;
58
#endif
59

weinig's avatar
weinig committed
60
    Event* event = ele;
61

darin's avatar
darin committed
62 63 64
    JSObject* listener = listenerObj();
    if (!listener)
        return;
mjs's avatar
mjs committed
65

weinig@apple.com's avatar
weinig@apple.com committed
66
    JSDOMWindow* window = windowObj();
andrew's avatar
andrew committed
67 68 69 70
    // Null check as clearWindowObj() can clear this and we still get called back by
    // xmlhttprequest objects. See http://bugs.webkit.org/show_bug.cgi?id=13275
    if (!window)
        return;
weinig@apple.com's avatar
weinig@apple.com committed
71
    Frame* frame = window->impl()->frame();
mjs's avatar
mjs committed
72
    if (!frame)
darin's avatar
darin committed
73
        return;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
74
    if (!frame->scriptProxy()->isEnabled())
darin's avatar
darin committed
75
        return;
darin's avatar
darin committed
76

darin's avatar
darin committed
77
    JSLock lock;
weinig's avatar
weinig committed
78

alice.liu@apple.com's avatar
alice.liu@apple.com committed
79
    JSGlobalObject* globalObject = frame->scriptProxy()->globalObject();
ggaren@apple.com's avatar
ggaren@apple.com committed
80
    ExecState* exec = globalObject->globalExec();
weinig's avatar
weinig committed
81

darin's avatar
darin committed
82 83
    JSValue* handleEventFuncValue = listener->get(exec, "handleEvent");
    JSObject* handleEventFunc = 0;
weinig's avatar
weinig committed
84
    if (handleEventFuncValue->isObject()) {
darin's avatar
darin committed
85 86 87 88
        handleEventFunc = static_cast<JSObject*>(handleEventFuncValue);
        if (!handleEventFunc->implementsCall())
            handleEventFunc = 0;
    }
weinig's avatar
weinig committed
89

darin's avatar
darin committed
90 91
    if (handleEventFunc || listener->implementsCall()) {
        ref();
weinig's avatar
weinig committed
92

darin's avatar
darin committed
93
        List args;
darin's avatar
darin committed
94
        args.append(toJS(exec, event));
weinig's avatar
weinig committed
95

darin's avatar
darin committed
96
        window->setCurrentEvent(event);
weinig's avatar
weinig committed
97

darin's avatar
darin committed
98
        JSValue* retval;
andersca's avatar
andersca committed
99
        if (handleEventFunc) {
ggaren@apple.com's avatar
ggaren@apple.com committed
100
            globalObject->startTimeoutCheck();
darin's avatar
darin committed
101
            retval = handleEventFunc->call(exec, listener, args);
andersca's avatar
andersca committed
102
        } else {
darin's avatar
darin committed
103 104 105 106
            JSObject* thisObj;
            if (isWindowEvent)
                thisObj = window;
            else
darin's avatar
darin committed
107
                thisObj = static_cast<JSObject*>(toJS(exec, event->currentTarget()));
ggaren@apple.com's avatar
ggaren@apple.com committed
108
            globalObject->startTimeoutCheck();
darin's avatar
darin committed
109 110
            retval = listener->call(exec, thisObj, args);
        }
ggaren@apple.com's avatar
ggaren@apple.com committed
111
        globalObject->stopTimeoutCheck();
darin's avatar
darin committed
112 113 114 115 116

        window->setCurrentEvent(0);

        if (exec->hadException()) {
            JSObject* exception = exec->exception()->toObject(exec);
mjs's avatar
mjs committed
117
            String message = exception->get(exec, exec->propertyNames().message)->toString(exec);
darin's avatar
darin committed
118
            int lineNumber = exception->get(exec, "line")->toInt32(exec);
darin's avatar
darin committed
119
            String sourceURL = exception->get(exec, "sourceURL")->toString(exec);
darin's avatar
darin committed
120
            if (Interpreter::shouldPrintExceptions())
121
                printf("(event handler):%s\n", message.utf8().data());
andersca's avatar
andersca committed
122
            if (Page* page = frame->page())
123
                page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, lineNumber, sourceURL);
darin's avatar
darin committed
124 125 126
            exec->clearException();
        } else {
            if (!retval->isUndefinedOrNull() && event->storesResultAsString())
darin's avatar
darin committed
127
                event->storeResult(retval->toString(exec));
weinig's avatar
weinig committed
128
            if (m_html) {
darin's avatar
darin committed
129 130
                bool retvalbool;
                if (retval->getBoolean(retvalbool) && !retvalbool)
darin's avatar
darin committed
131
                    event->preventDefault();
132
            }
darin's avatar
darin committed
133 134
        }

darin's avatar
darin committed
135
        Document::updateDocumentsRendering();
darin's avatar
darin committed
136 137
        deref();
    }
kocienda's avatar
kocienda committed
138 139
}

darin's avatar
darin committed
140
bool JSAbstractEventListener::isHTMLEventListener() const
kocienda's avatar
kocienda committed
141
{
weinig's avatar
weinig committed
142
    return m_html;
kocienda's avatar
kocienda committed
143 144
}

mjs's avatar
mjs committed
145 146
// -------------------------------------------------------------------------

weinig@apple.com's avatar
weinig@apple.com committed
147
JSUnprotectedEventListener::JSUnprotectedEventListener(JSObject* listener, JSDOMWindow* win, bool html)
weinig's avatar
weinig committed
148 149 150
    : JSAbstractEventListener(html)
    , m_listener(listener)
    , m_win(win)
mjs's avatar
mjs committed
151
{
weinig's avatar
weinig committed
152
    if (m_listener) {
weinig@apple.com's avatar
weinig@apple.com committed
153
        JSDOMWindow::UnprotectedListenersMap& listeners = html
weinig's avatar
weinig committed
154 155
            ? m_win->jsUnprotectedHTMLEventListeners() : m_win->jsUnprotectedEventListeners();
        listeners.set(m_listener, this);
darin's avatar
darin committed
156
    }
mjs's avatar
mjs committed
157 158 159 160
}

JSUnprotectedEventListener::~JSUnprotectedEventListener()
{
weinig's avatar
weinig committed
161
    if (m_listener && m_win) {
weinig@apple.com's avatar
weinig@apple.com committed
162
        JSDOMWindow::UnprotectedListenersMap& listeners = isHTMLEventListener()
weinig's avatar
weinig committed
163 164
            ? m_win->jsUnprotectedHTMLEventListeners() : m_win->jsUnprotectedEventListeners();
        listeners.remove(m_listener);
darin's avatar
darin committed
165
    }
mjs's avatar
mjs committed
166 167
}

darin's avatar
darin committed
168
JSObject* JSUnprotectedEventListener::listenerObj() const
weinig's avatar
weinig committed
169 170
{
    return m_listener;
mjs's avatar
mjs committed
171 172
}

weinig@apple.com's avatar
weinig@apple.com committed
173
JSDOMWindow* JSUnprotectedEventListener::windowObj() const
mjs's avatar
mjs committed
174
{
weinig's avatar
weinig committed
175
    return m_win;
mjs's avatar
mjs committed
176 177
}

sullivan's avatar
sullivan committed
178 179
void JSUnprotectedEventListener::clearWindowObj()
{
weinig's avatar
weinig committed
180
    m_win = 0;
sullivan's avatar
sullivan committed
181 182
}

183
void JSUnprotectedEventListener::mark()
mjs's avatar
mjs committed
184
{
185 186
    if (m_listener && !m_listener->marked())
        m_listener->mark();
mjs's avatar
mjs committed
187 188
}

weinig's avatar
weinig committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
#ifndef NDEBUG
#ifndef LOG_CHANNEL_PREFIX
#define LOG_CHANNEL_PREFIX Log
#endif
WTFLogChannel LogWebCoreEventListenerLeaks = { 0x00000000, "", WTFLogChannelOn };

struct EventListenerCounter {
    static unsigned count;
    ~EventListenerCounter()
    {
        if (count)
            LOG(WebCoreEventListenerLeaks, "LEAK: %u EventListeners\n", count);
    }
};
unsigned EventListenerCounter::count = 0;
static EventListenerCounter eventListenerCounter;
#endif

mjs's avatar
mjs committed
207 208
// -------------------------------------------------------------------------

weinig@apple.com's avatar
weinig@apple.com committed
209
JSEventListener::JSEventListener(JSObject* listener, JSDOMWindow* win, bool html)
weinig's avatar
weinig committed
210 211 212
    : JSAbstractEventListener(html)
    , m_listener(listener)
    , m_win(win)
mjs's avatar
mjs committed
213
{
weinig's avatar
weinig committed
214
    if (m_listener) {
weinig@apple.com's avatar
weinig@apple.com committed
215
        JSDOMWindow::ListenersMap& listeners = html
weinig's avatar
weinig committed
216 217
            ? m_win->jsHTMLEventListeners() : m_win->jsEventListeners();
        listeners.set(m_listener, this);
darin's avatar
darin committed
218
    }
weinig's avatar
weinig committed
219 220 221
#ifndef NDEBUG
    ++eventListenerCounter.count;
#endif
mjs's avatar
mjs committed
222 223 224 225
}

JSEventListener::~JSEventListener()
{
weinig's avatar
weinig committed
226
    if (m_listener && m_win) {
weinig@apple.com's avatar
weinig@apple.com committed
227
        JSDOMWindow::ListenersMap& listeners = isHTMLEventListener()
weinig's avatar
weinig committed
228 229
            ? m_win->jsHTMLEventListeners() : m_win->jsEventListeners();
        listeners.remove(m_listener);
darin's avatar
darin committed
230
    }
weinig's avatar
weinig committed
231 232 233
#ifndef NDEBUG
    --eventListenerCounter.count;
#endif
mjs's avatar
mjs committed
234
}
mjs's avatar
mjs committed
235

darin's avatar
darin committed
236
JSObject* JSEventListener::listenerObj() const
weinig's avatar
weinig committed
237 238
{
    return m_listener;
mjs's avatar
mjs committed
239 240
}

weinig@apple.com's avatar
weinig@apple.com committed
241
JSDOMWindow* JSEventListener::windowObj() const
mjs's avatar
mjs committed
242
{
weinig's avatar
weinig committed
243
    return m_win;
mjs's avatar
mjs committed
244 245
}

sullivan's avatar
sullivan committed
246 247
void JSEventListener::clearWindowObj()
{
weinig's avatar
weinig committed
248
    m_win = 0;
sullivan's avatar
sullivan committed
249 250
}

mjs's avatar
mjs committed
251 252
// -------------------------------------------------------------------------

weinig@apple.com's avatar
weinig@apple.com committed
253
JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& code, JSDOMWindow* win, Node* node, int lineNumber)
weinig's avatar
weinig committed
254 255 256 257 258 259
    : JSEventListener(0, win, true)
    , m_functionName(functionName)
    , m_code(code)
    , m_parsed(false)
    , m_lineNumber(lineNumber)
    , m_originalNode(node)
mjs's avatar
mjs committed
260
{
darin's avatar
darin committed
261
    // We don't retain the original node because we assume it
mjs's avatar
mjs committed
262 263 264 265
    // will stay alive as long as this handler object is around
    // and we need to avoid a reference cycle. If JS transfers
    // this handler to another node, parseCode will be called and
    // then originalNode is no longer needed.
mjs's avatar
mjs committed
266 267
}

darin's avatar
darin committed
268
JSObject* JSLazyEventListener::listenerObj() const
mjs's avatar
mjs committed
269
{
darin's avatar
darin committed
270
    parseCode();
weinig's avatar
weinig committed
271
    return m_listener;
mjs's avatar
mjs committed
272 273
}

darin's avatar
darin committed
274
JSValue* JSLazyEventListener::eventParameterName() const
eseidel's avatar
eseidel committed
275 276 277 278 279
{
    static ProtectedPtr<JSValue> eventString = jsString("event");
    return eventString.get();
}

mjs's avatar
mjs committed
280 281
void JSLazyEventListener::parseCode() const
{
weinig's avatar
weinig committed
282
    if (m_parsed)
darin's avatar
darin committed
283
        return;
weinig's avatar
weinig committed
284
    m_parsed = true;
darin's avatar
darin committed
285

weinig's avatar
weinig committed
286
    Frame* frame = windowObj()->impl()->frame();
alice.liu@apple.com's avatar
alice.liu@apple.com committed
287 288
    if (frame && frame->scriptProxy()->isEnabled()) {
        ExecState* exec = frame->scriptProxy()->globalObject()->globalExec();
darin's avatar
darin committed
289 290

        JSLock lock;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
291
        JSObject* constr = frame->scriptProxy()->globalObject()->functionConstructor();
darin's avatar
darin committed
292 293
        List args;

weinig@apple.com's avatar
weinig@apple.com committed
294
        UString sourceURL(frame->loader()->url().string());
darin's avatar
darin committed
295
        args.append(eventParameterName());
weinig's avatar
weinig committed
296 297
        args.append(jsString(m_code));
        m_listener = constr->construct(exec, args, m_functionName, sourceURL, m_lineNumber); // FIXME: is globalExec ok ?
darin's avatar
darin committed
298

weinig's avatar
weinig committed
299
        FunctionImp* listenerAsFunction = static_cast<FunctionImp*>(m_listener.get());
mjs's avatar
mjs committed
300

darin's avatar
darin committed
301 302 303 304
        if (exec->hadException()) {
            exec->clearException();

            // failed to parse, so let's just make this listener a no-op
weinig's avatar
weinig committed
305 306
            m_listener = 0;
        } else if (m_originalNode) {
darin's avatar
darin committed
307
            // Add the event's home element to the scope
darin's avatar
darin committed
308
            // (and the document, and the form - see JSHTMLElement::eventHandlerScope)
mjs's avatar
mjs committed
309
            ScopeChain scope = listenerAsFunction->scope();
darin's avatar
darin committed
310

weinig's avatar
weinig committed
311
            JSValue* thisObj = toJS(exec, m_originalNode);
darin's avatar
darin committed
312
            if (thisObj->isObject()) {
weinig's avatar
weinig committed
313
                static_cast<JSEventTargetNode*>(thisObj)->pushEventHandlerScope(exec, scope);
mjs's avatar
mjs committed
314
                listenerAsFunction->setScope(scope);
darin's avatar
darin committed
315
            }
mjs's avatar
mjs committed
316
        }
mjs's avatar
mjs committed
317 318 319
    }

    // no more need to keep the unparsed code around
darin's avatar
darin committed
320
    m_functionName = String();
weinig's avatar
weinig committed
321
    m_code = String();
darin's avatar
darin committed
322

weinig's avatar
weinig committed
323
    if (m_listener) {
weinig@apple.com's avatar
weinig@apple.com committed
324
        JSDOMWindow::ListenersMap& listeners = isHTMLEventListener()
mjs's avatar
mjs committed
325
            ? windowObj()->jsHTMLEventListeners() : windowObj()->jsEventListeners();
weinig's avatar
weinig committed
326
        listeners.set(m_listener, const_cast<JSLazyEventListener*>(this));
darin's avatar
darin committed
327
    }
mjs's avatar
mjs committed
328 329
}

darin's avatar
darin committed
330
JSValue* getNodeEventListener(EventTargetNode* n, const AtomicString& eventType)
kocienda's avatar
kocienda committed
331
{
weinig's avatar
weinig committed
332
    if (JSAbstractEventListener* listener = static_cast<JSAbstractEventListener*>(n->getHTMLEventListener(eventType))) {
darin's avatar
darin committed
333 334
        if (JSValue* obj = listener->listenerObj())
            return obj;
weinig's avatar
weinig committed
335
    }
darin's avatar
darin committed
336
    return jsNull();
kocienda's avatar
kocienda committed
337 338
}

weinig's avatar
weinig committed
339
} // namespace WebCore