Commit ed84b35a authored by ap@apple.com's avatar ap@apple.com

Reviewed by Darin Adler and Sam Weinig.

        Onclick not fired for an element copied with cloneContents() or cloneNode()
        https://bugs.webkit.org/show_bug.cgi?id=25130

        The change here is that JS event listeners don't keep a reference to a global object from
        where they were created, and instead take it as a parameter when parsing source code. Also,
        the listener creation won't fail just because it happens for an element in a frameless
        document.
        Thus, moving nodes between documents no longer results in having incorrect registered
        lazy event listeners on them.

        Tests: fast/events/attribute-listener-cloned-from-frameless-doc-context-2.html
               fast/events/attribute-listener-cloned-from-frameless-doc-context.html
               fast/events/attribute-listener-cloned-from-frameless-doc.xhtml
               fast/events/attribute-listener-extracted-from-frameless-doc-context-2.html
               fast/events/attribute-listener-extracted-from-frameless-doc-context.html

        * bindings/js/JSEventListener.cpp:
        (WebCore::JSEventListener::JSEventListener): Don't take a reference to JSDOMGlobalObject.
        (WebCore::JSEventListener::jsFunction): Take ScriptExecutionContext as a parameter for
        getting to JSDOMGlobalObject. It's not used in base class, but is in JSLazyEventListner.
        (WebCore::JSEventListener::markJSFunction): Don't mark the global object.
        (WebCore::JSEventListener::handleEvent): Get global object from ScriptExecutionContext.
        (WebCore::JSEventListener::reportError): Ditto.

        * bindings/js/JSEventListener.h: (WebCore::JSEventListener::create): Don't keep a reference
        to JSDOMGlobalObject.

        * bindings/js/JSLazyEventListener.cpp: (WebCore::JSLazyEventListener::parseCode): Listener
        creation was split between this function and ScriptEventListener; moved it here, as JS
        global object can be different now.

        * bindings/js/JSLazyEventListener.h: (WebCore::JSLazyEventListener::create): Keep source URL,
        which can not be determined at parsing time.

        * bindings/js/ScriptEventListener.cpp: (WebCore::createAttributeEventListener): Moved code
        for listener creation to JSLazyEventListener. XSSAuditor code remains here, because tests
        expect that errors are logged at document parsing time, and because I don't know what other
        side effects moving it vould have.

        * dom/EventListener.h: handleEvent() and reportError() now take ScriptExecutionContext,
        because JSC needs a global context here.

        * bindings/js/JSAbstractWorkerCustom.cpp:
        (WebCore::JSAbstractWorker::addEventListener):
        (WebCore::JSAbstractWorker::removeEventListener):
        * bindings/js/JSDOMApplicationCacheCustom.cpp:
        (WebCore::JSDOMApplicationCache::addEventListener):
        (WebCore::JSDOMApplicationCache::removeEventListener):
        * bindings/js/JSDOMGlobalObject.cpp:
        (WebCore::JSDOMGlobalObject::createJSAttributeEventListener):
        * bindings/js/JSDOMWindowCustom.cpp:
        (WebCore::JSDOMWindow::addEventListener):
        (WebCore::JSDOMWindow::removeEventListener):
        * bindings/js/JSEventSourceCustom.cpp:
        (WebCore::JSEventSource::addEventListener):
        (WebCore::JSEventSource::removeEventListener):
        * bindings/js/JSMessagePortCustom.cpp:
        (WebCore::JSMessagePort::addEventListener):
        (WebCore::JSMessagePort::removeEventListener):
        * bindings/js/JSNodeCustom.cpp:
        (WebCore::JSNode::addEventListener):
        (WebCore::JSNode::removeEventListener):
        * bindings/js/JSSVGElementInstanceCustom.cpp:
        (WebCore::JSSVGElementInstance::addEventListener):
        (WebCore::JSSVGElementInstance::removeEventListener):
        * bindings/js/JSWorkerContextCustom.cpp:
        (WebCore::JSWorkerContext::addEventListener):
        (WebCore::JSWorkerContext::removeEventListener):
        * bindings/js/JSXMLHttpRequestCustom.cpp:
        (WebCore::JSXMLHttpRequest::addEventListener):
        (WebCore::JSXMLHttpRequest::removeEventListener):
        * bindings/js/JSXMLHttpRequestUploadCustom.cpp:
        (WebCore::JSXMLHttpRequestUpload::addEventListener):
        (WebCore::JSXMLHttpRequestUpload::removeEventListener):
        * bindings/objc/ObjCEventListener.h:
        * bindings/objc/ObjCEventListener.mm:
        (WebCore::ObjCEventListener::handleEvent):
        * bindings/scripts/CodeGeneratorJS.pm:
        * dom/EventTarget.cpp:
        (WebCore::EventTarget::fireEventListeners):
        * inspector/InspectorDOMAgent.cpp:
        (WebCore::InspectorDOMAgent::handleEvent):
        * inspector/InspectorDOMAgent.h:
        * inspector/InspectorDOMStorageResource.cpp:
        (WebCore::InspectorDOMStorageResource::handleEvent):
        * inspector/InspectorDOMStorageResource.h:
        * loader/ImageDocument.cpp:
        (WebCore::ImageEventListener::handleEvent):
        * svg/animation/SVGSMILElement.cpp:
        (WebCore::ConditionEventListener::handleEvent):
        * workers/WorkerContext.cpp:
        (WebCore::WorkerContext::reportException):
        Don't pass global object to JSEventListener::create(), which no longer needs it.
        Note that some of these functions still have an early return for null global object, which
        can probably be removed in a later patch.
        Pass ScriptExecutionContext to EventListener methods that now need it.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@48767 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent c5034a35
