Rubber-stamped by Brady Eidson.
[webbrowser.git] / WebCore / inspector / InspectorController.cpp
blobfd319a4ed669d4b3781a6ed4a6ce52b028263322
1 /*
2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
30 #include "config.h"
31 #include "InspectorController.h"
33 #if ENABLE(INSPECTOR)
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>
86 #if ENABLE(DATABASE)
87 #include "Database.h"
88 #endif
90 #if ENABLE(DOM_STORAGE)
91 #include "Storage.h"
92 #include "StorageArea.h"
93 #endif
95 #if ENABLE(JAVASCRIPT_DEBUGGER)
96 #include "JavaScriptCallFrame.h"
97 #include "JavaScriptDebugServer.h"
98 #include "JSJavaScriptCallFrame.h"
100 #include <profiler/Profile.h>
101 #include <profiler/Profiler.h>
102 #include <runtime/JSLock.h>
103 #include <runtime/UString.h>
105 using namespace JSC;
106 #endif
107 using namespace std;
109 namespace WebCore {
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";
119 static const unsigned defaultAttachedHeight = 300;
120 static const float minimumAttachedHeight = 250.0f;
121 static const float maximumAttachedHeightRatio = 0.75f;
123 static unsigned s_inspectorControllerCount;
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
151 ASSERT_ARG(page, page);
152 ASSERT_ARG(client, client);
153 ++s_inspectorControllerCount;
156 InspectorController::~InspectorController()
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()));
164 deleteAllValues(m_frameResources);
165 deleteAllValues(m_consoleMessages);
167 ASSERT(s_inspectorControllerCount);
168 --s_inspectorControllerCount;
170 releaseDOMAgent();
172 m_inspectorBackend->disconnectController();
173 m_inspectorFrontendHost->disconnectController();
174 m_injectedScriptHost->disconnectController();
177 void InspectorController::inspectedPageDestroyed()
179 close();
181 if (m_scriptState) {
182 ScriptGlobalObject::remove(m_scriptState, "InspectorBackend");
183 ScriptGlobalObject::remove(m_scriptState, "InspectorFrontendHost");
184 ScriptGlobalObject::remove(m_scriptState, "InjectedScriptHost");
186 ASSERT(m_inspectedPage);
187 m_inspectedPage = 0;
189 m_client->inspectorDestroyed();
190 m_client = 0;
193 bool InspectorController::enabled() const
195 if (!m_inspectedPage)
196 return false;
197 return m_inspectedPage->settings()->developerExtrasEnabled();
200 String InspectorController::setting(const String& key) const
202 Settings::iterator it = m_settings.find(key);
203 if (it != m_settings.end())
204 return it->second;
206 String value;
207 m_client->populateSetting(key, &value);
208 m_settings.set(key, value);
209 return value;
212 void InspectorController::setSetting(const String& key, const String& value)
214 m_settings.set(key, value);
215 m_client->storeSetting(key, value);
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)
223 if (!node)
224 return false;
225 Frame* frame = node->document()->frame();
226 return frame && frame->script()->isEnabled();
229 void InspectorController::inspect(Node* node)
231 if (!canPassNodeToJavaScript(node) || !enabled())
232 return;
234 show();
236 if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
237 node = node->parentNode();
238 m_nodeToFocus = node;
240 if (!m_frontend) {
241 m_showAfterVisible = ElementsPanel;
242 return;
245 focusNode();
248 void InspectorController::focusNode()
250 if (!enabled())
251 return;
253 ASSERT(m_frontend);
254 ASSERT(m_nodeToFocus);
256 long id = m_domAgent->pushNodePathToFrontend(m_nodeToFocus.get());
257 m_frontend->updateFocusedNode(id);
258 m_nodeToFocus = 0;
261 void InspectorController::highlight(Node* node)
263 if (!enabled())
264 return;
265 ASSERT_ARG(node, node);
266 m_highlightedNode = node;
267 m_client->highlight(node);
270 void InspectorController::hideHighlight()
272 if (!enabled())
273 return;
274 m_highlightedNode = 0;
275 m_client->hideHighlight();
278 bool InspectorController::windowVisible()
280 return m_windowVisible;
283 void InspectorController::setWindowVisible(bool visible, bool attached)
285 if (visible == m_windowVisible || !m_frontend)
286 return;
288 m_windowVisible = visible;
290 if (m_windowVisible) {
291 setAttachedWindow(attached);
292 populateScriptObjects();
294 if (m_showAfterVisible == CurrentPanel) {
295 String lastActivePanelSetting = setting(lastActivePanelSettingName);
296 m_showAfterVisible = specialPanelForJSName(lastActivePanelSetting);
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();
321 m_showAfterVisible = CurrentPanel;
324 void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, ScriptCallStack* callStack)
326 if (!enabled())
327 return;
329 addConsoleMessage(callStack->state(), new ConsoleMessage(source, type, level, callStack, m_groupLevel, type == TraceMessageType));
332 void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID)
334 if (!enabled())
335 return;
337 addConsoleMessage(0, new ConsoleMessage(source, type, level, message, lineNumber, sourceID, m_groupLevel));
340 void InspectorController::addConsoleMessage(ScriptState* scriptState, ConsoleMessage* consoleMessage)
342 ASSERT(enabled());
343 ASSERT_ARG(consoleMessage, consoleMessage);
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());
358 void InspectorController::clearConsoleMessages(bool clearUI)
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();
371 void InspectorController::startGroup(MessageSource source, ScriptCallStack* callStack)
373 ++m_groupLevel;
375 addConsoleMessage(callStack->state(), new ConsoleMessage(source, StartGroupMessageType, LogMessageLevel, callStack, m_groupLevel));
378 void InspectorController::endGroup(MessageSource source, unsigned lineNumber, const String& sourceURL)
380 if (m_groupLevel == 0)
381 return;
383 --m_groupLevel;
385 addConsoleMessage(0, new ConsoleMessage(source, EndGroupMessageType, LogMessageLevel, String(), lineNumber, sourceURL, m_groupLevel));
388 void InspectorController::markTimeline(const String& message)
390 if (timelineAgent())
391 timelineAgent()->didMarkTimeline(message);
394 static unsigned constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
396 return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
399 void InspectorController::attachWindow()
401 if (!enabled())
402 return;
404 unsigned inspectedPageHeight = m_inspectedPage->mainFrame()->view()->visibleHeight();
406 m_client->attachWindow();
408 String attachedHeight = setting(inspectorAttachedHeightName);
409 bool success = true;
410 int height = attachedHeight.toInt(&success);
411 unsigned preferredHeight = success ? height : defaultAttachedHeight;
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));
418 void InspectorController::detachWindow()
420 if (!enabled())
421 return;
422 m_client->detachWindow();
425 void InspectorController::setAttachedWindow(bool attached)
427 if (!enabled() || !m_frontend)
428 return;
430 m_frontend->setAttachedWindow(attached);
433 void InspectorController::setAttachedWindowHeight(unsigned height)
435 if (!enabled())
436 return;
438 unsigned totalHeight = m_page->mainFrame()->view()->visibleHeight() + m_inspectedPage->mainFrame()->view()->visibleHeight();
439 unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
441 setSetting(inspectorAttachedHeightName, String::number(attachedHeight));
443 m_client->setAttachedWindowHeight(attachedHeight);
446 void InspectorController::storeLastActivePanel(const String& panelName)
448 setSetting(lastActivePanelSettingName, panelName);
451 void InspectorController::toggleSearchForNodeInPage()
453 if (!enabled())
454 return;
456 m_searchingForNode = !m_searchingForNode;
457 if (!m_searchingForNode)
458 hideHighlight();
461 void InspectorController::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
463 if (!enabled() || !m_searchingForNode)
464 return;
466 Node* node = result.innerNode();
467 if (node)
468 highlight(node);
471 void InspectorController::handleMousePressOnNode(Node* node)
473 if (!enabled())
474 return;
476 ASSERT(m_searchingForNode);
477 ASSERT(node);
478 if (!node)
479 return;
481 // inspect() will implicitly call ElementsPanel's focusedNodeChanged() and the hover feedback will be stopped there.
482 inspect(node);
485 void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame)
487 if (!enabled() || !m_frontend || frame != m_inspectedPage->mainFrame())
488 return;
489 resetInjectedScript();
492 void InspectorController::windowScriptObjectAvailable()
494 if (!m_page || !enabled())
495 return;
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());
505 void InspectorController::scriptObjectReady()
507 ASSERT(m_scriptState);
508 if (!m_scriptState)
509 return;
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);
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
528 // Make sure our window is visible now that the page loaded
529 showWindow();
531 m_client->inspectorWindowObjectCleared();
534 void InspectorController::setFrontendProxyObject(ScriptState* scriptState, ScriptObject webInspectorObj, ScriptObject injectedScriptObj)
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());
545 void InspectorController::show()
547 if (!enabled())
548 return;
550 if (!m_page) {
551 if (m_frontend)
552 return; // We are using custom frontend - no need to create page.
554 m_page = m_client->createPage();
555 if (!m_page)
556 return;
557 m_page->setParentInspectorController(this);
559 // showWindow() will be called after the page loads in scriptObjectReady()
560 return;
563 showWindow();
566 void InspectorController::showPanel(SpecialPanels panel)
568 if (!enabled())
569 return;
571 show();
573 if (!m_frontend) {
574 m_showAfterVisible = panel;
575 return;
578 if (panel == CurrentPanel)
579 return;
581 m_frontend->showPanel(panel);
584 void InspectorController::close()
586 if (!enabled())
587 return;
589 #if ENABLE(JAVASCRIPT_DEBUGGER)
590 stopUserInitiatedProfiling();
591 disableDebugger();
592 #endif
593 closeWindow();
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;
608 void InspectorController::showWindow()
610 ASSERT(enabled());
612 unsigned inspectedPageHeight = m_inspectedPage->mainFrame()->view()->visibleHeight();
614 m_client->showWindow();
616 String attachedHeight = setting(inspectorAttachedHeightName);
617 bool success = true;
618 int height = attachedHeight.toInt(&success);
619 unsigned preferredHeight = success ? height : defaultAttachedHeight;
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));
627 void InspectorController::closeWindow()
629 m_client->closeWindow();
632 void InspectorController::releaseDOMAgent()
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;
641 void InspectorController::populateScriptObjects()
643 ASSERT(m_frontend);
644 if (!m_frontend)
645 return;
647 m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
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());
653 unsigned messageCount = m_consoleMessages.size();
654 for (unsigned i = 0; i < messageCount; ++i)
655 m_consoleMessages[i]->addToConsole(m_frontend.get());
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
668 m_frontend->populateInterface();
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();
676 void InspectorController::resetScriptObjects()
678 if (!m_frontend)
679 return;
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);
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
696 if (m_timelineAgent)
697 m_timelineAgent->reset();
699 m_frontend->reset();
700 m_domAgent->reset();
701 m_objectGroups.clear();
702 m_idToWrappedObject.clear();
705 void InspectorController::pruneResources(ResourcesMap* resourceMap, DocumentLoader* loaderToKeep)
707 ASSERT_ARG(resourceMap, resourceMap);
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;
716 if (!loaderToKeep || !resource->isSameLoader(loaderToKeep)) {
717 removeResource(resource);
718 if (windowVisible())
719 resource->releaseScriptObject(m_frontend.get(), true);
724 void InspectorController::didCommitLoad(DocumentLoader* loader)
726 if (!enabled())
727 return;
729 ASSERT(m_inspectedPage);
731 if (loader->frame() == m_inspectedPage->mainFrame()) {
732 m_client->inspectedURLChanged(loader->url().string());
734 clearConsoleMessages(false);
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();
747 #if ENABLE(DATABASE)
748 m_databaseResources.clear();
749 #endif
750 #if ENABLE(DOM_STORAGE)
751 m_domStorageResources.clear();
752 #endif
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;
769 if (windowVisible()) {
770 m_frontend->didCommitLoad();
771 m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
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);
781 void InspectorController::frameDetachedFromParent(Frame* frame)
783 if (!enabled())
784 return;
785 if (ResourcesMap* resourceMap = m_frameResources.get(frame))
786 removeAllResources(resourceMap);
789 void InspectorController::addResource(InspectorResource* resource)
791 m_resources.set(resource->identifier(), resource);
792 m_knownResources.add(resource->requestURL());
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);
805 void InspectorController::removeResource(InspectorResource* resource)
807 m_resources.remove(resource->identifier());
808 String requestURL = resource->requestURL();
809 if (!requestURL.isNull())
810 m_knownResources.remove(requestURL);
812 Frame* frame = resource->frame();
813 ResourcesMap* resourceMap = m_frameResources.get(frame);
814 if (!resourceMap) {
815 ASSERT_NOT_REACHED();
816 return;
819 resourceMap->remove(resource->identifier());
820 if (resourceMap->isEmpty()) {
821 m_frameResources.remove(frame);
822 delete resourceMap;
826 InspectorResource* InspectorController::getTrackedResource(unsigned long identifier)
828 if (!enabled())
829 return 0;
831 if (m_resourceTrackingEnabled)
832 return m_resources.get(identifier).get();
834 bool isMainResource = m_mainResource && m_mainResource->identifier() == identifier;
835 if (isMainResource)
836 return m_mainResource.get();
838 return 0;
841 void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* cachedResource)
843 if (!enabled())
844 return;
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;
850 ASSERT(m_inspectedPage);
851 bool isMainResource = isMainResourceLoader(loader, KURL(ParsedURLString, cachedResource->url()));
852 ensureResourceTrackingSettingsLoaded();
853 if (!isMainResource && !m_resourceTrackingEnabled)
854 return;
856 RefPtr<InspectorResource> resource = InspectorResource::createCached(m_inspectedPage->progress()->createUniqueIdentifier() , loader, cachedResource);
858 if (isMainResource) {
859 m_mainResource = resource;
860 resource->markMainResource();
863 addResource(resource.get());
865 if (windowVisible())
866 resource->createScriptObject(m_frontend.get());
869 void InspectorController::identifierForInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
871 if (!enabled())
872 return;
873 ASSERT(m_inspectedPage);
875 bool isMainResource = isMainResourceLoader(loader, request.url());
876 ensureResourceTrackingSettingsLoaded();
877 if (!isMainResource && !m_resourceTrackingEnabled)
878 return;
880 RefPtr<InspectorResource> resource = InspectorResource::create(identifier, loader);
882 resource->updateRequest(request);
884 if (isMainResource) {
885 m_mainResource = resource;
886 resource->markMainResource();
889 addResource(resource.get());
891 if (windowVisible() && loader->frameLoader()->isLoadingFromCachedPage() && resource == m_mainResource)
892 resource->createScriptObject(m_frontend.get());
895 void InspectorController::mainResourceFiredDOMContentEvent(DocumentLoader* loader, const KURL& url)
897 if (!enabled() || !isMainResourceLoader(loader, url))
898 return;
900 if (m_mainResource) {
901 m_mainResource->markDOMContentEventTime();
902 if (windowVisible())
903 m_mainResource->updateScriptObject(m_frontend.get());
907 void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, const KURL& url)
909 if (!enabled() || !isMainResourceLoader(loader, url))
910 return;
912 if (m_mainResource) {
913 m_mainResource->markLoadEventTime();
914 if (windowVisible())
915 m_mainResource->updateScriptObject(m_frontend.get());
919 bool InspectorController::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
921 return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
924 void InspectorController::willSendRequest(unsigned long identifier, const ResourceRequest& request, const ResourceResponse& redirectResponse)
926 bool isMainResource = (m_mainResource && m_mainResource->identifier() == identifier);
927 if (m_timelineAgent)
928 m_timelineAgent->willSendResourceRequest(identifier, isMainResource, request);
930 RefPtr<InspectorResource> resource = getTrackedResource(identifier);
931 if (!resource)
932 return;
934 resource->startTiming();
936 if (!redirectResponse.isNull()) {
937 resource->updateRequest(request);
938 resource->updateResponse(redirectResponse);
941 if (resource != m_mainResource && windowVisible())
942 resource->createScriptObject(m_frontend.get());
945 void InspectorController::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
947 if (m_timelineAgent)
948 m_timelineAgent->didReceiveResourceResponse(identifier, response);
950 RefPtr<InspectorResource> resource = getTrackedResource(identifier);
951 if (!resource)
952 return;
954 resource->updateResponse(response);
955 resource->markResponseReceivedTime();
957 if (windowVisible())
958 resource->updateScriptObject(m_frontend.get());
961 void InspectorController::didReceiveContentLength(unsigned long identifier, int lengthReceived)
963 RefPtr<InspectorResource> resource = getTrackedResource(identifier);
964 if (!resource)
965 return;
967 resource->addLength(lengthReceived);
969 if (windowVisible())
970 resource->updateScriptObject(m_frontend.get());
973 void InspectorController::didFinishLoading(unsigned long identifier)
975 if (m_timelineAgent)
976 m_timelineAgent->didFinishLoadingResource(identifier, false);
978 RefPtr<InspectorResource> resource = getTrackedResource(identifier);
979 if (!resource)
980 return;
982 removeResource(resource.get());
984 resource->endTiming();
986 addResource(resource.get());
988 if (windowVisible())
989 resource->updateScriptObject(m_frontend.get());
992 void InspectorController::didFailLoading(unsigned long identifier, const ResourceError& /*error*/)
994 if (m_timelineAgent)
995 m_timelineAgent->didFinishLoadingResource(identifier, true);
997 RefPtr<InspectorResource> resource = getTrackedResource(identifier);
998 if (!resource)
999 return;
1001 removeResource(resource.get());
1003 resource->markFailed();
1004 resource->endTiming();
1006 addResource(resource.get());
1008 if (windowVisible())
1009 resource->updateScriptObject(m_frontend.get());
1012 void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString)
1014 if (!enabled() || !m_resourceTrackingEnabled)
1015 return;
1017 InspectorResource* resource = m_resources.get(identifier).get();
1018 if (!resource)
1019 return;
1021 resource->setXMLHttpResponseText(sourceString);
1023 if (windowVisible())
1024 resource->updateScriptObject(m_frontend.get());
1027 void InspectorController::scriptImported(unsigned long identifier, const String& sourceString)
1029 if (!enabled() || !m_resourceTrackingEnabled)
1030 return;
1032 InspectorResource* resource = m_resources.get(identifier).get();
1033 if (!resource)
1034 return;
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));
1040 if (windowVisible())
1041 resource->updateScriptObject(m_frontend.get());
1044 void InspectorController::enableResourceTracking(bool always, bool reload)
1046 if (!enabled())
1047 return;
1049 if (always)
1050 setSetting(resourceTrackingEnabledSettingName, "true");
1052 if (m_resourceTrackingEnabled)
1053 return;
1055 ASSERT(m_inspectedPage);
1056 m_resourceTrackingEnabled = true;
1057 if (m_frontend)
1058 m_frontend->resourceTrackingWasEnabled();
1060 if (reload)
1061 m_inspectedPage->mainFrame()->loader()->reload();
1064 void InspectorController::disableResourceTracking(bool always)
1066 if (!enabled())
1067 return;
1069 if (always)
1070 setSetting(resourceTrackingEnabledSettingName, "false");
1072 ASSERT(m_inspectedPage);
1073 m_resourceTrackingEnabled = false;
1074 if (m_frontend)
1075 m_frontend->resourceTrackingWasDisabled();
1078 void InspectorController::ensureResourceTrackingSettingsLoaded()
1080 if (m_resourceTrackingSettingsLoaded)
1081 return;
1082 m_resourceTrackingSettingsLoaded = true;
1084 String resourceTracking = setting(resourceTrackingEnabledSettingName);
1085 if (resourceTracking == "true")
1086 m_resourceTrackingEnabled = true;
1089 void InspectorController::startTimelineProfiler()
1091 if (!enabled())
1092 return;
1094 if (m_timelineAgent)
1095 return;
1097 m_timelineAgent = new InspectorTimelineAgent(m_frontend.get());
1098 if (m_frontend)
1099 m_frontend->timelineProfilerWasStarted();
1102 void InspectorController::stopTimelineProfiler()
1104 if (!enabled())
1105 return;
1107 if (!m_timelineAgent)
1108 return;
1110 m_timelineAgent = 0;
1111 if (m_frontend)
1112 m_frontend->timelineProfilerWasStopped();
1115 #if ENABLE(DATABASE)
1116 void InspectorController::selectDatabase(Database* database)
1118 if (!m_frontend)
1119 return;
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;
1129 Database* InspectorController::databaseForId(int databaseId)
1131 DatabaseResourcesMap::iterator it = m_databaseResources.find(databaseId);
1132 if (it == m_databaseResources.end())
1133 return 0;
1134 return it->second->database();
1137 void InspectorController::didOpenDatabase(Database* database, const String& domain, const String& name, const String& version)
1139 if (!enabled())
1140 return;
1142 RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
1144 m_databaseResources.set(resource->id(), resource);
1146 // Resources are only bound while visible.
1147 if (windowVisible())
1148 resource->bind(m_frontend.get());
1150 #endif
1152 void InspectorController::getCookies(long callId)
1154 if (!m_frontend)
1155 return;
1157 // If we can get raw cookies.
1158 ListHashSet<Cookie> rawCookiesList;
1160 // If we can't get raw cookies - fall back to String representation
1161 String stringCookiesList;
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;
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 Vector<Cookie> docCookiesList;
1172 rawCookiesImplemented = getRawCookies(document, document->cookieURL(), docCookiesList);
1174 if (!rawCookiesImplemented) {
1175 // FIXME: We need duplication checking for the String representation of cookies.
1176 ExceptionCode ec = 0;
1177 stringCookiesList += document->cookie(ec);
1178 // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here
1179 // because "document" is the document of the main frame of the page.
1180 ASSERT(!ec);
1181 } else {
1182 int cookiesSize = docCookiesList.size();
1183 for (int i = 0; i < cookiesSize; i++) {
1184 if (!rawCookiesList.contains(docCookiesList[i]))
1185 rawCookiesList.add(docCookiesList[i]);
1190 if (!rawCookiesImplemented)
1191 m_frontend->didGetCookies(callId, m_frontend->newScriptArray(), stringCookiesList);
1192 else
1193 m_frontend->didGetCookies(callId, buildArrayForCookies(rawCookiesList), String());
1196 ScriptArray InspectorController::buildArrayForCookies(ListHashSet<Cookie>& cookiesList)
1198 ScriptArray cookies = m_frontend->newScriptArray();
1200 ListHashSet<Cookie>::iterator end = cookiesList.end();
1201 ListHashSet<Cookie>::iterator it = cookiesList.begin();
1202 for (int i = 0; it != end; ++it, i++)
1203 cookies.set(i, buildObjectForCookie(*it));
1205 return cookies;
1208 ScriptObject InspectorController::buildObjectForCookie(const Cookie& cookie)
1210 ScriptObject value = m_frontend->newScriptObject();
1211 value.set("name", cookie.name);
1212 value.set("value", cookie.value);
1213 value.set("domain", cookie.domain);
1214 value.set("path", cookie.path);
1215 value.set("expires", cookie.expires);
1216 value.set("size", (cookie.name.length() + cookie.value.length()));
1217 value.set("httpOnly", cookie.httpOnly);
1218 value.set("secure", cookie.secure);
1219 value.set("session", cookie.session);
1220 return value;
1223 #if ENABLE(DOM_STORAGE)
1224 void InspectorController::didUseDOMStorage(StorageArea* storageArea, bool isLocalStorage, Frame* frame)
1226 if (!enabled())
1227 return;
1229 DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
1230 for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
1231 if (it->second->isSameHostAndType(frame, isLocalStorage))
1232 return;
1234 RefPtr<Storage> domStorage = Storage::create(frame, storageArea);
1235 RefPtr<InspectorDOMStorageResource> resource = InspectorDOMStorageResource::create(domStorage.get(), isLocalStorage, frame);
1237 m_domStorageResources.set(resource->id(), resource);
1239 // Resources are only bound while visible.
1240 if (windowVisible())
1241 resource->bind(m_frontend.get());
1244 void InspectorController::selectDOMStorage(Storage* storage)
1246 ASSERT(storage);
1247 if (!m_frontend)
1248 return;
1250 Frame* frame = storage->frame();
1251 bool isLocalStorage = (frame->domWindow()->localStorage() == storage);
1252 int storageResourceId = 0;
1253 DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
1254 for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it) {
1255 if (it->second->isSameHostAndType(frame, isLocalStorage)) {
1256 storageResourceId = it->first;
1257 break;
1260 if (storageResourceId)
1261 m_frontend->selectDOMStorage(storageResourceId);
1264 void InspectorController::getDOMStorageEntries(int callId, int storageId)
1266 if (!m_frontend)
1267 return;
1269 ScriptArray jsonArray = m_frontend->newScriptArray();
1270 InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1271 if (storageResource) {
1272 storageResource->startReportingChangesToFrontend();
1273 Storage* domStorage = storageResource->domStorage();
1274 for (unsigned i = 0; i < domStorage->length(); ++i) {
1275 String name(domStorage->key(i));
1276 String value(domStorage->getItem(name));
1277 ScriptArray entry = m_frontend->newScriptArray();
1278 entry.set(0, name);
1279 entry.set(1, value);
1280 jsonArray.set(i, entry);
1283 m_frontend->didGetDOMStorageEntries(callId, jsonArray);
1286 void InspectorController::setDOMStorageItem(long callId, long storageId, const String& key, const String& value)
1288 if (!m_frontend)
1289 return;
1291 bool success = false;
1292 InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1293 if (storageResource) {
1294 ExceptionCode exception = 0;
1295 storageResource->domStorage()->setItem(key, value, exception);
1296 success = (exception == 0);
1298 m_frontend->didSetDOMStorageItem(callId, success);
1301 void InspectorController::removeDOMStorageItem(long callId, long storageId, const String& key)
1303 if (!m_frontend)
1304 return;
1306 bool success = false;
1307 InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1308 if (storageResource) {
1309 storageResource->domStorage()->removeItem(key);
1310 success = true;
1312 m_frontend->didRemoveDOMStorageItem(callId, success);
1315 InspectorDOMStorageResource* InspectorController::getDOMStorageResourceForId(int storageId)
1317 DOMStorageResourcesMap::iterator it = m_domStorageResources.find(storageId);
1318 if (it == m_domStorageResources.end())
1319 return 0;
1320 return it->second.get();
1322 #endif
1324 void InspectorController::moveWindowBy(float x, float y) const
1326 if (!m_page || !enabled())
1327 return;
1329 FloatRect frameRect = m_page->chrome()->windowRect();
1330 frameRect.move(x, y);
1331 m_page->chrome()->setWindowRect(frameRect);
1334 #if ENABLE(JAVASCRIPT_DEBUGGER)
1335 void InspectorController::addProfile(PassRefPtr<Profile> prpProfile, unsigned lineNumber, const UString& sourceURL)
1337 if (!enabled())
1338 return;
1340 RefPtr<Profile> profile = prpProfile;
1341 m_profiles.add(profile->uid(), profile);
1343 if (m_frontend) {
1344 JSLock lock(SilenceAssertionsOnly);
1345 m_frontend->addProfileHeader(createProfileHeader(*profile));
1348 addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL);
1351 void InspectorController::addProfileFinishedMessageToConsole(PassRefPtr<Profile> prpProfile, unsigned lineNumber, const UString& sourceURL)
1353 RefPtr<Profile> profile = prpProfile;
1355 UString message = "Profile \"webkit-profile://";
1356 message += encodeWithURLEscapeSequences(CPUProfileType);
1357 message += "/";
1358 message += encodeWithURLEscapeSequences(profile->title());
1359 message += "#";
1360 message += UString::from(profile->uid());
1361 message += "\" finished.";
1362 addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
1365 void InspectorController::addStartProfilingMessageToConsole(const UString& title, unsigned lineNumber, const UString& sourceURL)
1367 UString message = "Profile \"webkit-profile://";
1368 message += encodeWithURLEscapeSequences(CPUProfileType);
1369 message += "/";
1370 message += encodeWithURLEscapeSequences(title);
1371 message += "#0\" started.";
1372 addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
1375 void InspectorController::getProfileHeaders(long callId)
1377 if (!m_frontend)
1378 return;
1379 ScriptArray result = m_frontend->newScriptArray();
1380 ProfilesMap::iterator profilesEnd = m_profiles.end();
1381 int i = 0;
1382 for (ProfilesMap::iterator it = m_profiles.begin(); it != profilesEnd; ++it)
1383 result.set(i++, createProfileHeader(*it->second));
1384 m_frontend->didGetProfileHeaders(callId, result);
1387 void InspectorController::getProfile(long callId, unsigned uid)
1389 if (!m_frontend)
1390 return;
1391 ProfilesMap::iterator it = m_profiles.find(uid);
1392 if (it != m_profiles.end())
1393 m_frontend->didGetProfile(callId, toJS(m_scriptState, it->second.get()));
1396 ScriptObject InspectorController::createProfileHeader(const JSC::Profile& profile)
1398 ScriptObject header = m_frontend->newScriptObject();
1399 header.set("title", profile.title());
1400 header.set("uid", profile.uid());
1401 header.set("typeId", UString(CPUProfileType));
1402 return header;
1405 UString InspectorController::getCurrentUserInitiatedProfileName(bool incrementProfileNumber = false)
1407 if (incrementProfileNumber)
1408 m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;
1410 UString title = UserInitiatedProfileName;
1411 title += ".";
1412 title += UString::from(m_currentUserInitiatedProfileNumber);
1414 return title;
1417 void InspectorController::startUserInitiatedProfilingSoon()
1419 m_startProfiling.startOneShot(0);
1422 void InspectorController::startUserInitiatedProfiling(Timer<InspectorController>*)
1424 if (!enabled())
1425 return;
1427 if (!profilerEnabled()) {
1428 enableProfiler(false, true);
1429 JavaScriptDebugServer::shared().recompileAllJSFunctions();
1432 m_recordingUserInitiatedProfile = true;
1434 UString title = getCurrentUserInitiatedProfileName(true);
1436 ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec();
1437 Profiler::profiler()->startProfiling(scriptState, title);
1439 addStartProfilingMessageToConsole(title, 0, UString());
1441 toggleRecordButton(true);
1444 void InspectorController::stopUserInitiatedProfiling()
1446 if (!enabled())
1447 return;
1449 m_recordingUserInitiatedProfile = false;
1451 UString title = getCurrentUserInitiatedProfileName();
1453 ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec();
1454 RefPtr<Profile> profile = Profiler::profiler()->stopProfiling(scriptState, title);
1455 if (profile)
1456 addProfile(profile, 0, UString());
1458 toggleRecordButton(false);
1461 void InspectorController::toggleRecordButton(bool isProfiling)
1463 if (!m_frontend)
1464 return;
1465 m_frontend->setRecordingProfile(isProfiling);
1468 void InspectorController::enableProfiler(bool always, bool skipRecompile)
1470 if (always)
1471 setSetting(profilerEnabledSettingName, "true");
1473 if (m_profilerEnabled)
1474 return;
1476 m_profilerEnabled = true;
1478 if (!skipRecompile)
1479 JavaScriptDebugServer::shared().recompileAllJSFunctionsSoon();
1481 if (m_frontend)
1482 m_frontend->profilerWasEnabled();
1485 void InspectorController::disableProfiler(bool always)
1487 if (always)
1488 setSetting(profilerEnabledSettingName, "false");
1490 if (!m_profilerEnabled)
1491 return;
1493 m_profilerEnabled = false;
1495 JavaScriptDebugServer::shared().recompileAllJSFunctionsSoon();
1497 if (m_frontend)
1498 m_frontend->profilerWasDisabled();
1501 void InspectorController::enableDebuggerFromFrontend(bool always)
1503 if (always)
1504 setSetting(debuggerEnabledSettingName, "true");
1506 ASSERT(m_inspectedPage);
1508 JavaScriptDebugServer::shared().addListener(this, m_inspectedPage);
1509 JavaScriptDebugServer::shared().clearBreakpoints();
1511 m_debuggerEnabled = true;
1512 m_frontend->debuggerWasEnabled();
1515 void InspectorController::enableDebugger()
1517 if (!enabled())
1518 return;
1520 if (m_debuggerEnabled)
1521 return;
1523 if (!m_scriptState || !m_frontend) {
1524 m_attachDebuggerWhenShown = true;
1525 } else {
1526 m_frontend->attachDebuggerWhenShown();
1527 m_attachDebuggerWhenShown = false;
1531 void InspectorController::disableDebugger(bool always)
1533 if (!enabled())
1534 return;
1536 if (always)
1537 setSetting(debuggerEnabledSettingName, "false");
1539 ASSERT(m_inspectedPage);
1541 JavaScriptDebugServer::shared().removeListener(this, m_inspectedPage);
1543 m_debuggerEnabled = false;
1544 m_attachDebuggerWhenShown = false;
1546 if (m_frontend)
1547 m_frontend->debuggerWasDisabled();
1550 void InspectorController::resumeDebugger()
1552 if (!m_debuggerEnabled)
1553 return;
1554 JavaScriptDebugServer::shared().continueProgram();
1557 // JavaScriptDebugListener functions
1559 void InspectorController::didParseSource(ExecState*, const SourceCode& source)
1561 m_frontend->parsedScriptSource(source);
1564 void InspectorController::failedToParseSource(ExecState*, const SourceCode& source, int errorLine, const UString& errorMessage)
1566 m_frontend->failedToParseScriptSource(source, errorLine, errorMessage);
1569 void InspectorController::didPause()
1571 ScriptFunctionCall function(m_scriptState, m_injectedScriptObj, "getCallFrames");
1572 ScriptValue callFrames = function.call();
1573 m_frontend->pausedScript(callFrames);
1576 void InspectorController::didContinue()
1578 m_frontend->resumedScript();
1581 #endif
1583 void InspectorController::evaluateForTestInFrontend(long callId, const String& script)
1585 if (m_frontend)
1586 m_frontend->evaluateForTestInFrontend(callId, script);
1587 else
1588 m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script));
1591 void InspectorController::didEvaluateForTestInFrontend(long callId, const String& jsonResult)
1593 ScriptState* scriptState = scriptStateFromPage(debuggerWorld(), m_inspectedPage);
1594 ScriptObject window;
1595 ScriptGlobalObject::get(scriptState, "window", window);
1596 ScriptFunctionCall function(scriptState, window, "didEvaluateForTestInFrontend");
1597 function.appendArgument(callId);
1598 function.appendArgument(jsonResult);
1599 function.call();
1602 static Path quadToPath(const FloatQuad& quad)
1604 Path quadPath;
1605 quadPath.moveTo(quad.p1());
1606 quadPath.addLineTo(quad.p2());
1607 quadPath.addLineTo(quad.p3());
1608 quadPath.addLineTo(quad.p4());
1609 quadPath.closeSubpath();
1610 return quadPath;
1613 static void drawOutlinedQuad(GraphicsContext& context, const FloatQuad& quad, const Color& fillColor)
1615 static const int outlineThickness = 2;
1616 static const Color outlineColor(62, 86, 180, 228);
1618 Path quadPath = quadToPath(quad);
1620 // Clip out the quad, then draw with a 2px stroke to get a pixel
1621 // of outline (because inflating a quad is hard)
1623 context.save();
1624 context.addPath(quadPath);
1625 context.clipOut(quadPath);
1627 context.addPath(quadPath);
1628 context.setStrokeThickness(outlineThickness);
1629 context.setStrokeColor(outlineColor, DeviceColorSpace);
1630 context.strokePath();
1632 context.restore();
1635 // Now do the fill
1636 context.addPath(quadPath);
1637 context.setFillColor(fillColor, DeviceColorSpace);
1638 context.fillPath();
1641 static void drawOutlinedQuadWithClip(GraphicsContext& context, const FloatQuad& quad, const FloatQuad& clipQuad, const Color& fillColor)
1643 context.save();
1644 Path clipQuadPath = quadToPath(clipQuad);
1645 context.clipOut(clipQuadPath);
1646 drawOutlinedQuad(context, quad, fillColor);
1647 context.restore();
1650 static void drawHighlightForBox(GraphicsContext& context, const FloatQuad& contentQuad, const FloatQuad& paddingQuad, const FloatQuad& borderQuad, const FloatQuad& marginQuad)
1652 static const Color contentBoxColor(125, 173, 217, 128);
1653 static const Color paddingBoxColor(125, 173, 217, 160);
1654 static const Color borderBoxColor(125, 173, 217, 192);
1655 static const Color marginBoxColor(125, 173, 217, 228);
1657 if (marginQuad != borderQuad)
1658 drawOutlinedQuadWithClip(context, marginQuad, borderQuad, marginBoxColor);
1659 if (borderQuad != paddingQuad)
1660 drawOutlinedQuadWithClip(context, borderQuad, paddingQuad, borderBoxColor);
1661 if (paddingQuad != contentQuad)
1662 drawOutlinedQuadWithClip(context, paddingQuad, contentQuad, paddingBoxColor);
1664 drawOutlinedQuad(context, contentQuad, contentBoxColor);
1667 static void drawHighlightForLineBoxes(GraphicsContext& context, const Vector<FloatQuad>& lineBoxQuads)
1669 static const Color lineBoxColor(125, 173, 217, 128);
1671 for (size_t i = 0; i < lineBoxQuads.size(); ++i)
1672 drawOutlinedQuad(context, lineBoxQuads[i], lineBoxColor);
1675 static inline void convertFromFrameToMainFrame(Frame* frame, IntRect& rect)
1677 rect = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(rect));
1680 static inline IntSize frameToMainFrameOffset(Frame* frame)
1682 IntPoint mainFramePoint = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(IntPoint()));
1683 return mainFramePoint - IntPoint();
1686 void InspectorController::drawNodeHighlight(GraphicsContext& context) const
1688 if (!m_highlightedNode)
1689 return;
1691 RenderObject* renderer = m_highlightedNode->renderer();
1692 Frame* containingFrame = m_highlightedNode->document()->frame();
1693 if (!renderer || !containingFrame)
1694 return;
1696 IntSize mainFrameOffset = frameToMainFrameOffset(containingFrame);
1697 IntRect boundingBox = renderer->absoluteBoundingBoxRect(true);
1698 boundingBox.move(mainFrameOffset);
1700 ASSERT(m_inspectedPage);
1702 FrameView* view = m_inspectedPage->mainFrame()->view();
1703 FloatRect overlayRect = view->visibleContentRect();
1704 if (!overlayRect.contains(boundingBox) && !boundingBox.contains(enclosingIntRect(overlayRect)))
1705 overlayRect = view->visibleContentRect();
1706 context.translate(-overlayRect.x(), -overlayRect.y());
1708 if (renderer->isBox()) {
1709 RenderBox* renderBox = toRenderBox(renderer);
1711 IntRect contentBox = renderBox->contentBoxRect();
1713 IntRect paddingBox(contentBox.x() - renderBox->paddingLeft(), contentBox.y() - renderBox->paddingTop(),
1714 contentBox.width() + renderBox->paddingLeft() + renderBox->paddingRight(), contentBox.height() + renderBox->paddingTop() + renderBox->paddingBottom());
1715 IntRect borderBox(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(),
1716 paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom());
1717 IntRect marginBox(borderBox.x() - renderBox->marginLeft(), borderBox.y() - renderBox->marginTop(),
1718 borderBox.width() + renderBox->marginLeft() + renderBox->marginRight(), borderBox.height() + renderBox->marginTop() + renderBox->marginBottom());
1720 FloatQuad absContentQuad = renderBox->localToAbsoluteQuad(FloatRect(contentBox));
1721 FloatQuad absPaddingQuad = renderBox->localToAbsoluteQuad(FloatRect(paddingBox));
1722 FloatQuad absBorderQuad = renderBox->localToAbsoluteQuad(FloatRect(borderBox));
1723 FloatQuad absMarginQuad = renderBox->localToAbsoluteQuad(FloatRect(marginBox));
1725 absContentQuad.move(mainFrameOffset);
1726 absPaddingQuad.move(mainFrameOffset);
1727 absBorderQuad.move(mainFrameOffset);
1728 absMarginQuad.move(mainFrameOffset);
1730 drawHighlightForBox(context, absContentQuad, absPaddingQuad, absBorderQuad, absMarginQuad);
1731 } else if (renderer->isRenderInline()) {
1732 RenderInline* renderInline = toRenderInline(renderer);
1734 // FIXME: We should show margins/padding/border for inlines.
1735 Vector<FloatQuad> lineBoxQuads;
1736 renderInline->absoluteQuads(lineBoxQuads);
1737 for (unsigned i = 0; i < lineBoxQuads.size(); ++i)
1738 lineBoxQuads[i] += mainFrameOffset;
1740 drawHighlightForLineBoxes(context, lineBoxQuads);
1744 void InspectorController::count(const String& title, unsigned lineNumber, const String& sourceID)
1746 String identifier = title + String::format("@%s:%d", sourceID.utf8().data(), lineNumber);
1747 HashMap<String, unsigned>::iterator it = m_counts.find(identifier);
1748 int count;
1749 if (it == m_counts.end())
1750 count = 1;
1751 else {
1752 count = it->second + 1;
1753 m_counts.remove(it);
1756 m_counts.add(identifier, count);
1758 String message = String::format("%s: %d", title.utf8().data(), count);
1759 addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceID);
1762 void InspectorController::startTiming(const String& title)
1764 m_times.add(title, currentTime() * 1000);
1767 bool InspectorController::stopTiming(const String& title, double& elapsed)
1769 HashMap<String, double>::iterator it = m_times.find(title);
1770 if (it == m_times.end())
1771 return false;
1773 double startTime = it->second;
1774 m_times.remove(it);
1776 elapsed = currentTime() * 1000 - startTime;
1777 return true;
1780 InspectorController::SpecialPanels InspectorController::specialPanelForJSName(const String& panelName)
1782 if (panelName == "elements")
1783 return ElementsPanel;
1784 else if (panelName == "resources")
1785 return ResourcesPanel;
1786 else if (panelName == "scripts")
1787 return ScriptsPanel;
1788 else if (panelName == "timeline")
1789 return TimelinePanel;
1790 else if (panelName == "profiles")
1791 return ProfilesPanel;
1792 else if (panelName == "storage" || panelName == "databases")
1793 return StoragePanel;
1794 else if (panelName == "console")
1795 return ConsolePanel;
1796 else
1797 return ElementsPanel;
1800 ScriptValue InspectorController::wrapObject(const ScriptValue& quarantinedObject, const String& objectGroup)
1802 ScriptFunctionCall function(m_scriptState, m_injectedScriptObj, "createProxyObject");
1803 function.appendArgument(quarantinedObject);
1804 if (quarantinedObject.isObject()) {
1805 long id = m_lastBoundObjectId++;
1806 String objectId = String::format("object#%ld", id);
1807 m_idToWrappedObject.set(objectId, quarantinedObject);
1808 ObjectGroupsMap::iterator it = m_objectGroups.find(objectGroup);
1809 if (it == m_objectGroups.end())
1810 it = m_objectGroups.set(objectGroup, Vector<String>()).first;
1811 it->second.append(objectId);
1812 function.appendArgument(objectId);
1814 ScriptValue wrapper = function.call();
1815 return wrapper;
1818 ScriptValue InspectorController::unwrapObject(const String& objectId)
1820 HashMap<String, ScriptValue>::iterator it = m_idToWrappedObject.find(objectId);
1821 if (it != m_idToWrappedObject.end())
1822 return it->second;
1823 return ScriptValue();
1826 void InspectorController::releaseWrapperObjectGroup(const String& objectGroup)
1828 ObjectGroupsMap::iterator groupIt = m_objectGroups.find(objectGroup);
1829 if (groupIt == m_objectGroups.end())
1830 return;
1832 Vector<String>& groupIds = groupIt->second;
1833 for (Vector<String>::iterator it = groupIds.begin(); it != groupIds.end(); ++it)
1834 m_idToWrappedObject.remove(*it);
1835 m_objectGroups.remove(groupIt);
1838 void InspectorController::resetInjectedScript()
1840 ScriptFunctionCall function(m_scriptState, m_injectedScriptObj, "reset");
1841 function.call();
1844 void InspectorController::deleteCookie(const String& cookieName, const String& domain)
1846 ResourcesMap::iterator resourcesEnd = m_resources.end();
1847 for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) {
1848 Document* document = it->second->frame()->document();
1849 if (document->url().host() == domain)
1850 WebCore::deleteCookie(document, document->cookieURL(), cookieName);
1854 } // namespace WebCore
1856 #endif // ENABLE(INSPECTOR)