2 * Copyright (C) 2010-2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "WebDevToolsAgentImpl.h"
34 #include "InspectorBackendDispatcher.h"
35 #include "InspectorFrontend.h"
36 #include "InspectorProtocolVersion.h"
37 #include "RuntimeEnabledFeatures.h"
38 #include "WebDataSource.h"
39 #include "WebDevToolsAgentClient.h"
40 #include "WebFrameImpl.h"
41 #include "WebInputEventConversion.h"
42 #include "WebMemoryUsageInfo.h"
43 #include "WebSettings.h"
44 #include "WebViewClient.h"
45 #include "WebViewImpl.h"
46 #include "bindings/v8/PageScriptDebugServer.h"
47 #include "bindings/v8/ScriptController.h"
48 #include "bindings/v8/V8Binding.h"
49 #include "bindings/v8/V8Utilities.h"
50 #include "core/dom/ExceptionCode.h"
51 #include "core/fetch/MemoryCache.h"
52 #include "core/frame/Frame.h"
53 #include "core/frame/FrameView.h"
54 #include "core/inspector/InjectedScriptHost.h"
55 #include "core/inspector/InspectorController.h"
56 #include "core/page/Page.h"
57 #include "core/page/PageGroup.h"
58 #include "core/rendering/RenderView.h"
59 #include "platform/JSONValues.h"
60 #include "platform/graphics/GraphicsContext.h"
61 #include "platform/network/ResourceError.h"
62 #include "platform/network/ResourceRequest.h"
63 #include "platform/network/ResourceResponse.h"
64 #include "public/platform/Platform.h"
65 #include "public/platform/WebRect.h"
66 #include "public/platform/WebString.h"
67 #include "public/platform/WebURL.h"
68 #include "public/platform/WebURLError.h"
69 #include "public/platform/WebURLRequest.h"
70 #include "public/platform/WebURLResponse.h"
71 #include "wtf/CurrentTime.h"
72 #include "wtf/MathExtras.h"
73 #include "wtf/Noncopyable.h"
74 #include "wtf/text/WTFString.h"
76 using namespace WebCore;
79 namespace OverlayZOrders {
80 // Use 99 as a big z-order number so that highlight is above other overlays.
81 static const int highlight = 99;
86 class ClientMessageLoopAdapter : public PageScriptDebugServer::ClientMessageLoop {
88 static void ensureClientMessageLoopCreated(WebDevToolsAgentClient* client)
92 OwnPtr<ClientMessageLoopAdapter> instance = adoptPtr(new ClientMessageLoopAdapter(adoptPtr(client->createClientMessageLoop())));
93 s_instance = instance.get();
94 PageScriptDebugServer::shared().setClientMessageLoop(instance.release());
97 static void inspectedViewClosed(WebViewImpl* view)
100 s_instance->m_frozenViews.remove(view);
103 static void didNavigate()
105 // Release render thread if necessary.
106 if (s_instance && s_instance->m_running)
107 PageScriptDebugServer::shared().continueProgram();
111 ClientMessageLoopAdapter(PassOwnPtr<blink::WebDevToolsAgentClient::WebKitClientMessageLoop> messageLoop)
113 , m_messageLoop(messageLoop) { }
116 virtual void run(Page* page)
122 Vector<WebViewImpl*> views;
124 // 1. Disable input events.
125 HashSet<Page*>::const_iterator end = page->group().pages().end();
126 for (HashSet<Page*>::const_iterator it = page->group().pages().begin(); it != end; ++it) {
127 WebViewImpl* view = WebViewImpl::fromPage(*it);
130 m_frozenViews.add(view);
132 view->setIgnoreInputEvents(true);
135 // 2. Disable active objects
136 WebView::willEnterModalLoop();
138 // 3. Process messages until quitNow is called.
139 m_messageLoop->run();
141 // 4. Resume active objects
142 WebView::didExitModalLoop();
144 // 5. Resume input events.
145 for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) {
146 if (m_frozenViews.contains(*it)) {
147 // The view was not closed during the dispatch.
148 (*it)->setIgnoreInputEvents(false);
152 // 6. All views have been resumed, clear the set.
153 m_frozenViews.clear();
158 virtual void quitNow()
160 m_messageLoop->quitNow();
164 OwnPtr<blink::WebDevToolsAgentClient::WebKitClientMessageLoop> m_messageLoop;
165 typedef HashSet<WebViewImpl*> FrozenViewsSet;
166 FrozenViewsSet m_frozenViews;
167 // FIXME: The ownership model for s_instance is somewhat complicated. Can we make this simpler?
168 static ClientMessageLoopAdapter* s_instance;
171 ClientMessageLoopAdapter* ClientMessageLoopAdapter::s_instance = 0;
173 class DebuggerTask : public PageScriptDebugServer::Task {
175 DebuggerTask(PassOwnPtr<WebDevToolsAgent::MessageDescriptor> descriptor)
176 : m_descriptor(descriptor)
180 virtual ~DebuggerTask() { }
183 if (WebDevToolsAgent* webagent = m_descriptor->agent())
184 webagent->dispatchOnInspectorBackend(m_descriptor->message());
188 OwnPtr<WebDevToolsAgent::MessageDescriptor> m_descriptor;
191 WebDevToolsAgentImpl::WebDevToolsAgentImpl(
192 WebViewImpl* webViewImpl,
193 WebDevToolsAgentClient* client)
194 : m_hostId(client->hostIdentifier())
196 , m_webViewImpl(webViewImpl)
198 , m_generatingEvent(false)
199 , m_deviceMetricsEnabled(false)
200 , m_emulateViewportEnabled(false)
201 , m_originalViewportEnabled(false)
202 , m_isOverlayScrollbarsEnabled(false)
204 ASSERT(m_hostId > 0);
205 ClientMessageLoopAdapter::ensureClientMessageLoopCreated(m_client);
208 WebDevToolsAgentImpl::~WebDevToolsAgentImpl()
210 ClientMessageLoopAdapter::inspectedViewClosed(m_webViewImpl);
212 blink::Platform::current()->currentThread()->removeTaskObserver(this);
215 void WebDevToolsAgentImpl::attach()
220 inspectorController()->connectFrontend(this);
221 inspectorController()->webViewResized(m_webViewImpl->size());
222 blink::Platform::current()->currentThread()->addTaskObserver(this);
226 void WebDevToolsAgentImpl::reattach(const WebString& savedState)
231 inspectorController()->reuseFrontend(this, savedState);
232 blink::Platform::current()->currentThread()->addTaskObserver(this);
236 void WebDevToolsAgentImpl::detach()
238 blink::Platform::current()->currentThread()->removeTaskObserver(this);
240 // Prevent controller from sending messages to the frontend.
241 InspectorController* ic = inspectorController();
242 ic->disconnectFrontend();
247 void WebDevToolsAgentImpl::didNavigate()
249 ClientMessageLoopAdapter::didNavigate();
252 void WebDevToolsAgentImpl::didBeginFrame(int frameId)
254 if (InspectorController* ic = inspectorController())
255 ic->didBeginFrame(frameId);
258 void WebDevToolsAgentImpl::didCancelFrame()
260 if (InspectorController* ic = inspectorController())
261 ic->didCancelFrame();
264 void WebDevToolsAgentImpl::willComposite()
266 if (InspectorController* ic = inspectorController())
270 void WebDevToolsAgentImpl::didComposite()
272 if (InspectorController* ic = inspectorController())
276 void WebDevToolsAgentImpl::didCreateScriptContext(WebFrameImpl* webframe, int worldId)
278 // Skip non main world contexts.
281 if (WebCore::Frame* frame = webframe->frame())
282 frame->script().setContextDebugId(m_hostId);
285 void WebDevToolsAgentImpl::webViewResized(const WebSize& size)
287 if (InspectorController* ic = inspectorController())
288 ic->webViewResized(IntSize());
291 bool WebDevToolsAgentImpl::handleInputEvent(WebCore::Page* page, const WebInputEvent& inputEvent)
293 if (!m_attached && !m_generatingEvent)
296 InspectorController* ic = inspectorController();
300 if (WebInputEvent::isGestureEventType(inputEvent.type) && inputEvent.type == WebInputEvent::GestureTap) {
301 // Only let GestureTab in (we only need it and we know PlatformGestureEventBuilder supports it).
302 PlatformGestureEvent gestureEvent = PlatformGestureEventBuilder(page->mainFrame()->view(), *static_cast<const WebGestureEvent*>(&inputEvent));
303 return ic->handleGestureEvent(page->mainFrame(), gestureEvent);
305 if (WebInputEvent::isMouseEventType(inputEvent.type) && inputEvent.type != WebInputEvent::MouseEnter) {
306 // PlatformMouseEventBuilder does not work with MouseEnter type, so we filter it out manually.
307 PlatformMouseEvent mouseEvent = PlatformMouseEventBuilder(page->mainFrame()->view(), *static_cast<const WebMouseEvent*>(&inputEvent));
308 return ic->handleMouseEvent(page->mainFrame(), mouseEvent);
310 if (WebInputEvent::isTouchEventType(inputEvent.type)) {
311 PlatformTouchEvent touchEvent = PlatformTouchEventBuilder(page->mainFrame()->view(), *static_cast<const WebTouchEvent*>(&inputEvent));
312 return ic->handleTouchEvent(page->mainFrame(), touchEvent);
314 if (WebInputEvent::isKeyboardEventType(inputEvent.type)) {
315 PlatformKeyboardEvent keyboardEvent = PlatformKeyboardEventBuilder(*static_cast<const WebKeyboardEvent*>(&inputEvent));
316 return ic->handleKeyboardEvent(page->mainFrame(), keyboardEvent);
321 void WebDevToolsAgentImpl::overrideDeviceMetrics(int width, int height, float deviceScaleFactor, bool emulateViewport, bool fitWindow)
323 if (!width && !height) {
324 if (m_deviceMetricsEnabled) {
325 m_deviceMetricsEnabled = false;
326 m_webViewImpl->setBackgroundColorOverride(Color::transparent);
327 RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(m_isOverlayScrollbarsEnabled);
328 disableViewportEmulation();
329 m_client->disableDeviceEmulation();
332 if (!m_deviceMetricsEnabled) {
333 m_deviceMetricsEnabled = true;
334 m_webViewImpl->setBackgroundColorOverride(Color::darkGray);
335 m_isOverlayScrollbarsEnabled = RuntimeEnabledFeatures::overlayScrollbarsEnabled();
336 RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(true);
339 enableViewportEmulation();
341 disableViewportEmulation();
342 m_client->enableDeviceEmulation(IntRect(10, 10, width, height), IntRect(0, 0, width, height), deviceScaleFactor, fitWindow);
346 void WebDevToolsAgentImpl::enableViewportEmulation()
348 if (m_emulateViewportEnabled)
350 m_emulateViewportEnabled = true;
351 m_originalViewportEnabled = RuntimeEnabledFeatures::cssViewportEnabled();
352 RuntimeEnabledFeatures::setCSSViewportEnabled(true);
353 m_webViewImpl->settings()->setViewportEnabled(true);
354 m_webViewImpl->settings()->setViewportMetaEnabled(true);
355 m_webViewImpl->setIgnoreViewportTagScaleLimits(true);
356 m_webViewImpl->setPageScaleFactorLimits(-1, -1);
357 m_webViewImpl->setZoomFactorOverride(1);
360 void WebDevToolsAgentImpl::disableViewportEmulation()
362 if (!m_emulateViewportEnabled)
364 RuntimeEnabledFeatures::setCSSViewportEnabled(m_originalViewportEnabled);
365 m_webViewImpl->settings()->setViewportEnabled(false);
366 m_webViewImpl->settings()->setViewportMetaEnabled(false);
367 m_webViewImpl->setIgnoreViewportTagScaleLimits(false);
368 m_webViewImpl->setPageScaleFactorLimits(1, 1);
369 m_webViewImpl->setZoomFactorOverride(0);
370 m_emulateViewportEnabled = false;
373 void WebDevToolsAgentImpl::getAllocatedObjects(HashSet<const void*>& set)
375 class CountingVisitor : public WebDevToolsAgentClient::AllocatedObjectVisitor {
377 CountingVisitor() : m_totalObjectsCount(0)
381 virtual bool visitObject(const void* ptr)
383 ++m_totalObjectsCount;
386 size_t totalObjectsCount() const
388 return m_totalObjectsCount;
392 size_t m_totalObjectsCount;
395 CountingVisitor counter;
396 m_client->visitAllocatedObjects(&counter);
398 class PointerCollector : public WebDevToolsAgentClient::AllocatedObjectVisitor {
400 explicit PointerCollector(size_t maxObjectsCount)
401 : m_maxObjectsCount(maxObjectsCount)
404 , m_pointers(new const void*[maxObjectsCount])
407 virtual ~PointerCollector()
411 virtual bool visitObject(const void* ptr)
413 if (m_index == m_maxObjectsCount) {
417 m_pointers[m_index++] = ptr;
421 bool success() const { return m_success; }
423 void copyTo(HashSet<const void*>& set)
425 for (size_t i = 0; i < m_index; i++)
426 set.add(m_pointers[i]);
430 const size_t m_maxObjectsCount;
433 const void** m_pointers;
436 // Double size to allow room for all objects that may have been allocated
437 // since we counted them.
438 size_t estimatedMaxObjectsCount = counter.totalObjectsCount() * 2;
440 PointerCollector collector(estimatedMaxObjectsCount);
441 m_client->visitAllocatedObjects(&collector);
442 if (collector.success()) {
443 collector.copyTo(set);
446 estimatedMaxObjectsCount *= 2;
450 void WebDevToolsAgentImpl::dumpUncountedAllocatedObjects(const HashMap<const void*, size_t>& map)
452 class InstrumentedObjectSizeProvider : public WebDevToolsAgentClient::InstrumentedObjectSizeProvider {
454 InstrumentedObjectSizeProvider(const HashMap<const void*, size_t>& map) : m_map(map) { }
455 virtual size_t objectSize(const void* ptr) const
457 HashMap<const void*, size_t>::const_iterator i = m_map.find(ptr);
458 return i == m_map.end() ? 0 : i->value;
462 const HashMap<const void*, size_t>& m_map;
465 InstrumentedObjectSizeProvider provider(map);
466 m_client->dumpUncountedAllocatedObjects(&provider);
469 void WebDevToolsAgentImpl::setTraceEventCallback(TraceEventCallback callback)
471 m_client->setTraceEventCallback(callback);
474 void WebDevToolsAgentImpl::startGPUEventsRecording()
476 m_client->startGPUEventsRecording();
479 void WebDevToolsAgentImpl::stopGPUEventsRecording()
481 m_client->stopGPUEventsRecording();
484 void WebDevToolsAgentImpl::processGPUEvent(double timestamp, int phase, bool foreign)
486 if (InspectorController* ic = inspectorController())
487 ic->processGPUEvent(timestamp, phase, foreign, 0);
490 void WebDevToolsAgentImpl::processGPUEvent(const GPUEvent& event)
492 if (InspectorController* ic = inspectorController())
493 ic->processGPUEvent(event.timestamp, event.phase, event.foreign, event.usedGPUMemoryBytes);
496 void WebDevToolsAgentImpl::dispatchKeyEvent(const PlatformKeyboardEvent& event)
498 m_generatingEvent = true;
499 WebKeyboardEvent webEvent = WebKeyboardEventBuilder(event);
500 if (!webEvent.keyIdentifier[0] && webEvent.type != WebInputEvent::Char)
501 webEvent.setKeyIdentifierFromWindowsKeyCode();
502 m_webViewImpl->handleInputEvent(webEvent);
503 m_generatingEvent = false;
506 void WebDevToolsAgentImpl::dispatchMouseEvent(const PlatformMouseEvent& event)
508 m_generatingEvent = true;
509 WebMouseEvent webEvent = WebMouseEventBuilder(m_webViewImpl->mainFrameImpl()->frameView(), event);
510 m_webViewImpl->handleInputEvent(webEvent);
511 m_generatingEvent = false;
514 void WebDevToolsAgentImpl::dispatchOnInspectorBackend(const WebString& message)
516 inspectorController()->dispatchMessageFromFrontend(message);
519 void WebDevToolsAgentImpl::inspectElementAt(const WebPoint& point)
521 m_webViewImpl->inspectElementAt(point);
524 InspectorController* WebDevToolsAgentImpl::inspectorController()
526 if (Page* page = m_webViewImpl->page())
527 return &page->inspectorController();
531 Frame* WebDevToolsAgentImpl::mainFrame()
533 if (Page* page = m_webViewImpl->page())
534 return page->mainFrame();
539 void WebDevToolsAgentImpl::paintPageOverlay(WebCanvas* canvas)
541 InspectorController* ic = inspectorController();
543 GraphicsContext context(canvas);
544 context.setCertainlyOpaque(false);
545 ic->drawHighlight(context);
549 void WebDevToolsAgentImpl::highlight()
551 m_webViewImpl->addPageOverlay(this, OverlayZOrders::highlight);
554 void WebDevToolsAgentImpl::hideHighlight()
556 m_webViewImpl->removePageOverlay(this);
559 bool WebDevToolsAgentImpl::sendMessageToFrontend(const String& message)
561 WebDevToolsAgentImpl* devToolsAgent = static_cast<WebDevToolsAgentImpl*>(m_webViewImpl->devToolsAgent());
564 m_client->sendMessageToInspectorFrontend(message);
568 void WebDevToolsAgentImpl::updateInspectorStateCookie(const String& state)
570 m_client->saveAgentRuntimeState(state);
573 void WebDevToolsAgentImpl::clearBrowserCache()
575 m_client->clearBrowserCache();
578 void WebDevToolsAgentImpl::clearBrowserCookies()
580 m_client->clearBrowserCookies();
583 void WebDevToolsAgentImpl::setProcessId(long processId)
585 inspectorController()->setProcessId(processId);
588 void WebDevToolsAgentImpl::setLayerTreeId(int layerTreeId)
590 inspectorController()->setLayerTreeId(layerTreeId);
593 void WebDevToolsAgentImpl::evaluateInWebInspector(long callId, const WebString& script)
595 InspectorController* ic = inspectorController();
596 ic->evaluateForTestInFrontend(callId, script);
599 void WebDevToolsAgentImpl::willProcessTask()
601 if (InspectorController* ic = inspectorController())
602 ic->willProcessTask();
605 void WebDevToolsAgentImpl::didProcessTask()
607 if (InspectorController* ic = inspectorController())
608 ic->didProcessTask();
611 WebString WebDevToolsAgent::inspectorProtocolVersion()
613 return WebCore::inspectorProtocolVersion();
616 bool WebDevToolsAgent::supportsInspectorProtocolVersion(const WebString& version)
618 return WebCore::supportsInspectorProtocolVersion(version);
621 void WebDevToolsAgent::interruptAndDispatch(MessageDescriptor* rawDescriptor)
623 // rawDescriptor can't be a PassOwnPtr because interruptAndDispatch is a WebKit API function.
624 OwnPtr<MessageDescriptor> descriptor = adoptPtr(rawDescriptor);
625 OwnPtr<DebuggerTask> task = adoptPtr(new DebuggerTask(descriptor.release()));
626 PageScriptDebugServer::interruptAndRun(task.release(), v8::Isolate::GetCurrent());
629 bool WebDevToolsAgent::shouldInterruptForMessage(const WebString& message)
632 if (!InspectorBackendDispatcher::getCommandName(message, &commandName))
634 return commandName == InspectorBackendDispatcher::commandName(InspectorBackendDispatcher::kDebugger_pauseCmd)
635 || commandName == InspectorBackendDispatcher::commandName(InspectorBackendDispatcher::kDebugger_setBreakpointCmd)
636 || commandName == InspectorBackendDispatcher::commandName(InspectorBackendDispatcher::kDebugger_setBreakpointByUrlCmd)
637 || commandName == InspectorBackendDispatcher::commandName(InspectorBackendDispatcher::kDebugger_removeBreakpointCmd)
638 || commandName == InspectorBackendDispatcher::commandName(InspectorBackendDispatcher::kDebugger_setBreakpointsActiveCmd)
639 || commandName == InspectorBackendDispatcher::commandName(InspectorBackendDispatcher::kProfiler_startCmd)
640 || commandName == InspectorBackendDispatcher::commandName(InspectorBackendDispatcher::kProfiler_stopCmd);
643 void WebDevToolsAgent::processPendingMessages()
645 PageScriptDebugServer::shared().runPendingTasks();