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