Web Inspector: introduce Page.captureScreenshot
[WebKit-https.git] / Source / WebKit / chromium / src / WebDevToolsAgentImpl.cpp
1 /*
2  * Copyright (C) 2010-2011 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 "WebDevToolsAgentImpl.h"
33
34 #include "ExceptionCode.h"
35 #include "Frame.h"
36 #include "FrameView.h"
37 #include "GraphicsContext.h"
38 #include "InjectedScriptHost.h"
39 #include "InspectorBackendDispatcher.h"
40 #include "InspectorController.h"
41 #include "InspectorFrontend.h"
42 #include "InspectorProtocolVersion.h"
43 #include "InspectorValues.h"
44 #include "MemoryCache.h"
45 #include "Page.h"
46 #include "PageGroup.h"
47 #include "PageScriptDebugServer.h"
48 #include "painting/GraphicsContextBuilder.h"
49 #include "RenderView.h"
50 #include "ResourceError.h"
51 #include "ResourceRequest.h"
52 #include "ResourceResponse.h"
53 #include "V8Binding.h"
54 #include "V8Utilities.h"
55 #include "WebDataSource.h"
56 #include "WebDevToolsAgentClient.h"
57 #include "WebFrameImpl.h"
58 #include "WebViewClient.h"
59 #include "WebViewImpl.h"
60 #include <public/Platform.h>
61 #include <public/WebRect.h>
62 #include <public/WebString.h>
63 #include <public/WebURL.h>
64 #include <public/WebURLError.h>
65 #include <public/WebURLRequest.h>
66 #include <public/WebURLResponse.h>
67 #include <wtf/CurrentTime.h>
68 #include <wtf/MathExtras.h>
69 #include <wtf/Noncopyable.h>
70 #include <wtf/OwnPtr.h>
71 #include <wtf/text/WTFString.h>
72
73 using namespace WebCore;
74 using namespace std;
75
76 namespace OverlayZOrders {
77 // Use 99 as a big z-order number so that highlight is above other overlays.
78 static const int highlight = 99;
79 }
80
81 namespace WebKit {
82
83 namespace BrowserDataHintStringValues {
84 static const char screenshot[] = "screenshot";
85 }
86
87 class ClientMessageLoopAdapter : public PageScriptDebugServer::ClientMessageLoop {
88 public:
89     static void ensureClientMessageLoopCreated(WebDevToolsAgentClient* client)
90     {
91         if (s_instance)
92             return;
93         OwnPtr<ClientMessageLoopAdapter> instance = adoptPtr(new ClientMessageLoopAdapter(adoptPtr(client->createClientMessageLoop())));
94         s_instance = instance.get();
95         PageScriptDebugServer::shared().setClientMessageLoop(instance.release());
96     }
97
98     static void inspectedViewClosed(WebViewImpl* view)
99     {
100         if (s_instance)
101             s_instance->m_frozenViews.remove(view);
102     }
103
104     static void didNavigate()
105     {
106         // Release render thread if necessary.
107         if (s_instance && s_instance->m_running)
108             PageScriptDebugServer::shared().continueProgram();
109     }
110
111 private:
112     ClientMessageLoopAdapter(PassOwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> messageLoop)
113         : m_running(false)
114         , m_messageLoop(messageLoop) { }
115
116
117     virtual void run(Page* page)
118     {
119         if (m_running)
120             return;
121         m_running = true;
122
123         Vector<WebViewImpl*> views;
124
125         // 1. Disable input events.
126         HashSet<Page*>::const_iterator end =  page->group().pages().end();
127         for (HashSet<Page*>::const_iterator it =  page->group().pages().begin(); it != end; ++it) {
128             WebViewImpl* view = WebViewImpl::fromPage(*it);
129             m_frozenViews.add(view);
130             views.append(view);
131             view->setIgnoreInputEvents(true);
132         }
133
134         // 2. Disable active objects
135         WebView::willEnterModalLoop();
136
137         // 3. Process messages until quitNow is called.
138         m_messageLoop->run();
139
140         // 4. Resume active objects
141         WebView::didExitModalLoop();
142
143         // 5. Resume input events.
144         for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) {
145             if (m_frozenViews.contains(*it)) {
146                 // The view was not closed during the dispatch.
147                 (*it)->setIgnoreInputEvents(false);
148             }
149         }
150
151         // 6. All views have been resumed, clear the set.
152         m_frozenViews.clear();
153
154         m_running = false;
155     }
156
157     virtual void quitNow()
158     {
159         m_messageLoop->quitNow();
160     }
161
162     bool m_running;
163     OwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> m_messageLoop;
164     typedef HashSet<WebViewImpl*> FrozenViewsSet;
165     FrozenViewsSet m_frozenViews;
166     // FIXME: The ownership model for s_instance is somewhat complicated. Can we make this simpler?
167     static ClientMessageLoopAdapter* s_instance;
168 };
169
170 ClientMessageLoopAdapter* ClientMessageLoopAdapter::s_instance = 0;
171
172 class DebuggerTask : public PageScriptDebugServer::Task {
173 public:
174     DebuggerTask(PassOwnPtr<WebDevToolsAgent::MessageDescriptor> descriptor)
175         : m_descriptor(descriptor)
176     {
177     }
178
179     virtual ~DebuggerTask() { }
180     virtual void run()
181     {
182         if (WebDevToolsAgent* webagent = m_descriptor->agent())
183             webagent->dispatchOnInspectorBackend(m_descriptor->message());
184     }
185
186 private:
187     OwnPtr<WebDevToolsAgent::MessageDescriptor> m_descriptor;
188 };
189
190 class DeviceMetricsSupport {
191 public:
192     DeviceMetricsSupport(WebViewImpl* webView)
193         : m_webView(webView)
194         , m_fitWindow(false)
195         , m_originalZoomFactor(0)
196     {
197     }
198
199     ~DeviceMetricsSupport()
200     {
201         restore();
202     }
203
204     void setDeviceMetrics(int width, int height, float textZoomFactor, bool fitWindow)
205     {
206         WebCore::FrameView* view = frameView();
207         if (!view)
208             return;
209
210         m_emulatedFrameSize = WebSize(width, height);
211         m_fitWindow = fitWindow;
212         m_originalZoomFactor = 0;
213         m_webView->setEmulatedTextZoomFactor(textZoomFactor);
214         applySizeOverrideInternal(view, FitWindowAllowed);
215         autoZoomPageToFitWidth(view->frame());
216
217         m_webView->sendResizeEventAndRepaint();
218     }
219
220     void autoZoomPageToFitWidthOnNavigation(Frame* frame)
221     {
222         FrameView* frameView = frame->view();
223         applySizeOverrideInternal(frameView, FitWindowNotAllowed);
224         m_originalZoomFactor = 0;
225         applySizeOverrideInternal(frameView, FitWindowAllowed);
226         autoZoomPageToFitWidth(frame);
227     }
228
229     void autoZoomPageToFitWidth(Frame* frame)
230     {
231         if (!frame)
232             return;
233
234         frame->setTextZoomFactor(m_webView->emulatedTextZoomFactor());
235         ensureOriginalZoomFactor(frame->view());
236         Document* document = frame->document();
237         float numerator = document->renderView() ? document->renderView()->viewWidth() : frame->view()->contentsWidth();
238         float factor = m_originalZoomFactor * (numerator / m_emulatedFrameSize.width);
239         frame->setPageAndTextZoomFactors(factor, m_webView->emulatedTextZoomFactor());
240         document->styleResolverChanged(RecalcStyleImmediately);
241         document->updateLayout();
242     }
243
244     void webViewResized()
245     {
246         if (!m_fitWindow)
247             return;
248
249         applySizeOverrideIfNecessary();
250         autoZoomPageToFitWidth(m_webView->mainFrameImpl()->frame());
251     }
252
253     void applySizeOverrideIfNecessary()
254     {
255         FrameView* view = frameView();
256         if (!view)
257             return;
258
259         applySizeOverrideInternal(view, FitWindowAllowed);
260     }
261
262 private:
263     enum FitWindowFlag { FitWindowAllowed, FitWindowNotAllowed };
264
265     void ensureOriginalZoomFactor(FrameView* frameView)
266     {
267         if (m_originalZoomFactor)
268             return;
269
270         m_webView->setPageScaleFactor(1, WebPoint());
271         m_webView->setZoomLevel(false, 0);
272         WebSize scaledEmulatedSize = scaledEmulatedFrameSize(frameView);
273         double denominator = frameView->contentsWidth();
274         if (!denominator)
275             denominator = 1;
276         m_originalZoomFactor = static_cast<double>(scaledEmulatedSize.width) / denominator;
277     }
278
279     void restore()
280     {
281         WebCore::FrameView* view = frameView();
282         if (!view)
283             return;
284
285         m_webView->setZoomLevel(false, 0);
286         m_webView->setEmulatedTextZoomFactor(1);
287         view->setHorizontalScrollbarLock(false);
288         view->setVerticalScrollbarLock(false);
289         view->setScrollbarModes(ScrollbarAuto, ScrollbarAuto, false, false);
290         view->setFrameRect(IntRect(IntPoint(), IntSize(m_webView->size())));
291         m_webView->sendResizeEventAndRepaint();
292     }
293
294     WebSize scaledEmulatedFrameSize(FrameView* frameView)
295     {
296         if (!m_fitWindow)
297             return m_emulatedFrameSize;
298
299         WebSize scrollbarDimensions = forcedScrollbarDimensions(frameView);
300
301         int overrideWidth = m_emulatedFrameSize.width;
302         int overrideHeight = m_emulatedFrameSize.height;
303
304         WebSize webViewSize = m_webView->size();
305         int availableViewWidth = max(webViewSize.width - scrollbarDimensions.width, 1);
306         int availableViewHeight = max(webViewSize.height - scrollbarDimensions.height, 1);
307
308         double widthRatio = static_cast<double>(overrideWidth) / availableViewWidth;
309         double heightRatio = static_cast<double>(overrideHeight) / availableViewHeight;
310         double dimensionRatio = max(widthRatio, heightRatio);
311         overrideWidth = static_cast<int>(ceil(static_cast<double>(overrideWidth) / dimensionRatio));
312         overrideHeight = static_cast<int>(ceil(static_cast<double>(overrideHeight) / dimensionRatio));
313
314         return WebSize(overrideWidth, overrideHeight);
315     }
316
317     WebSize forcedScrollbarDimensions(FrameView* frameView)
318     {
319         frameView->setScrollbarModes(ScrollbarAlwaysOn, ScrollbarAlwaysOn, true, true);
320
321         int verticalScrollbarWidth = 0;
322         int horizontalScrollbarHeight = 0;
323         if (Scrollbar* verticalBar = frameView->verticalScrollbar())
324             verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
325         if (Scrollbar* horizontalBar = frameView->horizontalScrollbar())
326             horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
327         return WebSize(verticalScrollbarWidth, horizontalScrollbarHeight);
328     }
329
330     void applySizeOverrideInternal(FrameView* frameView, FitWindowFlag fitWindowFlag)
331     {
332         WebSize scrollbarDimensions = forcedScrollbarDimensions(frameView);
333
334         WebSize effectiveEmulatedSize = (fitWindowFlag == FitWindowAllowed) ? scaledEmulatedFrameSize(frameView) : m_emulatedFrameSize;
335         int overrideWidth = effectiveEmulatedSize.width + scrollbarDimensions.width;
336         int overrideHeight = effectiveEmulatedSize.height + scrollbarDimensions.height;
337
338         if (IntSize(overrideWidth, overrideHeight) != frameView->size())
339             frameView->resize(overrideWidth, overrideHeight);
340
341         Document* doc = frameView->frame()->document();
342         doc->styleResolverChanged(RecalcStyleImmediately);
343         doc->updateLayout();
344     }
345
346     WebCore::FrameView* frameView()
347     {
348         return m_webView->mainFrameImpl() ? m_webView->mainFrameImpl()->frameView() : 0;
349     }
350
351     WebViewImpl* m_webView;
352     WebSize m_emulatedFrameSize;
353     bool m_fitWindow;
354     double m_originalZoomFactor;
355 };
356
357 class SerializingFrontendChannel : public InspectorFrontendChannel {
358 public:
359     virtual bool sendMessageToFrontend(const String& message)
360     {
361         m_message = message;
362         return true;
363     }
364     String m_message;
365 };
366
367 WebDevToolsAgentImpl::WebDevToolsAgentImpl(
368     WebViewImpl* webViewImpl,
369     WebDevToolsAgentClient* client)
370     : m_hostId(client->hostIdentifier())
371     , m_client(client)
372     , m_webViewImpl(webViewImpl)
373     , m_attached(false)
374     , m_sendWithBrowserDataHint(BrowserDataHintNone)
375 {
376     ASSERT(m_hostId > 0);
377 }
378
379 WebDevToolsAgentImpl::~WebDevToolsAgentImpl()
380 {
381     ClientMessageLoopAdapter::inspectedViewClosed(m_webViewImpl);
382     if (m_attached)
383         WebKit::Platform::current()->currentThread()->removeTaskObserver(this);
384 }
385
386 void WebDevToolsAgentImpl::attach()
387 {
388     if (m_attached)
389         return;
390
391     ClientMessageLoopAdapter::ensureClientMessageLoopCreated(m_client);
392     inspectorController()->connectFrontend(this);
393     inspectorController()->webViewResized(m_webViewImpl->size());
394     WebKit::Platform::current()->currentThread()->addTaskObserver(this);
395     m_attached = true;
396 }
397
398 void WebDevToolsAgentImpl::reattach(const WebString& savedState)
399 {
400     if (m_attached)
401         return;
402
403     ClientMessageLoopAdapter::ensureClientMessageLoopCreated(m_client);
404     inspectorController()->reconnectFrontend(this, savedState);
405     m_attached = true;
406 }
407
408 void WebDevToolsAgentImpl::detach()
409 {
410     WebKit::Platform::current()->currentThread()->removeTaskObserver(this);
411
412     // Prevent controller from sending messages to the frontend.
413     InspectorController* ic = inspectorController();
414     ic->disconnectFrontend();
415     ic->hideHighlight();
416     ic->close();
417     m_attached = false;
418 }
419
420 void WebDevToolsAgentImpl::didNavigate()
421 {
422     ClientMessageLoopAdapter::didNavigate();
423 }
424
425 void WebDevToolsAgentImpl::didCreateScriptContext(WebFrameImpl* webframe, int worldId)
426 {
427     // Skip non main world contexts.
428     if (worldId)
429         return;
430     if (WebCore::Frame* frame = webframe->frame())
431         frame->script()->setContextDebugId(m_hostId);
432 }
433
434 void WebDevToolsAgentImpl::mainFrameViewCreated(WebFrameImpl* webFrame)
435 {
436     if (m_metricsSupport)
437         m_metricsSupport->applySizeOverrideIfNecessary();
438 }
439
440 bool WebDevToolsAgentImpl::metricsOverridden()
441 {
442     return !!m_metricsSupport;
443 }
444
445 void WebDevToolsAgentImpl::webViewResized(const WebSize& size)
446 {
447     if (m_metricsSupport)
448         m_metricsSupport->webViewResized();
449     if (InspectorController* ic = inspectorController())
450         ic->webViewResized(m_metricsSupport ? IntSize(size.width, size.height) : IntSize());
451 }
452
453 void WebDevToolsAgentImpl::overrideDeviceMetrics(int width, int height, float fontScaleFactor, bool fitWindow)
454 {
455     if (!width && !height) {
456         if (m_metricsSupport)
457             m_metricsSupport.clear();
458         if (InspectorController* ic = inspectorController())
459             ic->webViewResized(IntSize());
460         return;
461     }
462
463     if (!m_metricsSupport)
464         m_metricsSupport = adoptPtr(new DeviceMetricsSupport(m_webViewImpl));
465
466     m_metricsSupport->setDeviceMetrics(width, height, fontScaleFactor, fitWindow);
467     if (InspectorController* ic = inspectorController()) {
468         WebSize size = m_webViewImpl->size();
469         ic->webViewResized(IntSize(size.width, size.height));
470     }
471 }
472
473 void WebDevToolsAgentImpl::autoZoomPageToFitWidth()
474 {
475     if (m_metricsSupport)
476         m_metricsSupport->autoZoomPageToFitWidthOnNavigation(m_webViewImpl->mainFrameImpl()->frame());
477 }
478
479 void WebDevToolsAgentImpl::getAllocatedObjects(HashSet<const void*>& set)
480 {
481     class CountingVisitor : public WebDevToolsAgentClient::AllocatedObjectVisitor {
482     public:
483         CountingVisitor() : m_totalObjectsCount(0)
484         {
485         }
486
487         virtual bool visitObject(const void* ptr)
488         {
489             ++m_totalObjectsCount;
490             return true;
491         }
492         size_t totalObjectsCount() const
493         {
494             return m_totalObjectsCount;
495         }
496
497     private:
498         size_t m_totalObjectsCount;
499     };
500
501     CountingVisitor counter;
502     m_client->visitAllocatedObjects(&counter);
503
504     class PointerCollector : public WebDevToolsAgentClient::AllocatedObjectVisitor {
505     public:
506         explicit PointerCollector(size_t maxObjectsCount)
507             : m_maxObjectsCount(maxObjectsCount)
508             , m_index(0)
509             , m_success(true)
510             , m_pointers(new const void*[maxObjectsCount])
511         {
512         }
513         virtual ~PointerCollector()
514         {
515             delete[] m_pointers;
516         }
517         virtual bool visitObject(const void* ptr)
518         {
519             if (m_index == m_maxObjectsCount) {
520                 m_success = false;
521                 return false;
522             }
523             m_pointers[m_index++] = ptr;
524             return true;
525         }
526
527         bool success() const { return m_success; }
528
529         void copyTo(HashSet<const void*>& set)
530         {
531             for (size_t i = 0; i < m_index; i++)
532                 set.add(m_pointers[i]);
533         }
534
535     private:
536         const size_t m_maxObjectsCount;
537         size_t m_index;
538         bool m_success;
539         const void** m_pointers;
540     };
541
542     // Double size to allow room for all objects that may have been allocated
543     // since we counted them.
544     size_t estimatedMaxObjectsCount = counter.totalObjectsCount() * 2;
545     while (true) {
546         PointerCollector collector(estimatedMaxObjectsCount);
547         m_client->visitAllocatedObjects(&collector);
548         if (collector.success()) {
549             collector.copyTo(set);
550             break;
551         }
552         estimatedMaxObjectsCount *= 2;
553     }
554 }
555
556 void WebDevToolsAgentImpl::dumpUncountedAllocatedObjects(const HashMap<const void*, size_t>& map)
557 {
558     class InstrumentedObjectSizeProvider : public WebDevToolsAgentClient::InstrumentedObjectSizeProvider {
559     public:
560         InstrumentedObjectSizeProvider(const HashMap<const void*, size_t>& map) : m_map(map) { }
561         virtual size_t objectSize(const void* ptr) const
562         {
563             HashMap<const void*, size_t>::const_iterator i = m_map.find(ptr);
564             return i == m_map.end() ? 0 : i->value;
565         }
566
567     private:
568         const HashMap<const void*, size_t>& m_map;
569     };
570
571     InstrumentedObjectSizeProvider provider(map);
572     m_client->dumpUncountedAllocatedObjects(&provider);
573 }
574
575 bool WebDevToolsAgentImpl::captureScreenshot(WTF::String* data)
576 {
577     // Value is going to be substituted with the actual data on the browser level.
578     *data = "{screenshot-placeholder}";
579     m_sendWithBrowserDataHint = BrowserDataHintScreenshot;
580     return true;
581 }
582
583 void WebDevToolsAgentImpl::dispatchOnInspectorBackend(const WebString& message)
584 {
585     inspectorController()->dispatchMessageFromFrontend(message);
586 }
587
588 void WebDevToolsAgentImpl::inspectElementAt(const WebPoint& point)
589 {
590     m_webViewImpl->inspectElementAt(point);
591 }
592
593 InspectorController* WebDevToolsAgentImpl::inspectorController()
594 {
595     if (Page* page = m_webViewImpl->page())
596         return page->inspectorController();
597     return 0;
598 }
599
600 Frame* WebDevToolsAgentImpl::mainFrame()
601 {
602     if (Page* page = m_webViewImpl->page())
603         return page->mainFrame();
604     return 0;
605 }
606
607 void WebDevToolsAgentImpl::inspectorDestroyed()
608 {
609     // Our lifetime is bound to the WebViewImpl.
610 }
611
612 InspectorFrontendChannel* WebDevToolsAgentImpl::openInspectorFrontend(InspectorController*)
613 {
614     return 0;
615 }
616
617 void WebDevToolsAgentImpl::closeInspectorFrontend()
618 {
619 }
620
621 void WebDevToolsAgentImpl::bringFrontendToFront()
622 {
623 }
624
625 // WebPageOverlay
626 void WebDevToolsAgentImpl::paintPageOverlay(WebCanvas* canvas)
627 {
628     InspectorController* ic = inspectorController();
629     if (ic) {
630         GraphicsContextBuilder builder(canvas);
631         GraphicsContext& context = builder.context();
632         context.platformContext()->setDrawingToImageBuffer(true);
633         ic->drawHighlight(context);
634     }
635 }
636
637 void WebDevToolsAgentImpl::highlight()
638 {
639     m_webViewImpl->addPageOverlay(this, OverlayZOrders::highlight);
640 }
641
642 void WebDevToolsAgentImpl::hideHighlight()
643 {
644     m_webViewImpl->removePageOverlay(this);
645 }
646
647 static String browserHintToString(WebDevToolsAgent::BrowserDataHint dataHint)
648 {
649     switch (dataHint) {
650     case WebDevToolsAgent::BrowserDataHintScreenshot:
651         return BrowserDataHintStringValues::screenshot;
652     case WebDevToolsAgent::BrowserDataHintNone:
653     default:
654         ASSERT_NOT_REACHED();
655     }
656     return String();
657 }
658
659 static WebDevToolsAgent::BrowserDataHint browserHintFromString(const String& value)
660 {
661     if (value == BrowserDataHintStringValues::screenshot)
662         return WebDevToolsAgent::BrowserDataHintScreenshot;
663     ASSERT_NOT_REACHED();
664     return WebDevToolsAgent::BrowserDataHintNone;
665 }
666
667 bool WebDevToolsAgentImpl::sendMessageToFrontend(const String& message)
668 {
669     WebDevToolsAgentImpl* devToolsAgent = static_cast<WebDevToolsAgentImpl*>(m_webViewImpl->devToolsAgent());
670     if (!devToolsAgent)
671         return false;
672
673     if (m_sendWithBrowserDataHint != BrowserDataHintNone) {
674         String prefix = browserHintToString(m_sendWithBrowserDataHint);
675         m_client->sendMessageToInspectorFrontend(makeString("<", prefix, ">", message));
676     } else
677         m_client->sendMessageToInspectorFrontend(message);
678
679     m_sendWithBrowserDataHint = BrowserDataHintNone;
680     return true;
681 }
682
683 void WebDevToolsAgentImpl::updateInspectorStateCookie(const String& state)
684 {
685     m_client->saveAgentRuntimeState(state);
686 }
687
688 void WebDevToolsAgentImpl::clearBrowserCache()
689 {
690     m_client->clearBrowserCache();
691 }
692
693 void WebDevToolsAgentImpl::clearBrowserCookies()
694 {
695     m_client->clearBrowserCookies();
696 }
697
698 void WebDevToolsAgentImpl::setProcessId(long processId)
699 {
700     inspectorController()->setProcessId(processId);
701 }
702
703 void WebDevToolsAgentImpl::evaluateInWebInspector(long callId, const WebString& script)
704 {
705     InspectorController* ic = inspectorController();
706     ic->evaluateForTestInFrontend(callId, script);
707 }
708
709 void WebDevToolsAgentImpl::willProcessTask()
710 {
711     if (InspectorController* ic = inspectorController())
712         ic->willProcessTask();
713 }
714
715 void WebDevToolsAgentImpl::didProcessTask()
716 {
717     if (InspectorController* ic = inspectorController())
718         ic->didProcessTask();
719 }
720
721 WebString WebDevToolsAgent::inspectorProtocolVersion()
722 {
723     return WebCore::inspectorProtocolVersion();
724 }
725
726 bool WebDevToolsAgent::supportsInspectorProtocolVersion(const WebString& version)
727 {
728     return WebCore::supportsInspectorProtocolVersion(version);
729 }
730
731 void WebDevToolsAgent::interruptAndDispatch(MessageDescriptor* rawDescriptor)
732 {
733     // rawDescriptor can't be a PassOwnPtr because interruptAndDispatch is a WebKit API function.
734     OwnPtr<MessageDescriptor> descriptor = adoptPtr(rawDescriptor);
735     OwnPtr<DebuggerTask> task = adoptPtr(new DebuggerTask(descriptor.release()));
736     PageScriptDebugServer::interruptAndRun(task.release());
737 }
738
739 bool WebDevToolsAgent::shouldInterruptForMessage(const WebString& message)
740 {
741     String commandName;
742     if (!InspectorBackendDispatcher::getCommandName(message, &commandName))
743         return false;
744     return commandName == InspectorBackendDispatcher::commandNames[InspectorBackendDispatcher::kDebugger_pauseCmd]
745         || commandName == InspectorBackendDispatcher::commandNames[InspectorBackendDispatcher::kDebugger_setBreakpointCmd]
746         || commandName == InspectorBackendDispatcher::commandNames[InspectorBackendDispatcher::kDebugger_setBreakpointByUrlCmd]
747         || commandName == InspectorBackendDispatcher::commandNames[InspectorBackendDispatcher::kDebugger_removeBreakpointCmd]
748         || commandName == InspectorBackendDispatcher::commandNames[InspectorBackendDispatcher::kDebugger_setBreakpointsActiveCmd]
749         || commandName == InspectorBackendDispatcher::commandNames[InspectorBackendDispatcher::kProfiler_startCmd]
750         || commandName == InspectorBackendDispatcher::commandNames[InspectorBackendDispatcher::kProfiler_stopCmd]
751         || commandName == InspectorBackendDispatcher::commandNames[InspectorBackendDispatcher::kProfiler_getProfileCmd];
752 }
753
754 void WebDevToolsAgent::processPendingMessages()
755 {
756     PageScriptDebugServer::shared().runPendingTasks();
757 }
758
759 WebString WebDevToolsAgent::inspectorDetachedEvent(const WebString& reason)
760 {
761     SerializingFrontendChannel channel;
762     InspectorFrontend::Inspector inspector(&channel);
763     inspector.detached(reason);
764     return channel.m_message;
765 }
766
767 WebString WebDevToolsAgent::workerDisconnectedFromWorkerEvent()
768 {
769     SerializingFrontendChannel channel;
770 #if ENABLE(WORKERS)
771     InspectorFrontend::Worker inspector(&channel);
772     inspector.disconnectedFromWorker();
773 #endif
774     return channel.m_message;
775 }
776
777 WebDevToolsAgent::BrowserDataHint WebDevToolsAgent::shouldPatchWithBrowserData(const char* message, size_t messageLength)
778 {
779     if (!messageLength || message[0] != '<')
780         return BrowserDataHintNone;
781
782     String messageString(message, messageLength);
783     size_t hintEnd = messageString.find(">", 1);
784     if (hintEnd == notFound)
785         return BrowserDataHintNone;
786     messageString = messageString.substring(1, hintEnd - 1);
787     return browserHintFromString(messageString);
788 }
789
790 WebString WebDevToolsAgent::patchWithBrowserData(const WebString& message, BrowserDataHint dataHint, const WebString& hintData)
791 {
792     String messageString = message;
793     size_t hintEnd = messageString.find(">");
794     if (hintEnd == notFound) {
795         ASSERT_NOT_REACHED();
796         return message;
797     }
798
799     messageString = messageString.substring(hintEnd + 1);
800     RefPtr<InspectorValue> messageObject = InspectorValue::parseJSON(messageString);
801     if (!messageObject || messageObject->type() != InspectorValue::TypeObject) {
802         ASSERT_NOT_REACHED();
803         return messageString;
804     }
805
806     RefPtr<InspectorObject> resultObject = messageObject->asObject()->getObject("result");
807     if (!resultObject) {
808         ASSERT_NOT_REACHED();
809         return messageString;
810     }
811
812     // Patch message below.
813     switch (dataHint) {
814     case BrowserDataHintScreenshot:
815         resultObject->setString("data", hintData);
816         break;
817     case BrowserDataHintNone:
818     default:
819         ASSERT_NOT_REACHED();
820     }
821     return messageObject->toJSONString();
822 }
823
824 } // namespace WebKit