2 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
3 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All Rights Reserved.
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.
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.
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
21 #include "JSEventListener.h"
26 #include "JSEventTarget.h"
27 #include <runtime/JSLock.h>
28 #include <wtf/RefCountedLeakCounter.h>
34 void JSAbstractEventListener::handleEvent(Event* event, bool isWindowEvent)
38 JSObject* listener = listenerObj();
42 JSDOMGlobalObject* globalObject = this->globalObject();
43 // Null check as clearGlobalObject() can clear this and we still get called back by
44 // xmlhttprequest objects. See http://bugs.webkit.org/show_bug.cgi?id=13275
45 // FIXME: Is this check still necessary? Requests are supposed to be stopped before clearGlobalObject() is called.
49 ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
50 if (!scriptExecutionContext)
53 if (scriptExecutionContext->isDocument()) {
54 JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject);
55 Frame* frame = window->impl()->frame();
58 // The window must still be active in its frame. See <https://bugs.webkit.org/show_bug.cgi?id=21921>.
59 // FIXME: A better fix for this may be to change DOMWindow::frame() to not return a frame the detached window used to be in.
60 if (frame->domWindow() != window->impl())
62 // FIXME: Is this check needed for other contexts?
63 ScriptController* script = frame->script();
64 if (!script->isEnabled() || script->isPaused())
68 ExecState* exec = globalObject->globalExec();
70 JSValuePtr handleEventFunction = listener->get(exec, Identifier(exec, "handleEvent"));
72 CallType callType = handleEventFunction.getCallData(callData);
73 if (callType == CallTypeNone) {
74 handleEventFunction = noValue();
75 callType = listener->getCallData(callData);
78 if (callType != CallTypeNone) {
82 args.append(toJS(exec, event));
84 Event* savedEvent = globalObject->currentEvent();
85 globalObject->setCurrentEvent(event);
87 // If this event handler is the first JavaScript to execute, then the
88 // dynamic global object should be set to the global object of the
89 // window in which the event occurred.
90 JSGlobalData* globalData = globalObject->globalData();
91 DynamicGlobalObjectScope globalObjectScope(exec, globalData->dynamicGlobalObject ? globalData->dynamicGlobalObject : globalObject);
94 if (handleEventFunction) {
95 globalObject->globalData()->timeoutChecker.start();
96 retval = call(exec, handleEventFunction, callType, callData, listener, args);
100 thisValue = globalObject->toThisObject(exec);
102 thisValue = toJS(exec, event->currentTarget());
103 globalObject->globalData()->timeoutChecker.start();
104 retval = call(exec, listener, callType, callData, thisValue, args);
106 globalObject->globalData()->timeoutChecker.stop();
108 globalObject->setCurrentEvent(savedEvent);
110 if (exec->hadException())
111 reportCurrentException(exec);
113 if (!retval.isUndefinedOrNull() && event->storesResultAsString())
114 event->storeResult(retval.toString(exec));
117 if (retval.getBoolean(retvalbool) && !retvalbool)
118 event->preventDefault();
122 if (scriptExecutionContext->isDocument())
123 Document::updateDocumentsRendering();
128 bool JSAbstractEventListener::isInline() const
133 // -------------------------------------------------------------------------
135 JSEventListener::JSEventListener(JSObject* listener, JSDOMGlobalObject* globalObject, bool isInline)
136 : JSAbstractEventListener(isInline)
137 , m_listener(listener)
138 , m_globalObject(globalObject)
141 JSDOMWindow::JSListenersMap& listeners = isInline
142 ? globalObject->jsInlineEventListeners() : globalObject->jsEventListeners();
143 listeners.set(m_listener, this);
147 JSEventListener::~JSEventListener()
149 if (m_listener && m_globalObject) {
150 JSDOMWindow::JSListenersMap& listeners = isInline()
151 ? m_globalObject->jsInlineEventListeners() : m_globalObject->jsEventListeners();
152 listeners.remove(m_listener);
156 JSObject* JSEventListener::listenerObj() const
161 JSDOMGlobalObject* JSEventListener::globalObject() const
163 return m_globalObject;
166 void JSEventListener::clearGlobalObject()
171 void JSEventListener::mark()
173 if (m_listener && !m_listener->marked())
178 static WTF::RefCountedLeakCounter eventListenerCounter("EventListener");
181 // -------------------------------------------------------------------------
183 JSProtectedEventListener::JSProtectedEventListener(JSObject* listener, JSDOMGlobalObject* globalObject, bool isInline)
184 : JSAbstractEventListener(isInline)
185 , m_listener(listener)
186 , m_globalObject(globalObject)
189 JSDOMWindow::ProtectedListenersMap& listeners = isInline
190 ? m_globalObject->jsProtectedInlineEventListeners() : m_globalObject->jsProtectedEventListeners();
191 listeners.set(m_listener, this);
194 eventListenerCounter.increment();
198 JSProtectedEventListener::~JSProtectedEventListener()
200 if (m_listener && m_globalObject) {
201 JSDOMWindow::ProtectedListenersMap& listeners = isInline()
202 ? m_globalObject->jsProtectedInlineEventListeners() : m_globalObject->jsProtectedEventListeners();
203 listeners.remove(m_listener);
206 eventListenerCounter.decrement();
210 JSObject* JSProtectedEventListener::listenerObj() const
215 JSDOMGlobalObject* JSProtectedEventListener::globalObject() const
217 return m_globalObject;
220 void JSProtectedEventListener::clearGlobalObject()
225 } // namespace WebCore