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