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 "JSEventListener.h"
22
23 #include "Event.h"
24 #include "Frame.h"
25 #include "JSEvent.h"
26 #include "JSEventTarget.h"
27 #include "JSMainThreadExecState.h"
28 #include "WorkerContext.h"
29 #include <runtime/JSLock.h>
30 #include <wtf/RefCountedLeakCounter.h>
31
32 using namespace JSC;
33
34 namespace WebCore {
35
JSEventListener(JSObject * function,JSObject * wrapper,bool isAttribute,DOMWrapperWorld * isolatedWorld)36 JSEventListener::JSEventListener(JSObject* function, JSObject* wrapper, bool isAttribute, DOMWrapperWorld* isolatedWorld)
37 : EventListener(JSEventListenerType)
38 , m_wrapper(*isolatedWorld->globalData(), wrapper)
39 , m_isAttribute(isAttribute)
40 , m_isolatedWorld(isolatedWorld)
41 {
42 if (wrapper)
43 m_jsFunction.set(*m_isolatedWorld->globalData(), wrapper, function);
44 else
45 ASSERT(!function);
46
47 }
48
~JSEventListener()49 JSEventListener::~JSEventListener()
50 {
51 }
52
initializeJSFunction(ScriptExecutionContext *) const53 JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext*) const
54 {
55 ASSERT_NOT_REACHED();
56 return 0;
57 }
58
markJSFunction(MarkStack & markStack)59 void JSEventListener::markJSFunction(MarkStack& markStack)
60 {
61 if (m_jsFunction)
62 markStack.append(&m_jsFunction);
63 }
64
handleEvent(ScriptExecutionContext * scriptExecutionContext,Event * event)65 void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event)
66 {
67 ASSERT(scriptExecutionContext);
68 if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden())
69 return;
70
71 JSLock lock(SilenceAssertionsOnly);
72
73 JSObject* jsFunction = this->jsFunction(scriptExecutionContext);
74 if (!jsFunction)
75 return;
76
77 JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, m_isolatedWorld.get());
78 if (!globalObject)
79 return;
80
81 if (scriptExecutionContext->isDocument()) {
82 JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject);
83 Frame* frame = window->impl()->frame();
84 if (!frame)
85 return;
86 // The window must still be active in its frame. See <https://bugs.webkit.org/show_bug.cgi?id=21921>.
87 // FIXME: A better fix for this may be to change DOMWindow::frame() to not return a frame the detached window used to be in.
88 if (frame->domWindow() != window->impl())
89 return;
90 // FIXME: Is this check needed for other contexts?
91 ScriptController* script = frame->script();
92 if (!script->canExecuteScripts(AboutToExecuteScript) || script->isPaused())
93 return;
94 }
95
96 ExecState* exec = globalObject->globalExec();
97 JSValue handleEventFunction = jsFunction->get(exec, Identifier(exec, "handleEvent"));
98
99 CallData callData;
100 CallType callType = getCallData(handleEventFunction, callData);
101 if (callType == CallTypeNone) {
102 handleEventFunction = JSValue();
103 callType = jsFunction->getCallData(callData);
104 }
105
106 if (callType != CallTypeNone) {
107 ref();
108
109 MarkedArgumentBuffer args;
110 args.append(toJS(exec, globalObject, event));
111
112 Event* savedEvent = globalObject->currentEvent();
113 globalObject->setCurrentEvent(event);
114
115 JSGlobalData& globalData = globalObject->globalData();
116 DynamicGlobalObjectScope globalObjectScope(globalData, globalData.dynamicGlobalObject ? globalData.dynamicGlobalObject : globalObject);
117
118 globalData.timeoutChecker.start();
119 JSValue retval;
120 if (handleEventFunction) {
121 retval = scriptExecutionContext->isDocument()
122 ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, jsFunction, args)
123 : JSC::call(exec, handleEventFunction, callType, callData, jsFunction, args);
124 } else {
125 JSValue currentTarget = toJS(exec, globalObject, event->currentTarget());
126 retval = scriptExecutionContext->isDocument()
127 ? JSMainThreadExecState::call(exec, jsFunction, callType, callData, currentTarget, args)
128 : JSC::call(exec, jsFunction, callType, callData, currentTarget, args);
129 }
130 globalData.timeoutChecker.stop();
131
132 globalObject->setCurrentEvent(savedEvent);
133
134 #if ENABLE(WORKERS)
135 if (scriptExecutionContext->isWorkerContext()) {
136 bool terminatorCausedException = (exec->hadException() && exec->exception().isObject() && asObject(exec->exception())->exceptionType() == Terminated);
137 if (terminatorCausedException || globalData.terminator.shouldTerminate())
138 static_cast<WorkerContext*>(scriptExecutionContext)->script()->forbidExecution();
139 }
140 #endif
141
142 if (exec->hadException()) {
143 event->target()->uncaughtExceptionInEventHandler();
144 reportCurrentException(exec);
145 } else {
146 if (!retval.isUndefinedOrNull() && event->storesResultAsString())
147 event->storeResult(ustringToString(retval.toString(exec)));
148 if (m_isAttribute) {
149 bool retvalbool;
150 if (retval.getBoolean(retvalbool) && !retvalbool)
151 event->preventDefault();
152 }
153 }
154
155 deref();
156 }
157 }
158
virtualisAttribute() const159 bool JSEventListener::virtualisAttribute() const
160 {
161 return m_isAttribute;
162 }
163
operator ==(const EventListener & listener)164 bool JSEventListener::operator==(const EventListener& listener)
165 {
166 if (const JSEventListener* jsEventListener = JSEventListener::cast(&listener))
167 return m_jsFunction == jsEventListener->m_jsFunction && m_isAttribute == jsEventListener->m_isAttribute;
168 return false;
169 }
170
171 } // namespace WebCore
172