Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2010 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 "WebDevToolsFrontendImpl.h"
     33 
     34 #include "BoundObject.h"
     35 #include "ContextMenuController.h"
     36 #include "ContextMenuItem.h"
     37 #include "DOMWindow.h"
     38 #include "DebuggerAgent.h"
     39 #include "DevToolsRPCJS.h"
     40 #include "Document.h"
     41 #include "Event.h"
     42 #include "Frame.h"
     43 #include "InspectorBackend.h"
     44 #include "InspectorController.h"
     45 #include "InspectorFrontendHost.h"
     46 #include "Node.h"
     47 #include "Page.h"
     48 #include "Pasteboard.h"
     49 #include "PlatformString.h"
     50 #include "ProfilerAgent.h"
     51 #include "SecurityOrigin.h"
     52 #include "Settings.h"
     53 #include "ToolsAgent.h"
     54 #include "V8Binding.h"
     55 #include "V8DOMWrapper.h"
     56 #include "V8InspectorFrontendHost.h"
     57 #include "V8Node.h"
     58 #include "V8Proxy.h"
     59 #include "V8Utilities.h"
     60 #include "WebDevToolsFrontendClient.h"
     61 #include "WebFrameImpl.h"
     62 #include "WebScriptSource.h"
     63 #include "WebViewImpl.h"
     64 #include <wtf/OwnPtr.h>
     65 #include <wtf/Vector.h>
     66 
     67 using namespace WebCore;
     68 
     69 namespace WebKit {
     70 
     71 static v8::Local<v8::String> ToV8String(const String& s)
     72 {
     73     if (s.isNull())
     74         return v8::Local<v8::String>();
     75 
     76     return v8::String::New(reinterpret_cast<const uint16_t*>(s.characters()), s.length());
     77 }
     78 
     79 DEFINE_RPC_JS_BOUND_OBJ(DebuggerAgent, DEBUGGER_AGENT_STRUCT, DebuggerAgentDelegate, DEBUGGER_AGENT_DELEGATE_STRUCT)
     80 DEFINE_RPC_JS_BOUND_OBJ(ProfilerAgent, PROFILER_AGENT_STRUCT, ProfilerAgentDelegate, PROFILER_AGENT_DELEGATE_STRUCT)
     81 DEFINE_RPC_JS_BOUND_OBJ(ToolsAgent, TOOLS_AGENT_STRUCT, ToolsAgentDelegate, TOOLS_AGENT_DELEGATE_STRUCT)
     82 
     83 WebDevToolsFrontend* WebDevToolsFrontend::create(
     84     WebView* view,
     85     WebDevToolsFrontendClient* client,
     86     const WebString& applicationLocale)
     87 {
     88     return new WebDevToolsFrontendImpl(
     89       static_cast<WebViewImpl*>(view),
     90       client,
     91       applicationLocale);
     92 }
     93 
     94 WebDevToolsFrontendImpl::WebDevToolsFrontendImpl(
     95     WebViewImpl* webViewImpl,
     96     WebDevToolsFrontendClient* client,
     97     const String& applicationLocale)
     98     : m_webViewImpl(webViewImpl)
     99     , m_client(client)
    100     , m_applicationLocale(applicationLocale)
    101     , m_loaded(false)
    102 {
    103     WebFrameImpl* frame = m_webViewImpl->mainFrameImpl();
    104     v8::HandleScope scope;
    105     v8::Handle<v8::Context> frameContext = V8Proxy::context(frame->frame());
    106 
    107     m_debuggerAgentObj.set(new JSDebuggerAgentBoundObj(this, frameContext, "RemoteDebuggerAgent"));
    108     m_profilerAgentObj.set(new JSProfilerAgentBoundObj(this, frameContext, "RemoteProfilerAgent"));
    109     m_toolsAgentObj.set(new JSToolsAgentBoundObj(this, frameContext, "RemoteToolsAgent"));
    110 
    111     // Debugger commands should be sent using special method.
    112     BoundObject debuggerCommandExecutorObj(frameContext, this, "RemoteDebuggerCommandExecutor");
    113     debuggerCommandExecutorObj.addProtoFunction(
    114         "DebuggerCommand",
    115         WebDevToolsFrontendImpl::jsDebuggerCommand);
    116     debuggerCommandExecutorObj.addProtoFunction(
    117         "DebuggerPauseScript",
    118         WebDevToolsFrontendImpl::jsDebuggerPauseScript);
    119     debuggerCommandExecutorObj.build();
    120 
    121     BoundObject devToolsHost(frameContext, this, "InspectorFrontendHost");
    122     devToolsHost.addProtoFunction(
    123         "loaded",
    124         WebDevToolsFrontendImpl::jsLoaded);
    125     devToolsHost.addProtoFunction(
    126         "platform",
    127         WebDevToolsFrontendImpl::jsPlatform);
    128     devToolsHost.addProtoFunction(
    129         "port",
    130         WebDevToolsFrontendImpl::jsPort);
    131     devToolsHost.addProtoFunction(
    132         "copyText",
    133         WebDevToolsFrontendImpl::jsCopyText);
    134     devToolsHost.addProtoFunction(
    135         "activateWindow",
    136         WebDevToolsFrontendImpl::jsActivateWindow);
    137     devToolsHost.addProtoFunction(
    138         "closeWindow",
    139         WebDevToolsFrontendImpl::jsCloseWindow);
    140     devToolsHost.addProtoFunction(
    141         "attach",
    142         WebDevToolsFrontendImpl::jsDockWindow);
    143     devToolsHost.addProtoFunction(
    144         "detach",
    145         WebDevToolsFrontendImpl::jsUndockWindow);
    146     devToolsHost.addProtoFunction(
    147         "localizedStringsURL",
    148         WebDevToolsFrontendImpl::jsLocalizedStringsURL);
    149     devToolsHost.addProtoFunction(
    150         "hiddenPanels",
    151         WebDevToolsFrontendImpl::jsHiddenPanels);
    152     devToolsHost.addProtoFunction(
    153         "setting",
    154         WebDevToolsFrontendImpl::jsSetting);
    155     devToolsHost.addProtoFunction(
    156         "setSetting",
    157         WebDevToolsFrontendImpl::jsSetSetting);
    158     devToolsHost.addProtoFunction(
    159         "windowUnloading",
    160         WebDevToolsFrontendImpl::jsWindowUnloading);
    161     devToolsHost.addProtoFunction(
    162         "showContextMenu",
    163         WebDevToolsFrontendImpl::jsShowContextMenu);
    164     devToolsHost.build();
    165 }
    166 
    167 WebDevToolsFrontendImpl::~WebDevToolsFrontendImpl()
    168 {
    169     if (m_menuProvider)
    170         m_menuProvider->disconnect();
    171 }
    172 
    173 void WebDevToolsFrontendImpl::dispatchMessageFromAgent(const WebDevToolsMessageData& data)
    174 {
    175     Vector<String> v;
    176     v.append(data.className);
    177     v.append(data.methodName);
    178     for (size_t i = 0; i < data.arguments.size(); i++)
    179         v.append(data.arguments[i]);
    180     if (!m_loaded) {
    181         m_pendingIncomingMessages.append(v);
    182         return;
    183     }
    184     executeScript(v);
    185 }
    186 
    187 void WebDevToolsFrontendImpl::executeScript(const Vector<String>& v)
    188 {
    189     WebFrameImpl* frame = m_webViewImpl->mainFrameImpl();
    190     v8::HandleScope scope;
    191     v8::Handle<v8::Context> frameContext = V8Proxy::context(frame->frame());
    192     v8::Context::Scope contextScope(frameContext);
    193     v8::Handle<v8::Value> dispatchFunction = frameContext->Global()->Get(v8::String::New("devtools$$dispatch"));
    194     ASSERT(dispatchFunction->IsFunction());
    195     v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(dispatchFunction);
    196     Vector< v8::Handle<v8::Value> > args;
    197     for (size_t i = 0; i < v.size(); i++)
    198         args.append(ToV8String(v.at(i)));
    199     function->Call(frameContext->Global(), args.size(), args.data());
    200 }
    201 
    202 void WebDevToolsFrontendImpl::dispatchOnWebInspector(const String& methodName, const String& param)
    203 {
    204     WebFrameImpl* frame = m_webViewImpl->mainFrameImpl();
    205     v8::HandleScope scope;
    206     v8::Handle<v8::Context> frameContext = V8Proxy::context(frame->frame());
    207     v8::Context::Scope contextScope(frameContext);
    208 
    209     v8::Handle<v8::Value> webInspector = frameContext->Global()->Get(v8::String::New("WebInspector"));
    210     ASSERT(webInspector->IsObject());
    211     v8::Handle<v8::Object> webInspectorObj = v8::Handle<v8::Object>::Cast(webInspector);
    212 
    213     v8::Handle<v8::Value> method = webInspectorObj->Get(ToV8String(methodName));
    214     ASSERT(method->IsFunction());
    215     v8::Handle<v8::Function> methodFunc = v8::Handle<v8::Function>::Cast(method);
    216     v8::Handle<v8::Value> args[] = {
    217       ToV8String(param)
    218     };
    219     methodFunc->Call(frameContext->Global(), 1, args);
    220 }
    221 
    222 void WebDevToolsFrontendImpl::sendRpcMessage(const WebDevToolsMessageData& data)
    223 {
    224     m_client->sendMessageToAgent(data);
    225 }
    226 
    227 void WebDevToolsFrontendImpl::contextMenuItemSelected(ContextMenuItem* item)
    228 {
    229     int itemNumber = item->action() - ContextMenuItemBaseCustomTag;
    230     dispatchOnWebInspector("contextMenuItemSelected", String::number(itemNumber));
    231 }
    232 
    233 void WebDevToolsFrontendImpl::contextMenuCleared()
    234 {
    235     dispatchOnWebInspector("contextMenuCleared", "");
    236 }
    237 
    238 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsLoaded(const v8::Arguments& args)
    239 {
    240     WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value());
    241     frontend->m_loaded = true;
    242 
    243     // Grant the devtools page the ability to have source view iframes.
    244     Page* page = V8Proxy::retrieveFrameForEnteredContext()->page();
    245     SecurityOrigin* origin = page->mainFrame()->domWindow()->securityOrigin();
    246     origin->grantUniversalAccess();
    247 
    248     for (Vector<Vector<String> >::iterator it = frontend->m_pendingIncomingMessages.begin();
    249          it != frontend->m_pendingIncomingMessages.end();
    250          ++it) {
    251         frontend->executeScript(*it);
    252     }
    253     frontend->m_pendingIncomingMessages.clear();
    254     return v8::Undefined();
    255 }
    256 
    257 // static
    258 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsPlatform(const v8::Arguments& args)
    259 {
    260 #if defined(OS_MACOSX)
    261     return v8String("mac");
    262 #elif defined(OS_LINUX)
    263     return v8String("linux");
    264 #elif defined(OS_WIN)
    265     return v8String("windows");
    266 #else
    267     return v8String("unknown");
    268 #endif
    269 }
    270 
    271 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsPort(const v8::Arguments& args)
    272 {
    273     return v8::Undefined();
    274 }
    275 
    276 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsCopyText(const v8::Arguments& args)
    277 {
    278     String text = WebCore::toWebCoreStringWithNullCheck(args[0]);
    279     Pasteboard::generalPasteboard()->writePlainText(text);
    280     return v8::Undefined();
    281 }
    282 
    283 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsActivateWindow(const v8::Arguments& args)
    284 {
    285     WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value());
    286     frontend->m_client->activateWindow();
    287     return v8::Undefined();
    288 }
    289 
    290 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsCloseWindow(const v8::Arguments& args)
    291 {
    292     WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value());
    293     frontend->m_client->closeWindow();
    294     return v8::Undefined();
    295 }
    296 
    297 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsDockWindow(const v8::Arguments& args)
    298 {
    299     WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value());
    300     frontend->m_client->dockWindow();
    301     return v8::Undefined();
    302 }
    303 
    304 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsUndockWindow(const v8::Arguments& args)
    305 {
    306     WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value());
    307     frontend->m_client->undockWindow();
    308     return v8::Undefined();
    309 }
    310 
    311 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsLocalizedStringsURL(const v8::Arguments& args)
    312 {
    313     return v8::Undefined();
    314 }
    315 
    316 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsHiddenPanels(const v8::Arguments& args)
    317 {
    318     return v8String("");
    319 }
    320 
    321 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsDebuggerCommand(const v8::Arguments& args)
    322 {
    323     WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value());
    324     WebString command = WebCore::toWebCoreStringWithNullCheck(args[0]);
    325     frontend->m_client->sendDebuggerCommandToAgent(command);
    326     return v8::Undefined();
    327 }
    328 
    329 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsSetting(const v8::Arguments& args)
    330 {
    331     return v8::Undefined();
    332 }
    333 
    334 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsSetSetting(const v8::Arguments& args)
    335 {
    336     return v8::Undefined();
    337 }
    338 
    339 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsDebuggerPauseScript(const v8::Arguments& args)
    340 {
    341     WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value());
    342     frontend->m_client->sendDebuggerPauseScript();
    343     return v8::Undefined();
    344 }
    345 
    346 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsWindowUnloading(const v8::Arguments& args)
    347 {
    348     // TODO(pfeldman): Implement this.
    349     return v8::Undefined();
    350 }
    351 
    352 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsShowContextMenu(const v8::Arguments& args)
    353 {
    354     if (args.Length() < 2)
    355         return v8::Undefined();
    356 
    357     v8::Local<v8::Object> eventWrapper = v8::Local<v8::Object>::Cast(args[0]);
    358     if (V8DOMWrapper::domWrapperType(eventWrapper) != V8ClassIndex::MOUSEEVENT)
    359         return v8::Undefined();
    360 
    361     Event* event = V8Event::toNative(eventWrapper);
    362     if (!args[1]->IsArray())
    363         return v8::Undefined();
    364 
    365     v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(args[1]);
    366     Vector<ContextMenuItem*> items;
    367 
    368     for (size_t i = 0; i < array->Length(); ++i) {
    369         v8::Local<v8::Object> item = v8::Local<v8::Object>::Cast(array->Get(v8::Integer::New(i)));
    370         v8::Local<v8::Value> label = item->Get(v8::String::New("label"));
    371         v8::Local<v8::Value> id = item->Get(v8::String::New("id"));
    372         if (label->IsUndefined() || id->IsUndefined()) {
    373           items.append(new ContextMenuItem(SeparatorType,
    374                                            ContextMenuItemTagNoAction,
    375                                            String()));
    376         } else {
    377           ContextMenuAction typedId = static_cast<ContextMenuAction>(
    378               ContextMenuItemBaseCustomTag + id->ToInt32()->Value());
    379           items.append(new ContextMenuItem(ActionType,
    380                                            typedId,
    381                                            toWebCoreStringWithNullCheck(label)));
    382         }
    383     }
    384 
    385     WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value());
    386 
    387     frontend->m_menuProvider = MenuProvider::create(frontend, items);
    388 
    389     ContextMenuController* menuController = frontend->m_webViewImpl->page()->contextMenuController();
    390     menuController->showContextMenu(event, frontend->m_menuProvider);
    391 
    392     return v8::Undefined();
    393 }
    394 
    395 } // namespace WebKit
    396