2009-09-24 Alexey Proskuryakov <ap@apple.com>
Reviewed by Darin Adler and Sam Weinig.
Onclick not fired for an element copied with cloneContents() or cloneNode()
https://bugs.webkit.org/show_bug.cgi?id=25130
* fast/events/attribute-listener-cloned-from-frameless-doc-context-2-expected.txt: Added.
* fast/events/attribute-listener-cloned-from-frameless-doc-context-2.html: Added.
* fast/events/attribute-listener-cloned-from-frameless-doc-context-expected.txt: Added.
* fast/events/attribute-listener-cloned-from-frameless-doc-context.html: Added.
* fast/events/attribute-listener-cloned-from-frameless-doc-expected.txt: Added.
* fast/events/attribute-listener-cloned-from-frameless-doc.xhtml: Added.
* fast/events/attribute-listener-extracted-from-frameless-doc-context-2-expected.txt: Added.
* fast/events/attribute-listener-extracted-from-frameless-doc-context-2.html: Added.
* fast/events/attribute-listener-extracted-from-frameless-doc-context-expected.txt: Added.
* fast/events/attribute-listener-extracted-from-frameless-doc-context.html: Added.
2009-09-25 Darin Adler <darin@apple.com>
Checked in a file that was created by make-script-test-wrappers.
......
<html>
<body onload="test()">
<iframe src='data:text/html,<a id="a" href="#" onclick="document.write(window != top ? &apos;<p>FAIL</p>&apos; : &apos;<p>PASS</p>&apos;); return false">link</a>'></iframe>
<script>
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function test()
{
var doc = frames[0].document;
var range=doc.createRange();
range.selectNodeContents(doc.body);
var frag=range.cloneContents();
document.body.appendChild(frag);
frames[0].location = "data:text/html,";
frames[0].frameElement.onload = test2;
}
function test2()
{
var event = top.document.createEvent('MouseEvent');
event.initEvent('click', true, true);
top.document.getElementsByTagName('a')[0].dispatchEvent(event);
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
</body>
</html>
<html>
<body onload="test()">
<iframe src='data:text/html,<a id="a" href="#" onclick="document.write(window != top ? &apos;<p>FAIL</p>&apos; : &apos;<p>PASS</p>&apos;); return false">link</a>'></iframe>
<script>
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function test()
{
var doc = frames[0].document;
var range=doc.createRange();
range.selectNodeContents(doc.body);
var frag=range.cloneContents();
document.body.appendChild(frag);
var event = top.document.createEvent('MouseEvent');
event.initEvent('click', true, true);
top.document.getElementsByTagName('a')[0].dispatchEvent(event);
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
</body>
</html>
ALERT: SUCCESS
Test that an element that was imported from a frameless document has functional attribute event listeners. Should alert "SUCCESS".
Link (clicked automatically).Element.onclick result is correct.
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<p>Test that an element that was imported from a frameless document has functional attribute event listeners. Should alert "SUCCESS".</p>
<script>
<![CDATA[
if (window.layoutTestController)
layoutTestController.dumpAsText();
var doc = (new DOMParser).parseFromString('<html xmlns="http://www.w3.org/1999/xhtml"><a id="a" href="javascript:alert(\'FAIL\')" onclick="alert(\'SUCCESS\'); return false">Link (clicked automatically).</a></html>', "application/xhtml+xml");
var a = doc.documentElement.firstChild.cloneNode(true);
document.getElementsByTagName("body")[0].appendChild(a);
var event = document.createEvent('MouseEvent');
event.initEvent('click', true, true);
document.getElementsByTagName('a')[0].dispatchEvent(event);
if (0 == document.getElementsByTagName('a')[0].onclick.toString().indexOf("function onclick"))
document.body.appendChild(document.createTextNode("Element.onclick result is correct."));
]]>
</script>
</body>
</html>
<html>
<body onload="test()">
<iframe src='data:text/html,<a id="a" href="#" onclick="document.write(window != top ? &apos;<p>FAIL</p>&apos; : &apos;<p>PASS</p>&apos;); return false">link</a>'></iframe>
<script>
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function test()
{
var doc = frames[0].document;
var range=doc.createRange();
range.selectNodeContents(doc.body);
var frag=range.extractContents();
document.body.appendChild(frag);
frames[0].location = "data:text/html,";
frames[0].frameElement.onload = test2;
}
function test2()
{
var event = top.document.createEvent('MouseEvent');
event.initEvent('click', true, true);
top.document.getElementsByTagName('a')[0].dispatchEvent(event);
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
</body>
</html>
<html>
<body onload="test()">
<iframe src='data:text/html,<a id="a" href="#" onclick="document.write(window != top ? &apos;<p>FAIL</p>&apos; : &apos;<p>PASS</p>&apos;); return false">link</a>'></iframe>
<script>
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function test()
{
var doc = frames[0].document;
var range=doc.createRange();
range.selectNodeContents(doc.body);
var frag=range.extractContents();
document.body.appendChild(frag);
var event = top.document.createEvent('MouseEvent');
event.initEvent('click', true, true);
top.document.getElementsByTagName('a')[0].dispatchEvent(event);
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
</body>
</html>
2009-09-24 Alexey Proskuryakov <ap@apple.com>
Reviewed by Darin Adler and Sam Weinig.
Onclick not fired for an element copied with cloneContents() or cloneNode()
https://bugs.webkit.org/show_bug.cgi?id=25130
The change here is that JS event listeners don't keep a reference to a global object from
where they were created, and instead take it as a parameter when parsing source code. Also,
the listener creation won't fail just because it happens for an element in a frameless
document.
Thus, moving nodes between documents no longer results in having incorrect registered
lazy event listeners on them.
Tests: fast/events/attribute-listener-cloned-from-frameless-doc-context-2.html
fast/events/attribute-listener-cloned-from-frameless-doc-context.html
fast/events/attribute-listener-cloned-from-frameless-doc.xhtml
fast/events/attribute-listener-extracted-from-frameless-doc-context-2.html
fast/events/attribute-listener-extracted-from-frameless-doc-context.html
* bindings/js/JSEventListener.cpp:
(WebCore::JSEventListener::JSEventListener): Don't take a reference to JSDOMGlobalObject.
(WebCore::JSEventListener::jsFunction): Take ScriptExecutionContext as a parameter for
getting to JSDOMGlobalObject. It's not used in base class, but is in JSLazyEventListner.
(WebCore::JSEventListener::markJSFunction): Don't mark the global object.
(WebCore::JSEventListener::handleEvent): Get global object from ScriptExecutionContext.
(WebCore::JSEventListener::reportError): Ditto.
* bindings/js/JSEventListener.h: (WebCore::JSEventListener::create): Don't keep a reference
to JSDOMGlobalObject.
* bindings/js/JSLazyEventListener.cpp: (WebCore::JSLazyEventListener::parseCode): Listener
creation was split between this function and ScriptEventListener; moved it here, as JS
global object can be different now.
* bindings/js/JSLazyEventListener.h: (WebCore::JSLazyEventListener::create): Keep source URL,
which can not be determined at parsing time.
* bindings/js/ScriptEventListener.cpp: (WebCore::createAttributeEventListener): Moved code
for listener creation to JSLazyEventListener. XSSAuditor code remains here, because tests
expect that errors are logged at document parsing time, and because I don't know what other
side effects moving it vould have.
* dom/EventListener.h: handleEvent() and reportError() now take ScriptExecutionContext,
because JSC needs a global context here.
* bindings/js/JSAbstractWorkerCustom.cpp:
(WebCore::JSAbstractWorker::addEventListener):
(WebCore::JSAbstractWorker::removeEventListener):
* bindings/js/JSDOMApplicationCacheCustom.cpp:
(WebCore::JSDOMApplicationCache::addEventListener):
(WebCore::JSDOMApplicationCache::removeEventListener):
* bindings/js/JSDOMGlobalObject.cpp:
(WebCore::JSDOMGlobalObject::createJSAttributeEventListener):
* bindings/js/JSDOMWindowCustom.cpp:
(WebCore::JSDOMWindow::addEventListener):
(WebCore::JSDOMWindow::removeEventListener):
* bindings/js/JSEventSourceCustom.cpp:
(WebCore::JSEventSource::addEventListener):
(WebCore::JSEventSource::removeEventListener):
* bindings/js/JSMessagePortCustom.cpp:
(WebCore::JSMessagePort::addEventListener):
(WebCore::JSMessagePort::removeEventListener):
* bindings/js/JSNodeCustom.cpp:
(WebCore::JSNode::addEventListener):
(WebCore::JSNode::removeEventListener):
* bindings/js/JSSVGElementInstanceCustom.cpp:
(WebCore::JSSVGElementInstance::addEventListener):
(WebCore::JSSVGElementInstance::removeEventListener):
* bindings/js/JSWorkerContextCustom.cpp:
(WebCore::JSWorkerContext::addEventListener):
(WebCore::JSWorkerContext::removeEventListener):
* bindings/js/JSXMLHttpRequestCustom.cpp:
(WebCore::JSXMLHttpRequest::addEventListener):
(WebCore::JSXMLHttpRequest::removeEventListener):
* bindings/js/JSXMLHttpRequestUploadCustom.cpp:
(WebCore::JSXMLHttpRequestUpload::addEventListener):
(WebCore::JSXMLHttpRequestUpload::removeEventListener):
* bindings/objc/ObjCEventListener.h:
* bindings/objc/ObjCEventListener.mm:
(WebCore::ObjCEventListener::handleEvent):
* bindings/scripts/CodeGeneratorJS.pm:
* dom/EventTarget.cpp:
(WebCore::EventTarget::fireEventListeners):
* inspector/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::handleEvent):
* inspector/InspectorDOMAgent.h:
* inspector/InspectorDOMStorageResource.cpp:
(WebCore::InspectorDOMStorageResource::handleEvent):
* inspector/InspectorDOMStorageResource.h:
* loader/ImageDocument.cpp:
(WebCore::ImageEventListener::handleEvent):
* svg/animation/SVGSMILElement.cpp:
(WebCore::ConditionEventListener::handleEvent):
* workers/WorkerContext.cpp:
(WebCore::WorkerContext::reportException):
Don't pass global object to JSEventListener::create(), which no longer needs it.
Note that some of these functions still have an early return for null global object, which
can probably be removed in a later patch.
Pass ScriptExecutionContext to EventListener methods that now need it.
2009-09-25 Enrica Casucci <enrica@apple.com>
Reviewed by Darin Adler, Dan Bernstein, Adele Peterson, and others.
......@@ -54,7 +54,7 @@ JSValue JSAbstractWorker::addEventListener(ExecState* exec, const ArgList& args)
if (!listener.isObject())
return jsUndefined();
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), globalObject, false), args.at(2).toBoolean(exec));
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false), args.at(2).toBoolean(exec));
return jsUndefined();
}
......@@ -68,7 +68,7 @@ JSValue JSAbstractWorker::removeEventListener(ExecState* exec, const ArgList& ar
if (!listener.isObject())
return jsUndefined();
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), globalObject, false).get(), args.at(2).toBoolean(exec));
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false).get(), args.at(2).toBoolean(exec));
return jsUndefined();
}
......
......@@ -95,7 +95,7 @@ JSValue JSDOMApplicationCache::addEventListener(ExecState* exec, const ArgList&
if (!listener.isObject())
return jsUndefined();
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), globalObject, false), args.at(2).toBoolean(exec));
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false), args.at(2).toBoolean(exec));
return jsUndefined();
}
......@@ -109,7 +109,7 @@ JSValue JSDOMApplicationCache::removeEventListener(ExecState* exec, const ArgLis
if (!listener.isObject())
return jsUndefined();
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), globalObject, false).get(), args.at(2).toBoolean(exec));
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false).get(), args.at(2).toBoolean(exec));
return jsUndefined();
}
......
......@@ -68,7 +68,7 @@ PassRefPtr<JSEventListener> JSDOMGlobalObject::createJSAttributeEventListener(JS
if (!val.isObject())
return 0;
return JSEventListener::create(asObject(val), this, true).get();
return JSEventListener::create(asObject(val), true).get();
}
void JSDOMGlobalObject::setCurrentEvent(Event* evt)
......
......@@ -1034,7 +1034,7 @@ JSValue JSDOMWindow::addEventListener(ExecState* exec, const ArgList& args)
if (!listener.isObject())
return jsUndefined();
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false), args.at(2).toBoolean(exec));
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false), args.at(2).toBoolean(exec));
return jsUndefined();
}
......@@ -1048,7 +1048,7 @@ JSValue JSDOMWindow::removeEventListener(ExecState* exec, const ArgList& args)
if (!listener.isObject())
return jsUndefined();
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false).get(), args.at(2).toBoolean(exec));
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false).get(), args.at(2).toBoolean(exec));
return jsUndefined();
}
......
......@@ -31,20 +31,18 @@ using namespace JSC;
namespace WebCore {
JSEventListener::JSEventListener(JSObject* function, JSDOMGlobalObject* globalObject, bool isAttribute)
JSEventListener::JSEventListener(JSObject* function, bool isAttribute)
: EventListener(JSEventListenerType)
, m_jsFunction(function)
, m_globalObject(globalObject)
, m_isAttribute(isAttribute)
{
ASSERT(m_globalObject);
}
JSEventListener::~JSEventListener()
{
}
JSObject* JSEventListener::jsFunction() const
JSObject* JSEventListener::jsFunction(ScriptExecutionContext*) const
{
return m_jsFunction;
}
......@@ -53,20 +51,22 @@ void JSEventListener::markJSFunction(MarkStack& markStack)
{
if (m_jsFunction)
markStack.append(m_jsFunction);
markStack.append(m_globalObject);
}
void JSEventListener::handleEvent(Event* event)
void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event)
{
ASSERT(scriptExecutionContext);
if (!scriptExecutionContext)
return;
JSLock lock(SilenceAssertionsOnly);
JSObject* jsFunction = this->jsFunction();
JSObject* jsFunction = this->jsFunction(scriptExecutionContext);
if (!jsFunction)
return;
JSDOMGlobalObject* globalObject = m_globalObject;
ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
if (!scriptExecutionContext)
JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext);
if (!globalObject)
return;
if (scriptExecutionContext->isDocument()) {
......@@ -132,15 +132,15 @@ void JSEventListener::handleEvent(Event* event)
}
}
bool JSEventListener::reportError(const String& message, const String& url, int lineNumber)
bool JSEventListener::reportError(ScriptExecutionContext* context, const String& message, const String& url, int lineNumber)
{
JSLock lock(SilenceAssertionsOnly);
JSObject* jsFunction = this->jsFunction();
JSObject* jsFunction = this->jsFunction(context);
if (!jsFunction)
return false;
JSDOMGlobalObject* globalObject = m_globalObject;
JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context);
ExecState* exec = globalObject->globalExec();
CallData callData;
......
......@@ -30,9 +30,9 @@ namespace WebCore {
class JSEventListener : public EventListener {
public:
static PassRefPtr<JSEventListener> create(JSC::JSObject* listener, JSDOMGlobalObject* globalObject, bool isAttribute)
static PassRefPtr<JSEventListener> create(JSC::JSObject* listener, bool isAttribute)
{
return adoptRef(new JSEventListener(listener, globalObject, isAttribute));
return adoptRef(new JSEventListener(listener, isAttribute));
}
static const JSEventListener* cast(const EventListener* listener)
......@@ -49,20 +49,19 @@ namespace WebCore {
// Returns true if this event listener was created for an event handler attribute, like "onload" or "onclick".
bool isAttribute() const { return m_isAttribute; }
virtual JSC::JSObject* jsFunction() const;
virtual JSC::JSObject* jsFunction(ScriptExecutionContext*) const;
private:
virtual void markJSFunction(JSC::MarkStack&);
virtual void handleEvent(Event*);
virtual bool reportError(const String& message, const String& url, int lineNumber);
virtual void handleEvent(ScriptExecutionContext*, Event*);
virtual bool reportError(ScriptExecutionContext*, const String& message, const String& url, int lineNumber);
virtual bool virtualisAttribute() const;
void clearJSFunctionInline();
protected:
JSEventListener(JSC::JSObject* function, JSDOMGlobalObject*, bool isAttribute);
JSEventListener(JSC::JSObject* function, bool isAttribute);
mutable JSC::JSObject* m_jsFunction;
JSDOMGlobalObject* m_globalObject;
bool m_isAttribute;
};
......
......@@ -53,7 +53,7 @@ JSValue JSEventSource::addEventListener(ExecState* exec, const ArgList& args)
if (!listener.isObject())
return jsUndefined();
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), globalObject, false).get(), args.at(2).toBoolean(exec));
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false).get(), args.at(2).toBoolean(exec));
return jsUndefined();
}
......@@ -67,7 +67,7 @@ JSValue JSEventSource::removeEventListener(ExecState* exec, const ArgList& args)
if (!listener.isObject())
return jsUndefined();
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), globalObject, false).get(), args.at(2).toBoolean(exec));
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false).get(), args.at(2).toBoolean(exec));
return jsUndefined();
}
......
......@@ -35,12 +35,13 @@ namespace WebCore {
static WTF::RefCountedLeakCounter eventListenerCounter("JSLazyEventListener");
#endif
JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, JSDOMGlobalObject* globalObject, Node* node, int lineNumber)
: JSEventListener(0, globalObject, true)
JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, Node* node, const String& sourceURL, int lineNumber)
: JSEventListener(0, true)
, m_functionName(functionName)
, m_eventParameterName(eventParameterName)
, m_code(code)
, m_parsed(false)
, m_sourceURL(sourceURL)
, m_lineNumber(lineNumber)
, m_originalNode(node)
{
......@@ -67,23 +68,43 @@ JSLazyEventListener::~JSLazyEventListener()
#endif
}
JSObject* JSLazyEventListener::jsFunction() const
JSObject* JSLazyEventListener::jsFunction(ScriptExecutionContext* executionContext) const
{
parseCode();
parseCode(executionContext);
return m_jsFunction;
}
void JSLazyEventListener::parseCode() const
void JSLazyEventListener::parseCode(ScriptExecutionContext* executionContext) const
{
ASSERT(executionContext);
ASSERT(executionContext->isDocument());
if (!executionContext)
return;
if (m_parsed)
return;
ScriptExecutionContext* executionContext = m_globalObject->scriptExecutionContext();
ASSERT(executionContext);
if (!executionContext)
Frame* frame = static_cast<Document*>(executionContext)->frame();
if (!frame)
return;
ScriptController* scriptController = frame->script();
if (!scriptController->isEnabled())
return;
JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext);
if (!globalObject)
return;
// Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating.
if (m_originalNode) {
JSLock lock(SilenceAssertionsOnly);
// FIXME: Should pass the global object associated with the node
toJS(globalObject->globalExec(), globalObject, m_originalNode);
}
if (executionContext->isDocument()) {
JSDOMWindow* window = static_cast<JSDOMWindow*>(m_globalObject);
JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject);
Frame* frame = window->impl()->frame();
if (!frame)
return;
......@@ -95,16 +116,13 @@ void JSLazyEventListener::parseCode() const
m_parsed = true;
ExecState* exec = m_globalObject->globalExec();
ExecState* exec = globalObject->globalExec();
MarkedArgumentBuffer args;
UString sourceURL(executionContext->url().string());
args.append(jsNontrivialString(exec, m_eventParameterName));
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_jsFunction = constructFunction(exec, args, Identifier(exec, m_functionName), sourceURL, m_lineNumber); // FIXME: is globalExec ok?
m_jsFunction = constructFunction(exec, args, Identifier(exec, m_functionName), m_sourceURL, m_lineNumber); // FIXME: is globalExec ok?
JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_jsFunction);
......@@ -118,7 +136,7 @@ void JSLazyEventListener::parseCode() const
// (and the document, and the form - see JSHTMLElement::eventHandlerScope)
ScopeChain scope = listenerAsFunction->scope();
JSValue thisObj = toJS(exec, m_globalObject, m_originalNode);
JSValue thisObj = toJS(exec, globalObject, m_originalNode);
if (thisObj.isObject()) {
static_cast<JSNode*>(asObject(thisObj))->pushEventHandlerScope(exec, scope);
listenerAsFunction->setScope(scope);
......@@ -129,6 +147,7 @@ void JSLazyEventListener::parseCode() const
m_functionName = String();
m_code = String();
m_eventParameterName = String();
m_sourceURL = String();
}
} // namespace WebCore
......@@ -29,24 +29,25 @@ namespace WebCore {
class JSLazyEventListener : public JSEventListener {
public:
static PassRefPtr<JSLazyEventListener> create(const String& functionName, const String& eventParameterName, const String& code, JSDOMGlobalObject* globalObject, Node* node, int lineNumber)
static PassRefPtr<JSLazyEventListener> create(const String& functionName, const String& eventParameterName, const String& code, Node* node, const String& sourceURL, int lineNumber)
{
return adoptRef(new JSLazyEventListener(functionName, eventParameterName, code, globalObject, node, lineNumber));
return adoptRef(new JSLazyEventListener(functionName, eventParameterName, code, node, sourceURL, lineNumber));
}
virtual ~JSLazyEventListener();
private:
JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, JSDOMGlobalObject*, Node*, int lineNumber);
JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, Node*, const String& sourceURL, int lineNumber);