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 "ScriptController.h" 31#include "WorkerGlobalScope.h" 32#include <runtime/ExceptionHelpers.h> 33#include <runtime/JSLock.h> 34#include <runtime/VMEntryScope.h> 35#include <wtf/Ref.h> 36#include <wtf/RefCountedLeakCounter.h> 37 38using namespace JSC; 39 40namespace WebCore { 41 42JSEventListener::JSEventListener(JSObject* function, JSObject* wrapper, bool isAttribute, DOMWrapperWorld& isolatedWorld) 43 : EventListener(JSEventListenerType) 44 , m_wrapper(wrapper) 45 , m_isAttribute(isAttribute) 46 , m_isolatedWorld(&isolatedWorld) 47{ 48 if (wrapper) { 49 JSC::Heap::heap(wrapper)->writeBarrier(wrapper, function); 50 m_jsFunction = JSC::Weak<JSC::JSObject>(function); 51 } else 52 ASSERT(!function); 53} 54 55JSEventListener::~JSEventListener() 56{ 57} 58 59JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext*) const 60{ 61 return 0; 62} 63 64void JSEventListener::visitJSFunction(SlotVisitor& visitor) 65{ 66 // If m_wrapper is 0, then m_jsFunction is zombied, and should never be accessed. 67 if (!m_wrapper) 68 return; 69 70 visitor.appendUnbarrieredWeak(&m_jsFunction); 71} 72 73void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event) 74{ 75 ASSERT(scriptExecutionContext); 76 if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden()) 77 return; 78 79 JSLockHolder lock(scriptExecutionContext->vm()); 80 81 JSObject* jsFunction = this->jsFunction(scriptExecutionContext); 82 if (!jsFunction) 83 return; 84 85 JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, *m_isolatedWorld); 86 if (!globalObject) 87 return; 88 89 if (scriptExecutionContext->isDocument()) { 90 JSDOMWindow* window = jsCast<JSDOMWindow*>(globalObject); 91 if (!window->impl().isCurrentlyDisplayedInFrame()) 92 return; 93 // FIXME: Is this check needed for other contexts? 94 ScriptController& script = window->impl().frame()->script(); 95 if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused()) 96 return; 97 } 98 99 ExecState* exec = globalObject->globalExec(); 100 JSValue handleEventFunction = jsFunction; 101 102 CallData callData; 103 CallType callType = getCallData(handleEventFunction, callData); 104 // If jsFunction is not actually a function, see if it implements the EventListener interface and use that 105 if (callType == CallTypeNone) { 106 handleEventFunction = jsFunction->get(exec, Identifier(exec, "handleEvent")); 107 callType = getCallData(handleEventFunction, callData); 108 } 109 110 if (callType != CallTypeNone) { 111 Ref<JSEventListener> protect(*this); 112 113 MarkedArgumentBuffer args; 114 args.append(toJS(exec, globalObject, event)); 115 116 Event* savedEvent = globalObject->currentEvent(); 117 globalObject->setCurrentEvent(event); 118 119 VM& vm = globalObject->vm(); 120 VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject); 121 122 InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData); 123 124 JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction; 125 JSValue exception; 126 JSValue retval = scriptExecutionContext->isDocument() 127 ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, thisValue, args, &exception) 128 : JSC::call(exec, handleEventFunction, callType, callData, thisValue, args, &exception); 129 130 InspectorInstrumentation::didCallFunction(cookie, scriptExecutionContext); 131 132 globalObject->setCurrentEvent(savedEvent); 133 134 if (scriptExecutionContext->isWorkerGlobalScope()) { 135 bool terminatorCausedException = (exec->hadException() && isTerminatedExecutionException(exec->exception())); 136 if (terminatorCausedException || (vm.watchdog && vm.watchdog->didFire())) 137 toWorkerGlobalScope(scriptExecutionContext)->script()->forbidExecution(); 138 } 139 140 if (exception) { 141 event->target()->uncaughtExceptionInEventHandler(); 142 reportException(exec, exception); 143 } else { 144 if (!retval.isUndefinedOrNull() && event->isBeforeUnloadEvent()) 145 toBeforeUnloadEvent(event)->setReturnValue(retval.toString(exec)->value(exec)); 146 if (m_isAttribute) { 147 if (retval.isFalse()) 148 event->preventDefault(); 149 } 150 } 151 } 152} 153 154bool JSEventListener::virtualisAttribute() const 155{ 156 return m_isAttribute; 157} 158 159bool JSEventListener::operator==(const EventListener& listener) 160{ 161 if (const JSEventListener* jsEventListener = JSEventListener::cast(&listener)) 162 return m_jsFunction == jsEventListener->m_jsFunction && m_isAttribute == jsEventListener->m_isAttribute; 163 return false; 164} 165 166} // namespace WebCore 167