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"
24
#include "Console.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"
darin@apple.com's avatar
darin@apple.com committed
34
#include "ScriptController.h"
darin@apple.com's avatar
darin@apple.com committed
35
#include <kjs/FunctionPrototype.h>
kocienda's avatar
kocienda committed
36

weinig@apple.com's avatar
weinig@apple.com committed
37 38
using namespace KJS;

weinig's avatar
weinig committed
39 40
namespace WebCore {

darin's avatar
darin committed
41
using namespace EventNames;
darin's avatar
darin committed
42

darin@apple.com's avatar
darin@apple.com committed
43
void JSAbstractEventListener::handleEvent(Event* event, bool isWindowEvent)
kocienda's avatar
kocienda committed
44
{
darin's avatar
darin committed
45 46 47
    JSObject* listener = listenerObj();
    if (!listener)
        return;
mjs's avatar
mjs committed
48

49 50
    JSDOMWindow* window = this->window();
    // Null check as clearWindow() can clear this and we still get called back by
andrew's avatar
andrew committed
51
    // xmlhttprequest objects. See http://bugs.webkit.org/show_bug.cgi?id=13275
52
    if (!window)
andrew's avatar
andrew committed
53
        return;
54
    Frame* frame = window->impl()->frame();
mjs's avatar
mjs committed
55
    if (!frame)
darin's avatar
darin committed
56
        return;
darin@apple.com's avatar
darin@apple.com committed
57 58
    ScriptController* script = frame->script();
    if (!script->isEnabled() || script->isPaused())
darin's avatar
darin committed
59
        return;
darin's avatar
darin committed
60

darin's avatar
darin committed
61
    JSLock lock;
weinig's avatar
weinig committed
62

63
    ExecState* exec = window->globalExec();
weinig's avatar
weinig committed
64

darin's avatar
darin committed
65 66
    JSValue* handleEventFuncValue = listener->get(exec, "handleEvent");
    JSObject* handleEventFunc = 0;
weinig's avatar
weinig committed
67
    if (handleEventFuncValue->isObject()) {
darin's avatar
darin committed
68 69 70 71
        handleEventFunc = static_cast<JSObject*>(handleEventFuncValue);
        if (!handleEventFunc->implementsCall())
            handleEventFunc = 0;
    }
weinig's avatar
weinig committed
72

darin's avatar
darin committed
73 74
    if (handleEventFunc || listener->implementsCall()) {
        ref();
weinig's avatar
weinig committed
75

darin@apple.com's avatar
darin@apple.com committed
76
        ArgList args;
darin's avatar
darin committed
77
        args.append(toJS(exec, event));
weinig's avatar
weinig committed
78

79 80
        Event* savedEvent = window->currentEvent();
        window->setCurrentEvent(event);
weinig's avatar
weinig committed
81

darin's avatar
darin committed
82
        JSValue* retval;
andersca's avatar
andersca committed
83
        if (handleEventFunc) {
84
            window->startTimeoutCheck();
ggaren@apple.com's avatar
ggaren@apple.com committed
85
            retval = handleEventFunc->callAsFunction(exec, listener, args);
andersca's avatar
andersca committed
86
        } else {
darin's avatar
darin committed
87 88
            JSObject* thisObj;
            if (isWindowEvent)
89
                thisObj = window->shell();
darin's avatar
darin committed
90
            else
darin's avatar
darin committed
91
                thisObj = static_cast<JSObject*>(toJS(exec, event->currentTarget()));
92
            window->startTimeoutCheck();
ggaren@apple.com's avatar
ggaren@apple.com committed
93
            retval = listener->callAsFunction(exec, thisObj, args);
darin's avatar
darin committed
94
        }
95
        window->stopTimeoutCheck();
darin's avatar
darin committed
96

97
        window->setCurrentEvent(savedEvent);
darin's avatar
darin committed
98 99 100

        if (exec->hadException()) {
            JSObject* exception = exec->exception()->toObject(exec);
mjs's avatar
mjs committed
101
            String message = exception->get(exec, exec->propertyNames().message)->toString(exec);
darin's avatar
darin committed
102
            int lineNumber = exception->get(exec, "line")->toInt32(exec);
darin's avatar
darin committed
103
            String sourceURL = exception->get(exec, "sourceURL")->toString(exec);
104
            frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, lineNumber, sourceURL);
darin's avatar
darin committed
105 106 107
            exec->clearException();
        } else {
            if (!retval->isUndefinedOrNull() && event->storesResultAsString())
darin's avatar
darin committed
108
                event->storeResult(retval->toString(exec));
darin@apple.com's avatar
darin@apple.com committed
109
            if (m_isHTML) {
darin's avatar
darin committed
110 111
                bool retvalbool;
                if (retval->getBoolean(retvalbool) && !retvalbool)
darin's avatar
darin committed
112
                    event->preventDefault();
113
            }
darin's avatar
darin committed
114 115
        }

darin's avatar
darin committed
116
        Document::updateDocumentsRendering();
darin's avatar
darin committed
117 118
        deref();
    }
kocienda's avatar
kocienda committed
119 120
}

