1 /* 2 * Copyright (C) 2001 Peter Kelly (pmk (at) 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 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 49 JSEventListener::~JSEventListener() 50 { 51 } 52 53 JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext*) const 54 { 55 ASSERT_NOT_REACHED(); 56 return 0; 57 } 58 59 void JSEventListener::markJSFunction(MarkStack& markStack) 60 { 61 if (m_jsFunction) 62 markStack.append(&m_jsFunction); 63 } 64 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 159 bool JSEventListener::virtualisAttribute() const 160 { 161 return m_isAttribute; 162 } 163 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