/* * Copyright (C) 2007-2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "V8InjectedScriptHost.h" #include "V8Database.h" #include "V8HTMLAllCollection.h" #include "V8HTMLCollection.h" #include "V8Node.h" #include "V8NodeList.h" #include "V8Storage.h" #include "bindings/v8/BindingSecurity.h" #include "bindings/v8/ExceptionState.h" #include "bindings/v8/ScriptDebugServer.h" #include "bindings/v8/ScriptValue.h" #include "bindings/v8/V8AbstractEventListener.h" #include "bindings/v8/V8Binding.h" #include "bindings/v8/V8HiddenPropertyName.h" #include "bindings/v8/V8ScriptRunner.h" #include "bindings/v8/custom/V8Float32ArrayCustom.h" #include "bindings/v8/custom/V8Float64ArrayCustom.h" #include "bindings/v8/custom/V8Int16ArrayCustom.h" #include "bindings/v8/custom/V8Int32ArrayCustom.h" #include "bindings/v8/custom/V8Int8ArrayCustom.h" #include "bindings/v8/custom/V8Uint16ArrayCustom.h" #include "bindings/v8/custom/V8Uint32ArrayCustom.h" #include "bindings/v8/custom/V8Uint8ArrayCustom.h" #include "bindings/v8/custom/V8Uint8ClampedArrayCustom.h" #include "core/inspector/InjectedScript.h" #include "core/inspector/InjectedScriptHost.h" #include "core/inspector/InspectorDOMAgent.h" #include "modules/webdatabase/Database.h" #include "platform/JSONValues.h" namespace WebCore { Node* InjectedScriptHost::scriptValueAsNode(ScriptValue value) { v8::HandleScope scope(value.isolate()); if (!value.isObject() || value.isNull()) return 0; return V8Node::toNative(v8::Handle::Cast(value.v8Value())); } ScriptValue InjectedScriptHost::nodeAsScriptValue(ScriptState* state, Node* node) { v8::Isolate* isolate = state->isolate(); v8::HandleScope scope(isolate); v8::Local context = state->context(); v8::Context::Scope contextScope(context); ExceptionState exceptionState(v8::Handle(), isolate); if (!BindingSecurity::shouldAllowAccessToNode(node, exceptionState)) return ScriptValue(v8::Null(isolate), isolate); return ScriptValue(toV8(node, v8::Handle(), isolate), isolate); } void V8InjectedScriptHost::inspectedObjectMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() < 1) return; if (!info[0]->IsInt32()) { throwTypeError("argument has to be an integer", info.GetIsolate()); return; } InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); InjectedScriptHost::InspectableObject* object = host->inspectedObject(info[0]->ToInt32()->Value()); v8SetReturnValue(info, object->get(ScriptState::current()).v8Value()); } void V8InjectedScriptHost::internalConstructorNameMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() < 1) return; if (!info[0]->IsObject()) return; v8SetReturnValue(info, info[0]->ToObject()->GetConstructorName()); } void V8InjectedScriptHost::isHTMLAllCollectionMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() < 1) return; if (!info[0]->IsObject()) { v8SetReturnValue(info, false); return; } v8SetReturnValue(info, V8HTMLAllCollection::hasInstance(info[0], info.GetIsolate(), worldType(info.GetIsolate()))); } void V8InjectedScriptHost::typeMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() < 1) return; v8::Isolate* isolate = info.GetIsolate(); v8::Handle value = info[0]; if (value->IsString()) { v8SetReturnValue(info, v8AtomicString(isolate, "string")); return; } if (value->IsArray()) { v8SetReturnValue(info, v8AtomicString(isolate, "array")); return; } if (value->IsBoolean()) { v8SetReturnValue(info, v8AtomicString(isolate, "boolean")); return; } if (value->IsNumber()) { v8SetReturnValue(info, v8AtomicString(isolate, "number")); return; } if (value->IsDate()) { v8SetReturnValue(info, v8AtomicString(isolate, "date")); return; } if (value->IsRegExp()) { v8SetReturnValue(info, v8AtomicString(isolate, "regexp")); return; } WrapperWorldType currentWorldType = worldType(isolate); if (V8Node::hasInstance(value, isolate, currentWorldType)) { v8SetReturnValue(info, v8AtomicString(isolate, "node")); return; } if (V8NodeList::hasInstance(value, isolate, currentWorldType)) { v8SetReturnValue(info, v8AtomicString(isolate, "array")); return; } if (V8HTMLCollection::hasInstance(value, isolate, currentWorldType)) { v8SetReturnValue(info, v8AtomicString(isolate, "array")); return; } if (V8Int8Array::hasInstance(value, isolate, currentWorldType) || V8Int16Array::hasInstance(value, isolate, currentWorldType) || V8Int32Array::hasInstance(value, isolate, currentWorldType)) { v8SetReturnValue(info, v8AtomicString(isolate, "array")); return; } if (V8Uint8Array::hasInstance(value, isolate, currentWorldType) || V8Uint16Array::hasInstance(value, isolate, currentWorldType) || V8Uint32Array::hasInstance(value, isolate, currentWorldType)) { v8SetReturnValue(info, v8AtomicString(isolate, "array")); return; } if (V8Float32Array::hasInstance(value, isolate, currentWorldType) || V8Float64Array::hasInstance(value, isolate, currentWorldType)) { v8SetReturnValue(info, v8AtomicString(isolate, "array")); return; } if (V8Uint8ClampedArray::hasInstance(value, isolate, currentWorldType)) { v8SetReturnValue(info, v8AtomicString(isolate, "array")); return; } } static bool setFunctionName(v8::Handle& result, const v8::Handle& value, v8::Isolate* isolate) { if (value->IsString() && v8::Handle::Cast(value)->Length()) { result->Set(v8AtomicString(isolate, "functionName"), value); return true; } return false; } void V8InjectedScriptHost::functionDetailsMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() < 1) return; v8::Isolate* isolate = info.GetIsolate(); v8::Handle value = info[0]; if (!value->IsFunction()) return; v8::Handle function = v8::Handle::Cast(value); int lineNumber = function->GetScriptLineNumber(); int columnNumber = function->GetScriptColumnNumber(); v8::Local location = v8::Object::New(); location->Set(v8AtomicString(isolate, "lineNumber"), v8::Integer::New(lineNumber, isolate)); location->Set(v8AtomicString(isolate, "columnNumber"), v8::Integer::New(columnNumber, isolate)); location->Set(v8AtomicString(isolate, "scriptId"), v8::Integer::New(function->ScriptId(), isolate)->ToString()); v8::Local result = v8::Object::New(); result->Set(v8AtomicString(isolate, "location"), location); if (!setFunctionName(result, function->GetDisplayName(), isolate) && !setFunctionName(result, function->GetName(), isolate) && !setFunctionName(result, function->GetInferredName(), isolate)) result->Set(v8AtomicString(isolate, "functionName"), v8AtomicString(isolate, "")); InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); ScriptDebugServer& debugServer = host->scriptDebugServer(); v8::Handle scopes = debugServer.functionScopes(function); if (!scopes.IsEmpty() && scopes->IsArray()) result->Set(v8AtomicString(isolate, "rawScopes"), scopes); v8SetReturnValue(info, result); } void V8InjectedScriptHost::getInternalPropertiesMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() < 1) return; v8::Handle object = v8::Handle::Cast(info[0]); InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); ScriptDebugServer& debugServer = host->scriptDebugServer(); v8SetReturnValue(info, debugServer.getInternalProperties(object)); } static v8::Handle getJSListenerFunctions(Document* document, const EventListenerInfo& listenerInfo, v8::Isolate* isolate) { v8::Local result = v8::Array::New(isolate); size_t handlersCount = listenerInfo.eventListenerVector.size(); for (size_t i = 0, outputIndex = 0; i < handlersCount; ++i) { RefPtr listener = listenerInfo.eventListenerVector[i].listener; if (listener->type() != EventListener::JSEventListenerType) { ASSERT_NOT_REACHED(); continue; } V8AbstractEventListener* v8Listener = static_cast(listener.get()); v8::Local context = toV8Context(document, v8Listener->world()); // Hide listeners from other contexts. if (context != isolate->GetCurrentContext()) continue; v8::Local function; { // getListenerObject() may cause JS in the event attribute to get compiled, potentially unsuccessfully. v8::TryCatch block; function = v8Listener->getListenerObject(document); if (block.HasCaught()) continue; } ASSERT(!function.IsEmpty()); v8::Local listenerEntry = v8::Object::New(); listenerEntry->Set(v8AtomicString(isolate, "listener"), function); listenerEntry->Set(v8AtomicString(isolate, "useCapture"), v8::Boolean::New(isolate, listenerInfo.eventListenerVector[i].useCapture)); result->Set(v8::Number::New(isolate, outputIndex++), listenerEntry); } return result; } void V8InjectedScriptHost::getEventListenersMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() < 1) return; v8::Local value = info[0]; if (!V8Node::hasInstance(value, info.GetIsolate(), worldType(info.GetIsolate()))) return; Node* node = V8Node::toNative(value->ToObject()); if (!node) return; InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); Vector listenersArray; host->getEventListenersImpl(node, listenersArray); v8::Local result = v8::Object::New(); for (size_t i = 0; i < listenersArray.size(); ++i) { v8::Handle listeners = getJSListenerFunctions(&node->document(), listenersArray[i], info.GetIsolate()); if (!listeners->Length()) continue; AtomicString eventType = listenersArray[i].eventType; result->Set(v8String(info.GetIsolate(), eventType), listeners); } v8SetReturnValue(info, result); } void V8InjectedScriptHost::inspectMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() < 2) return; InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); ScriptValue object(info[0], info.GetIsolate()); ScriptValue hints(info[1], info.GetIsolate()); host->inspectImpl(object.toJSONValue(ScriptState::current()), hints.toJSONValue(ScriptState::current())); } void V8InjectedScriptHost::databaseIdMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() > 0 && V8Database::hasInstance(info[0], info.GetIsolate(), worldType(info.GetIsolate()))) { Database* database = V8Database::toNative(v8::Handle::Cast(info[0])); if (database) { InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); { v8SetReturnValueStringOrUndefined(info, host->databaseIdImpl(database), info.GetIsolate()); return; } } } } void V8InjectedScriptHost::storageIdMethodCustom(const v8::FunctionCallbackInfo& info) { if (info.Length() > 0 && V8Storage::hasInstance(info[0], info.GetIsolate(), worldType(info.GetIsolate()))) { Storage* storage = V8Storage::toNative(v8::Handle::Cast(info[0])); if (storage) { InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); v8SetReturnValueStringOrUndefined(info, host->storageIdImpl(storage), info.GetIsolate()); return; } } } void V8InjectedScriptHost::evaluateMethodCustom(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); if (info.Length() < 1) { isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(isolate, "One argument expected."))); return; } v8::Handle expression = info[0]->ToString(); if (expression.IsEmpty()) { isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(isolate, "The argument must be a string."))); return; } ASSERT(!isolate->GetCurrentContext().IsEmpty()); v8::TryCatch tryCatch; v8::Handle result = V8ScriptRunner::compileAndRunInternalScript(expression, info.GetIsolate()); if (tryCatch.HasCaught()) { v8SetReturnValue(info, tryCatch.ReThrow()); return; } v8SetReturnValue(info, result); } void V8InjectedScriptHost::setFunctionVariableValueMethodCustom(const v8::FunctionCallbackInfo& info) { v8::Handle functionValue = info[0]; int scopeIndex = info[1]->Int32Value(); String variableName = toCoreStringWithUndefinedOrNullCheck(info[2]); v8::Handle newValue = info[3]; InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); ScriptDebugServer& debugServer = host->scriptDebugServer(); v8SetReturnValue(info, debugServer.setFunctionVariableValue(functionValue, scopeIndex, variableName, newValue)); } static bool getFunctionLocation(const v8::FunctionCallbackInfo& info, String* scriptId, int* lineNumber, int* columnNumber) { if (info.Length() < 1) return false; v8::Handle fn = info[0]; if (!fn->IsFunction()) return false; v8::Handle function = v8::Handle::Cast(fn); *lineNumber = function->GetScriptLineNumber(); *columnNumber = function->GetScriptColumnNumber(); if (*lineNumber == v8::Function::kLineOffsetNotFound || *columnNumber == v8::Function::kLineOffsetNotFound) return false; *scriptId = String::number(function->ScriptId()); return true; } void V8InjectedScriptHost::debugFunctionMethodCustom(const v8::FunctionCallbackInfo& info) { String scriptId; int lineNumber; int columnNumber; if (!getFunctionLocation(info, &scriptId, &lineNumber, &columnNumber)) return; InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); host->debugFunction(scriptId, lineNumber, columnNumber); } void V8InjectedScriptHost::undebugFunctionMethodCustom(const v8::FunctionCallbackInfo& info) { String scriptId; int lineNumber; int columnNumber; if (!getFunctionLocation(info, &scriptId, &lineNumber, &columnNumber)) return; InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); host->undebugFunction(scriptId, lineNumber, columnNumber); } void V8InjectedScriptHost::monitorFunctionMethodCustom(const v8::FunctionCallbackInfo& info) { String scriptId; int lineNumber; int columnNumber; if (!getFunctionLocation(info, &scriptId, &lineNumber, &columnNumber)) return; v8::Handle name; if (info.Length() > 0 && info[0]->IsFunction()) { v8::Handle function = v8::Handle::Cast(info[0]); name = function->GetName(); if (!name->IsString() || !v8::Handle::Cast(name)->Length()) name = function->GetInferredName(); } InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); host->monitorFunction(scriptId, lineNumber, columnNumber, toCoreStringWithUndefinedOrNullCheck(name)); } void V8InjectedScriptHost::unmonitorFunctionMethodCustom(const v8::FunctionCallbackInfo& info) { String scriptId; int lineNumber; int columnNumber; if (!getFunctionLocation(info, &scriptId, &lineNumber, &columnNumber)) return; InjectedScriptHost* host = V8InjectedScriptHost::toNative(info.Holder()); host->unmonitorFunction(scriptId, lineNumber, columnNumber); } } // namespace WebCore