Title: [110315] trunk
Revision
110315
Author
[email protected]
Date
2012-03-09 12:12:35 -0800 (Fri, 09 Mar 2012)

Log Message

[V8] Fix object scope for inline event attribute handlers
https://bugs.webkit.org/show_bug.cgi?id=80329

Reviewed by Ojan Vafai.

Source/WebCore:

We now create the funciton inside the with-statements with the current scope objects.
This is important for a few reasons:

- We need to use the real objects and not just lookup the JS properties because these might have been overridden.
- We need to use the node, form and document at the time of the preparation and not at the time of calling.
- We need to ensure that event/evt is bound closer than a property with the same name in the object environment
  created by the with-statements.

Tests: fast/dom/inline-event-attributes-lookup-removed-form.html
       fast/dom/inline-event-attributes-lookup-removed.html
       fast/dom/inline-event-attributes-lookup.html

* bindings/v8/ScriptEventListener.cpp:
(WebCore::eventParameterName):
(WebCore):
(WebCore::createAttributeEventListener):
* bindings/v8/V8LazyEventListener.cpp:
(WebCore::V8LazyEventListener::V8LazyEventListener):
(WebCore):
(WebCore::toObjectWrapper):
(WebCore::V8LazyEventListener::callListenerFunction):
(WebCore::V8LazyEventListener::prepareListenerObject):
* bindings/v8/V8LazyEventListener.h:
(WebCore):
(WebCore::V8LazyEventListener::create):
(V8LazyEventListener):

LayoutTests:

* fast/dom/inline-event-attributes-lookup-expected.txt: Added.
* fast/dom/inline-event-attributes-lookup-removed-expected.txt: Added.
* fast/dom/inline-event-attributes-lookup-removed-form-expected.txt: Added.
* fast/dom/inline-event-attributes-lookup-removed-form.html: Added.
* fast/dom/inline-event-attributes-lookup-removed.html: Added.
* fast/dom/inline-event-attributes-lookup.html: Added.
* fast/forms/lazy-event-listener-scope-chain-expected.txt:
* fast/forms/lazy-event-listener-scope-chain.html:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (110314 => 110315)


--- trunk/LayoutTests/ChangeLog	2012-03-09 20:07:11 UTC (rev 110314)
+++ trunk/LayoutTests/ChangeLog	2012-03-09 20:12:35 UTC (rev 110315)
@@ -1,3 +1,19 @@
+2012-03-08  Erik Arvidsson  <[email protected]>
+
+        [V8] Fix object scope for inline event attribute handlers
+        https://bugs.webkit.org/show_bug.cgi?id=80329
+
+        Reviewed by Ojan Vafai.
+
+        * fast/dom/inline-event-attributes-lookup-expected.txt: Added.
+        * fast/dom/inline-event-attributes-lookup-removed-expected.txt: Added.
+        * fast/dom/inline-event-attributes-lookup-removed-form-expected.txt: Added.
+        * fast/dom/inline-event-attributes-lookup-removed-form.html: Added.
+        * fast/dom/inline-event-attributes-lookup-removed.html: Added.
+        * fast/dom/inline-event-attributes-lookup.html: Added.
+        * fast/forms/lazy-event-listener-scope-chain-expected.txt:
+        * fast/forms/lazy-event-listener-scope-chain.html:
+
 2012-03-09  Ojan Vafai  <[email protected]>
 
         Add image/png svn:mime-type to all png images that currently lack

Added: trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-expected.txt (0 => 110315)


--- trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-expected.txt	2012-03-09 20:12:35 UTC (rev 110315)
@@ -0,0 +1,17 @@
+Tests the lookup in inline event handlers
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS result is "test-input-a"
+PASS result is "test-form-b"
+PASS result is "document-c"
+PASS result is "div-a"
+PASS result is "document-b"
+PASS result is "document-c"
+PASS result is div
+PASS result is not "FAIL"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-expected.txt (0 => 110315)


--- trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-expected.txt	2012-03-09 20:12:35 UTC (rev 110315)
@@ -0,0 +1,10 @@
+Tests that lookup in inline event handlers keeps references alive
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS result is "PASS"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-form-expected.txt (0 => 110315)


--- trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-form-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-form-expected.txt	2012-03-09 20:12:35 UTC (rev 110315)
@@ -0,0 +1,10 @@
+Tests that lookup in inline event handlers keeps references alive
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS result is "PASS"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-form.html (0 => 110315)


--- trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-form.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed-form.html	2012-03-09 20:12:35 UTC (rev 110315)
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<script src=""
+<form id="test-form">
+    <input id="test-input">
+</form>
+<script>
+
+description('Tests that lookup in inline event handlers keeps references alive');
+
+function forceGC()
+{
+    if (window.GCController)
+        return GCController.collect();
+
+    // Force garbage collection
+    for (var ndx = 0; ndx < 99000; ndx++)
+        var str = new String("1234");
+}
+
+var result;
+var pa = '(FAIL input)'
+var ss = '(FAIL form)'
+
+var input = document.getElementById('test-input');
+input.pa = 'PA';
+input.form.ss = 'SS';
+input.setAttribute('onclick', 'result = pa + ss');
+
+var f = input.onclick;
+
+input.parentNode.removeChild(input);
+input = null;
+forceGC();
+
+f();
+shouldBeEqualToString('result', 'PASS');
+
+</script>
+<script src=""
\ No newline at end of file

Added: trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed.html (0 => 110315)


--- trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/inline-event-attributes-lookup-removed.html	2012-03-09 20:12:35 UTC (rev 110315)
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<script src=""
+<script>
+
+description('Tests that lookup in inline event handlers keeps references alive');
+
+function forceGC()
+{
+    if (window.GCController)
+        return GCController.collect();
+
+    // Force garbage collection
+    for (var ndx = 0; ndx < 99000; ndx++)
+        var str = new String("1234");
+}
+
+function dispatchClick(element)
+{
+    var clickEvent = document.createEvent('MouseEvent');
+    clickEvent.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0,false, false, false, false, 0, null);
+    element.dispatchEvent(clickEvent);
+}
+
+var result;
+var custom = 'FAIL';
+
+var div1 = document.createElement('div');
+div1.custom = 'PASS';
+div1.setAttribute('onclick', 'result = custom');
+
+var div2 = document.createElement('div');
+div2._onclick_ = div1.onclick;
+
+div1 = null;
+forceGC();
+
+dispatchClick(div2);
+shouldBeEqualToString('result', 'PASS');
+
+</script>
+<script src=""
\ No newline at end of file

Added: trunk/LayoutTests/fast/dom/inline-event-attributes-lookup.html (0 => 110315)


--- trunk/LayoutTests/fast/dom/inline-event-attributes-lookup.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/inline-event-attributes-lookup.html	2012-03-09 20:12:35 UTC (rev 110315)
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<script src=""
+<form id="test-form">
+    <input id="test-input">
+</form>
+<script>
+
+description('Tests the lookup in inline event handlers');
+
+function dispatchClick(element)
+{
+    var clickEvent = document.createEvent('MouseEvent');
+    clickEvent.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+    element.dispatchEvent(clickEvent);
+}
+
+var result;
+var testForm = document.getElementById('test-form');
+var testInput = document.getElementById('test-input');
+
+document.a = 'document-a';
+testForm.a = 'test-form-a';
+testInput.a = 'test-input-a';
+
+document.b = 'document-b';
+testForm.b = 'test-form-b';
+
+document.c = 'document-c';
+
+testInput.setAttribute('onclick', 'result = a');
+dispatchClick(testInput);
+shouldBeEqualToString('result', 'test-input-a');
+
+testInput.setAttribute('onclick', 'result = b');
+dispatchClick(testInput);
+shouldBeEqualToString('result', 'test-form-b');
+
+testInput.setAttribute('onclick', 'result = c');
+dispatchClick(testInput);
+shouldBeEqualToString('result', 'document-c');
+
+
+var div = document.createElement('div');
+
+div.a = 'div-a';
+div.setAttribute('onclick', 'result = a');
+dispatchClick(div);
+shouldBeEqualToString('result', 'div-a');
+
+div.form = {b: 'fake-b'};
+div.setAttribute('onclick', 'result = b');
+dispatchClick(div);
+shouldBeEqualToString('result', 'document-b');
+
+div.ownerDocument = {c: 'fake-c'};
+div.setAttribute('onclick', 'result = c');
+dispatchClick(div);
+shouldBeEqualToString('result', 'document-c');
+
+div.setAttribute('onclick', 'result = this');
+dispatchClick(div);
+shouldBe('result', 'div');
+
+div.event = 'FAIL';
+div.setAttribute('onclick', 'result = event');
+dispatchClick(div);
+shouldNotBe('result', '"FAIL"');
+
+</script>
+<script src=""
\ No newline at end of file

Modified: trunk/LayoutTests/fast/forms/lazy-event-listener-scope-chain-expected.txt (110314 => 110315)


--- trunk/LayoutTests/fast/forms/lazy-event-listener-scope-chain-expected.txt	2012-03-09 20:07:11 UTC (rev 110314)
+++ trunk/LayoutTests/fast/forms/lazy-event-listener-scope-chain-expected.txt	2012-03-09 20:12:35 UTC (rev 110315)
@@ -1,4 +1,9 @@
-This test tests that a lazy event listener attached to a form element keeps its form in the scope chain when the listener is called by _javascript_. 
+This test tests that a lazy event listener attached to a form element keeps its form in the scope chain when the listener is called by _javascript_.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS result is "abc"
+PASS result is "abc"
+PASS result is "abc"
  
-abc
-

Modified: trunk/LayoutTests/fast/forms/lazy-event-listener-scope-chain.html (110314 => 110315)


--- trunk/LayoutTests/fast/forms/lazy-event-listener-scope-chain.html	2012-03-09 20:07:11 UTC (rev 110314)
+++ trunk/LayoutTests/fast/forms/lazy-event-listener-scope-chain.html	2012-03-09 20:12:35 UTC (rev 110315)
@@ -1,32 +1,35 @@
-<html>
-
+<!DOCTYPE html>
+<script src=""
+<form action="" _onsubmit_="runTest(username.value)">
+<input type="text" value="abc" name="username">
+<input type="submit" name="login" value="Login">
+</form>
 <script>
-if (window.layoutTestController)
-  layoutTestController.dumpAsText();
 
-function run_test(x) {
-  var r = document.getElementById('result');
-  r.innerHTML = x + '<br>';
-}
+description('This test tests that a lazy event listener attached to a form element keeps its form in the scope chain when the listener is called by _javascript_.');
 
-function setup() {
-  var f = document.getElementsByTagName('form')[0];
-  f.old_f = f.onsubmit;
-  f._onsubmit_ = function() {this.old_f();};
-  f.login.click();
+var result;
+
+function runTest(x)
+{
+    result = x;
+    shouldBeEqualToString('result', 'abc');
 }
 
-</script>
-This test tests that a lazy event listener attached to a form element
-keeps its form in the scope chain when the listener is called by 
-_javascript_. <br>
+var f = document.querySelector('form');
 
-<body _onload_="setup()">
-<form action="" _onsubmit_="run_test(username.value)">
-<input type="text" value="abc" name="username"/>
-<input type="submit" name="login" value="Login"/>
-</form>
+f.onsubmit();
 
-<div id="result"></div>
-</body>
-</html>
+// Should keep the ObjectEnvironment even when called without context.
+(0, f.onsubmit)();
+
+f.oldF = f.onsubmit;
+
+f._onsubmit_ = function() {
+    this.oldF();
+};
+
+f.login.click();
+
+</script>
+<script src=""

Modified: trunk/Source/WebCore/ChangeLog (110314 => 110315)


--- trunk/Source/WebCore/ChangeLog	2012-03-09 20:07:11 UTC (rev 110314)
+++ trunk/Source/WebCore/ChangeLog	2012-03-09 20:12:35 UTC (rev 110315)
@@ -1,3 +1,37 @@
+2012-03-08  Erik Arvidsson  <[email protected]>
+
+        [V8] Fix object scope for inline event attribute handlers
+        https://bugs.webkit.org/show_bug.cgi?id=80329
+
+        Reviewed by Ojan Vafai.
+
+        We now create the funciton inside the with-statements with the current scope objects.
+        This is important for a few reasons:
+
+        - We need to use the real objects and not just lookup the JS properties because these might have been overridden.
+        - We need to use the node, form and document at the time of the preparation and not at the time of calling.
+        - We need to ensure that event/evt is bound closer than a property with the same name in the object environment
+          created by the with-statements.
+
+        Tests: fast/dom/inline-event-attributes-lookup-removed-form.html
+               fast/dom/inline-event-attributes-lookup-removed.html
+               fast/dom/inline-event-attributes-lookup.html
+
+        * bindings/v8/ScriptEventListener.cpp:
+        (WebCore::eventParameterName):
+        (WebCore):
+        (WebCore::createAttributeEventListener):
+        * bindings/v8/V8LazyEventListener.cpp:
+        (WebCore::V8LazyEventListener::V8LazyEventListener):
+        (WebCore):
+        (WebCore::toObjectWrapper):
+        (WebCore::V8LazyEventListener::callListenerFunction):
+        (WebCore::V8LazyEventListener::prepareListenerObject):
+        * bindings/v8/V8LazyEventListener.h:
+        (WebCore):
+        (WebCore::V8LazyEventListener::create):
+        (V8LazyEventListener):
+
 2012-03-09  Stephen Chenney  <[email protected]>
 
         Crash in WebCore::SVGUseElement::instanceForShadowTreeElement

Modified: trunk/Source/WebCore/bindings/v8/ScriptEventListener.cpp (110314 => 110315)


--- trunk/Source/WebCore/bindings/v8/ScriptEventListener.cpp	2012-03-09 20:07:11 UTC (rev 110314)
+++ trunk/Source/WebCore/bindings/v8/ScriptEventListener.cpp	2012-03-09 20:12:35 UTC (rev 110315)
@@ -42,6 +42,13 @@
 
 namespace WebCore {
 
+static const String& eventParameterName(bool isSVGEvent)
+{
+    DEFINE_STATIC_LOCAL(const String, eventString, ("event"));
+    DEFINE_STATIC_LOCAL(const String, evtString, ("evt"));
+    return isSVGEvent ? evtString : eventString;
+}
+
 PassRefPtr<V8LazyEventListener> createAttributeEventListener(Node* node, Attribute* attr)
 {
     ASSERT(node);
@@ -57,12 +64,11 @@
         ScriptController* scriptController = frame->script();
         if (!scriptController->canExecuteScripts(AboutToExecuteScript))
             return 0;
-
         position = scriptController->eventHandlerPosition();
         sourceURL = node->document()->url().string();
     }
 
-    return V8LazyEventListener::create(attr->localName().string(), node->isSVGElement(), attr->value(), sourceURL, position, WorldContextHandle(UseMainWorld));
+    return V8LazyEventListener::create(attr->localName().string(), eventParameterName(node->isSVGElement()), attr->value(), sourceURL, position, node, WorldContextHandle(UseMainWorld));
 }
 
 PassRefPtr<V8LazyEventListener> createAttributeEventListener(Frame* frame, Attribute* attr)
@@ -80,7 +86,8 @@
 
     TextPosition position = scriptController->eventHandlerPosition();
     String sourceURL = frame->document()->url().string();
-    return V8LazyEventListener::create(attr->localName().string(), frame->document()->isSVGDocument(), attr->value(), sourceURL, position, WorldContextHandle(UseMainWorld));
+
+    return V8LazyEventListener::create(attr->localName().string(), eventParameterName(frame->document()->isSVGDocument()), attr->value(), sourceURL, position, 0, WorldContextHandle(UseMainWorld));
 }
 
 String eventListenerHandlerBody(Document* document, EventListener* listener)

Modified: trunk/Source/WebCore/bindings/v8/V8LazyEventListener.cpp (110314 => 110315)


--- trunk/Source/WebCore/bindings/v8/V8LazyEventListener.cpp	2012-03-09 20:07:11 UTC (rev 110314)
+++ trunk/Source/WebCore/bindings/v8/V8LazyEventListener.cpp	2012-03-09 20:12:35 UTC (rev 110315)
@@ -32,9 +32,17 @@
 #include "V8LazyEventListener.h"
 
 #include "ContentSecurityPolicy.h"
