• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ToV8String(const String & s)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 
DEFINE_RPC_JS_BOUND_OBJ(DebuggerAgent,DEBUGGER_AGENT_STRUCT,DebuggerAgentDelegate,DEBUGGER_AGENT_DELEGATE_STRUCT)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 
WebDevToolsFrontendImpl(WebViewImpl * webViewImpl,WebDevToolsFrontendClient * client,const String & applicationLocale)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 
~WebDevToolsFrontendImpl()167 WebDevToolsFrontendImpl::~WebDevToolsFrontendImpl()
168 {
169     if (m_menuProvider)
170         m_menuProvider->disconnect();
171 }
172 
dispatchMessageFromAgent(const WebDevToolsMessageData & data)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 
executeScript(const Vector<String> & v)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 
dispatchOnWebInspector(const String & methodName,const String & param)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 
sendRpcMessage(const WebDevToolsMessageData & data)222 void WebDevToolsFrontendImpl::sendRpcMessage(const WebDevToolsMessageData& data)
223 {
224     m_client->sendMessageToAgent(data);
225 }
226 
contextMenuItemSelected(ContextMenuItem * item)227 void WebDevToolsFrontendImpl::contextMenuItemSelected(ContextMenuItem* item)
228 {
229     int itemNumber = item->action() - ContextMenuItemBaseCustomTag;
230     dispatchOnWebInspector("contextMenuItemSelected", String::number(itemNumber));
231 }
232 
contextMenuCleared()233 void WebDevToolsFrontendImpl::contextMenuCleared()
234 {
235     dispatchOnWebInspector("contextMenuCleared", "");
236 }
237 
jsLoaded(const v8::Arguments & args)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
jsPlatform(const v8::Arguments & args)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 
jsPort(const v8::Arguments & args)271 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsPort(const v8::Arguments& args)
272 {
273     return v8::Undefined();
274 }
275 
jsCopyText(const v8::Arguments & args)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 
jsActivateWindow(const v8::Arguments & args)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 
jsCloseWindow(const v8::Arguments & args)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 
jsDockWindow(const v8::Arguments & args)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 
jsUndockWindow(const v8::Arguments & args)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 
jsLocalizedStringsURL(const v8::Arguments & args)311 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsLocalizedStringsURL(const v8::Arguments& args)
312 {
313     return v8::Undefined();
314 }
315 
jsHiddenPanels(const v8::Arguments & args)316 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsHiddenPanels(const v8::Arguments& args)
317 {
318     return v8String("");
319 }
320 
jsDebuggerCommand(const v8::Arguments & args)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 
jsSetting(const v8::Arguments & args)329 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsSetting(const v8::Arguments& args)
330 {
331     return v8::Undefined();
332 }
333 
jsSetSetting(const v8::Arguments & args)334 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsSetSetting(const v8::Arguments& args)
335 {
336     return v8::Undefined();
337 }
338 
jsDebuggerPauseScript(const v8::Arguments & args)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 
jsWindowUnloading(const v8::Arguments & args)346 v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsWindowUnloading(const v8::Arguments& args)
347 {
348     // TODO(pfeldman): Implement this.
349     return v8::Undefined();
350 }
351 
jsShowContextMenu(const v8::Arguments & args)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