darin's avatar
darin committed
121
bool JSAbstractEventListener::isHTMLEventListener() const
kocienda's avatar
kocienda committed
122
{
darin@apple.com's avatar
darin@apple.com committed
123
    return m_isHTML;
kocienda's avatar
kocienda committed
124 125
}

mjs's avatar
mjs committed
126 127
// -------------------------------------------------------------------------

darin@apple.com's avatar
darin@apple.com committed
128 129
JSUnprotectedEventListener::JSUnprotectedEventListener(JSObject* listener, JSDOMWindow* window, bool isHTML)
    : JSAbstractEventListener(isHTML)
weinig's avatar
weinig committed
130
    , m_listener(listener)
131
    , m_window(window)
mjs's avatar
mjs committed
132
{
weinig's avatar
weinig committed
133
    if (m_listener) {
darin@apple.com's avatar
darin@apple.com committed
134
        JSDOMWindow::UnprotectedListenersMap& listeners = isHTML
135
            ? window->jsUnprotectedHTMLEventListeners() : window->jsUnprotectedEventListeners();
weinig's avatar
weinig committed
136
        listeners.set(m_listener, this);
darin's avatar
darin committed
137
    }
mjs's avatar
mjs committed
138 139 140 141
}

JSUnprotectedEventListener::~JSUnprotectedEventListener()
{
142
    if (m_listener && m_window) {
weinig@apple.com's avatar
weinig@apple.com committed
143
        JSDOMWindow::UnprotectedListenersMap& listeners = isHTMLEventListener()
144
            ? m_window->jsUnprotectedHTMLEventListeners() : m_window->jsUnprotectedEventListeners();
weinig's avatar
weinig committed
145
        listeners.remove(m_listener);
darin's avatar
darin committed
146
    }
mjs's avatar
mjs committed
147 148
}

darin's avatar
darin committed
149
JSObject* JSUnprotectedEventListener::listenerObj() const
weinig's avatar
weinig committed
150 151
{
    return m_listener;
mjs's avatar
mjs committed
152 153
}

154
JSDOMWindow* JSUnprotectedEventListener::window() const
mjs's avatar
mjs committed
155
{
156
    return m_window;
mjs's avatar
mjs committed
157 158
}

159
void JSUnprotectedEventListener::clearWindow()
sullivan's avatar
sullivan committed
160
{
161
    m_window = 0;
sullivan's avatar
sullivan committed
162 163
}

164
void JSUnprotectedEventListener::mark()
mjs's avatar
mjs committed
165
{
166 167
    if (m_listener && !m_listener->marked())
        m_listener->mark();
mjs's avatar
mjs committed
168 169
}

weinig's avatar
weinig committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
#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
188 189
// -------------------------------------------------------------------------

darin@apple.com's avatar
darin@apple.com committed
190 191
JSEventListener::JSEventListener(JSObject* listener, JSDOMWindow* window, bool isHTML)
    : JSAbstractEventListener(isHTML)
weinig's avatar
weinig committed
192
    , m_listener(listener)
193
    , m_window(window)
mjs's avatar
mjs committed
194
{
weinig's avatar
weinig committed
195
    if (m_listener) {
darin@apple.com's avatar
darin@apple.com committed
196
        JSDOMWindow::ListenersMap& listeners = isHTML
197
            ? m_window->jsHTMLEventListeners() : m_window->jsEventListeners();
weinig's avatar
weinig committed
198
        listeners.set(m_listener, this);
darin's avatar
darin committed
199
    }
weinig's avatar
weinig committed
200 201 202
#ifndef NDEBUG
    ++eventListenerCounter.count;
#endif
mjs's avatar
mjs committed
203 204 205 206
}

JSEventListener::~JSEventListener()
{
207
    if (m_listener && m_window) {
weinig@apple.com's avatar
weinig@apple.com committed
208
        JSDOMWindow::ListenersMap& listeners = isHTMLEventListener()
209
            ? m_window->jsHTMLEventListeners() : m_window->jsEventListeners();
weinig's avatar
weinig committed
210
        listeners.remove(m_listener);
darin's avatar
darin committed
211
    }
weinig's avatar
weinig committed
212 213 214
#ifndef NDEBUG
    --eventListenerCounter.count;
#endif
mjs's avatar
mjs committed
215
}
mjs's avatar
mjs committed
216

darin's avatar
darin committed
217
JSObject* JSEventListener::listenerObj() const
weinig's avatar
weinig committed
218 219
{
    return m_listener;
mjs's avatar
mjs committed
220 221
}

222
JSDOMWindow* JSEventListener::window() const
mjs's avatar
mjs committed
223
{
224
    return m_window;
mjs's avatar
mjs committed
225 226
}

227
void JSEventListener::clearWindow()
sullivan's avatar
sullivan committed
228
{
229
    m_window = 0;
sullivan's avatar
sullivan committed
230 231
}

