c6d9ca01a7fc12d9d17faa3ecdce69a073873c86
[WebKit-https.git] / Source / WebCore / inspector / InspectorAgent.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "InspectorAgent.h"
32
33 #if ENABLE(INSPECTOR)
34
35 #include "CachedResource.h"
36 #include "CachedResourceLoader.h"
37 #include "Chrome.h"
38 #include "Cookie.h"
39 #include "CookieJar.h"
40 #include "DOMWindow.h"
41 #include "DOMWrapperWorld.h"
42 #include "Document.h"
43 #include "DocumentLoader.h"
44 #include "Element.h"
45 #include "FloatConversion.h"
46 #include "FloatQuad.h"
47 #include "FloatRect.h"
48 #include "Frame.h"
49 #include "FrameLoadRequest.h"
50 #include "FrameLoader.h"
51 #include "FrameTree.h"
52 #include "FrameView.h"
53 #include "GraphicsContext.h"
54 #include "HTMLFrameOwnerElement.h"
55 #include "HTTPHeaderMap.h"
56 #include "HitTestResult.h"
57 #include "InjectedScript.h"
58 #include "InjectedScriptHost.h"
59 #include "InspectorBrowserDebuggerAgent.h"
60 #include "InspectorCSSAgent.h"
61 #include "InspectorClient.h"
62 #include "InspectorConsoleAgent.h"
63 #include "InspectorController.h"
64 #include "InspectorDOMAgent.h"
65 #include "InspectorDOMStorageResource.h"
66 #include "InspectorDatabaseResource.h"
67 #include "InspectorDebuggerAgent.h"
68 #include "InspectorFrontend.h"
69 #include "InspectorFrontendClient.h"
70 #include "InspectorInstrumentation.h"
71 #include "InspectorProfilerAgent.h"
72 #include "InspectorResourceAgent.h"
73 #include "InspectorRuntimeAgent.h"
74 #include "InspectorState.h"
75 #include "InspectorTimelineAgent.h"
76 #include "InspectorValues.h"
77 #include "InspectorWorkerResource.h"
78 #include "IntRect.h"
79 #include "Page.h"
80 #include "ProgressTracker.h"
81 #include "Range.h"
82 #include "RenderInline.h"
83 #include "ResourceRequest.h"
84 #include "ResourceResponse.h"
85 #include "ScriptArguments.h"
86 #include "ScriptCallStack.h"
87 #include "ScriptFunctionCall.h"
88 #include "ScriptObject.h"
89 #include "ScriptProfile.h"
90 #include "ScriptProfiler.h"
91 #include "ScriptSourceCode.h"
92 #include "ScriptState.h"
93 #include "SecurityOrigin.h"
94 #include "Settings.h"
95 #include "SharedBuffer.h"
96 #include "TextEncoding.h"
97 #include "TextIterator.h"
98 #include "TextRun.h"
99 #include "UserGestureIndicator.h"
100 #include "WindowFeatures.h"
101 #include <wtf/CurrentTime.h>
102 #include <wtf/ListHashSet.h>
103 #include <wtf/RefCounted.h>
104 #include <wtf/StdLibExtras.h>
105 #include <wtf/UnusedParam.h>
106 #include <wtf/text/StringConcatenate.h>
107
108 #if ENABLE(DATABASE)
109 #include "Database.h"
110 #include "InspectorDatabaseAgent.h"
111 #endif
112
113 #if ENABLE(DOM_STORAGE)
114 #include "InspectorDOMStorageAgent.h"
115 #include "Storage.h"
116 #include "StorageArea.h"
117 #endif
118
119 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
120 #include "InspectorApplicationCacheAgent.h"
121 #endif
122
123 #if ENABLE(FILE_SYSTEM)
124 #include "InspectorFileSystemAgent.h"
125 #endif
126
127 using namespace std;
128
129 namespace WebCore {
130
131 namespace InspectorAgentState {
132 static const char searchingForNode[] = "searchingForNode";
133 static const char timelineProfilerEnabled[] = "timelineProfilerEnabled";
134 static const char userInitiatedProfiling[] = "userInitiatedProfiling";
135 static const char debuggerEnabled[] = "debuggerEnabled";
136 static const char profilerEnabled[] = "profilerEnabled";
137 }
138
139 static const char scriptsPanelName[] = "scripts";
140 static const char consolePanelName[] = "console";
141 static const char profilesPanelName[] = "profiles";
142
143 InspectorAgent::InspectorAgent(Page* page, InspectorClient* client)
144     : m_inspectedPage(page)
145     , m_client(client)
146     , m_frontend(0)
147     , m_cssAgent(new InspectorCSSAgent())
148     , m_state(new InspectorState(client))
149     , m_injectedScriptHost(InjectedScriptHost::create(this))
150     , m_consoleAgent(new InspectorConsoleAgent(this))
151 #if ENABLE(JAVASCRIPT_DEBUGGER)
152     , m_profilerAgent(InspectorProfilerAgent::create(this))
153 #endif
154 {
155     ASSERT_ARG(page, page);
156     ASSERT_ARG(client, client);
157     InspectorInstrumentation::bindInspectorAgent(m_inspectedPage, this);
158 }
159
160 InspectorAgent::~InspectorAgent()
161 {
162     // These should have been cleared in inspectedPageDestroyed().
163     ASSERT(!m_client);
164     ASSERT(!m_inspectedPage);
165     ASSERT(!m_highlightedNode);
166 }
167
168 void InspectorAgent::inspectedPageDestroyed()
169 {
170     if (m_frontend)
171         m_frontend->disconnectFromBackend();
172
173     hideHighlight();
174
175 #if ENABLE(JAVASCRIPT_DEBUGGER)
176     m_debuggerAgent.clear();
177     m_browserDebuggerAgent.clear();
178 #endif
179
180     ASSERT(m_inspectedPage);
181     InspectorInstrumentation::unbindInspectorAgent(m_inspectedPage);
182     m_inspectedPage = 0;
183
184     releaseFrontendLifetimeAgents();
185     m_injectedScriptHost->disconnectController();
186
187     m_client->inspectorDestroyed();
188     m_client = 0;
189 }
190
191 bool InspectorAgent::searchingForNodeInPage() const
192 {
193     return m_state->getBoolean(InspectorAgentState::searchingForNode);
194 }
195
196 void InspectorAgent::restoreInspectorStateFromCookie(const String& inspectorStateCookie)
197 {
198     m_state->restoreFromInspectorCookie(inspectorStateCookie);
199
200     m_frontend->frontendReused();
201     m_frontend->inspectedURLChanged(inspectedURL().string());
202     pushDataCollectedOffline();
203
204     m_resourceAgent = InspectorResourceAgent::restore(m_inspectedPage, m_state.get(), m_frontend);
205
206     if (m_state->getBoolean(InspectorAgentState::timelineProfilerEnabled))
207         startTimelineProfiler();
208
209 #if ENABLE(JAVASCRIPT_DEBUGGER)
210     restoreDebugger(false);
211     restoreProfiler(ProfilerRestoreResetAgent);
212     if (m_state->getBoolean(InspectorAgentState::userInitiatedProfiling))
213         startUserInitiatedProfiling();
214 #endif
215 }
216
217 void InspectorAgent::inspect(Node* node)
218 {
219     if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
220         node = node->parentNode();
221     m_nodeToFocus = node;
222
223     if (!m_frontend)
224         return;
225
226     focusNode();
227 }
228
229 void InspectorAgent::focusNode()
230 {
231     if (!enabled())
232         return;
233
234     ASSERT(m_frontend);
235     ASSERT(m_nodeToFocus);
236
237     long id = m_domAgent->pushNodePathToFrontend(m_nodeToFocus.get());
238     m_frontend->updateFocusedNode(id);
239     m_nodeToFocus = 0;
240 }
241
242 void InspectorAgent::highlight(Node* node)
243 {
244     if (!enabled())
245         return;
246     ASSERT_ARG(node, node);
247     m_highlightedNode = node;
248     m_client->highlight(node);
249 }
250
251 void InspectorAgent::highlightDOMNode(long nodeId)
252 {
253     Node* node = 0;
254     if (m_domAgent && (node = m_domAgent->nodeForId(nodeId)))
255         highlight(node);
256 }
257
258 void InspectorAgent::highlightFrame(unsigned long frameId)
259 {
260     Frame* mainFrame = m_inspectedPage->mainFrame();
261     for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext(mainFrame)) {
262         if (reinterpret_cast<uintptr_t>(frame) == frameId && frame->ownerElement()) {
263             highlight(frame->ownerElement());
264             return;
265         }
266     }
267 }
268
269 void InspectorAgent::hideHighlight()
270 {
271     if (!enabled())
272         return;
273     m_highlightedNode = 0;
274     m_client->hideHighlight();
275 }
276
277 void InspectorAgent::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
278 {
279     if (!enabled() || !searchingForNodeInPage())
280         return;
281
282     Node* node = result.innerNode();
283     while (node && node->nodeType() == Node::TEXT_NODE)
284         node = node->parentNode();
285     if (node)
286         highlight(node);
287 }
288
289 bool InspectorAgent::handleMousePress()
290 {
291     if (!enabled() || !searchingForNodeInPage())
292         return false;
293
294     if (m_highlightedNode) {
295         RefPtr<Node> node = m_highlightedNode;
296         setSearchingForNode(false);
297         inspect(node.get());
298     }
299     return true;
300 }
301
302 void InspectorAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
303 {
304     if (world != mainThreadNormalWorld())
305         return;
306
307     if (enabled()) {
308         if (m_frontend && frame == m_inspectedPage->mainFrame())
309             m_injectedScriptHost->discardInjectedScripts();
310
311         if (m_scriptsToEvaluateOnLoad.size()) {
312             ScriptState* scriptState = mainWorldScriptState(frame);
313             for (Vector<String>::iterator it = m_scriptsToEvaluateOnLoad.begin();
314                   it != m_scriptsToEvaluateOnLoad.end(); ++it) {
315                 m_injectedScriptHost->injectScript(*it, scriptState);
316             }
317         }
318     }
319
320     if (!m_inspectorExtensionAPI.isEmpty())
321         m_injectedScriptHost->injectScript(m_inspectorExtensionAPI, mainWorldScriptState(frame));
322 }
323
324 void InspectorAgent::setSearchingForNode(bool enabled)
325 {
326     if (searchingForNodeInPage() == enabled)
327         return;
328     m_state->setBoolean(InspectorAgentState::searchingForNode, enabled);
329     if (!enabled)
330         hideHighlight();
331 }
332
333 void InspectorAgent::setSearchingForNode(bool enabled, bool* newState)
334 {
335     *newState = enabled;
336     setSearchingForNode(enabled);
337 }
338
339 void InspectorAgent::setFrontend(InspectorFrontend* inspectorFrontend)
340 {
341     releaseFrontendLifetimeAgents();
342     m_frontend = inspectorFrontend;
343     createFrontendLifetimeAgents();
344
345     m_cssAgent->setDOMAgent(m_domAgent.get());
346
347     if (m_timelineAgent)
348         m_timelineAgent->resetFrontendProxyObject(m_frontend);
349
350     m_consoleAgent->setFrontend(m_frontend);
351
352     // Initialize Web Inspector title.
353     m_frontend->inspectedURLChanged(inspectedURL().string());
354 }
355
356 void InspectorAgent::disconnectFrontend()
357 {
358     if (!m_frontend)
359         return;
360
361     m_frontend = 0;
362
363 #if ENABLE(JAVASCRIPT_DEBUGGER)
364     // If the window is being closed with the debugger enabled,
365     // remember this state to re-enable debugger on the next window
366     // opening.
367     disableDebugger();
368 #endif
369     setSearchingForNode(false);
370     unbindAllResources();
371
372     hideHighlight();
373
374 #if ENABLE(JAVASCRIPT_DEBUGGER)
375     m_profilerAgent->setFrontend(0);
376     m_profilerAgent->stopUserInitiatedProfiling(true);
377 #endif
378     m_consoleAgent->setFrontend(0);
379
380     releaseFrontendLifetimeAgents();
381     m_timelineAgent.clear();
382     m_userAgentOverride = "";
383 }
384
385 InspectorResourceAgent* InspectorAgent::resourceAgent()
386 {
387     if (!m_resourceAgent && m_frontend)
388         m_resourceAgent = InspectorResourceAgent::create(m_inspectedPage, m_state.get(), m_frontend);
389     return m_resourceAgent.get();
390 }
391
392 void InspectorAgent::createFrontendLifetimeAgents()
393 {
394     m_domAgent = InspectorDOMAgent::create(m_injectedScriptHost.get(), m_frontend);
395     m_runtimeAgent = InspectorRuntimeAgent::create(m_injectedScriptHost.get());
396
397 #if ENABLE(DATABASE)
398     m_databaseAgent = InspectorDatabaseAgent::create(&m_databaseResources, m_frontend);
399 #endif
400
401 #if ENABLE(DOM_STORAGE)
402     m_domStorageAgent = InspectorDOMStorageAgent::create(&m_domStorageResources, m_frontend);
403 #endif
404
405 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
406     m_applicationCacheAgent = new InspectorApplicationCacheAgent(this, m_frontend);
407 #endif
408
409 #if ENABLE(FILE_SYSTEM)
410     m_fileSystemAgent = InspectorFileSystemAgent::create(this, m_frontend);
411 #endif
412 }
413
414 void InspectorAgent::releaseFrontendLifetimeAgents()
415 {
416     m_resourceAgent.clear();
417     m_runtimeAgent.clear();
418
419     // This should be invoked prior to m_domAgent destruction.
420     m_cssAgent->setDOMAgent(0);
421
422     // m_domAgent is RefPtr. Remove DOM listeners first to ensure that there are
423     // no references to the DOM agent from the DOM tree.
424     if (m_domAgent)
425         m_domAgent->reset();
426     m_domAgent.clear();
427
428 #if ENABLE(DATABASE)
429     if (m_databaseAgent)
430         m_databaseAgent->clearFrontend();
431     m_databaseAgent.clear();
432 #endif
433
434 #if ENABLE(DOM_STORAGE)
435     m_domStorageAgent.clear();
436 #endif
437
438 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
439     m_applicationCacheAgent.clear();
440 #endif
441
442 #if ENABLE(FILE_SYSTEM)
443     if (m_fileSystemAgent)
444         m_fileSystemAgent->stop();
445         m_fileSystemAgent.clear();
446 #endif
447     stopTimelineProfiler();
448 }
449
450 void InspectorAgent::populateScriptObjects()
451 {
452     ASSERT(m_frontend);
453     if (!m_frontend)
454         return;
455
456 #if ENABLE(JAVASCRIPT_DEBUGGER)
457     if (m_profilerAgent->enabled())
458         m_frontend->profilerWasEnabled();
459 #endif
460
461     pushDataCollectedOffline();
462
463     if (m_nodeToFocus)
464         focusNode();
465
466     if (!m_requiredPanel.isEmpty()) {
467         m_frontend->showPanel(m_requiredPanel);
468         m_requiredPanel = "";
469     }
470
471     restoreDebugger(true);
472     restoreProfiler(ProfilerRestoreNoAction);
473
474     // Dispatch pending frontend commands
475     for (Vector<pair<long, String> >::iterator it = m_pendingEvaluateTestCommands.begin(); it != m_pendingEvaluateTestCommands.end(); ++it)
476         m_frontend->evaluateForTestInFrontend((*it).first, (*it).second);
477     m_pendingEvaluateTestCommands.clear();
478 }
479
480 void InspectorAgent::pushDataCollectedOffline()
481 {
482     m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
483
484 #if ENABLE(DATABASE)
485     DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end();
486     for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
487         it->second->bind(m_frontend);
488 #endif
489 #if ENABLE(DOM_STORAGE)
490     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
491     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
492         it->second->bind(m_frontend);
493 #endif
494 #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(WORKERS)
495     WorkersMap::iterator workersEnd = m_workers.end();
496     for (WorkersMap::iterator it = m_workers.begin(); it != workersEnd; ++it) {
497         InspectorWorkerResource* worker = it->second.get();
498         m_frontend->didCreateWorker(worker->id(), worker->url(), worker->isSharedWorker());
499     }
500 #endif
501 }
502
503 void InspectorAgent::restoreDebugger(bool eraseStickyBreakpoints)
504 {
505     ASSERT(m_frontend);
506 #if ENABLE(JAVASCRIPT_DEBUGGER)
507     if (m_state->getBoolean(InspectorAgentState::debuggerEnabled))
508         enableDebugger(eraseStickyBreakpoints);
509 #endif
510 }
511
512 void InspectorAgent::restoreProfiler(ProfilerRestoreAction action)
513 {
514     ASSERT(m_frontend);
515 #if ENABLE(JAVASCRIPT_DEBUGGER)
516     m_profilerAgent->setFrontend(m_frontend);
517     if (m_state->getBoolean(InspectorAgentState::profilerEnabled))
518         enableProfiler();
519     if (action == ProfilerRestoreResetAgent)
520         m_profilerAgent->resetFrontendProfiles();
521 #endif
522 }
523
524 void InspectorAgent::unbindAllResources()
525 {
526 #if ENABLE(DATABASE)
527     DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end();
528     for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
529         it->second->unbind();
530 #endif
531 #if ENABLE(DOM_STORAGE)
532     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
533     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
534         it->second->unbind();
535 #endif
536     if (m_timelineAgent)
537         m_timelineAgent->reset();
538 }
539
540 void InspectorAgent::didCommitLoad(DocumentLoader* loader)
541 {
542     if (!enabled())
543         return;
544
545     if (m_resourceAgent)
546         m_resourceAgent->didCommitLoad(loader);
547     
548     ASSERT(m_inspectedPage);
549
550     if (loader->frame() == m_inspectedPage->mainFrame()) {
551         if (m_frontend)
552             m_frontend->inspectedURLChanged(loader->url().string());
553
554         m_injectedScriptHost->discardInjectedScripts();
555         m_consoleAgent->reset();
556
557 #if ENABLE(JAVASCRIPT_DEBUGGER)
558         if (m_debuggerAgent) {
559             KURL url = inspectedURLWithoutFragment();
560             m_debuggerAgent->inspectedURLChanged(url);
561             if (m_browserDebuggerAgent)
562                 m_browserDebuggerAgent->inspectedURLChanged(url);
563         }
564 #endif
565
566 #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC)
567         m_profilerAgent->stopUserInitiatedProfiling(true);
568         m_profilerAgent->resetState();
569 #endif
570
571         // unbindAllResources should be called before database and DOM storage
572         // resources are cleared so that it has a chance to unbind them.
573         unbindAllResources();
574
575         if (m_frontend) {
576             m_frontend->reset();
577             m_domAgent->reset();
578             m_cssAgent->reset();
579         }
580 #if ENABLE(WORKERS)
581         m_workers.clear();
582 #endif
583 #if ENABLE(DATABASE)
584         m_databaseResources.clear();
585 #endif
586 #if ENABLE(DOM_STORAGE)
587         m_domStorageResources.clear();
588 #endif
589
590         if (m_frontend)
591             m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
592     }
593 }
594
595 void InspectorAgent::mainResourceFiredDOMContentEvent(DocumentLoader* loader, const KURL& url)
596 {
597     if (!enabled() || !isMainResourceLoader(loader, url))
598         return;
599
600     if (m_timelineAgent)
601         m_timelineAgent->didMarkDOMContentEvent();
602     if (m_frontend)
603         m_frontend->domContentEventFired(currentTime());
604 }
605
606 void InspectorAgent::mainResourceFiredLoadEvent(DocumentLoader* loader, const KURL& url)
607 {
608     if (!enabled() || !isMainResourceLoader(loader, url))
609         return;
610
611     if (m_timelineAgent)
612         m_timelineAgent->didMarkLoadEvent();
613     if (m_frontend)
614         m_frontend->loadEventFired(currentTime());
615 }
616
617 bool InspectorAgent::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
618 {
619     return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
620 }
621
622 void InspectorAgent::setUserAgentOverride(const String& userAgent)
623 {
624     m_userAgentOverride = userAgent;
625 }
626
627 void InspectorAgent::applyUserAgentOverride(String* userAgent) const
628 {
629     if (!m_userAgentOverride.isEmpty())
630         *userAgent = m_userAgentOverride;
631 }
632
633 void InspectorAgent::startTimelineProfiler()
634 {
635     if (!enabled())
636         return;
637
638     if (m_timelineAgent)
639         return;
640
641     m_timelineAgent = new InspectorTimelineAgent(m_frontend);
642     if (m_frontend)
643         m_frontend->timelineProfilerWasStarted();
644
645     m_state->setBoolean(InspectorAgentState::timelineProfilerEnabled, true);
646 }
647
648 void InspectorAgent::stopTimelineProfiler()
649 {
650     if (!enabled())
651         return;
652
653     if (!m_timelineAgent)
654         return;
655
656     m_timelineAgent = 0;
657     if (m_frontend)
658         m_frontend->timelineProfilerWasStopped();
659
660     m_state->setBoolean(InspectorAgentState::timelineProfilerEnabled, false);
661 }
662
663 #if ENABLE(WORKERS)
664 class PostWorkerNotificationToFrontendTask : public ScriptExecutionContext::Task {
665 public:
666     static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action)
667     {
668         return new PostWorkerNotificationToFrontendTask(worker, action);
669     }
670
671 private:
672     PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker, InspectorAgent::WorkerAction action)
673         : m_worker(worker)
674         , m_action(action)
675     {
676     }
677
678     virtual void performTask(ScriptExecutionContext* scriptContext)
679     {
680         if (scriptContext->isDocument()) {
681             if (InspectorAgent* inspectorAgent = static_cast<Document*>(scriptContext)->page()->inspectorController()->m_inspectorAgent.get())
682                 inspectorAgent->postWorkerNotificationToFrontend(*m_worker, m_action);
683         }
684     }
685
686 private:
687     RefPtr<InspectorWorkerResource> m_worker;
688     InspectorAgent::WorkerAction m_action;
689 };
690
691 void InspectorAgent::postWorkerNotificationToFrontend(const InspectorWorkerResource& worker, InspectorAgent::WorkerAction action)
692 {
693     if (!m_frontend)
694         return;
695 #if ENABLE(JAVASCRIPT_DEBUGGER)
696     switch (action) {
697     case InspectorAgent::WorkerCreated:
698         m_frontend->didCreateWorker(worker.id(), worker.url(), worker.isSharedWorker());
699         break;
700     case InspectorAgent::WorkerDestroyed:
701         m_frontend->didDestroyWorker(worker.id());
702         break;
703     }
704 #endif
705 }
706
707 void InspectorAgent::didCreateWorker(intptr_t id, const String& url, bool isSharedWorker)
708 {
709     if (!enabled())
710         return;
711
712     RefPtr<InspectorWorkerResource> workerResource(InspectorWorkerResource::create(id, url, isSharedWorker));
713     m_workers.set(id, workerResource);
714     if (m_inspectedPage && m_frontend)
715         m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource, InspectorAgent::WorkerCreated));
716 }
717
718 void InspectorAgent::didDestroyWorker(intptr_t id)
719 {
720     if (!enabled())
721         return;
722
723     WorkersMap::iterator workerResource = m_workers.find(id);
724     if (workerResource == m_workers.end())
725         return;
726     if (m_inspectedPage && m_frontend)
727         m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource->second, InspectorAgent::WorkerDestroyed));
728     m_workers.remove(workerResource);
729 }
730 #endif // ENABLE(WORKERS)
731
732 #if ENABLE(DATABASE)
733 void InspectorAgent::didOpenDatabase(PassRefPtr<Database> database, const String& domain, const String& name, const String& version)
734 {
735     if (!enabled())
736         return;
737
738     RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
739
740     m_databaseResources.set(resource->id(), resource);
741
742     // Resources are only bound while visible.
743     if (m_frontend)
744         resource->bind(m_frontend);
745 }
746 #endif
747
748 void InspectorAgent::getCookies(RefPtr<InspectorArray>* cookies, WTF::String* cookiesString)
749 {
750     // If we can get raw cookies.
751     ListHashSet<Cookie> rawCookiesList;
752
753     // If we can't get raw cookies - fall back to String representation
754     String stringCookiesList;
755
756     // Return value to getRawCookies should be the same for every call because
757     // the return value is platform/network backend specific, and the call will
758     // always return the same true/false value.
759     bool rawCookiesImplemented = false;
760
761     for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext(m_inspectedPage->mainFrame())) {
762         Document* document = frame->document();
763         const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources();
764         CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
765         for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
766             Vector<Cookie> docCookiesList;
767             rawCookiesImplemented = getRawCookies(document, KURL(ParsedURLString, it->second->url()), docCookiesList);
768
769             if (!rawCookiesImplemented) {
770                 // FIXME: We need duplication checking for the String representation of cookies.
771                 ExceptionCode ec = 0;
772                 stringCookiesList += document->cookie(ec);
773                 // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here
774                 // because "document" is the document of the main frame of the page.
775                 ASSERT(!ec);
776             } else {
777                 int cookiesSize = docCookiesList.size();
778                 for (int i = 0; i < cookiesSize; i++) {
779                     if (!rawCookiesList.contains(docCookiesList[i]))
780                         rawCookiesList.add(docCookiesList[i]);
781                 }
782             }
783         }
784     }
785
786     if (rawCookiesImplemented)
787         *cookies = buildArrayForCookies(rawCookiesList);
788     else
789         *cookiesString = stringCookiesList;
790 }
791
792 PassRefPtr<InspectorArray> InspectorAgent::buildArrayForCookies(ListHashSet<Cookie>& cookiesList)
793 {
794     RefPtr<InspectorArray> cookies = InspectorArray::create();
795
796     ListHashSet<Cookie>::iterator end = cookiesList.end();
797     ListHashSet<Cookie>::iterator it = cookiesList.begin();
798     for (int i = 0; it != end; ++it, i++)
799         cookies->pushObject(buildObjectForCookie(*it));
800
801     return cookies;
802 }
803
804 PassRefPtr<InspectorObject> InspectorAgent::buildObjectForCookie(const Cookie& cookie)
805 {
806     RefPtr<InspectorObject> value = InspectorObject::create();
807     value->setString("name", cookie.name);
808     value->setString("value", cookie.value);
809     value->setString("domain", cookie.domain);
810     value->setString("path", cookie.path);
811     value->setNumber("expires", cookie.expires);
812     value->setNumber("size", (cookie.name.length() + cookie.value.length()));
813     value->setBoolean("httpOnly", cookie.httpOnly);
814     value->setBoolean("secure", cookie.secure);
815     value->setBoolean("session", cookie.session);
816     return value;
817 }
818
819 void InspectorAgent::deleteCookie(const String& cookieName, const String& domain)
820 {
821     for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext(m_inspectedPage->mainFrame())) {
822         Document* document = frame->document();
823         if (document->url().host() != domain)
824             continue;
825         const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources();
826         CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
827         for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it)
828             WebCore::deleteCookie(document, KURL(ParsedURLString, it->second->url()), cookieName);
829     }
830 }
831
832 #if ENABLE(DOM_STORAGE)
833 void InspectorAgent::didUseDOMStorage(StorageArea* storageArea, bool isLocalStorage, Frame* frame)
834 {
835     if (!enabled())
836         return;
837
838     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
839     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
840         if (it->second->isSameHostAndType(frame, isLocalStorage))
841             return;
842
843     RefPtr<Storage> domStorage = Storage::create(frame, storageArea);
844     RefPtr<InspectorDOMStorageResource> resource = InspectorDOMStorageResource::create(domStorage.get(), isLocalStorage, frame);
845
846     m_domStorageResources.set(resource->id(), resource);
847
848     // Resources are only bound while visible.
849     if (m_frontend)
850         resource->bind(m_frontend);
851 }
852 #endif
853
854 #if ENABLE(WEB_SOCKETS)
855 void InspectorAgent::didCreateWebSocket(unsigned long identifier, const KURL& requestURL, const KURL& documentURL)
856 {
857     if (!enabled())
858         return;
859     ASSERT(m_inspectedPage);
860
861     if (m_resourceAgent)
862         m_resourceAgent->didCreateWebSocket(identifier, requestURL);
863     UNUSED_PARAM(documentURL);
864 }
865
866 void InspectorAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request)
867 {
868     if (m_resourceAgent)
869         m_resourceAgent->willSendWebSocketHandshakeRequest(identifier, request);
870 }
871
872 void InspectorAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response)
873 {
874     if (m_resourceAgent)
875         m_resourceAgent->didReceiveWebSocketHandshakeResponse(identifier, response);
876 }
877
878 void InspectorAgent::didCloseWebSocket(unsigned long identifier)
879 {
880     if (m_resourceAgent)
881         m_resourceAgent->didCloseWebSocket(identifier);
882 }
883 #endif // ENABLE(WEB_SOCKETS)
884
885 #if ENABLE(JAVASCRIPT_DEBUGGER)
886 bool InspectorAgent::isRecordingUserInitiatedProfile() const
887 {
888     return m_profilerAgent->isRecordingUserInitiatedProfile();
889 }
890
891 void InspectorAgent::startUserInitiatedProfiling()
892 {
893     if (!enabled())
894         return;
895     m_profilerAgent->startUserInitiatedProfiling();
896     m_state->setBoolean(InspectorAgentState::userInitiatedProfiling, true);
897 }
898
899 void InspectorAgent::stopUserInitiatedProfiling()
900 {
901     if (!enabled())
902         return;
903     m_profilerAgent->stopUserInitiatedProfiling();
904     m_state->setBoolean(InspectorAgentState::userInitiatedProfiling, false);
905     showPanel(profilesPanelName);
906 }
907
908 bool InspectorAgent::profilerEnabled() const
909 {
910     return enabled() && m_profilerAgent->enabled();
911 }
912
913 void InspectorAgent::enableProfiler()
914 {
915     if (profilerEnabled())
916         return;
917     m_state->setBoolean(InspectorAgentState::profilerEnabled, true);
918     m_profilerAgent->enable(false);
919 }
920
921 void InspectorAgent::disableProfiler()
922 {
923     m_state->setBoolean(InspectorAgentState::profilerEnabled, false);
924     m_profilerAgent->disable();
925 }
926 #endif
927
928 #if ENABLE(JAVASCRIPT_DEBUGGER)
929 void InspectorAgent::showAndEnableDebugger()
930 {
931     if (!enabled())
932         return;
933
934     if (debuggerEnabled())
935         return;
936
937     if (!m_frontend) {
938         m_state->setBoolean(InspectorAgentState::debuggerEnabled, true);
939         showPanel(scriptsPanelName);
940     } else
941         enableDebugger(true);
942 }
943
944 void InspectorAgent::enableDebugger(bool eraseStickyBreakpoints)
945 {
946     if (debuggerEnabled())
947         return;
948     m_state->setBoolean(InspectorAgentState::debuggerEnabled, true);
949     ASSERT(m_inspectedPage);
950
951     m_debuggerAgent = InspectorDebuggerAgent::create(this, m_frontend, eraseStickyBreakpoints);
952     m_browserDebuggerAgent = InspectorBrowserDebuggerAgent::create(this, eraseStickyBreakpoints);
953
954     m_frontend->debuggerWasEnabled();
955 }
956
957 void InspectorAgent::disableDebugger()
958 {
959     if (!enabled())
960         return;
961     ASSERT(m_inspectedPage);
962     m_debuggerAgent.clear();
963     m_browserDebuggerAgent.clear();
964
965     if (m_frontend) {
966         m_frontend->debuggerWasDisabled();
967         m_state->setBoolean(InspectorAgentState::debuggerEnabled, false);
968     }
969 }
970
971 void InspectorAgent::resume()
972 {
973     if (m_debuggerAgent)
974         m_debuggerAgent->resume();
975 }
976 #endif
977
978 void InspectorAgent::evaluateForTestInFrontend(long callId, const String& script)
979 {
980     if (m_frontend)
981         m_frontend->evaluateForTestInFrontend(callId, script);
982     else
983         m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script));
984 }
985
986 void InspectorAgent::didEvaluateForTestInFrontend(long callId, const String& jsonResult)
987 {
988     ScriptState* scriptState = scriptStateFromPage(debuggerWorld(), m_inspectedPage);
989     ScriptObject window;
990     ScriptGlobalObject::get(scriptState, "window", window);
991     ScriptFunctionCall function(window, "didEvaluateForTestInFrontend");
992     function.appendArgument(callId);
993     function.appendArgument(jsonResult);
994     function.call();
995 }
996
997 static Path quadToPath(const FloatQuad& quad)
998 {
999     Path quadPath;
1000     quadPath.moveTo(quad.p1());
1001     quadPath.addLineTo(quad.p2());
1002     quadPath.addLineTo(quad.p3());
1003     quadPath.addLineTo(quad.p4());
1004     quadPath.closeSubpath();
1005     return quadPath;
1006 }
1007
1008 static void drawOutlinedQuad(GraphicsContext& context, const FloatQuad& quad, const Color& fillColor)
1009 {
1010     static const int outlineThickness = 2;
1011     static const Color outlineColor(62, 86, 180, 228);
1012
1013     Path quadPath = quadToPath(quad);
1014
1015     // Clip out the quad, then draw with a 2px stroke to get a pixel
1016     // of outline (because inflating a quad is hard)
1017     {
1018         context.save();
1019         context.clipOut(quadPath);
1020
1021         context.setStrokeThickness(outlineThickness);
1022         context.setStrokeColor(outlineColor, ColorSpaceDeviceRGB);
1023         context.strokePath(quadPath);
1024
1025         context.restore();
1026     }
1027
1028     // Now do the fill
1029     context.setFillColor(fillColor, ColorSpaceDeviceRGB);
1030     context.fillPath(quadPath);
1031 }
1032
1033 static void drawOutlinedQuadWithClip(GraphicsContext& context, const FloatQuad& quad, const FloatQuad& clipQuad, const Color& fillColor)
1034 {
1035     context.save();
1036     Path clipQuadPath = quadToPath(clipQuad);
1037     context.clipOut(clipQuadPath);
1038     drawOutlinedQuad(context, quad, fillColor);
1039     context.restore();
1040 }
1041
1042 static void drawHighlightForBox(GraphicsContext& context, const FloatQuad& contentQuad, const FloatQuad& paddingQuad, const FloatQuad& borderQuad, const FloatQuad& marginQuad)
1043 {
1044     static const Color contentBoxColor(125, 173, 217, 128);
1045     static const Color paddingBoxColor(125, 173, 217, 160);
1046     static const Color borderBoxColor(125, 173, 217, 192);
1047     static const Color marginBoxColor(125, 173, 217, 228);
1048
1049     if (marginQuad != borderQuad)
1050         drawOutlinedQuadWithClip(context, marginQuad, borderQuad, marginBoxColor);
1051     if (borderQuad != paddingQuad)
1052         drawOutlinedQuadWithClip(context, borderQuad, paddingQuad, borderBoxColor);
1053     if (paddingQuad != contentQuad)
1054         drawOutlinedQuadWithClip(context, paddingQuad, contentQuad, paddingBoxColor);
1055
1056     drawOutlinedQuad(context, contentQuad, contentBoxColor);
1057 }
1058
1059 static void drawHighlightForLineBoxesOrSVGRenderer(GraphicsContext& context, const Vector<FloatQuad>& lineBoxQuads)
1060 {
1061     static const Color lineBoxColor(125, 173, 217, 128);
1062
1063     for (size_t i = 0; i < lineBoxQuads.size(); ++i)
1064         drawOutlinedQuad(context, lineBoxQuads[i], lineBoxColor);
1065 }
1066
1067 static inline void convertFromFrameToMainFrame(Frame* frame, IntRect& rect)
1068 {
1069     rect = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(rect));
1070 }
1071
1072 static inline IntSize frameToMainFrameOffset(Frame* frame)
1073 {
1074     IntPoint mainFramePoint = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(IntPoint()));
1075     return mainFramePoint - IntPoint();
1076 }
1077
1078 void InspectorAgent::drawNodeHighlight(GraphicsContext& context) const
1079 {
1080     if (!m_highlightedNode)
1081         return;
1082
1083     RenderObject* renderer = m_highlightedNode->renderer();
1084     Frame* containingFrame = m_highlightedNode->document()->frame();
1085     if (!renderer || !containingFrame)
1086         return;
1087
1088     IntSize mainFrameOffset = frameToMainFrameOffset(containingFrame);
1089     IntRect boundingBox = renderer->absoluteBoundingBoxRect(true);
1090     boundingBox.move(mainFrameOffset);
1091
1092     IntRect titleReferenceBox = boundingBox;
1093
1094     ASSERT(m_inspectedPage);
1095
1096     FrameView* view = m_inspectedPage->mainFrame()->view();
1097     FloatRect overlayRect = view->visibleContentRect();
1098     if (!overlayRect.contains(boundingBox) && !boundingBox.contains(enclosingIntRect(overlayRect)))
1099         overlayRect = view->visibleContentRect();
1100     context.translate(-overlayRect.x(), -overlayRect.y());
1101
1102     // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads().
1103 #if ENABLE(SVG)
1104     bool isSVGRenderer = renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot();
1105 #else
1106     bool isSVGRenderer = false;
1107 #endif
1108
1109     if (renderer->isBox() && !isSVGRenderer) {
1110         RenderBox* renderBox = toRenderBox(renderer);
1111
1112         IntRect contentBox = renderBox->contentBoxRect();
1113
1114         IntRect paddingBox(contentBox.x() - renderBox->paddingLeft(), contentBox.y() - renderBox->paddingTop(),
1115                            contentBox.width() + renderBox->paddingLeft() + renderBox->paddingRight(), contentBox.height() + renderBox->paddingTop() + renderBox->paddingBottom());
1116         IntRect borderBox(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(),
1117                           paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom());
1118         IntRect marginBox(borderBox.x() - renderBox->marginLeft(), borderBox.y() - renderBox->marginTop(),
1119                           borderBox.width() + renderBox->marginLeft() + renderBox->marginRight(), borderBox.height() + renderBox->marginTop() + renderBox->marginBottom());
1120
1121         titleReferenceBox = marginBox;
1122         titleReferenceBox.move(mainFrameOffset);
1123         titleReferenceBox.move(boundingBox.x(), boundingBox.y());
1124
1125         FloatQuad absContentQuad = renderBox->localToAbsoluteQuad(FloatRect(contentBox));
1126         FloatQuad absPaddingQuad = renderBox->localToAbsoluteQuad(FloatRect(paddingBox));
1127         FloatQuad absBorderQuad = renderBox->localToAbsoluteQuad(FloatRect(borderBox));
1128         FloatQuad absMarginQuad = renderBox->localToAbsoluteQuad(FloatRect(marginBox));
1129
1130         absContentQuad.move(mainFrameOffset);
1131         absPaddingQuad.move(mainFrameOffset);
1132         absBorderQuad.move(mainFrameOffset);
1133         absMarginQuad.move(mainFrameOffset);
1134
1135         drawHighlightForBox(context, absContentQuad, absPaddingQuad, absBorderQuad, absMarginQuad);
1136     } else if (renderer->isRenderInline() || isSVGRenderer) {
1137         // FIXME: We should show margins/padding/border for inlines.
1138         Vector<FloatQuad> lineBoxQuads;
1139         renderer->absoluteQuads(lineBoxQuads);
1140         for (unsigned i = 0; i < lineBoxQuads.size(); ++i)
1141             lineBoxQuads[i] += mainFrameOffset;
1142
1143         drawHighlightForLineBoxesOrSVGRenderer(context, lineBoxQuads);
1144     }
1145
1146     // Draw node title if necessary.
1147
1148     if (!m_highlightedNode->isElementNode())
1149         return;
1150
1151     WebCore::Settings* settings = containingFrame->settings();
1152     drawElementTitle(context, titleReferenceBox, overlayRect, settings);
1153 }
1154
1155 void InspectorAgent::drawElementTitle(GraphicsContext& context, const IntRect& boundingBox, const FloatRect& overlayRect, WebCore::Settings* settings) const
1156 {
1157     static const int rectInflatePx = 4;
1158     static const int fontHeightPx = 12;
1159     static const int borderWidthPx = 1;
1160     static const Color tooltipBackgroundColor(255, 255, 194, 255);
1161     static const Color tooltipBorderColor(Color::black);
1162     static const Color tooltipFontColor(Color::black);
1163
1164     Element* element = static_cast<Element*>(m_highlightedNode.get());
1165     bool isXHTML = element->document()->isXHTMLDocument();
1166     String nodeTitle = isXHTML ? element->nodeName() : element->nodeName().lower();
1167     const AtomicString& idValue = element->getIdAttribute();
1168     if (!idValue.isNull() && !idValue.isEmpty()) {
1169         nodeTitle += "#";
1170         nodeTitle += idValue;
1171     }
1172     if (element->hasClass() && element->isStyledElement()) {
1173         const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames();
1174         size_t classNameCount = classNamesString.size();
1175         if (classNameCount) {
1176             HashSet<AtomicString> usedClassNames;
1177             for (size_t i = 0; i < classNameCount; ++i) {
1178                 const AtomicString& className = classNamesString[i];
1179                 if (usedClassNames.contains(className))
1180                     continue;
1181                 usedClassNames.add(className);
1182                 nodeTitle += ".";
1183                 nodeTitle += className;
1184             }
1185         }
1186     }
1187
1188     Element* highlightedElement = m_highlightedNode->isElementNode() ? static_cast<Element*>(m_highlightedNode.get()) : 0;
1189     nodeTitle += " [";
1190     nodeTitle += String::number(highlightedElement ? highlightedElement->offsetWidth() : boundingBox.width());
1191     nodeTitle.append(static_cast<UChar>(0x00D7)); // &times;
1192     nodeTitle += String::number(highlightedElement ? highlightedElement->offsetHeight() : boundingBox.height());
1193     nodeTitle += "]";
1194
1195     FontDescription desc;
1196     FontFamily family;
1197     family.setFamily(settings->fixedFontFamily());
1198     desc.setFamily(family);
1199     desc.setComputedSize(fontHeightPx);
1200     Font font = Font(desc, 0, 0);
1201     font.update(0);
1202
1203     TextRun nodeTitleRun(nodeTitle);
1204     IntPoint titleBasePoint = IntPoint(boundingBox.x(), boundingBox.maxY() - 1);
1205     titleBasePoint.move(rectInflatePx, rectInflatePx);
1206     IntRect titleRect = enclosingIntRect(font.selectionRectForText(nodeTitleRun, titleBasePoint, fontHeightPx));
1207     titleRect.inflate(rectInflatePx);
1208
1209     // The initial offsets needed to compensate for a 1px-thick border stroke (which is not a part of the rectangle).
1210     int dx = -borderWidthPx;
1211     int dy = borderWidthPx;
1212
1213     // If the tip sticks beyond the right of overlayRect, right-align the tip with the said boundary.
1214     if (titleRect.maxX() > overlayRect.maxX())
1215         dx = overlayRect.maxX() - titleRect.maxX();
1216
1217     // If the tip sticks beyond the left of overlayRect, left-align the tip with the said boundary.
1218     if (titleRect.x() + dx < overlayRect.x())
1219         dx = overlayRect.x() - titleRect.x() - borderWidthPx;
1220
1221     // If the tip sticks beyond the bottom of overlayRect, show the tip at top of bounding box.
1222     if (titleRect.maxY() > overlayRect.maxY()) {
1223         dy = boundingBox.y() - titleRect.maxY() - borderWidthPx;
1224         // If the tip still sticks beyond the bottom of overlayRect, bottom-align the tip with the said boundary.
1225         if (titleRect.maxY() + dy > overlayRect.maxY())
1226             dy = overlayRect.maxY() - titleRect.maxY();
1227     }
1228
1229     // If the tip sticks beyond the top of overlayRect, show the tip at top of overlayRect.
1230     if (titleRect.y() + dy < overlayRect.y())
1231         dy = overlayRect.y() - titleRect.y() + borderWidthPx;
1232
1233     titleRect.move(dx, dy);
1234     context.setStrokeColor(tooltipBorderColor, ColorSpaceDeviceRGB);
1235     context.setStrokeThickness(borderWidthPx);
1236     context.setFillColor(tooltipBackgroundColor, ColorSpaceDeviceRGB);
1237     context.drawRect(titleRect);
1238     context.setFillColor(tooltipFontColor, ColorSpaceDeviceRGB);
1239     context.drawText(font, nodeTitleRun, IntPoint(titleRect.x() + rectInflatePx, titleRect.y() + font.fontMetrics().height()));
1240 }
1241
1242 void InspectorAgent::openInInspectedWindow(const String& url)
1243 {
1244     Frame* mainFrame = m_inspectedPage->mainFrame();
1245
1246     FrameLoadRequest request(mainFrame->document()->securityOrigin(), ResourceRequest(), "_blank");
1247
1248     bool created;
1249     WindowFeatures windowFeatures;
1250     Frame* newFrame = WebCore::createWindow(mainFrame, mainFrame, request, windowFeatures, created);
1251     if (!newFrame)
1252         return;
1253
1254     UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
1255     newFrame->loader()->setOpener(mainFrame);
1256     newFrame->page()->setOpenedByDOM();
1257     newFrame->loader()->changeLocation(mainFrame->document()->securityOrigin(), newFrame->loader()->completeURL(url), "", false, false);
1258 }
1259
1260 void InspectorAgent::addScriptToEvaluateOnLoad(const String& source)
1261 {
1262     m_scriptsToEvaluateOnLoad.append(source);
1263 }
1264
1265 void InspectorAgent::removeAllScriptsToEvaluateOnLoad()
1266 {
1267     m_scriptsToEvaluateOnLoad.clear();
1268 }
1269
1270 void InspectorAgent::setInspectorExtensionAPI(const String& source)
1271 {
1272     m_inspectorExtensionAPI = source;
1273 }
1274
1275 KURL InspectorAgent::inspectedURL() const
1276 {
1277     return m_inspectedPage->mainFrame()->document()->url();
1278 }
1279
1280 KURL InspectorAgent::inspectedURLWithoutFragment() const
1281 {
1282     KURL url = inspectedURL();
1283     url.removeFragmentIdentifier();
1284     return url;
1285 }
1286
1287 void InspectorAgent::reloadPage(bool ignoreCache)
1288 {
1289     m_inspectedPage->mainFrame()->loader()->reload(ignoreCache);
1290 }
1291
1292 bool InspectorAgent::enabled() const
1293 {
1294     if (!m_inspectedPage)
1295         return false;
1296     return m_inspectedPage->settings()->developerExtrasEnabled();
1297 }
1298
1299 void InspectorAgent::showConsole()
1300 {
1301     showPanel(consolePanelName);
1302 }
1303
1304 void InspectorAgent::showPanel(const String& panel)
1305 {
1306     if (!m_frontend) {
1307         m_requiredPanel = panel;
1308         return;
1309     }
1310     m_frontend->showPanel(panel);
1311 }
1312
1313 } // namespace WebCore
1314
1315 #endif // ENABLE(INSPECTOR)