REGRESSION (r125251): wrapper lifetimes of SVGElementInstance are incorrect
[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 "JSEvent.h"
27 #include "JSEventTarget.h"
28 #include "JSMainThreadExecState.h"
29 #include "JSMainThreadExecStateInstrumentation.h"
30 #include "JSSVGElementInstance.h"
31 #include "ScriptController.h"
32 #include "WorkerGlobalScope.h"
33 #include <runtime/ExceptionHelpers.h>
34 #include <runtime/JSLock.h>
35 #include <runtime/VMEntryScope.h>
36 #include <wtf/Ref.h>
37 #include <wtf/RefCountedLeakCounter.h>
38
39 using namespace JSC;
40
41 namespace WebCore {
42
43 JSEventListener::JSEventListener(JSObject* function, JSObject* wrapper, bool isAttribute, DOMWrapperWorld& isolatedWorld)
44     : EventListener(JSEventListenerType)
45     , m_wrapper(wrapper)
46     , m_isAttribute(isAttribute)
47     , m_isolatedWorld(&isolatedWorld)
48 {
49     if (wrapper) {
50         JSC::Heap::heap(wrapper)->writeBarrier(wrapper, function);
51         m_jsFunction = JSC::Weak<JSC::JSObject>(function);
52     } else
53         ASSERT(!function);
54 }
55
56 JSEventListener::~JSEventListener()
57 {
58 }
59
60 JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext*) const
61 {
62     return 0;
63 }
64
65 void JSEventListener::visitJSFunction(SlotVisitor& visitor)
66 {
67     // If m_wrapper is 0, then m_jsFunction is zombied, and should never be accessed.
68     if (!m_wrapper)
69         return;
70
71     visitor.appendUnbarrieredWeak(&m_jsFunction);
72 }
73
74 void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event)
75 {
76     ASSERT(scriptExecutionContext);
77     if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden())
78         return;
79
80     JSLockHolder lock(scriptExecutionContext->vm());
81
82     JSObject* jsFunction = this->jsFunction(scriptExecutionContext);
83     if (!jsFunction)
84         return;
85
86     JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, *m_isolatedWorld);
87     if (!globalObject)
88         return;
89
90     if (scriptExecutionContext->isDocument()) {
91         JSDOMWindow* window = jsCast<JSDOMWindow*>(globalObject);
92         if (!window->impl().isCurrentlyDisplayedInFrame())
93             return;
94         // FIXME: Is this check needed for other contexts?
95         ScriptController& script = window->impl().frame()->script();
96         if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused())
97             return;
98     }
99
100     ExecState* exec = globalObject->globalExec();
101     JSValue handleEventFunction = jsFunction;
102
103     CallData callData;
104     CallType callType = getCallData(handleEventFunction, callData);
105     // If jsFunction is not actually a function, see if it implements the EventListener interface and use that
106     if (callType == CallTypeNone) {
107         handleEventFunction = jsFunction->get(exec, Identifier(exec, "handleEvent"));
108         callType = getCallData(handleEventFunction, callData);
109     }
110
111     if (callType != CallTypeNone) {
112         Ref<JSEventListener> protect(*this);
113
114         MarkedArgumentBuffer args;
115         args.append(toJS(exec, globalObject, event));
116
117         Event* savedEvent = globalObject->currentEvent();
118         globalObject->setCurrentEvent(event);
119
120         VM& vm = globalObject->vm();
121         VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject);
122
123         InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData);
124
125         JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction;
126         JSValue exception;
127         JSValue retval = scriptExecutionContext->isDocument()
128             ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, thisValue, args, &exception)
129             : JSC::call(exec, handleEventFunction, callType, callData, thisValue, args, &exception);
130
131         InspectorInstrumentation::didCallFunction(cookie, scriptExecutionContext);
132
133         globalObject->setCurrentEvent(savedEvent);
134
135         if (is<WorkerGlobalScope>(*scriptExecutionContext)) {
136             bool terminatorCausedException = (exec->hadException() && isTerminatedExecutionException(exec->exception()));
137             if (terminatorCausedException || (vm.watchdog && vm.watchdog->didFire()))
138                 downcast<WorkerGlobalScope>(*scriptExecutionContext).script()->forbidExecution();
139         }
140
141         if (exception) {
142             event->target()->uncaughtExceptionInEventHandler();
143             reportException(exec, exception);
144         } else {
145             if (!retval.isUndefinedOrNull() && is<BeforeUnloadEvent>(*event))
146                 downcast<BeforeUnloadEvent>(*event).setReturnValue(retval.toString(exec)->value(exec));
147             if (m_isAttribute) {
148                 if (retval.isFalse())
149                     event->preventDefault();
150             }
151         }
152     }
153 }
154
155 bool JSEventListener::virtualisAttribute() const
156 {
157     return m_isAttribute;
158 }
159
160 bool JSEventListener::operator==(const EventListener& listener)
161 {
162     if (const JSEventListener* jsEventListener = JSEventListener::cast(&listener))
163         return m_jsFunction == jsEventListener->m_jsFunction && m_isAttribute == jsEventListener->m_isAttribute;
164     return false;
165 }
166
167 // SVGElementInstance forwards listeners to its corresponding element, so the listeners are
168 // protected by the wrapper of the corresponding element, not the element instance's wrapper.
169
170 bool forwardsEventListeners(JSC::JSObject& object)
171 {
172     if (object.classInfo() == JSSVGElementInstance::info())
173         return true;
174     ASSERT(!object.inherits(JSSVGElementInstance::info()));
175     return false;
176 }
177
178 static JSC::JSObject& correspondingElementWrapper(JSC::ExecState& state, JSC::JSObject& wrapper)
179 {
180     JSSVGElementInstance& castedWrapper = *jsCast<JSSVGElementInstance*>(&wrapper);
181     return *asObject(toJS(&state, castedWrapper.globalObject(), *castedWrapper.impl().correspondingElement()));
182 }
183
184 RefPtr<JSEventListener> createJSEventListenerForAttribute(JSC::ExecState& state, JSC::JSValue listener, JSSVGElementInstance& wrapper)
185 {
186     return createJSEventListenerForAttribute(state, listener, correspondingElementWrapper(state, wrapper));
187 }
188
189 Ref<JSEventListener> createJSEventListenerForAdd(JSC::ExecState& state, JSC::JSObject& listener, JSC::JSObject& wrapper)
190 {
191     JSC::JSObject& actualWrapper = forwardsEventListeners(wrapper) ? correspondingElementWrapper(state, wrapper) : wrapper;
192     ASSERT(!forwardsEventListeners(actualWrapper));
193     return JSEventListener::create(&listener, &actualWrapper, false, currentWorld(&state));
194 }
195
196 } // namespace WebCore