📄 jseventlistener.cpp
字号:
/* * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */#include "config.h"#include "JSEventListener.h"#include "CString.h"#include "Console.h"#include "DOMWindow.h"#include "Document.h"#include "Event.h"#include "Frame.h"#include "FrameLoader.h"#include "JSDOMWindow.h"#include "JSEvent.h"#include "JSEventTarget.h"#include "JSNode.h"#include "ScriptController.h"#include <runtime/FunctionConstructor.h>#include <runtime/JSLock.h>#include <wtf/RefCountedLeakCounter.h>using namespace JSC;namespace WebCore {ASSERT_CLASS_FITS_IN_CELL(JSAbstractEventListener)void JSAbstractEventListener::handleEvent(Event* event, bool isWindowEvent){ JSLock lock(false); JSObject* listener = listenerObj(); if (!listener) return; JSDOMGlobalObject* globalObject = this->globalObject(); // Null check as clearGlobalObject() can clear this and we still get called back by // xmlhttprequest objects. See http://bugs.webkit.org/show_bug.cgi?id=13275 // FIXME: Is this check still necessary? Requests are supposed to be stopped before clearGlobalObject() is called. if (!globalObject) return; ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext(); if (!scriptExecutionContext) return; if (scriptExecutionContext->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); Frame* frame = window->impl()->frame(); if (!frame) return; // The window must still be active in its frame. See <https://bugs.webkit.org/show_bug.cgi?id=21921>. // FIXME: A better fix for this may be to change DOMWindow::frame() to not return a frame the detached window used to be in. if (frame->domWindow() != window->impl()) return; // FIXME: Is this check needed for other contexts? ScriptController* script = frame->script(); if (!script->isEnabled() || script->isPaused()) return; } ExecState* exec = globalObject->globalExec(); JSValuePtr handleEventFunction = listener->get(exec, Identifier(exec, "handleEvent")); CallData callData; CallType callType = handleEventFunction.getCallData(callData); if (callType == CallTypeNone) { handleEventFunction = noValue(); callType = listener->getCallData(callData); } if (callType != CallTypeNone) { ref(); ArgList args; args.append(toJS(exec, event)); Event* savedEvent = globalObject->currentEvent(); globalObject->setCurrentEvent(event); // If this event handler is the first JavaScript to execute, then the // dynamic global object should be set to the global object of the // window in which the event occurred. JSGlobalData* globalData = globalObject->globalData(); DynamicGlobalObjectScope globalObjectScope(exec, globalData->dynamicGlobalObject ? globalData->dynamicGlobalObject : globalObject); JSValuePtr retval; if (handleEventFunction) { globalObject->globalData()->timeoutChecker.start(); retval = call(exec, handleEventFunction, callType, callData, listener, args); } else { JSValuePtr thisValue; if (isWindowEvent) thisValue = globalObject->toThisObject(exec); else thisValue = toJS(exec, event->currentTarget()); globalObject->globalData()->timeoutChecker.start(); retval = call(exec, listener, callType, callData, thisValue, args); } globalObject->globalData()->timeoutChecker.stop(); globalObject->setCurrentEvent(savedEvent); if (exec->hadException()) reportCurrentException(exec); else { if (!retval.isUndefinedOrNull() && event->storesResultAsString()) event->storeResult(retval.toString(exec)); if (m_isInline) { bool retvalbool; if (retval.getBoolean(retvalbool) && !retvalbool) event->preventDefault(); } } if (scriptExecutionContext->isDocument()) Document::updateDocumentsRendering(); deref(); }}bool JSAbstractEventListener::isInline() const{ return m_isInline;}// -------------------------------------------------------------------------JSUnprotectedEventListener::JSUnprotectedEventListener(JSObject* listener, JSDOMGlobalObject* globalObject, bool isInline) : JSAbstractEventListener(isInline) , m_listener(listener) , m_globalObject(globalObject){ if (m_listener) { JSDOMWindow::UnprotectedListenersMap& listeners = isInline ? globalObject->jsUnprotectedInlineEventListeners() : globalObject->jsUnprotectedEventListeners(); listeners.set(m_listener, this); }}JSUnprotectedEventListener::~JSUnprotectedEventListener(){ if (m_listener && m_globalObject) { JSDOMWindow::UnprotectedListenersMap& listeners = isInline() ? m_globalObject->jsUnprotectedInlineEventListeners() : m_globalObject->jsUnprotectedEventListeners(); listeners.remove(m_listener); }}JSObject* JSUnprotectedEventListener::listenerObj() const{ return m_listener;}JSDOMGlobalObject* JSUnprotectedEventListener::globalObject() const{ return m_globalObject;}void JSUnprotectedEventListener::clearGlobalObject(){ m_globalObject = 0;}void JSUnprotectedEventListener::mark(){ if (m_listener && !m_listener->marked()) m_listener->mark();}#ifndef NDEBUGstatic WTF::RefCountedLeakCounter eventListenerCounter("EventListener");#endif// -------------------------------------------------------------------------JSEventListener::JSEventListener(JSObject* listener, JSDOMGlobalObject* globalObject, bool isInline) : JSAbstractEventListener(isInline) , m_listener(listener) , m_globalObject(globalObject){ if (m_listener) { JSDOMWindow::ListenersMap& listeners = isInline ? m_globalObject->jsInlineEventListeners() : m_globalObject->jsEventListeners(); listeners.set(m_listener, this); }#ifndef NDEBUG eventListenerCounter.increment();#endif}JSEventListener::~JSEventListener(){ if (m_listener && m_globalObject) { JSDOMWindow::ListenersMap& listeners = isInline() ? m_globalObject->jsInlineEventListeners() : m_globalObject->jsEventListeners(); listeners.remove(m_listener); }#ifndef NDEBUG eventListenerCounter.decrement();#endif}JSObject* JSEventListener::listenerObj() const{ return m_listener;}JSDOMGlobalObject* JSEventListener::globalObject() const{ return m_globalObject;}void JSEventListener::clearGlobalObject(){ m_globalObject = 0;}// -------------------------------------------------------------------------JSLazyEventListener::JSLazyEventListener(LazyEventListenerType type, const String& functionName, const String& code, JSDOMGlobalObject* globalObject, Node* node, int lineNumber) : JSEventListener(0, globalObject, true) , m_functionName(functionName) , m_code(code) , m_parsed(false) , m_lineNumber(lineNumber) , m_originalNode(node) , m_type(type){ // We don't retain the original node because we assume it // will stay alive as long as this handler object is around // and we need to avoid a reference cycle. If JS transfers // this handler to another node, parseCode will be called and // then originalNode is no longer needed. // A JSLazyEventListener can be created with a line number of zero when it is created with // a setAttribute call from JavaScript, so make the line number 1 in that case. if (m_lineNumber == 0) m_lineNumber = 1;}JSObject* JSLazyEventListener::listenerObj() const{ parseCode(); return m_listener;}// Helper functioninline JSValuePtr eventParameterName(JSLazyEventListener::LazyEventListenerType type, ExecState* exec){ switch (type) { case JSLazyEventListener::HTMLLazyEventListener: return jsNontrivialString(exec, "event");#if ENABLE(SVG) case JSLazyEventListener::SVGLazyEventListener: return jsNontrivialString(exec, "evt");#endif default: ASSERT_NOT_REACHED(); return jsUndefined(); }}void JSLazyEventListener::parseCode() const{ if (m_parsed) return; if (globalObject()->scriptExecutionContext()->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject()); Frame* frame = window->impl()->frame(); if (!frame) return; // FIXME: Is this check needed for non-Document contexts? ScriptController* script = frame->script(); if (!script->isEnabled() || script->isPaused()) return; } m_parsed = true; ExecState* exec = globalObject()->globalExec(); ArgList args; UString sourceURL(globalObject()->scriptExecutionContext()->url().string()); args.append(eventParameterName(m_type, exec)); args.append(jsString(exec, m_code)); // FIXME: Passing the document's URL to construct is not always correct, since this event listener might // have been added with setAttribute from a script, and we should pass String() in that case. m_listener = constructFunction(exec, args, Identifier(exec, m_functionName), sourceURL, m_lineNumber); // FIXME: is globalExec ok? JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_listener.get()); if (exec->hadException()) { exec->clearException(); // failed to parse, so let's just make this listener a no-op m_listener = 0; } else if (m_originalNode) { // Add the event's home element to the scope // (and the document, and the form - see JSHTMLElement::eventHandlerScope) ScopeChain scope = listenerAsFunction->scope(); JSValuePtr thisObj = toJS(exec, m_originalNode); if (thisObj.isObject()) { static_cast<JSNode*>(asObject(thisObj))->pushEventHandlerScope(exec, scope); listenerAsFunction->setScope(scope); } } // no more need to keep the unparsed code around m_functionName = String(); m_code = String(); if (m_listener) { ASSERT(isInline()); JSDOMWindow::ListenersMap& listeners = globalObject()->jsInlineEventListeners(); listeners.set(m_listener, const_cast<JSLazyEventListener*>(this)); }}} // namespace WebCore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -