1/* 2 * Copyright (C) 2006, 2007, 2008, 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32#include "V8LazyEventListener.h" 33 34#include "ContentSecurityPolicy.h" 35#include "Frame.h" 36#include "V8Binding.h" 37#include "V8HiddenPropertyName.h" 38#include "V8Proxy.h" 39#include "WorldContextHandle.h" 40 41#include <wtf/StdLibExtras.h> 42 43namespace WebCore { 44 45V8LazyEventListener::V8LazyEventListener(const String& functionName, bool isSVGEvent, const String& code, const String sourceURL, const TextPosition0& position, const WorldContextHandle& worldContext) 46 : V8AbstractEventListener(true, worldContext) 47 , m_functionName(functionName) 48 , m_isSVGEvent(isSVGEvent) 49 , m_code(code) 50 , m_sourceURL(sourceURL) 51 , m_position(position) 52{ 53} 54 55v8::Local<v8::Value> V8LazyEventListener::callListenerFunction(ScriptExecutionContext* context, v8::Handle<v8::Value> jsEvent, Event* event) 56{ 57 v8::Local<v8::Object> listenerObject = getListenerObject(context); 58 if (listenerObject.IsEmpty()) 59 return v8::Local<v8::Value>(); 60 61 v8::Local<v8::Function> handlerFunction = v8::Local<v8::Function>::Cast(listenerObject); 62 v8::Local<v8::Object> receiver = getReceiverObject(event); 63 if (handlerFunction.IsEmpty() || receiver.IsEmpty()) 64 return v8::Local<v8::Value>(); 65 66 v8::Handle<v8::Value> parameters[1] = { jsEvent }; 67 68 if (V8Proxy* proxy = V8Proxy::retrieve(context)) 69 return proxy->callFunction(handlerFunction, receiver, 1, parameters); 70 71 return v8::Local<v8::Value>(); 72} 73 74static v8::Handle<v8::Value> V8LazyEventListenerToString(const v8::Arguments& args) 75{ 76 return args.Holder()->GetHiddenValue(V8HiddenPropertyName::toStringString()); 77} 78 79void V8LazyEventListener::prepareListenerObject(ScriptExecutionContext* context) 80{ 81 if (hasExistingListenerObject()) 82 return; 83 84 if (context->isDocument() && !static_cast<Document*>(context)->contentSecurityPolicy()->allowInlineEventHandlers()) 85 return; 86 87 v8::HandleScope handleScope; 88 89 V8Proxy* proxy = V8Proxy::retrieve(context); 90 if (!proxy) 91 return; 92 93 // Use the outer scope to hold context. 94 v8::Local<v8::Context> v8Context = worldContext().adjustedContext(proxy); 95 // Bail out if we cannot get the context. 96 if (v8Context.IsEmpty()) 97 return; 98 99 v8::Context::Scope scope(v8Context); 100 101 // FIXME: cache the wrapper function. 102 103 // Nodes other than the document object, when executing inline event handlers push document, form, and the target node on the scope chain. 104 // We do this by using 'with' statement. 105 // See chrome/fast/forms/form-action.html 106 // chrome/fast/forms/selected-index-value.html 107 // base/fast/overflow/onscroll-layer-self-destruct.html 108 // 109 // Don't use new lines so that lines in the modified handler 110 // have the same numbers as in the original code. 111 String code = "(function (evt) {" \ 112 "with (this.ownerDocument ? this.ownerDocument : {}) {" \ 113 "with (this.form ? this.form : {}) {" \ 114 "with (this) {" \ 115 "return (function(evt){"; 116 code.append(m_code); 117 // Insert '\n' otherwise //-style comments could break the handler. 118 code.append( "\n}).call(this, evt);}}}})"); 119 v8::Handle<v8::String> codeExternalString = v8ExternalString(code); 120 v8::Handle<v8::Script> script = V8Proxy::compileScript(codeExternalString, m_sourceURL, m_position); 121 if (!script.IsEmpty()) { 122 v8::Local<v8::Value> value = proxy->runScript(script, false); 123 if (!value.IsEmpty()) { 124 ASSERT(value->IsFunction()); 125 126 v8::Local<v8::Function> wrappedFunction = v8::Local<v8::Function>::Cast(value); 127 128 // Change the toString function on the wrapper function to avoid it 129 // returning the source for the actual wrapper function. Instead it 130 // returns source for a clean wrapper function with the event 131 // argument wrapping the event source code. The reason for this is 132 // that some web sites use toString on event functions and eval the 133 // source returned (sometimes a RegExp is applied as well) for some 134 // other use. That fails miserably if the actual wrapper source is 135 // returned. 136 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, toStringTemplate, ()); 137 if (toStringTemplate.IsEmpty()) 138 toStringTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(V8LazyEventListenerToString)); 139 v8::Local<v8::Function> toStringFunction; 140 if (!toStringTemplate.IsEmpty()) 141 toStringFunction = toStringTemplate->GetFunction(); 142 if (!toStringFunction.IsEmpty()) { 143 String toStringResult = "function "; 144 toStringResult.append(m_functionName); 145 toStringResult.append("("); 146 toStringResult.append(m_isSVGEvent ? "evt" : "event"); 147 toStringResult.append(") {\n "); 148 toStringResult.append(m_code); 149 toStringResult.append("\n}"); 150 wrappedFunction->SetHiddenValue(V8HiddenPropertyName::toStringString(), v8ExternalString(toStringResult)); 151 wrappedFunction->Set(v8::String::New("toString"), toStringFunction); 152 } 153 154 wrappedFunction->SetName(v8::String::New(fromWebCoreString(m_functionName), m_functionName.length())); 155 156 setListenerObject(wrappedFunction); 157 } 158 } 159} 160 161} // namespace WebCore 162