+#include "Document.h"
 #include "Frame.h"
+#include "HTMLElement.h"
+#include "HTMLFormElement.h"
+#include "Node.h"
 #include "V8Binding.h"
+#include "V8DOMWrapper.h"
+#include "V8Document.h"
+#include "V8HTMLFormElement.h"
 #include "V8HiddenPropertyName.h"
+#include "V8Node.h"
 #include "V8Proxy.h"
 #include "WorldContextHandle.h"
 
@@ -42,23 +50,38 @@
 
 namespace WebCore {
 
-V8LazyEventListener::V8LazyEventListener(const String& functionName, bool isSVGEvent, const String& code, const String sourceURL, const TextPosition& position, const WorldContextHandle& worldContext)
+V8LazyEventListener::V8LazyEventListener(const AtomicString& functionName, const AtomicString& eventParameterName, const String& code, const String sourceURL, const TextPosition& position, PassRefPtr<Node> node, const WorldContextHandle& worldContext)
     : V8AbstractEventListener(true, worldContext)
     , m_functionName(functionName)
-    , m_isSVGEvent(isSVGEvent)
+    , m_eventParameterName(eventParameterName)
     , m_code(code)
     , m_sourceURL(sourceURL)
+    , m_node(node)
+    , m_formElement(0)
     , m_position(position)
 {
+    if (m_node && m_node->isHTMLElement())
+        m_formElement = static_cast<HTMLElement*>(m_node.get())->form();
 }
 
+template<typename T>
+v8::Handle<v8::Object> toObjectWrapper(T* domObject)
+{
+    if (!domObject)
+        return v8::Object::New();
+    v8::Handle<v8::Value> value = toV8(domObject);
+    if (value.IsEmpty())
+        return v8::Object::New();
+    return value.As<v8::Object>();
+}
+
 v8::Local<v8::Value> V8LazyEventListener::callListenerFunction(ScriptExecutionContext* context, v8::Handle<v8::Value> jsEvent, Event* event)
 {
     v8::Local<v8::Object> listenerObject = getListenerObject(context);
     if (listenerObject.IsEmpty())
         return v8::Local<v8::Value>();
 
-    v8::Local<v8::Function> handlerFunction = v8::Local<v8::Function>::Cast(listenerObject);
+    v8::Local<v8::Function> handlerFunction = listenerObject.As<v8::Function>();
     v8::Local<v8::Object> receiver = getReceiverObject(event);
     if (handlerFunction.IsEmpty() || receiver.IsEmpty())
         return v8::Local<v8::Value>();
@@ -104,8 +127,6 @@
 
     v8::Context::Scope scope(v8Context);
 
-    // FIXME: cache the wrapper function.
-
     // Nodes other than the document object, when executing inline event
     // handlers push document, form, and the target node on the scope chain.
     // We do this by using 'with' statement.
@@ -115,66 +136,86 @@
     //
     // Don't use new lines so that lines in the modified handler
     // have the same numbers as in the original code.
-    // FIXME: This approach is a giant hack! What if m_code escapes to run
-    //        arbitrary script?
-    String eventParameterName = m_isSVGEvent ? "evt" : "event";
-    String code = "(function(";
-    code.append(eventParameterName);
-    code.append(") {" \
-            "with (this.ownerDocument ? this.ownerDocument : {}) {" \
-            "with (this.form ? this.form : {}) {" \
-            "with (this) {" \
-            "return (function(");
-    code.append(eventParameterName);
-    code.append("){");
+    // FIXME: V8 does not allow us to programmatically create object environments so
+    //        we have to do this hack! What if m_code escapes to run arbitrary script?
+    //
+    String code = "(function() {" \
+        "with (arguments[2]) {" \
+        "with (arguments[1]) {" \
+        "with (arguments[0]) {";
+    code.append("return function(");
+    code.append(m_eventParameterName);
+    code.append(") {");
+
+    int codePrexixLength = code.length();
+
     code.append(m_code);
     // Insert '\n' otherwise //-style comments could break the handler.
-    code.append("\n}).call(this, ");
-    code.append(eventParameterName);
-    code.append(");}}}})");
+    code.append("\n};}}}})");
     v8::Handle<v8::String> codeExternalString = v8ExternalString(code);
-    v8::Handle<v8::Script> script = V8Proxy::compileScript(codeExternalString, m_sourceURL, m_position);
-    if (!script.IsEmpty()) {
-        // Call v8::Script::Run() directly to avoid an erroneous call to V8RecursionScope::didLeaveScriptContext().
-        // FIXME: Remove this code when we stop doing the 'with' hack above.
-        v8::Local<v8::Value> value = script->Run();
-        if (!value.IsEmpty()) {
-            ASSERT(value->IsFunction());
 
-            v8::Local<v8::Function> wrappedFunction = v8::Local<v8::Function>::Cast(value);
+    TextPosition adjustedPosition(m_position.m_line, OrdinalNumber::fromZeroBasedInt(m_position.m_column.zeroBasedInt() - codePrexixLength));
 
-            // Change the toString function on the wrapper function to avoid it
-            // returning the source for the actual wrapper function. Instead it
-            // returns source for a clean wrapper function with the event
-            // argument wrapping the event source code. The reason for this is
-            // that some web sites use toString on event functions and eval the
-            // source returned (sometimes a RegExp is applied as well) for some
-            // other use. That fails miserably if the actual wrapper source is
-            // returned.
-            v8::Persistent<v8::FunctionTemplate>& toStringTemplate =
-                V8BindingPerIsolateData::current()->lazyEventListenerToStringTemplate();
-            if (toStringTemplate.IsEmpty())
-                toStringTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(V8LazyEventListenerToString));
-            v8::Local<v8::Function> toStringFunction;
-            if (!toStringTemplate.IsEmpty())
-                toStringFunction = toStringTemplate->GetFunction();
-            if (!toStringFunction.IsEmpty()) {
-                String toStringResult = "function ";
-                toStringResult.append(m_functionName);
-                toStringResult.append("(");
-                toStringResult.append(eventParameterName);
-                toStringResult.append(") {\n  ");
-                toStringResult.append(m_code);
-                toStringResult.append("\n}");
-                wrappedFunction->SetHiddenValue(V8HiddenPropertyName::toStringString(), v8ExternalString(toStringResult));
-                wrappedFunction->Set(v8::String::New("toString"), toStringFunction);
-            }
+    v8::Handle<v8::Script> script = V8Proxy::compileScript(codeExternalString, m_sourceURL, adjustedPosition);
+    if (script.IsEmpty())
+        return;
 
-            wrappedFunction->SetName(v8::String::New(fromWebCoreString(m_functionName), m_functionName.length()));
+    // Call v8::Script::Run() directly to avoid an erroneous call to V8RecursionScope::didLeaveScriptContext().
+    // FIXME: Remove this code when we stop doing the 'with' hack above.
+    v8::Local<v8::Value> value = script->Run();
+    if (value.IsEmpty())
+        return;
 
-            setListenerObject(wrappedFunction);
-        }
+    // Call the outer function to get the inner function.
+    ASSERT(value->IsFunction());
+    v8::Local<v8::Function> intermediateFunction = value.As<v8::Function>();
+
+    v8::Handle<v8::Object> nodeWrapper = toObjectWrapper<Node>(m_node.get());
+    v8::Handle<v8::Object> formWrapper = toObjectWrapper<HTMLFormElement>(m_formElement.get());
+    v8::Handle<v8::Object> documentWrapper = toObjectWrapper<Document>(m_node ? m_node->ownerDocument() : 0);
+
+    m_node.clear();
+    m_formElement.clear();
+
+    v8::Handle<v8::Value> parameters[3] = { nodeWrapper, formWrapper, documentWrapper };
+
+    // Use Call directly to avoid an erroneous call to V8RecursionScope::didLeaveScriptContext().
+    // FIXME: Remove this code when we stop doing the 'with' hack above.
+    v8::Local<v8::Value> innerValue = intermediateFunction->Call(v8Context->Global(), 3, parameters);
+
+    ASSERT(innerValue->IsFunction());
+    v8::Local<v8::Function> wrappedFunction = innerValue.As<v8::Function>();
+
+    // Change the toString function on the wrapper function to avoid it
+    // returning the source for the actual wrapper function. Instead it
+    // returns source for a clean wrapper function with the event
+    // argument wrapping the event source code. The reason for this is
+    // that some web sites use toString on event functions and eval the
+    // source returned (sometimes a RegExp is applied as well) for some
+    // other use. That fails miserably if the actual wrapper source is
+    // returned.
+    v8::Persistent<v8::FunctionTemplate>& toStringTemplate =
+        V8BindingPerIsolateData::current()->lazyEventListenerToStringTemplate();
+    if (toStringTemplate.IsEmpty())
+        toStringTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(V8LazyEventListenerToString));
+    v8::Local<v8::Function> toStringFunction;
+    if (!toStringTemplate.IsEmpty())
+        toStringFunction = toStringTemplate->GetFunction();
+    if (!toStringFunction.IsEmpty()) {
+        String toStringResult = "function ";
+        toStringResult.append(m_functionName);
+        toStringResult.append("(");
+        toStringResult.append(m_eventParameterName);
+        toStringResult.append(") {\n  ");
+        toStringResult.append(m_code);
+        toStringResult.append("\n}");
+        wrappedFunction->SetHiddenValue(V8HiddenPropertyName::toStringString(), v8ExternalString(toStringResult));
+        wrappedFunction->Set(v8::String::New("toString"), toStringFunction);
     }
+
+    wrappedFunction->SetName(v8::String::New(fromWebCoreString(m_functionName), m_functionName.length()));
+
+    setListenerObject(wrappedFunction);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/bindings/v8/V8LazyEventListener.h (110314 => 110315)


--- trunk/Source/WebCore/bindings/v8/V8LazyEventListener.h	2012-03-09 20:07:11 UTC (rev 110314)
+++ trunk/Source/WebCore/bindings/v8/V8LazyEventListener.h	2012-03-09 20:12:35 UTC (rev 110315)
@@ -41,14 +41,16 @@
 
     class Event;
     class Frame;
+    class HTMLFormElement;
+    class Node;
 
     // V8LazyEventListener is a wrapper for a _javascript_ code string that is compiled and evaluated when an event is fired.
-    // A V8LazyEventListener is always a HTML event handler.
+    // A V8LazyEventListener is either a HTML or SVG event handler.
     class V8LazyEventListener : public V8AbstractEventListener {
     public:
-        static PassRefPtr<V8LazyEventListener> create(const String& functionName, bool isSVGEvent, const String& code, const String& sourceURL, const TextPosition& position, const WorldContextHandle& worldContext)
+        static PassRefPtr<V8LazyEventListener> create(const AtomicString& functionName, const AtomicString& eventParameterName, const String& code, const String& sourceURL, const TextPosition& position, PassRefPtr<Node> node, const WorldContextHandle& worldContext)
         {
-            return adoptRef(new V8LazyEventListener(functionName, isSVGEvent, code, sourceURL, position, worldContext));
+            return adoptRef(new V8LazyEventListener(functionName, eventParameterName, code, sourceURL, position, node, worldContext));
         }
 
         virtual bool isLazy() const { return true; }
@@ -57,7 +59,7 @@
         virtual void prepareListenerObject(ScriptExecutionContext*);
 
     private:
-        V8LazyEventListener(const String& functionName, bool isSVGEvent, const String& code, const String sourceURL, const TextPosition&, const WorldContextHandle&);
+        V8LazyEventListener(const AtomicString& functionName, const AtomicString& eventParameterName, const String& code, const String sourceURL, const TextPosition&, PassRefPtr<Node>, const WorldContextHandle&);
 
         virtual v8::Local<v8::Value> callListenerFunction(ScriptExecutionContext*, v8::Handle<v8::Value> jsEvent, Event*);
 
@@ -67,10 +69,12 @@
         // SVGUseElement::transferEventListenersToShadowTree
         virtual bool wasCreatedFromMarkup() const { return true; }
 
-        String m_functionName;
-        bool m_isSVGEvent;
+        AtomicString m_functionName;
+        AtomicString m_eventParameterName;
         String m_code;
         String m_sourceURL;
+        RefPtr<Node> m_node;
+        RefPtr<HTMLFormElement> m_formElement;
         TextPosition m_position;
     };
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to