Home | History | Annotate | Download | only in custom
      1 /*
      2  * Copyright (C) 2007-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 "V8InjectedScriptHost.h"
     33 
     34 #include "DOMWindow.h"
     35 #include "Database.h"
     36 #include "Frame.h"
     37 #include "InjectedScript.h"
     38 #include "InjectedScriptHost.h"
     39 #include "InspectorController.h"
     40 #include "Node.h"
     41 #include "Page.h"
     42 #include "SerializedScriptValue.h"
     43 
     44 #include "V8Binding.h"
     45 #include "V8Database.h"
     46 #include "V8Node.h"
     47 #include "V8Proxy.h"
     48 #include "V8Storage.h"
     49 #include <wtf/RefPtr.h>
     50 
     51 namespace WebCore {
     52 
     53 static void WeakReferenceCallback(v8::Persistent<v8::Value> object, void* parameter)
     54 {
     55     InjectedScriptHost* nativeObject = static_cast<InjectedScriptHost*>(parameter);
     56     nativeObject->deref();
     57     object.Dispose();
     58 }
     59 
     60 static v8::Local<v8::Object> createInjectedScriptHostV8Wrapper(InjectedScriptHost* host)
     61 {
     62     V8ClassIndex::V8WrapperType descriptorType = V8ClassIndex::INJECTEDSCRIPTHOST;
     63     v8::Local<v8::Function> function = V8InjectedScriptHost::GetTemplate()->GetFunction();
     64     if (function.IsEmpty()) {
     65         // Return if allocation failed.
     66         return v8::Local<v8::Object>();
     67     }
     68     v8::Local<v8::Object> instance = SafeAllocation::newInstance(function);
     69     if (instance.IsEmpty()) {
     70         // Avoid setting the wrapper if allocation failed.
     71         return v8::Local<v8::Object>();
     72     }
     73     V8DOMWrapper::setDOMWrapper(instance, V8ClassIndex::ToInt(descriptorType), host);
     74     // Create a weak reference to the v8 wrapper of InspectorBackend to deref
     75     // InspectorBackend when the wrapper is garbage collected.
     76     host->ref();
     77     v8::Persistent<v8::Object> weakHandle = v8::Persistent<v8::Object>::New(instance);
     78     weakHandle.MakeWeak(host, &WeakReferenceCallback);
     79     return instance;
     80 }
     81 
     82 static ScriptObject createInjectedScript(const String& scriptSource, InjectedScriptHost* injectedScriptHost, ScriptState* inspectedScriptState, long id)
     83 {
     84     v8::HandleScope scope;
     85 
     86     v8::Local<v8::Context> inspectedContext = inspectedScriptState->context();
     87     v8::Context::Scope contextScope(inspectedContext);
     88 
     89     // Call custom code to create InjectedScripHost wrapper specific for the context
     90     // instead of calling toV8() that would create the
     91     // wrapper in the current context.
     92     // FIXME: make it possible to use generic bindings factory for InjectedScriptHost.
     93     v8::Local<v8::Object> scriptHostWrapper = createInjectedScriptHostV8Wrapper(injectedScriptHost);
     94     if (scriptHostWrapper.IsEmpty())
     95         return ScriptObject();
     96 
     97     v8::Local<v8::Object> windowGlobal = inspectedContext->Global();
     98 
     99     // Inject javascript into the context. The compiled script is supposed to evaluate into
    100     // a single anonymous function(it's anonymous to avoid cluttering the global object with
    101     // inspector's stuff) the function is called a few lines below with InjectedScriptHost wrapper,
    102     // injected script id and explicit reference to the inspected global object. The function is expected
    103     // to create and configure InjectedScript instance that is going to be used by the inspector.
    104     v8::Local<v8::Script> script = v8::Script::Compile(v8String(scriptSource));
    105     v8::Local<v8::Value> v = script->Run();
    106     ASSERT(!v.IsEmpty());
    107     ASSERT(v->IsFunction());
    108 
    109     v8::Handle<v8::Value> args[] = {
    110       scriptHostWrapper,
    111       windowGlobal,
    112       v8::Number::New(id)
    113     };
    114     v8::Local<v8::Value> injectedScriptValue = v8::Function::Cast(*v)->Call(windowGlobal, 3, args);
    115     v8::Local<v8::Object> injectedScript(v8::Object::Cast(*injectedScriptValue));
    116     return ScriptObject(inspectedScriptState, injectedScript);
    117 }
    118 
    119 v8::Handle<v8::Value> V8InjectedScriptHost::nodeForIdCallback(const v8::Arguments& args)
    120 {
    121     INC_STATS("InjectedScriptHost.nodeForId()");
    122     if (args.Length() < 1)
    123         return v8::Undefined();
    124 
    125     InjectedScriptHost* host = V8InjectedScriptHost::toNative(args.Holder());
    126 
    127     Node* node = host->nodeForId(args[0]->ToInt32()->Value());
    128     if (!node)
    129         return v8::Undefined();
    130 
    131     InspectorController* ic = host->inspectorController();
    132     if (!ic)
    133         return v8::Undefined();
    134 
    135     return toV8(node);
    136 }
    137 
    138 v8::Handle<v8::Value> V8InjectedScriptHost::pushNodePathToFrontendCallback(const v8::Arguments& args)
    139 {
    140     INC_STATS("InjectedScriptHost.pushNodePathToFrontend()");
    141     if (args.Length() < 3)
    142         return v8::Undefined();
    143 
    144     InjectedScriptHost* host = V8InjectedScriptHost::toNative(args.Holder());
    145     Node* node = V8Node::toNative(v8::Handle<v8::Object>::Cast(args[0]));
    146     bool withChildren = args[1]->ToBoolean()->Value();
    147     bool selectInUI = args[2]->ToBoolean()->Value();
    148     if (node)
    149         return v8::Number::New(host->pushNodePathToFrontend(node, withChildren, selectInUI));
    150 
    151     return v8::Undefined();
    152 }
    153 
    154 #if ENABLE(DATABASE)
    155 v8::Handle<v8::Value> V8InjectedScriptHost::databaseForIdCallback(const v8::Arguments& args)
    156 {
    157     INC_STATS("InjectedScriptHost.databaseForId()");
    158     if (args.Length() < 1)
    159         return v8::Undefined();
    160 
    161     InjectedScriptHost* host = V8InjectedScriptHost::toNative(args.Holder());
    162     Database* database = host->databaseForId(args[0]->ToInt32()->Value());
    163     if (!database)
    164         return v8::Undefined();
    165     return toV8(database);
    166 }
    167 
    168 v8::Handle<v8::Value> V8InjectedScriptHost::selectDatabaseCallback(const v8::Arguments& args)
    169 {
    170     INC_STATS("InjectedScriptHost.selectDatabase()");
    171     if (args.Length() < 1)
    172         return v8::Undefined();
    173 
    174     InjectedScriptHost* host = V8InjectedScriptHost::toNative(args.Holder());
    175     Database* database = V8Database::toNative(v8::Handle<v8::Object>::Cast(args[0]));
    176     if (database)
    177         host->selectDatabase(database);
    178 
    179     return v8::Undefined();
    180 }
    181 #endif
    182 
    183 #if ENABLE(DOM_STORAGE)
    184 v8::Handle<v8::Value> V8InjectedScriptHost::selectDOMStorageCallback(const v8::Arguments& args)
    185 {
    186     INC_STATS("InjectedScriptHost.selectDOMStorage()");
    187     if (args.Length() < 1)
    188         return v8::Undefined();
    189 
    190     InjectedScriptHost* host = V8InjectedScriptHost::toNative(args.Holder());
    191     Storage* storage = V8Storage::toNative(v8::Handle<v8::Object>::Cast(args[0]));
    192     if (storage)
    193         host->selectDOMStorage(storage);
    194 
    195     return v8::Undefined();
    196 }
    197 #endif
    198 
    199 v8::Handle<v8::Value> V8InjectedScriptHost::reportDidDispatchOnInjectedScriptCallback(const v8::Arguments& args)
    200 {
    201     INC_STATS("InjectedScriptHost.reportDidDispatchOnInjectedScript()");
    202     if (args.Length() < 3)
    203         return v8::Undefined();
    204     InjectedScriptHost* host = V8InjectedScriptHost::toNative(args.Holder());
    205     int callId = args[0]->ToInt32()->Value();
    206     RefPtr<SerializedScriptValue> result(SerializedScriptValue::create(args[1]));
    207     bool isException = args[2]->ToBoolean()->Value();
    208     host->reportDidDispatchOnInjectedScript(callId, result.get(), isException);
    209     return v8::Undefined();
    210 }
    211 
    212 InjectedScript InjectedScriptHost::injectedScriptFor(ScriptState* inspectedScriptState)
    213 {
    214     v8::HandleScope handleScope;
    215     v8::Local<v8::Context> context = inspectedScriptState->context();
    216     v8::Context::Scope contextScope(context);
    217 
    218     v8::Local<v8::Object> global = context->Global();
    219     // Skip proxy object. The proxy object will survive page navigation while we need
    220     // an object whose lifetime consides with that of the inspected context.
    221     global = v8::Local<v8::Object>::Cast(global->GetPrototype());
    222 
    223     v8::Local<v8::String> key = v8::String::New("Devtools_InjectedScript");
    224     v8::Local<v8::Value> val = global->GetHiddenValue(key);
    225     if (!val.IsEmpty() && val->IsObject())
    226         return InjectedScript(ScriptObject(inspectedScriptState, v8::Local<v8::Object>::Cast(val)));
    227 
    228     ASSERT(!m_injectedScriptSource.isEmpty());
    229     ScriptObject injectedScriptObject = createInjectedScript(m_injectedScriptSource, this, inspectedScriptState, m_nextInjectedScriptId);
    230     InjectedScript result(injectedScriptObject);
    231     m_idToInjectedScript.set(m_nextInjectedScriptId, result);
    232     ++m_nextInjectedScriptId;
    233     global->SetHiddenValue(key, injectedScriptObject.v8Object());
    234     return result;
    235 }
    236 
    237 } // namespace WebCore
    238