finally blocks should not set the exception stack trace when re-throwing the exception.
[WebKit-https.git] / Source / WebCore / bindings / js / JSEventListener.cpp
1 /*
2  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
3  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013 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 "BeforeUnloadEvent.h"
24 #include "Event.h"
25 #include "Frame.h"
26 #include "HTMLElement.h"
27 #include "JSDocument.h"
28 #include "JSEvent.h"
29 #include "JSEventTarget.h"
30 #include "JSMainThreadExecState.h"
31 #include "JSMainThreadExecStateInstrumentation.h"
32 #include "ScriptController.h"
33 #include "WorkerGlobalScope.h"
34 #include <runtime/ExceptionHelpers.h>
35 #include <runtime/JSLock.h>
36 #include <runtime/VMEntryScope.h>
37 #include <wtf/Ref.h>
38 #include <wtf/RefCountedLeakCounter.h>
39
40 using namespace JSC;
41
42 namespace WebCore {
43
44 JSEventListener::JSEventListener(JSObject* function, JSObject* wrapper, bool isAttribute, DOMWrapperWorld& isolatedWorld)
45     : EventListener(JSEventListenerType)
46     , m_wrapper(wrapper)
47     , m_isAttribute(isAttribute)
48     , m_isolatedWorld(&isolatedWorld)
49 {
50     if (wrapper) {
51         JSC::Heap::heap(wrapper)->writeBarrier(wrapper, function);
52         m_jsFunction = JSC::Weak<JSC::JSObject>(function);
53     } else
54         ASSERT(!function);
55 }
56
57 JSEventListener::~JSEventListener()
58 {
59 }
60
61 JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext*) const
62 {
63     return 0;
64 }
65
66 void JSEventListener::visitJSFunction(SlotVisitor& visitor)
67 {
68     // If m_wrapper is 0, then m_jsFunction is zombied, and should never be accessed.
69     if (!m_wrapper)
70         return;
71
72     visitor.appendUnbarrieredWeak(&m_jsFunction);
73 }
74
75 void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event)
76 {
77     ASSERT(scriptExecutionContext);
78     if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden())
79         return;
80
81     JSLockHolder lock(scriptExecutionContext->vm());
82
83     JSObject* jsFunction = this->jsFunction(scriptExecutionContext);
84     if (!jsFunction)
85         return;
86
87     JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, *m_isolatedWorld);
88     if (!globalObject)
89         return;
90
91     if (scriptExecutionContext->isDocument()) {
92         JSDOMWindow* window = jsCast<JSDOMWindow*>(globalObject);
93         if (!window->impl().isCurrentlyDisplayedInFrame())
94             return;
95         // FIXME: Is this check needed for other contexts?
96         ScriptController& script = window->impl().frame()->script();
97         if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused())
98             return;
99     }
100
101     ExecState* exec = globalObject->globalExec();
102     JSValue handleEventFunction = jsFunction;
103
104     CallData callData;
105     CallType callType = getCallData(handleEventFunction, callData);
106     // If jsFunction is not actually a function, see if it implements the EventListener interface and use that
107     if (callType == CallTypeNone) {
108         handleEventFunction = jsFunction->get(exec, Identifier::fromString(exec, "handleEvent"));
109         callType = getCallData(handleEventFunction, callData);
110     }
111
112     if (callType != CallTypeNone) {
113         Ref<JSEventListener> protect(*this);
114
115         MarkedArgumentBuffer args;
116         args.append(toJS(exec, globalObject, event));
117
118         Event* savedEvent = globalObject->currentEvent();
119         globalObject->setCurrentEvent(event);
120
121         VM& vm = globalObject->vm();
122         VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject);
123
124         InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData);
125
126         JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction;
127         Exception* exception;
128         JSValue retval = scriptExecutionContext->isDocument()
129             ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, thisValue, args, exception)
130             : JSC::call(exec, handleEventFunction, callType, callData, thisValue, args, exception);
131
132         InspectorInstrumentation::didCallFunction(cookie, scriptExecutionContext);
133
134         globalObject->setCurrentEvent(savedEvent);
135
136         if (is<WorkerGlobalScope>(*scriptExecutionContext)) {
137             bool terminatorCausedException = (exec->hadException() && isTerminatedExecutionException(exec->exception()));
138             if (terminatorCausedException || (vm.watchdog && vm.watchdog->didFire()))
139                 downcast<WorkerGlobalScope>(*scriptExecutionContext).script()->forbidExecution();
140         }
141
142         if (exception) {
143             event->target()->uncaughtExceptionInEventHandler();
144             reportException(exec, exception);
145         } else {
146             if (!retval.isUndefinedOrNull() && is<BeforeUnloadEvent>(*event))
147                 downcast<BeforeUnloadEvent>(*event).setReturnValue(retval.toString(exec)->value(exec));
148             if (m_isAttribute) {
149                 if (retval.isFalse())
150                     event->preventDefault();
151             }
152         }
153     }
154 }
155
156 bool JSEventListener::virtualisAttribute() const
157 {
158     return m_isAttribute;
159 }
160
161 bool JSEventListener::operator==(const EventListener& listener)
162 {
163     if (const JSEventListener* jsEventListener = JSEventListener::cast(&listener))
164         return m_jsFunction == jsEventListener->m_jsFunction && m_isAttribute == jsEventListener->m_isAttribute;
165     return false;
166 }
167
168 Ref<JSEventListener> createJSEventListenerForAdd(JSC::ExecState& state, JSC::JSObject& listener, JSC::JSObject& wrapper)
169 {
170     // FIXME: This abstraction is no longer needed. It was part of support for SVGElementInstance.
171     // We should remove it and simplify the bindings generation scripts.
172     return JSEventListener::create(&listener, &wrapper, false, currentWorld(&state));
173 }
174
175 static inline JSC::JSValue eventHandlerAttribute(EventListener* abstractListener, ScriptExecutionContext& context)
176 {
177     if (!abstractListener)
178         return jsNull();
179
180     auto* listener = JSEventListener::cast(abstractListener);
181     if (!listener)
182         return jsNull();
183
184     auto* function = listener->jsFunction(&context);
185     if (!function)
186         return jsNull();
187
188     return function;
189 }
190
191 static inline RefPtr<JSEventListener> createEventListenerForEventHandlerAttribute(JSC::ExecState& state, JSC::JSValue listener, JSC::JSObject& wrapper)
192 {
193     if (!listener.isObject())
194         return nullptr;
195     return JSEventListener::create(asObject(listener), &wrapper, true, currentWorld(&state));
196 }
197
198 JSC::JSValue eventHandlerAttribute(EventTarget& target, const AtomicString& eventType)
199 {
200     return eventHandlerAttribute(target.getAttributeEventListener(eventType), *target.scriptExecutionContext());
201 }
202
203 void setEventHandlerAttribute(JSC::ExecState& state, JSC::JSObject& wrapper, EventTarget& target, const AtomicString& eventType, JSC::JSValue value)
204 {
205     target.setAttributeEventListener(eventType, createEventListenerForEventHandlerAttribute(state, value, wrapper));
206 }
207
208 JSC::JSValue windowEventHandlerAttribute(HTMLElement& element, const AtomicString& eventType)
209 {
210     auto& document = element.document();
211     return eventHandlerAttribute(document.getWindowAttributeEventListener(eventType), document);
212 }
213
214 void setWindowEventHandlerAttribute(JSC::ExecState& state, JSC::JSObject& wrapper, HTMLElement& element, const AtomicString& eventType, JSC::JSValue value)
215 {
216     ASSERT(wrapper.globalObject());
217     element.document().setWindowAttributeEventListener(eventType, createEventListenerForEventHandlerAttribute(state, value, *wrapper.globalObject()));
218 }
219
220 JSC::JSValue windowEventHandlerAttribute(DOMWindow& window, const AtomicString& eventType)
221 {
222     return eventHandlerAttribute(window, eventType);
223 }
224
225 void setWindowEventHandlerAttribute(JSC::ExecState& state, JSC::JSObject& wrapper, DOMWindow& window, const AtomicString& eventType, JSC::JSValue value)
226 {
227     setEventHandlerAttribute(state, wrapper, window, eventType, value);
228 }
229
230 JSC::JSValue documentEventHandlerAttribute(HTMLElement& element, const AtomicString& eventType)
231 {
232     auto& document = element.document();
233     return eventHandlerAttribute(document.getAttributeEventListener(eventType), document);
234 }
235
236 void setDocumentEventHandlerAttribute(JSC::ExecState& state, JSC::JSObject& wrapper, HTMLElement& element, const AtomicString& eventType, JSC::JSValue value)
237 {
238     ASSERT(wrapper.globalObject());
239     auto& document = element.document();
240     auto* documentWrapper = jsDocumentCast(toJS(&state, JSC::jsCast<JSDOMGlobalObject*>(wrapper.globalObject()), document));
241     ASSERT(documentWrapper);
242     document.setAttributeEventListener(eventType, createEventListenerForEventHandlerAttribute(state, value, *documentWrapper));
243 }
244
245 JSC::JSValue documentEventHandlerAttribute(Document& document, const AtomicString& eventType)
246 {
247     return eventHandlerAttribute(document, eventType);
248 }
249
250 void setDocumentEventHandlerAttribute(JSC::ExecState& state, JSC::JSObject& wrapper, Document& document, const AtomicString& eventType, JSC::JSValue value)
251 {
252     setEventHandlerAttribute(state, wrapper, document, eventType, value);
253 }
254
255 } // namespace WebCore