c269825d8a21f0badb6be9d625f1be2dd242b91f
[WebKit-https.git] / WebCore / inspector / InspectorController.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009 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 "InspectorController.h"
32
33 #if ENABLE(INSPECTOR)
34
35 #include "CString.h"
36 #include "CachedResource.h"
37 #include "Console.h"
38 #include "ConsoleMessage.h"
39 #include "Cookie.h"
40 #include "CookieJar.h"
41 #include "Document.h"
42 #include "DocumentLoader.h"
43 #include "DOMWindow.h"
44 #include "Element.h"
45 #include "FloatConversion.h"
46 #include "FloatQuad.h"
47 #include "FloatRect.h"
48 #include "Frame.h"
49 #include "FrameLoader.h"
50 #include "FrameTree.h"
51 #include "FrameView.h"
52 #include "GraphicsContext.h"
53 #include "HTMLFrameOwnerElement.h"
54 #include "HitTestResult.h"
55 #include "InspectorBackend.h"
56 #include "InjectedScriptHost.h"
57 #include "InspectorClient.h"
58 #include "InspectorFrontend.h"
59 #include "InspectorFrontendHost.h"
60 #include "InspectorDatabaseResource.h"
61 #include "InspectorDOMAgent.h"
62 #include "InspectorDOMStorageResource.h"
63 #include "InspectorTimelineAgent.h"
64 #include "InspectorResource.h"
65 #include "JavaScriptProfile.h"
66 #include "Page.h"
67 #include "ProgressTracker.h"
68 #include "Range.h"
69 #include "RenderInline.h"
70 #include "ResourceRequest.h"
71 #include "ResourceResponse.h"
72 #include "ScriptCallStack.h"
73 #include "ScriptFunctionCall.h"
74 #include "ScriptObject.h"
75 #include "ScriptString.h"
76 #include "SecurityOrigin.h"
77 #include "Settings.h"
78 #include "SharedBuffer.h"
79 #include "TextEncoding.h"
80 #include "TextIterator.h"
81 #include <wtf/CurrentTime.h>
82 #include <wtf/ListHashSet.h>
83 #include <wtf/RefCounted.h>
84 #include <wtf/StdLibExtras.h>
85
86 #if ENABLE(DATABASE)
87 #include "Database.h"
88 #endif
89
90 #if ENABLE(DOM_STORAGE)
91 #include "Storage.h"
92 #include "StorageArea.h"
93 #endif
94
95 #if ENABLE(JAVASCRIPT_DEBUGGER)
96 #include "JavaScriptCallFrame.h"
97 #include "JavaScriptDebugServer.h"
98 #include "JSJavaScriptCallFrame.h"
99
100 #include <profiler/Profile.h>
101 #include <profiler/Profiler.h>
102 #include <runtime/JSLock.h>
103 #include <runtime/UString.h>
104
105 using namespace JSC;
106 #endif
107 using namespace std;
108
109 namespace WebCore {
110
111 static const char* const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
112 static const char* const CPUProfileType = "CPU";
113 static const char* const resourceTrackingEnabledSettingName = "resourceTrackingEnabled";
114 static const char* const debuggerEnabledSettingName = "debuggerEnabled";
115 static const char* const profilerEnabledSettingName = "profilerEnabled";
116 static const char* const inspectorAttachedHeightName = "inspectorAttachedHeight";
117 static const char* const lastActivePanelSettingName = "lastActivePanel";
118
119 static const unsigned defaultAttachedHeight = 300;
120 static const float minimumAttachedHeight = 250.0f;
121 static const float maximumAttachedHeightRatio = 0.75f;
122
123 static unsigned s_inspectorControllerCount;
124
125 InspectorController::InspectorController(Page* page, InspectorClient* client)
126     : m_inspectedPage(page)
127     , m_client(client)
128     , m_page(0)
129     , m_scriptState(0)
130     , m_windowVisible(false)
131     , m_showAfterVisible(CurrentPanel)
132     , m_groupLevel(0)
133     , m_searchingForNode(false)
134     , m_previousMessage(0)
135     , m_resourceTrackingEnabled(false)
136     , m_resourceTrackingSettingsLoaded(false)
137     , m_inspectorBackend(InspectorBackend::create(this))
138     , m_inspectorFrontendHost(InspectorFrontendHost::create(this, client))
139     , m_injectedScriptHost(InjectedScriptHost::create(this))
140     , m_lastBoundObjectId(1)
141 #if ENABLE(JAVASCRIPT_DEBUGGER)
142     , m_debuggerEnabled(false)
143     , m_attachDebuggerWhenShown(false)
144     , m_profilerEnabled(false)
145     , m_recordingUserInitiatedProfile(false)
146     , m_currentUserInitiatedProfileNumber(-1)
147     , m_nextUserInitiatedProfileNumber(1)
148     , m_startProfiling(this, &InspectorController::startUserInitiatedProfiling)
149 #endif
150 {
151     ASSERT_ARG(page, page);
152     ASSERT_ARG(client, client);
153     ++s_inspectorControllerCount;
154 }
155
156 InspectorController::~InspectorController()
157 {
158     // These should have been cleared in inspectedPageDestroyed().
159     ASSERT(!m_client);
160     ASSERT(!m_scriptState);
161     ASSERT(!m_inspectedPage);
162     ASSERT(!m_page || (m_page && !m_page->parentInspectorController()));
163
164     deleteAllValues(m_frameResources);
165     deleteAllValues(m_consoleMessages);
166
167     ASSERT(s_inspectorControllerCount);
168     --s_inspectorControllerCount;
169
170     releaseDOMAgent();
171
172     m_inspectorBackend->disconnectController();
173     m_inspectorFrontendHost->disconnectController();
174     m_injectedScriptHost->disconnectController();
175 }
176
177 void InspectorController::inspectedPageDestroyed()
178 {
179     close();
180
181     if (m_scriptState) {
182         ScriptGlobalObject::remove(m_scriptState, "InspectorBackend");
183         ScriptGlobalObject::remove(m_scriptState, "InspectorFrontendHost");
184         ScriptGlobalObject::remove(m_scriptState, "InjectedScriptHost");
185     }
186     ASSERT(m_inspectedPage);
187     m_inspectedPage = 0;
188
189     m_client->inspectorDestroyed();
190     m_client = 0;
191 }
192
193 bool InspectorController::enabled() const
194 {
195     if (!m_inspectedPage)
196         return false;
197     return m_inspectedPage->settings()->developerExtrasEnabled();
198 }
199
200 String InspectorController::setting(const String& key) const
201 {
202     Settings::iterator it = m_settings.find(key);
203     if (it != m_settings.end())
204         return it->second;
205
206     String value;
207     m_client->populateSetting(key, &value);
208     m_settings.set(key, value);
209     return value;
210 }
211
212 void InspectorController::setSetting(const String& key, const String& value)
213 {
214     m_settings.set(key, value);
215     m_client->storeSetting(key, value);
216 }
217
218 // Trying to inspect something in a frame with JavaScript disabled would later lead to
219 // crashes trying to create JavaScript wrappers. Some day we could fix this issue, but
220 // for now prevent crashes here by never targeting a node in such a frame.
221 static bool canPassNodeToJavaScript(Node* node)
222 {
223     if (!node)
224         return false;
225     Frame* frame = node->document()->frame();
226     return frame && frame->script()->isEnabled();
227 }
228
229 void InspectorController::inspect(Node* node)
230 {
231     if (!canPassNodeToJavaScript(node) || !enabled())
232         return;
233
234     show();
235
236     if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
237         node = node->parentNode();
238     m_nodeToFocus = node;
239
240     if (!m_frontend) {
241         m_showAfterVisible = ElementsPanel;
242         return;
243     }
244
245     focusNode();
246 }
247
248 void InspectorController::focusNode()
249 {
250     if (!enabled())
251         return;
252
253     ASSERT(m_frontend);
254     ASSERT(m_nodeToFocus);
255
256     long id = m_domAgent->pushNodePathToFrontend(m_nodeToFocus.get());
257     m_frontend->updateFocusedNode(id);
258     m_nodeToFocus = 0;
259 }
260
261 void InspectorController::highlight(Node* node)
262 {
263     if (!enabled())
264         return;
265     ASSERT_ARG(node, node);
266     m_highlightedNode = node;
267     m_client->highlight(node);
268 }
269
270 void InspectorController::hideHighlight()
271 {
272     if (!enabled())
273         return;
274     m_highlightedNode = 0;
275     m_client->hideHighlight();
276 }
277
278 bool InspectorController::windowVisible()
279 {
280     return m_windowVisible;
281 }
282
283 void InspectorController::setWindowVisible(bool visible, bool attached)
284 {
285     if (visible == m_windowVisible || !m_frontend)
286         return;
287
288     m_windowVisible = visible;
289
290     if (m_windowVisible) {
291         setAttachedWindow(attached);
292         populateScriptObjects();
293
294         if (m_showAfterVisible == CurrentPanel) {
295           String lastActivePanelSetting = setting(lastActivePanelSettingName);
296           m_showAfterVisible = specialPanelForJSName(lastActivePanelSetting);
297         }
298
299         if (m_nodeToFocus)
300             focusNode();
301 #if ENABLE(JAVASCRIPT_DEBUGGER)
302         if (m_attachDebuggerWhenShown)
303             enableDebugger();
304 #endif
305         showPanel(m_showAfterVisible);
306     } else {
307 #if ENABLE(JAVASCRIPT_DEBUGGER)
308         // If the window is being closed with the debugger enabled,
309         // remember this state to re-enable debugger on the next window
310         // opening.
311         bool debuggerWasEnabled = m_debuggerEnabled;
312         disableDebugger();
313         if (debuggerWasEnabled)
314             m_attachDebuggerWhenShown = true;
315 #endif
316         if (m_searchingForNode)
317             toggleSearchForNodeInPage();
318         resetScriptObjects();
319         stopTimelineProfiler();
320     }
321     m_showAfterVisible = CurrentPanel;
322 }
323
324 void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, ScriptCallStack* callStack)
325 {
326     if (!enabled())
327         return;
328
329     addConsoleMessage(callStack->state(), new ConsoleMessage(source, type, level, callStack, m_groupLevel, type == TraceMessageType));
330 }
331
332 void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID)
333 {
334     if (!enabled())
335         return;
336
337     addConsoleMessage(0, new ConsoleMessage(source, type, level, message, lineNumber, sourceID, m_groupLevel));
338 }
339
340 void InspectorController::addConsoleMessage(ScriptState* scriptState, ConsoleMessage* consoleMessage)
341 {
342     ASSERT(enabled());
343     ASSERT_ARG(consoleMessage, consoleMessage);
344
345     if (m_previousMessage && m_previousMessage->isEqual(scriptState, consoleMessage)) {
346         m_previousMessage->incrementCount();
347         delete consoleMessage;
348         if (windowVisible())
349             m_previousMessage->updateRepeatCountInConsole(m_frontend.get());
350     } else {
351         m_previousMessage = consoleMessage;
352         m_consoleMessages.append(consoleMessage);
353         if (windowVisible())
354             m_previousMessage->addToConsole(m_frontend.get());
355     }
356 }
357
358 void InspectorController::clearConsoleMessages(bool clearUI)
359 {
360     deleteAllValues(m_consoleMessages);
361     m_consoleMessages.clear();
362     m_previousMessage = 0;
363     m_groupLevel = 0;
364     releaseWrapperObjectGroup("console");
365     if (m_domAgent)
366         m_domAgent->releaseDanglingNodes();
367     if (clearUI && m_frontend)
368         m_frontend->clearConsoleMessages();
369 }
370
371 void InspectorController::startGroup(MessageSource source, ScriptCallStack* callStack)
372 {
373     ++m_groupLevel;
374
375     addConsoleMessage(callStack->state(), new ConsoleMessage(source, StartGroupMessageType, LogMessageLevel, callStack, m_groupLevel));
376 }
377
378 void InspectorController::endGroup(MessageSource source, unsigned lineNumber, const String& sourceURL)
379 {
380     if (m_groupLevel == 0)
381         return;
382
383     --m_groupLevel;
384
385     addConsoleMessage(0, new ConsoleMessage(source, EndGroupMessageType, LogMessageLevel, String(), lineNumber, sourceURL, m_groupLevel));
386 }
387
388 void InspectorController::markTimeline(const String& message)
389 {
390     if (timelineAgent())
391         timelineAgent()->didMarkTimeline(message);
392 }
393
394 static unsigned constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
395 {
396     return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
397 }
398
399 void InspectorController::attachWindow()
400 {
401     if (!enabled())
402         return;
403
404     unsigned inspectedPageHeight = m_inspectedPage->mainFrame()->view()->visibleHeight();
405
406     m_client->attachWindow();
407
408     String attachedHeight = setting(inspectorAttachedHeightName);
409     bool success = true;
410     int height = attachedHeight.toInt(&success);
411     unsigned preferredHeight = success ? height : defaultAttachedHeight;
412
413     // We need to constrain the window height here in case the user has resized the inspected page's window so that
414     // the user's preferred height would be too big to display.
415     m_client->setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
416 }
417
418 void InspectorController::detachWindow()
419 {
420     if (!enabled())
421         return;
422     m_client->detachWindow();
423 }
424
425 void InspectorController::setAttachedWindow(bool attached)
426 {
427     if (!enabled() || !m_frontend)
428         return;
429
430     m_frontend->setAttachedWindow(attached);
431 }
432
433 void InspectorController::setAttachedWindowHeight(unsigned height)
434 {
435     if (!enabled())
436         return;
437     
438     unsigned totalHeight = m_page->mainFrame()->view()->visibleHeight() + m_inspectedPage->mainFrame()->view()->visibleHeight();
439     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
440     
441     setSetting(inspectorAttachedHeightName, String::number(attachedHeight));
442     
443     m_client->setAttachedWindowHeight(attachedHeight);
444 }
445
446 void InspectorController::storeLastActivePanel(const String& panelName)
447 {
448     setSetting(lastActivePanelSettingName, panelName);
449 }
450
451 void InspectorController::toggleSearchForNodeInPage()
452 {
453     if (!enabled())
454         return;
455
456     m_searchingForNode = !m_searchingForNode;
457     if (!m_searchingForNode)
458         hideHighlight();
459 }
460
461 void InspectorController::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
462 {
463     if (!enabled() || !m_searchingForNode)
464         return;
465
466     Node* node = result.innerNode();
467     if (node)
468         highlight(node);
469 }
470
471 void InspectorController::handleMousePressOnNode(Node* node)
472 {
473     if (!enabled())
474         return;
475
476     ASSERT(m_searchingForNode);
477     ASSERT(node);
478     if (!node)
479         return;
480
481     // inspect() will implicitly call ElementsPanel's focusedNodeChanged() and the hover feedback will be stopped there.
482     inspect(node);
483 }
484
485 void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame)
486 {
487     if (!enabled() || !m_frontend || frame != m_inspectedPage->mainFrame())
488         return;
489     resetInjectedScript();
490 }
491
492 void InspectorController::windowScriptObjectAvailable()
493 {
494     if (!m_page || !enabled())
495         return;
496
497     // Grant the inspector the ability to script the inspected page.
498     m_page->mainFrame()->document()->securityOrigin()->grantUniversalAccess();
499     m_scriptState = scriptStateFromPage(debuggerWorld(), m_page);
500     ScriptGlobalObject::set(m_scriptState, "InspectorBackend", m_inspectorBackend.get());
501     ScriptGlobalObject::set(m_scriptState, "InspectorFrontendHost", m_inspectorFrontendHost.get());
502     ScriptGlobalObject::set(m_scriptState, "InjectedScriptHost", m_injectedScriptHost.get());
503 }
504
505 void InspectorController::scriptObjectReady()
506 {
507     ASSERT(m_scriptState);
508     if (!m_scriptState)
509         return;
510
511     ScriptObject webInspectorObj;
512     if (!ScriptGlobalObject::get(m_scriptState, "WebInspector", webInspectorObj))
513         return;
514     ScriptObject injectedScriptObj;
515     if (!ScriptGlobalObject::get(m_scriptState, "InjectedScript", injectedScriptObj))
516         return;
517     setFrontendProxyObject(m_scriptState, webInspectorObj, injectedScriptObj);
518
519 #if ENABLE(JAVASCRIPT_DEBUGGER)
520     String debuggerEnabled = setting(debuggerEnabledSettingName);
521     if (debuggerEnabled == "true")
522         enableDebugger();
523     String profilerEnabled = setting(profilerEnabledSettingName);
524     if (profilerEnabled == "true")
525         enableProfiler();
526 #endif
527
528     // Make sure our window is visible now that the page loaded
529     showWindow();
530
531     m_client->inspectorWindowObjectCleared();
532 }
533
534 void InspectorController::setFrontendProxyObject(ScriptState* scriptState, ScriptObject webInspectorObj, ScriptObject injectedScriptObj)
535 {
536     m_scriptState = scriptState;
537     m_injectedScriptObj = injectedScriptObj;
538     m_frontend.set(new InspectorFrontend(this, scriptState, webInspectorObj));
539     releaseDOMAgent();
540     m_domAgent = InspectorDOMAgent::create(m_frontend.get());
541     if (m_timelineAgent)
542         m_timelineAgent->resetFrontendProxyObject(m_frontend.get());
543 }
544
545 void InspectorController::show()
546 {
547     if (!enabled())
548         return;
549     
550     if (!m_page) {
551         if (m_frontend)
552             return;  // We are using custom frontend - no need to create page.
553
554         m_page = m_client->createPage();
555         if (!m_page)
556             return;
557         m_page->setParentInspectorController(this);
558
559         // showWindow() will be called after the page loads in scriptObjectReady()
560         return;
561     }
562
563     showWindow();
564 }
565
566 void InspectorController::showPanel(SpecialPanels panel)
567 {
568     if (!enabled())
569         return;
570
571     show();
572
573     if (!m_frontend) {
574         m_showAfterVisible = panel;
575         return;
576     }
577
578     if (panel == CurrentPanel)
579         return;
580
581     m_frontend->showPanel(panel);
582 }
583
584 void InspectorController::close()
585 {
586     if (!enabled())
587         return;
588
589 #if ENABLE(JAVASCRIPT_DEBUGGER)
590     stopUserInitiatedProfiling();
591     disableDebugger();
592 #endif
593     closeWindow();
594
595     m_injectedScriptObj = ScriptObject();
596     releaseDOMAgent();
597     m_frontend.set(0);
598     m_timelineAgent = 0;
599     m_scriptState = 0;
600     if (m_page) {
601         if (!m_page->mainFrame() || !m_page->mainFrame()->loader() || !m_page->mainFrame()->loader()->isLoading()) {
602             m_page->setParentInspectorController(0);
603             m_page = 0;
604         }
605     }
606 }
607
608 void InspectorController::showWindow()
609 {
610     ASSERT(enabled());
611
612     unsigned inspectedPageHeight = m_inspectedPage->mainFrame()->view()->visibleHeight();
613
614     m_client->showWindow();
615
616     String attachedHeight = setting(inspectorAttachedHeightName);
617     bool success = true;
618     int height = attachedHeight.toInt(&success);
619     unsigned preferredHeight = success ? height : defaultAttachedHeight;
620
621     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
622     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
623     // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
624     m_client->setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
625 }
626
627 void InspectorController::closeWindow()
628 {
629     m_client->closeWindow();
630 }
631
632 void InspectorController::releaseDOMAgent()
633 {
634     // m_domAgent is RefPtr. Remove DOM listeners first to ensure that there are
635     // no references to the DOM agent from the DOM tree.
636     if (m_domAgent)
637         m_domAgent->reset();
638     m_domAgent = 0;
639 }
640
641 void InspectorController::populateScriptObjects()
642 {
643     ASSERT(m_frontend);
644     if (!m_frontend)
645         return;
646
647     m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
648
649     ResourcesMap::iterator resourcesEnd = m_resources.end();
650     for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it)
651         it->second->createScriptObject(m_frontend.get());
652
653     unsigned messageCount = m_consoleMessages.size();
654     for (unsigned i = 0; i < messageCount; ++i)
655         m_consoleMessages[i]->addToConsole(m_frontend.get());
656
657 #if ENABLE(DATABASE)
658     DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end();
659     for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
660         it->second->bind(m_frontend.get());
661 #endif
662 #if ENABLE(DOM_STORAGE)
663     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
664     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
665         it->second->bind(m_frontend.get());
666 #endif
667
668     m_frontend->populateInterface();
669
670     // Dispatch pending frontend commands
671     for (Vector<pair<long, String> >::iterator it = m_pendingEvaluateTestCommands.begin(); it != m_pendingEvaluateTestCommands.end(); ++it)
672         m_frontend->evaluateForTestInFrontend((*it).first, (*it).second);
673     m_pendingEvaluateTestCommands.clear();
674 }
675
676 void InspectorController::resetScriptObjects()
677 {
678     if (!m_frontend)
679         return;
680
681     ResourcesMap::iterator resourcesEnd = m_resources.end();
682     for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it)
683         it->second->releaseScriptObject(m_frontend.get(), false);
684
685 #if ENABLE(DATABASE)
686     DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end();
687     for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
688         it->second->unbind();
689 #endif
690 #if ENABLE(DOM_STORAGE)
691     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
692     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
693         it->second->unbind();
694 #endif
695
696     if (m_timelineAgent)
697         m_timelineAgent->reset();
698
699     m_frontend->reset();
700     m_domAgent->reset();
701     m_objectGroups.clear();
702     m_idToWrappedObject.clear();
703 }
704
705 void InspectorController::pruneResources(ResourcesMap* resourceMap, DocumentLoader* loaderToKeep)
706 {
707     ASSERT_ARG(resourceMap, resourceMap);
708
709     ResourcesMap mapCopy(*resourceMap);
710     ResourcesMap::iterator end = mapCopy.end();
711     for (ResourcesMap::iterator it = mapCopy.begin(); it != end; ++it) {
712         InspectorResource* resource = (*it).second.get();
713         if (resource == m_mainResource)
714             continue;
715
716         if (!loaderToKeep || !resource->isSameLoader(loaderToKeep)) {
717             removeResource(resource);
718             if (windowVisible())
719                 resource->releaseScriptObject(m_frontend.get(), true);
720         }
721     }
722 }
723
724 void InspectorController::didCommitLoad(DocumentLoader* loader)
725 {
726     if (!enabled())
727         return;
728
729     ASSERT(m_inspectedPage);
730
731     if (loader->frame() == m_inspectedPage->mainFrame()) {
732         m_client->inspectedURLChanged(loader->url().string());
733
734         clearConsoleMessages(false);
735
736         m_times.clear();
737         m_counts.clear();
738 #if ENABLE(JAVASCRIPT_DEBUGGER)
739         m_profiles.clear();
740         m_currentUserInitiatedProfileNumber = 1;
741         m_nextUserInitiatedProfileNumber = 1;
742 #endif
743         // resetScriptObjects should be called before database and DOM storage
744         // resources are cleared so that it has a chance to unbind them.
745         resetScriptObjects();
746
747 #if ENABLE(DATABASE)
748         m_databaseResources.clear();
749 #endif
750 #if ENABLE(DOM_STORAGE)
751         m_domStorageResources.clear();
752 #endif
753
754         if (m_frontend) {
755             if (!loader->frameLoader()->isLoadingFromCachedPage()) {
756                 ASSERT(m_mainResource && m_mainResource->isSameLoader(loader));
757                 // We don't add the main resource until its load is committed. This is
758                 // needed to keep the load for a user-entered URL from showing up in the
759                 // list of resources for the page they are navigating away from.
760                 if (windowVisible())
761                     m_mainResource->createScriptObject(m_frontend.get());
762             } else {
763                 // Pages loaded from the page cache are committed before
764                 // m_mainResource is the right resource for this load, so we
765                 // clear it here. It will be re-assigned in
766                 // identifierForInitialRequest.
767                 m_mainResource = 0;
768             }
769             if (windowVisible()) {
770                 m_frontend->didCommitLoad();
771                 m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
772             }
773         }
774     }
775
776     for (Frame* frame = loader->frame(); frame; frame = frame->tree()->traverseNext(loader->frame()))
777         if (ResourcesMap* resourceMap = m_frameResources.get(frame))
778             pruneResources(resourceMap, loader);
779 }
780
781 void InspectorController::frameDetachedFromParent(Frame* frame)
782 {
783     if (!enabled())
784         return;
785     if (ResourcesMap* resourceMap = m_frameResources.get(frame))
786         removeAllResources(resourceMap);
787 }
788
789 void InspectorController::addResource(InspectorResource* resource)
790 {
791     m_resources.set(resource->identifier(), resource);
792     m_knownResources.add(resource->requestURL());
793
794     Frame* frame = resource->frame();
795     ResourcesMap* resourceMap = m_frameResources.get(frame);
796     if (resourceMap)
797         resourceMap->set(resource->identifier(), resource);
798     else {
799         resourceMap = new ResourcesMap;
800         resourceMap->set(resource->identifier(), resource);
801         m_frameResources.set(frame, resourceMap);
802     }
803 }
804
805 void InspectorController::removeResource(InspectorResource* resource)
806 {
807     m_resources.remove(resource->identifier());
808     String requestURL = resource->requestURL();
809     if (!requestURL.isNull())
810         m_knownResources.remove(requestURL);
811
812     Frame* frame = resource->frame();
813     ResourcesMap* resourceMap = m_frameResources.get(frame);
814     if (!resourceMap) {
815         ASSERT_NOT_REACHED();
816         return;
817     }
818
819     resourceMap->remove(resource->identifier());
820     if (resourceMap->isEmpty()) {
821         m_frameResources.remove(frame);
822         delete resourceMap;
823     }
824 }
825
826 InspectorResource* InspectorController::getTrackedResource(unsigned long identifier)
827 {
828     if (!enabled())
829         return 0;
830
831     if (m_resourceTrackingEnabled)
832         return m_resources.get(identifier).get();
833
834     bool isMainResource = m_mainResource && m_mainResource->identifier() == identifier;
835     if (isMainResource)
836         return m_mainResource.get();
837
838     return 0;
839 }
840
841 void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* cachedResource)
842 {
843     if (!enabled())
844         return;
845
846     // If the resource URL is already known, we don't need to add it again since this is just a cached load.
847     if (m_knownResources.contains(cachedResource->url()))
848         return;
849
850     ASSERT(m_inspectedPage);
851     bool isMainResource = isMainResourceLoader(loader, KURL(ParsedURLString, cachedResource->url()));
852     ensureResourceTrackingSettingsLoaded();
853     if (!isMainResource && !m_resourceTrackingEnabled)
854         return;
855
856     RefPtr<InspectorResource> resource = InspectorResource::createCached(m_inspectedPage->progress()->createUniqueIdentifier() , loader, cachedResource);
857
858     if (isMainResource) {
859         m_mainResource = resource;
860         resource->markMainResource();
861     }
862
863     addResource(resource.get());
864
865     if (windowVisible())
866         resource->createScriptObject(m_frontend.get());
867 }
868
869 void InspectorController::identifierForInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
870 {
871     if (!enabled())
872         return;
873     ASSERT(m_inspectedPage);
874
875     bool isMainResource = isMainResourceLoader(loader, request.url());
876     ensureResourceTrackingSettingsLoaded();
877     if (!isMainResource && !m_resourceTrackingEnabled)
878         return;
879
880     RefPtr<InspectorResource> resource = InspectorResource::create(identifier, loader);
881
882     resource->updateRequest(request);
883
884     if (isMainResource) {
885         m_mainResource = resource;
886         resource->markMainResource();
887     }
888
889     addResource(resource.get());
890
891     if (windowVisible() && loader->frameLoader()->isLoadingFromCachedPage() && resource == m_mainResource)
892         resource->createScriptObject(m_frontend.get());
893 }
894
895 void InspectorController::mainResourceFiredDOMContentEvent(DocumentLoader* loader, const KURL& url)
896 {
897     if (!enabled() || !isMainResourceLoader(loader, url))
898         return;
899
900     if (m_mainResource) {
901         m_mainResource->markDOMContentEventTime();
902         if (windowVisible())
903             m_mainResource->updateScriptObject(m_frontend.get());
904     }
905 }
906
907 void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, const KURL& url)
908 {
909     if (!enabled() || !isMainResourceLoader(loader, url))
910         return;
911
912     if (m_mainResource) {
913         m_mainResource->markLoadEventTime();
914         if (windowVisible())
915             m_mainResource->updateScriptObject(m_frontend.get());
916     }
917 }
918
919 bool InspectorController::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
920 {
921     return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
922 }
923
924 void InspectorController::willSendRequest(unsigned long identifier, const ResourceRequest& request, const ResourceResponse& redirectResponse)
925 {
926     bool isMainResource = (m_mainResource && m_mainResource->identifier() == identifier);
927     if (m_timelineAgent)
928         m_timelineAgent->willSendResourceRequest(identifier, isMainResource, request);
929
930     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
931     if (!resource)
932         return;
933
934     resource->startTiming();
935
936     if (!redirectResponse.isNull()) {
937         resource->updateRequest(request);
938         resource->updateResponse(redirectResponse);
939     }
940
941     if (resource != m_mainResource && windowVisible())
942         resource->createScriptObject(m_frontend.get());
943 }
944
945 void InspectorController::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
946 {
947     if (m_timelineAgent)
948         m_timelineAgent->didReceiveResourceResponse(identifier, response);
949
950     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
951     if (!resource)
952         return;
953
954     resource->updateResponse(response);
955     resource->markResponseReceivedTime();
956
957     if (windowVisible())
958         resource->updateScriptObject(m_frontend.get());
959 }
960
961 void InspectorController::didReceiveContentLength(unsigned long identifier, int lengthReceived)
962 {
963     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
964     if (!resource)
965         return;
966
967     resource->addLength(lengthReceived);
968
969     if (windowVisible())
970         resource->updateScriptObject(m_frontend.get());
971 }
972
973 void InspectorController::didFinishLoading(unsigned long identifier)
974 {
975     if (m_timelineAgent)
976         m_timelineAgent->didFinishLoadingResource(identifier, false);
977
978     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
979     if (!resource)
980         return;
981
982     removeResource(resource.get());
983
984     resource->endTiming();
985
986     addResource(resource.get());
987
988     if (windowVisible())
989         resource->updateScriptObject(m_frontend.get());
990 }
991
992 void InspectorController::didFailLoading(unsigned long identifier, const ResourceError& /*error*/)
993 {
994     if (m_timelineAgent)
995         m_timelineAgent->didFinishLoadingResource(identifier, true);
996
997     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
998     if (!resource)
999         return;
1000
1001     removeResource(resource.get());
1002
1003     resource->markFailed();
1004     resource->endTiming();
1005
1006     addResource(resource.get());
1007
1008     if (windowVisible())
1009         resource->updateScriptObject(m_frontend.get());
1010 }
1011
1012 void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString)
1013 {
1014     if (!enabled() || !m_resourceTrackingEnabled)
1015         return;
1016
1017     InspectorResource* resource = m_resources.get(identifier).get();
1018     if (!resource)
1019         return;
1020
1021     resource->setXMLHttpResponseText(sourceString);
1022
1023     if (windowVisible())
1024         resource->updateScriptObject(m_frontend.get());
1025 }
1026
1027 void InspectorController::scriptImported(unsigned long identifier, const String& sourceString)
1028 {
1029     if (!enabled() || !m_resourceTrackingEnabled)
1030         return;
1031     
1032     InspectorResource* resource = m_resources.get(identifier).get();
1033     if (!resource)
1034         return;
1035     
1036     // FIXME: imported script and XHR response are currently viewed as the same
1037     // thing by the Inspector. They should be made into distinct types.
1038     resource->setXMLHttpResponseText(ScriptString(sourceString));
1039     
1040     if (windowVisible())
1041         resource->updateScriptObject(m_frontend.get());
1042 }
1043
1044 void InspectorController::enableResourceTracking(bool always, bool reload)
1045 {
1046     if (!enabled())
1047         return;
1048
1049     if (always)
1050         setSetting(resourceTrackingEnabledSettingName, "true");
1051
1052     if (m_resourceTrackingEnabled)
1053         return;
1054
1055     ASSERT(m_inspectedPage);
1056     m_resourceTrackingEnabled = true;
1057     if (m_frontend)
1058         m_frontend->resourceTrackingWasEnabled();
1059
1060     if (reload)
1061         m_inspectedPage->mainFrame()->loader()->reload();
1062 }
1063
1064 void InspectorController::disableResourceTracking(bool always)
1065 {
1066     if (!enabled())
1067         return;
1068
1069     if (always)
1070         setSetting(resourceTrackingEnabledSettingName, "false");
1071
1072     ASSERT(m_inspectedPage);
1073     m_resourceTrackingEnabled = false;
1074     if (m_frontend)
1075         m_frontend->resourceTrackingWasDisabled();
1076 }
1077
1078 void InspectorController::ensureResourceTrackingSettingsLoaded()
1079 {
1080     if (m_resourceTrackingSettingsLoaded)
1081         return;
1082     m_resourceTrackingSettingsLoaded = true;
1083
1084     String resourceTracking = setting(resourceTrackingEnabledSettingName);
1085     if (resourceTracking == "true")
1086         m_resourceTrackingEnabled = true;
1087 }
1088
1089 void InspectorController::startTimelineProfiler()
1090 {
1091     if (!enabled())
1092         return;
1093
1094     if (m_timelineAgent)
1095         return;
1096
1097     m_timelineAgent = new InspectorTimelineAgent(m_frontend.get());
1098     if (m_frontend)
1099         m_frontend->timelineProfilerWasStarted();
1100 }
1101
1102 void InspectorController::stopTimelineProfiler()
1103 {
1104     if (!enabled())
1105         return;
1106
1107     if (!m_timelineAgent)
1108         return;
1109
1110     m_timelineAgent = 0;
1111     if (m_frontend)
1112         m_frontend->timelineProfilerWasStopped();
1113 }
1114
1115 #if ENABLE(DATABASE)
1116 void InspectorController::selectDatabase(Database* database)
1117 {
1118     if (!m_frontend)
1119         return;
1120
1121     for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != m_databaseResources.end(); ++it) {
1122         if (it->second->database() == database) {
1123             m_frontend->selectDatabase(it->first);
1124             break;
1125         }
1126     }
1127 }
1128
1129 Database* InspectorController::databaseForId(int databaseId)
1130 {
1131     DatabaseResourcesMap::iterator it = m_databaseResources.find(databaseId);
1132     if (it == m_databaseResources.end())
1133         return 0;
1134     return it->second->database();
1135 }
1136
1137 void InspectorController::didOpenDatabase(Database* database, const String& domain, const String& name, const String& version)
1138 {
1139     if (!enabled())
1140         return;
1141
1142     RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
1143
1144     m_databaseResources.set(resource->id(), resource);
1145
1146     // Resources are only bound while visible.
1147     if (windowVisible())
1148         resource->bind(m_frontend.get());
1149 }
1150 #endif
1151
1152 void InspectorController::getCookies(long callId, const String& host)
1153 {
1154     if (!m_frontend)
1155         return;
1156     
1157     // If we can get raw cookies.
1158     ListHashSet<Cookie> rawCookiesList;
1159     
1160     // If we can't get raw cookies - fall back to String representation
1161     String stringCookiesList;
1162     
1163     // Return value to getRawCookies should be the same for every call because
1164     // the return value is platform/network backend specific, and the call will
1165     // always return the same true/false value.
1166     bool rawCookiesImplemented = false;
1167     
1168     ResourcesMap::iterator resourcesEnd = m_resources.end();
1169     for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) {
1170         Document* document = it->second->frame()->document();
1171         if (document->url().host() == host) {
1172             Vector<Cookie> docCookiesList;
1173             rawCookiesImplemented = getRawCookies(document, document->cookieURL(), docCookiesList);
1174             
1175             if (!rawCookiesImplemented) {
1176                 // FIXME: We need duplication checking for the String representation of cookies.
1177                 ExceptionCode ec = 0;
1178                 stringCookiesList += document->cookie(ec);
1179                 // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here
1180                 // because "document" is the document of the main frame of the page.
1181                 ASSERT(!ec);
1182             } else {
1183                 int cookiesSize = docCookiesList.size();
1184                 for (int i = 0; i < cookiesSize; i++) {
1185                     if (!rawCookiesList.contains(docCookiesList[i]))
1186                         rawCookiesList.add(docCookiesList[i]);
1187                 }
1188             }
1189         }
1190     }
1191     
1192     if (!rawCookiesImplemented)
1193         m_frontend->didGetCookies(callId, m_frontend->newScriptArray(), stringCookiesList);
1194     else
1195         m_frontend->didGetCookies(callId, buildArrayForCookies(rawCookiesList), String());
1196 }
1197     
1198 ScriptArray InspectorController::buildArrayForCookies(ListHashSet<Cookie>& cookiesList)
1199 {
1200     ScriptArray cookies = m_frontend->newScriptArray();
1201
1202     ListHashSet<Cookie>::iterator end = cookiesList.end();
1203     ListHashSet<Cookie>::iterator it = cookiesList.begin();
1204     for (int i = 0; it != end; ++it, i++)
1205         cookies.set(i, buildObjectForCookie(*it));
1206
1207     return cookies;
1208 }
1209
1210 ScriptObject InspectorController::buildObjectForCookie(const Cookie& cookie)
1211 {
1212     ScriptObject value = m_frontend->newScriptObject();
1213     value.set("name", cookie.name);
1214     value.set("value", cookie.value);
1215     value.set("domain", cookie.domain);
1216     value.set("path", cookie.path);
1217     value.set("expires", cookie.expires);
1218     value.set("size", (cookie.name.length() + cookie.value.length()));
1219     value.set("httpOnly", cookie.httpOnly);
1220     value.set("secure", cookie.secure);
1221     value.set("session", cookie.session);
1222     return value;
1223 }
1224     
1225 #if ENABLE(DOM_STORAGE)
1226 void InspectorController::didUseDOMStorage(StorageArea* storageArea, bool isLocalStorage, Frame* frame)
1227 {
1228     if (!enabled())
1229         return;
1230
1231     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
1232     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
1233         if (it->second->isSameHostAndType(frame, isLocalStorage))
1234             return;
1235
1236     RefPtr<Storage> domStorage = Storage::create(frame, storageArea);
1237     RefPtr<InspectorDOMStorageResource> resource = InspectorDOMStorageResource::create(domStorage.get(), isLocalStorage, frame);
1238
1239     m_domStorageResources.set(resource->id(), resource);
1240
1241     // Resources are only bound while visible.
1242     if (windowVisible())
1243         resource->bind(m_frontend.get());
1244 }
1245
1246 void InspectorController::selectDOMStorage(Storage* storage)
1247 {
1248     ASSERT(storage);
1249     if (!m_frontend)
1250         return;
1251
1252     Frame* frame = storage->frame();
1253     bool isLocalStorage = (frame->domWindow()->localStorage() == storage);
1254     int storageResourceId = 0;
1255     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
1256     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it) {
1257         if (it->second->isSameHostAndType(frame, isLocalStorage)) {
1258             storageResourceId = it->first;
1259             break;
1260         }
1261     }
1262     if (storageResourceId)
1263         m_frontend->selectDOMStorage(storageResourceId);
1264 }
1265
1266 void InspectorController::getDOMStorageEntries(int callId, int storageId)
1267 {
1268     if (!m_frontend)
1269         return;
1270
1271     ScriptArray jsonArray = m_frontend->newScriptArray();
1272     InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1273     if (storageResource) {
1274         storageResource->startReportingChangesToFrontend();
1275         Storage* domStorage = storageResource->domStorage();
1276         for (unsigned i = 0; i < domStorage->length(); ++i) {
1277             String name(domStorage->key(i));
1278             String value(domStorage->getItem(name));
1279             ScriptArray entry = m_frontend->newScriptArray();
1280             entry.set(0, name);
1281             entry.set(1, value);
1282             jsonArray.set(i, entry);
1283         }
1284     }
1285     m_frontend->didGetDOMStorageEntries(callId, jsonArray);
1286 }
1287
1288 void InspectorController::setDOMStorageItem(long callId, long storageId, const String& key, const String& value)
1289 {
1290     if (!m_frontend)
1291         return;
1292
1293     bool success = false;
1294     InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1295     if (storageResource) {
1296         ExceptionCode exception = 0;
1297         storageResource->domStorage()->setItem(key, value, exception);
1298         success = (exception == 0);
1299     }
1300     m_frontend->didSetDOMStorageItem(callId, success);
1301 }
1302
1303 void InspectorController::removeDOMStorageItem(long callId, long storageId, const String& key)
1304 {
1305     if (!m_frontend)
1306         return;
1307
1308     bool success = false;
1309     InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1310     if (storageResource) {
1311         storageResource->domStorage()->removeItem(key);
1312         success = true;
1313     }
1314     m_frontend->didRemoveDOMStorageItem(callId, success);
1315 }
1316
1317 InspectorDOMStorageResource* InspectorController::getDOMStorageResourceForId(int storageId)
1318 {
1319     DOMStorageResourcesMap::iterator it = m_domStorageResources.find(storageId);
1320     if (it == m_domStorageResources.end())
1321         return 0;
1322     return it->second.get();
1323 }
1324 #endif
1325
1326 void InspectorController::moveWindowBy(float x, float y) const
1327 {
1328     if (!m_page || !enabled())
1329         return;
1330
1331     FloatRect frameRect = m_page->chrome()->windowRect();
1332     frameRect.move(x, y);
1333     m_page->chrome()->setWindowRect(frameRect);
1334 }
1335
1336 #if ENABLE(JAVASCRIPT_DEBUGGER)
1337 void InspectorController::addProfile(PassRefPtr<Profile> prpProfile, unsigned lineNumber, const UString& sourceURL)
1338 {
1339     if (!enabled())
1340         return;
1341
1342     RefPtr<Profile> profile = prpProfile;
1343     m_profiles.add(profile->uid(), profile);
1344
1345     if (m_frontend) {
1346         JSLock lock(SilenceAssertionsOnly);
1347         m_frontend->addProfileHeader(createProfileHeader(*profile));
1348     }
1349
1350     addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL);
1351 }
1352
1353 void InspectorController::addProfileFinishedMessageToConsole(PassRefPtr<Profile> prpProfile, unsigned lineNumber, const UString& sourceURL)
1354 {
1355     RefPtr<Profile> profile = prpProfile;
1356
1357     UString message = "Profile \"webkit-profile://";
1358     message += encodeWithURLEscapeSequences(CPUProfileType);
1359     message += "/";
1360     message += encodeWithURLEscapeSequences(profile->title());
1361     message += "#";
1362     message += UString::from(profile->uid());
1363     message += "\" finished.";
1364     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
1365 }
1366
1367 void InspectorController::addStartProfilingMessageToConsole(const UString& title, unsigned lineNumber, const UString& sourceURL)
1368 {
1369     UString message = "Profile \"webkit-profile://";
1370     message += encodeWithURLEscapeSequences(CPUProfileType);
1371     message += "/";
1372     message += encodeWithURLEscapeSequences(title);
1373     message += "#0\" started.";
1374     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
1375 }
1376
1377 void InspectorController::getProfileHeaders(long callId)
1378 {
1379     if (!m_frontend)
1380         return;
1381     ScriptArray result = m_frontend->newScriptArray();
1382     ProfilesMap::iterator profilesEnd = m_profiles.end();
1383     int i = 0;
1384     for (ProfilesMap::iterator it = m_profiles.begin(); it != profilesEnd; ++it)
1385         result.set(i++, createProfileHeader(*it->second));
1386     m_frontend->didGetProfileHeaders(callId, result);
1387 }
1388
1389 void InspectorController::getProfile(long callId, unsigned uid)
1390 {
1391     if (!m_frontend)
1392         return;
1393     ProfilesMap::iterator it = m_profiles.find(uid);
1394     if (it != m_profiles.end())
1395         m_frontend->didGetProfile(callId, toJS(m_scriptState, it->second.get()));
1396 }
1397
1398 ScriptObject InspectorController::createProfileHeader(const JSC::Profile& profile)
1399 {
1400     ScriptObject header = m_frontend->newScriptObject();
1401     header.set("title", profile.title());
1402     header.set("uid", profile.uid());
1403     header.set("typeId", UString(CPUProfileType));
1404     return header;
1405 }
1406
1407 UString InspectorController::getCurrentUserInitiatedProfileName(bool incrementProfileNumber = false)
1408 {
1409     if (incrementProfileNumber)
1410         m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;        
1411
1412     UString title = UserInitiatedProfileName;
1413     title += ".";
1414     title += UString::from(m_currentUserInitiatedProfileNumber);
1415     
1416     return title;
1417 }
1418
1419 void InspectorController::startUserInitiatedProfilingSoon()
1420 {
1421     m_startProfiling.startOneShot(0);
1422 }
1423
1424 void InspectorController::startUserInitiatedProfiling(Timer<InspectorController>*)
1425 {
1426     if (!enabled())
1427         return;
1428
1429     if (!profilerEnabled()) {
1430         enableProfiler(false, true);
1431         JavaScriptDebugServer::shared().recompileAllJSFunctions();
1432     }
1433
1434     m_recordingUserInitiatedProfile = true;
1435
1436     UString title = getCurrentUserInitiatedProfileName(true);
1437
1438     ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec();
1439     Profiler::profiler()->startProfiling(scriptState, title);
1440
1441     addStartProfilingMessageToConsole(title, 0, UString());
1442
1443     toggleRecordButton(true);
1444 }
1445
1446 void InspectorController::stopUserInitiatedProfiling()
1447 {
1448     if (!enabled())
1449         return;
1450
1451     m_recordingUserInitiatedProfile = false;
1452
1453     UString title = getCurrentUserInitiatedProfileName();
1454
1455     ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec();
1456     RefPtr<Profile> profile = Profiler::profiler()->stopProfiling(scriptState, title);
1457     if (profile)
1458         addProfile(profile, 0, UString());
1459
1460     toggleRecordButton(false);
1461 }
1462
1463 void InspectorController::toggleRecordButton(bool isProfiling)
1464 {
1465     if (!m_frontend)
1466         return;
1467     m_frontend->setRecordingProfile(isProfiling);
1468 }
1469
1470 void InspectorController::enableProfiler(bool always, bool skipRecompile)
1471 {
1472     if (always)
1473         setSetting(profilerEnabledSettingName, "true");
1474
1475     if (m_profilerEnabled)
1476         return;
1477
1478     m_profilerEnabled = true;
1479
1480     if (!skipRecompile)
1481         JavaScriptDebugServer::shared().recompileAllJSFunctionsSoon();
1482
1483     if (m_frontend)
1484         m_frontend->profilerWasEnabled();
1485 }
1486
1487 void InspectorController::disableProfiler(bool always)
1488 {
1489     if (always)
1490         setSetting(profilerEnabledSettingName, "false");
1491
1492     if (!m_profilerEnabled)
1493         return;
1494
1495     m_profilerEnabled = false;
1496
1497     JavaScriptDebugServer::shared().recompileAllJSFunctionsSoon();
1498
1499     if (m_frontend)
1500         m_frontend->profilerWasDisabled();
1501 }
1502
1503 void InspectorController::enableDebuggerFromFrontend(bool always)
1504 {
1505     if (always)
1506         setSetting(debuggerEnabledSettingName, "true");
1507
1508     ASSERT(m_inspectedPage);
1509
1510     JavaScriptDebugServer::shared().addListener(this, m_inspectedPage);
1511     JavaScriptDebugServer::shared().clearBreakpoints();
1512
1513     m_debuggerEnabled = true;
1514     m_frontend->debuggerWasEnabled();
1515 }
1516
1517 void InspectorController::enableDebugger()
1518 {
1519     if (!enabled())
1520         return;
1521
1522     if (m_debuggerEnabled)
1523         return;
1524
1525     if (!m_scriptState || !m_frontend) {
1526         m_attachDebuggerWhenShown = true;
1527     } else {
1528         m_frontend->attachDebuggerWhenShown();
1529         m_attachDebuggerWhenShown = false;
1530     }
1531 }
1532
1533 void InspectorController::disableDebugger(bool always)
1534 {
1535     if (!enabled())
1536         return;
1537
1538     if (always)
1539         setSetting(debuggerEnabledSettingName, "false");
1540
1541     ASSERT(m_inspectedPage);
1542
1543     JavaScriptDebugServer::shared().removeListener(this, m_inspectedPage);
1544
1545     m_debuggerEnabled = false;
1546     m_attachDebuggerWhenShown = false;
1547
1548     if (m_frontend)
1549         m_frontend->debuggerWasDisabled();
1550 }
1551
1552 void InspectorController::resumeDebugger()
1553 {
1554     if (!m_debuggerEnabled)
1555         return;
1556     JavaScriptDebugServer::shared().continueProgram();
1557 }
1558
1559 // JavaScriptDebugListener functions
1560
1561 void InspectorController::didParseSource(ExecState*, const SourceCode& source)
1562 {
1563     m_frontend->parsedScriptSource(source);
1564 }
1565
1566 void InspectorController::failedToParseSource(ExecState*, const SourceCode& source, int errorLine, const UString& errorMessage)
1567 {
1568     m_frontend->failedToParseScriptSource(source, errorLine, errorMessage);
1569 }
1570
1571 void InspectorController::didPause()
1572 {
1573     ScriptFunctionCall function(m_scriptState, m_injectedScriptObj, "getCallFrames");
1574     ScriptValue callFrames = function.call();
1575     m_frontend->pausedScript(callFrames);
1576 }
1577
1578 void InspectorController::didContinue()
1579 {
1580     m_frontend->resumedScript();
1581 }
1582
1583 #endif
1584
1585 void InspectorController::evaluateForTestInFrontend(long callId, const String& script)
1586 {
1587     if (m_frontend)
1588         m_frontend->evaluateForTestInFrontend(callId, script);
1589     else
1590         m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script));
1591 }
1592
1593 void InspectorController::didEvaluateForTestInFrontend(long callId, const String& jsonResult)
1594 {
1595     ScriptState* scriptState = scriptStateFromPage(debuggerWorld(), m_inspectedPage);
1596     ScriptObject window;
1597     ScriptGlobalObject::get(scriptState, "window", window);
1598     ScriptFunctionCall function(scriptState, window, "didEvaluateForTestInFrontend");
1599     function.appendArgument(callId);
1600     function.appendArgument(jsonResult);
1601     function.call();
1602 }
1603
1604 static Path quadToPath(const FloatQuad& quad)
1605 {
1606     Path quadPath;
1607     quadPath.moveTo(quad.p1());
1608     quadPath.addLineTo(quad.p2());
1609     quadPath.addLineTo(quad.p3());
1610     quadPath.addLineTo(quad.p4());
1611     quadPath.closeSubpath();
1612     return quadPath;
1613 }
1614
1615 static void drawOutlinedQuad(GraphicsContext& context, const FloatQuad& quad, const Color& fillColor)
1616 {
1617     static const int outlineThickness = 2;
1618     static const Color outlineColor(62, 86, 180, 228);
1619
1620     Path quadPath = quadToPath(quad);
1621
1622     // Clip out the quad, then draw with a 2px stroke to get a pixel
1623     // of outline (because inflating a quad is hard)
1624     {
1625         context.save();
1626         context.addPath(quadPath);
1627         context.clipOut(quadPath);
1628
1629         context.addPath(quadPath);
1630         context.setStrokeThickness(outlineThickness);
1631         context.setStrokeColor(outlineColor, DeviceColorSpace);
1632         context.strokePath();
1633
1634         context.restore();
1635     }
1636     
1637     // Now do the fill
1638     context.addPath(quadPath);
1639     context.setFillColor(fillColor, DeviceColorSpace);
1640     context.fillPath();
1641 }
1642
1643 static void drawOutlinedQuadWithClip(GraphicsContext& context, const FloatQuad& quad, const FloatQuad& clipQuad, const Color& fillColor)
1644 {
1645     context.save();
1646     Path clipQuadPath = quadToPath(clipQuad);
1647     context.clipOut(clipQuadPath);
1648     drawOutlinedQuad(context, quad, fillColor);
1649     context.restore();
1650 }
1651
1652 static void drawHighlightForBox(GraphicsContext& context, const FloatQuad& contentQuad, const FloatQuad& paddingQuad, const FloatQuad& borderQuad, const FloatQuad& marginQuad)
1653 {
1654     static const Color contentBoxColor(125, 173, 217, 128);
1655     static const Color paddingBoxColor(125, 173, 217, 160);
1656     static const Color borderBoxColor(125, 173, 217, 192);
1657     static const Color marginBoxColor(125, 173, 217, 228);
1658
1659     if (marginQuad != borderQuad)
1660         drawOutlinedQuadWithClip(context, marginQuad, borderQuad, marginBoxColor);
1661     if (borderQuad != paddingQuad)
1662         drawOutlinedQuadWithClip(context, borderQuad, paddingQuad, borderBoxColor);
1663     if (paddingQuad != contentQuad)
1664         drawOutlinedQuadWithClip(context, paddingQuad, contentQuad, paddingBoxColor);
1665
1666     drawOutlinedQuad(context, contentQuad, contentBoxColor);
1667 }
1668
1669 static void drawHighlightForLineBoxes(GraphicsContext& context, const Vector<FloatQuad>& lineBoxQuads)
1670 {
1671     static const Color lineBoxColor(125, 173, 217, 128);
1672
1673     for (size_t i = 0; i < lineBoxQuads.size(); ++i)
1674         drawOutlinedQuad(context, lineBoxQuads[i], lineBoxColor);
1675 }
1676
1677 static inline void convertFromFrameToMainFrame(Frame* frame, IntRect& rect)
1678 {
1679     rect = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(rect));
1680 }
1681
1682 static inline IntSize frameToMainFrameOffset(Frame* frame)
1683 {
1684     IntPoint mainFramePoint = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(IntPoint()));
1685     return mainFramePoint - IntPoint();
1686 }
1687
1688 void InspectorController::drawNodeHighlight(GraphicsContext& context) const
1689 {
1690     if (!m_highlightedNode)
1691         return;
1692
1693     RenderObject* renderer = m_highlightedNode->renderer();
1694     Frame* containingFrame = m_highlightedNode->document()->frame();
1695     if (!renderer || !containingFrame)
1696         return;
1697
1698     IntSize mainFrameOffset = frameToMainFrameOffset(containingFrame);
1699     IntRect boundingBox = renderer->absoluteBoundingBoxRect(true);
1700     boundingBox.move(mainFrameOffset);
1701
1702     ASSERT(m_inspectedPage);
1703
1704     FrameView* view = m_inspectedPage->mainFrame()->view();
1705     FloatRect overlayRect = view->visibleContentRect();
1706     if (!overlayRect.contains(boundingBox) && !boundingBox.contains(enclosingIntRect(overlayRect)))
1707         overlayRect = view->visibleContentRect();
1708     context.translate(-overlayRect.x(), -overlayRect.y());
1709
1710     if (renderer->isBox()) {
1711         RenderBox* renderBox = toRenderBox(renderer);
1712
1713         IntRect contentBox = renderBox->contentBoxRect();
1714
1715         IntRect paddingBox(contentBox.x() - renderBox->paddingLeft(), contentBox.y() - renderBox->paddingTop(),
1716                            contentBox.width() + renderBox->paddingLeft() + renderBox->paddingRight(), contentBox.height() + renderBox->paddingTop() + renderBox->paddingBottom());
1717         IntRect borderBox(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(),
1718                           paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom());
1719         IntRect marginBox(borderBox.x() - renderBox->marginLeft(), borderBox.y() - renderBox->marginTop(),
1720                           borderBox.width() + renderBox->marginLeft() + renderBox->marginRight(), borderBox.height() + renderBox->marginTop() + renderBox->marginBottom());
1721
1722         FloatQuad absContentQuad = renderBox->localToAbsoluteQuad(FloatRect(contentBox));
1723         FloatQuad absPaddingQuad = renderBox->localToAbsoluteQuad(FloatRect(paddingBox));
1724         FloatQuad absBorderQuad = renderBox->localToAbsoluteQuad(FloatRect(borderBox));
1725         FloatQuad absMarginQuad = renderBox->localToAbsoluteQuad(FloatRect(marginBox));
1726
1727         absContentQuad.move(mainFrameOffset);
1728         absPaddingQuad.move(mainFrameOffset);
1729         absBorderQuad.move(mainFrameOffset);
1730         absMarginQuad.move(mainFrameOffset);
1731
1732         drawHighlightForBox(context, absContentQuad, absPaddingQuad, absBorderQuad, absMarginQuad);
1733     } else if (renderer->isRenderInline()) {
1734         RenderInline* renderInline = toRenderInline(renderer);
1735
1736         // FIXME: We should show margins/padding/border for inlines.
1737         Vector<FloatQuad> lineBoxQuads;
1738         renderInline->absoluteQuads(lineBoxQuads);
1739         for (unsigned i = 0; i < lineBoxQuads.size(); ++i)
1740             lineBoxQuads[i] += mainFrameOffset;
1741
1742         drawHighlightForLineBoxes(context, lineBoxQuads);
1743     }
1744 }
1745
1746 void InspectorController::count(const String& title, unsigned lineNumber, const String& sourceID)
1747 {
1748     String identifier = title + String::format("@%s:%d", sourceID.utf8().data(), lineNumber);
1749     HashMap<String, unsigned>::iterator it = m_counts.find(identifier);
1750     int count;
1751     if (it == m_counts.end())
1752         count = 1;
1753     else {
1754         count = it->second + 1;
1755         m_counts.remove(it);
1756     }
1757
1758     m_counts.add(identifier, count);
1759
1760     String message = String::format("%s: %d", title.utf8().data(), count);
1761     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceID);
1762 }
1763
1764 void InspectorController::startTiming(const String& title)
1765 {
1766     m_times.add(title, currentTime() * 1000);
1767 }
1768
1769 bool InspectorController::stopTiming(const String& title, double& elapsed)
1770 {
1771     HashMap<String, double>::iterator it = m_times.find(title);
1772     if (it == m_times.end())
1773         return false;
1774
1775     double startTime = it->second;
1776     m_times.remove(it);
1777     
1778     elapsed = currentTime() * 1000 - startTime;
1779     return true;
1780 }
1781
1782 InspectorController::SpecialPanels InspectorController::specialPanelForJSName(const String& panelName)
1783 {
1784     if (panelName == "elements")
1785         return ElementsPanel;
1786     else if (panelName == "resources")
1787         return ResourcesPanel;
1788     else if (panelName == "scripts")
1789         return ScriptsPanel;
1790     else if (panelName == "timeline")
1791         return TimelinePanel;
1792     else if (panelName == "profiles")
1793         return ProfilesPanel;
1794     else if (panelName == "storage" || panelName == "databases")
1795         return StoragePanel;
1796     else if (panelName == "console")
1797         return ConsolePanel;
1798     else
1799         return ElementsPanel;
1800 }
1801
1802 ScriptValue InspectorController::wrapObject(const ScriptValue& quarantinedObject, const String& objectGroup)
1803 {
1804     ScriptFunctionCall function(m_scriptState, m_injectedScriptObj, "createProxyObject");
1805     function.appendArgument(quarantinedObject);
1806     if (quarantinedObject.isObject()) {
1807         long id = m_lastBoundObjectId++;
1808         String objectId = String::format("object#%ld", id);
1809         m_idToWrappedObject.set(objectId, quarantinedObject);
1810         ObjectGroupsMap::iterator it = m_objectGroups.find(objectGroup);
1811         if (it == m_objectGroups.end())
1812             it = m_objectGroups.set(objectGroup, Vector<String>()).first;
1813         it->second.append(objectId);
1814         function.appendArgument(objectId);
1815     }
1816     ScriptValue wrapper = function.call();
1817     return wrapper;
1818 }
1819
1820 ScriptValue InspectorController::unwrapObject(const String& objectId)
1821 {
1822     HashMap<String, ScriptValue>::iterator it = m_idToWrappedObject.find(objectId);
1823     if (it != m_idToWrappedObject.end())
1824         return it->second;
1825     return ScriptValue();
1826 }
1827
1828 void InspectorController::releaseWrapperObjectGroup(const String& objectGroup)
1829 {
1830     ObjectGroupsMap::iterator groupIt = m_objectGroups.find(objectGroup);
1831     if (groupIt == m_objectGroups.end())
1832         return;
1833
1834     Vector<String>& groupIds = groupIt->second;
1835     for (Vector<String>::iterator it = groupIds.begin(); it != groupIds.end(); ++it)
1836         m_idToWrappedObject.remove(*it);
1837     m_objectGroups.remove(groupIt);
1838 }
1839
1840 void InspectorController::resetInjectedScript()
1841 {
1842     ScriptFunctionCall function(m_scriptState, m_injectedScriptObj, "reset");
1843     function.call();
1844 }
1845
1846 void InspectorController::deleteCookie(const String& cookieName, const String& domain)
1847 {
1848     ResourcesMap::iterator resourcesEnd = m_resources.end();
1849     for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) {
1850         Document* document = it->second->frame()->document();
1851         if (document->url().host() == domain)
1852             WebCore::deleteCookie(document, document->cookieURL(), cookieName);
1853     }
1854 }
1855
1856 } // namespace WebCore
1857     
1858 #endif // ENABLE(INSPECTOR)