mjs's avatar
mjs committed
232 233
// -------------------------------------------------------------------------

234 235
JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& code, JSDOMWindow* window, Node* node, int lineNumber)
    : JSEventListener(0, window, true)
weinig's avatar
weinig committed
236 237 238 239 240
    , m_functionName(functionName)
    , m_code(code)
    , m_parsed(false)
    , m_lineNumber(lineNumber)
    , m_originalNode(node)
mjs's avatar
mjs committed
241
{
darin's avatar
darin committed
242
    // We don't retain the original node because we assume it
mjs's avatar
mjs committed
243 244 245 246
    // 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.
247 248 249 250 251

    // A JSLazyEventListener can be created with a line number of zero when it is created with
    // a setAttribute call from JavaScript, so make the line number 1 in that case.
    if (m_lineNumber == 0)
        m_lineNumber = 1;
mjs's avatar
mjs committed
252 253
}

darin's avatar
darin committed
254
JSObject* JSLazyEventListener::listenerObj() const
mjs's avatar
mjs committed
255
{
darin's avatar
darin committed
256
    parseCode();
weinig's avatar
weinig committed
257
    return m_listener;
mjs's avatar
mjs committed
258 259
}

darin's avatar
darin committed
260
JSValue* JSLazyEventListener::eventParameterName() const
eseidel's avatar
eseidel committed
261
{
262
    static ProtectedPtr<JSValue> eventString = jsString("event");
eseidel's avatar
eseidel committed
263 264 265
    return eventString.get();
}

mjs's avatar
mjs committed
266 267
void JSLazyEventListener::parseCode() const
{
weinig's avatar
weinig committed
268
    if (m_parsed)
darin's avatar
darin committed
269
        return;
weinig's avatar
weinig committed
270
    m_parsed = true;
darin's avatar
darin committed
271

272
    Frame* frame = window()->impl()->frame();
darin@apple.com's avatar
darin@apple.com committed
273
    if (frame && frame->script()->isEnabled()) {
274
        ExecState* exec = window()->globalExec();
darin's avatar
darin committed
275 276

        JSLock lock;
277
        JSObject* constr = window()->functionConstructor();
darin@apple.com's avatar
darin@apple.com committed
278
        ArgList args;
darin's avatar
darin committed
279

weinig@apple.com's avatar
weinig@apple.com committed
280
        UString sourceURL(frame->loader()->url().string());
darin's avatar
darin committed
281
        args.append(eventParameterName());
282
        args.append(jsString(m_code));
283 284 285

        // FIXME: Passing the document's URL to construct is not always correct, since this event listener might
        // have been added with setAttribute from a script, and we should pass String() in that case.
weinig's avatar
weinig committed
286
        m_listener = constr->construct(exec, args, m_functionName, sourceURL, m_lineNumber); // FIXME: is globalExec ok ?
darin's avatar
darin committed
287

darin@apple.com's avatar
darin@apple.com committed
288
        JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_listener.get());
mjs's avatar
mjs committed
289

darin's avatar
darin committed
290 291 292 293
        if (exec->hadException()) {
            exec->clearException();

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

weinig's avatar
weinig committed
300
            JSValue* thisObj = toJS(exec, m_originalNode);
darin's avatar
darin committed
301
            if (thisObj->isObject()) {
weinig's avatar
weinig committed
302
                static_cast<JSEventTargetNode*>(thisObj)->pushEventHandlerScope(exec, scope);
mjs's avatar
mjs committed
303
                listenerAsFunction->setScope(scope);
darin's avatar
darin committed
304
            }
mjs's avatar
mjs committed
305
        }
mjs's avatar
mjs committed
306 307 308
    }

    // no more need to keep the unparsed code around
darin's avatar
darin committed
309
    m_functionName = String();
weinig's avatar
weinig committed
310
    m_code = String();
darin's avatar
darin committed
311

weinig's avatar
weinig committed
312
    if (m_listener) {
weinig@apple.com's avatar
weinig@apple.com committed
313
        JSDOMWindow::ListenersMap& listeners = isHTMLEventListener()
314
            ? window()->jsHTMLEventListeners() : window()->jsEventListeners();
weinig's avatar
weinig committed
315
        listeners.set(m_listener, const_cast<JSLazyEventListener*>(this));
darin's avatar
darin committed
316
    }
mjs's avatar
mjs committed
317 318
}

darin's avatar
darin committed
319
JSValue* getNodeEventListener(EventTargetNode* n, const AtomicString& eventType)
kocienda's avatar
kocienda committed
320
{
weinig's avatar
weinig committed
321
    if (JSAbstractEventListener* listener = static_cast<JSAbstractEventListener*>(n->getHTMLEventListener(eventType))) {
darin's avatar
darin committed
322 323
        if (JSValue* obj = listener->listenerObj())
            return obj;
weinig's avatar
weinig committed
324
    }
darin's avatar
darin committed
325
    return jsNull();
kocienda's avatar
kocienda committed
326 327
}

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