• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008 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 #include "CString.h"
34 #include "CachedResource.h"
35 #include "Console.h"
36 #include "ConsoleMessage.h"
37 #include "Document.h"
38 #include "DocumentLoader.h"
39 #include "Element.h"
40 #include "FloatConversion.h"
41 #include "FloatQuad.h"
42 #include "FloatRect.h"
43 #include "Frame.h"
44 #include "FrameLoader.h"
45 #include "FrameTree.h"
46 #include "FrameView.h"
47 #include "GraphicsContext.h"
48 #include "HTMLFrameOwnerElement.h"
49 #include "HitTestResult.h"
50 #include "InspectorBackend.h"
51 #include "InspectorClient.h"
52 #include "InspectorFrontend.h"
53 #include "InspectorDatabaseResource.h"
54 #include "InspectorDOMAgent.h"
55 #include "InspectorDOMStorageResource.h"
56 #include "InspectorResource.h"
57 #include "JavaScriptProfile.h"
58 #include "Page.h"
59 #include "Range.h"
60 #include "RenderInline.h"
61 #include "ResourceRequest.h"
62 #include "ResourceResponse.h"
63 #include "ScriptCallStack.h"
64 #include "ScriptObject.h"
65 #include "ScriptString.h"
66 #include "SecurityOrigin.h"
67 #include "Settings.h"
68 #include "SharedBuffer.h"
69 #include "TextEncoding.h"
70 #include "TextIterator.h"
71 #include <wtf/CurrentTime.h>
72 #include <wtf/RefCounted.h>
73 #include <wtf/StdLibExtras.h>
74 
75 #if ENABLE(DATABASE)
76 #include "Database.h"
77 #endif
78 
79 #if ENABLE(DOM_STORAGE)
80 #include "Storage.h"
81 #include "StorageArea.h"
82 #endif
83 
84 #if ENABLE(JAVASCRIPT_DEBUGGER)
85 #include "JavaScriptCallFrame.h"
86 #include "JavaScriptDebugServer.h"
87 #include "JSJavaScriptCallFrame.h"
88 
89 #include <profiler/Profile.h>
90 #include <profiler/Profiler.h>
91 #include <runtime/JSLock.h>
92 #include <runtime/UString.h>
93 
94 using namespace JSC;
95 #endif
96 using namespace std;
97 
98 namespace WebCore {
99 
100 static const char* const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
101 static const char* const resourceTrackingEnabledSettingName = "resourceTrackingEnabled";
102 static const char* const debuggerEnabledSettingName = "debuggerEnabled";
103 static const char* const profilerEnabledSettingName = "profilerEnabled";
104 static const char* const inspectorAttachedHeightName = "inspectorAttachedHeight";
105 static const char* const lastActivePanelSettingName = "lastActivePanel";
106 
107 static const unsigned defaultAttachedHeight = 300;
108 static const float minimumAttachedHeight = 250.0f;
109 static const float maximumAttachedHeightRatio = 0.75f;
110 
111 static unsigned s_inspectorControllerCount;
112 static HashMap<String, InspectorController::Setting*>* s_settingCache;
113 
InspectorController(Page * page,InspectorClient * client)114 InspectorController::InspectorController(Page* page, InspectorClient* client)
115     : m_inspectedPage(page)
116     , m_client(client)
117     , m_page(0)
118     , m_scriptState(0)
119     , m_windowVisible(false)
120     , m_showAfterVisible(CurrentPanel)
121     , m_nextIdentifier(-2)
122     , m_groupLevel(0)
123     , m_searchingForNode(false)
124     , m_previousMessage(0)
125     , m_resourceTrackingEnabled(false)
126     , m_resourceTrackingSettingsLoaded(false)
127     , m_inspectorBackend(InspectorBackend::create(this, client))
128 #if ENABLE(JAVASCRIPT_DEBUGGER)
129     , m_debuggerEnabled(false)
130     , m_attachDebuggerWhenShown(false)
131     , m_profilerEnabled(false)
132     , m_recordingUserInitiatedProfile(false)
133     , m_currentUserInitiatedProfileNumber(-1)
134     , m_nextUserInitiatedProfileNumber(1)
135     , m_startProfiling(this, &InspectorController::startUserInitiatedProfiling)
136 #endif
137 {
138     ASSERT_ARG(page, page);
139     ASSERT_ARG(client, client);
140     ++s_inspectorControllerCount;
141 }
142 
~InspectorController()143 InspectorController::~InspectorController()
144 {
145     // These should have been cleared in inspectedPageDestroyed().
146     ASSERT(!m_client);
147     ASSERT(!m_scriptState);
148     ASSERT(!m_inspectedPage);
149     ASSERT(!m_page || (m_page && !m_page->parentInspectorController()));
150 
151     deleteAllValues(m_frameResources);
152     deleteAllValues(m_consoleMessages);
153 
154     ASSERT(s_inspectorControllerCount);
155     --s_inspectorControllerCount;
156 
157     if (!s_inspectorControllerCount && s_settingCache) {
158         deleteAllValues(*s_settingCache);
159         delete s_settingCache;
160         s_settingCache = 0;
161     }
162 
163     m_inspectorBackend->disconnectController();
164 }
165 
inspectedPageDestroyed()166 void InspectorController::inspectedPageDestroyed()
167 {
168     close();
169 
170     if (m_scriptState)
171         ScriptGlobalObject::remove(m_scriptState, "InspectorController");
172 
173     if (m_page) {
174         m_page->setParentInspectorController(0);
175         m_page = 0;
176     }
177 
178     ASSERT(m_inspectedPage);
179     m_inspectedPage = 0;
180 
181     m_client->inspectorDestroyed();
182     m_client = 0;
183 }
184 
enabled() const185 bool InspectorController::enabled() const
186 {
187     if (!m_inspectedPage)
188         return false;
189     return m_inspectedPage->settings()->developerExtrasEnabled();
190 }
191 
setting(const String & key) const192 const InspectorController::Setting& InspectorController::setting(const String& key) const
193 {
194     if (!s_settingCache)
195         s_settingCache = new HashMap<String, Setting*>;
196 
197     if (Setting* cachedSetting = s_settingCache->get(key))
198         return *cachedSetting;
199 
200     Setting* newSetting = new Setting;
201     s_settingCache->set(key, newSetting);
202 
203     m_client->populateSetting(key, *newSetting);
204 
205     return *newSetting;
206 }
207 
setSetting(const String & key,const Setting & setting)208 void InspectorController::setSetting(const String& key, const Setting& setting)
209 {
210     if (setting.type() == Setting::NoType) {
211         if (s_settingCache) {
212             Setting* cachedSetting = s_settingCache->get(key);
213             if (cachedSetting) {
214                 s_settingCache->remove(key);
215                 delete cachedSetting;
216             }
217         }
218 
219         m_client->removeSetting(key);
220         return;
221     }
222 
223     if (!s_settingCache)
224         s_settingCache = new HashMap<String, Setting*>;
225 
226     if (Setting* cachedSetting = s_settingCache->get(key))
227         *cachedSetting = setting;
228     else
229         s_settingCache->set(key, new Setting(setting));
230 
231     m_client->storeSetting(key, setting);
232 }
233 
234 // Trying to inspect something in a frame with JavaScript disabled would later lead to
235 // crashes trying to create JavaScript wrappers. Some day we could fix this issue, but
236 // for now prevent crashes here by never targeting a node in such a frame.
canPassNodeToJavaScript(Node * node)237 static bool canPassNodeToJavaScript(Node* node)
238 {
239     if (!node)
240         return false;
241     Frame* frame = node->document()->frame();
242     return frame && frame->script()->isEnabled();
243 }
244 
inspect(Node * node)245 void InspectorController::inspect(Node* node)
246 {
247     if (!canPassNodeToJavaScript(node) || !enabled())
248         return;
249 
250     show();
251 
252     if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
253         node = node->parentNode();
254     m_nodeToFocus = node;
255 
256     if (!m_frontend) {
257         m_showAfterVisible = ElementsPanel;
258         return;
259     }
260 
261     if (windowVisible())
262         focusNode();
263 }
264 
focusNode()265 void InspectorController::focusNode()
266 {
267     if (!enabled())
268         return;
269 
270     ASSERT(m_frontend);
271     ASSERT(m_nodeToFocus);
272 
273     m_frontend->updateFocusedNode(m_nodeToFocus.get());
274     m_nodeToFocus = 0;
275 }
276 
highlight(Node * node)277 void InspectorController::highlight(Node* node)
278 {
279     if (!enabled())
280         return;
281     ASSERT_ARG(node, node);
282     m_highlightedNode = node;
283     m_client->highlight(node);
284 }
285 
hideHighlight()286 void InspectorController::hideHighlight()
287 {
288     if (!enabled())
289         return;
290     m_highlightedNode = 0;
291     m_client->hideHighlight();
292 }
293 
windowVisible()294 bool InspectorController::windowVisible()
295 {
296     return m_windowVisible;
297 }
298 
setWindowVisible(bool visible,bool attached)299 void InspectorController::setWindowVisible(bool visible, bool attached)
300 {
301     if (visible == m_windowVisible)
302         return;
303 
304     m_windowVisible = visible;
305 
306     if (!m_frontend)
307         return;
308 
309     if (m_windowVisible) {
310         setAttachedWindow(attached);
311         populateScriptObjects();
312 
313         // Console panel is implemented as a 'fast view', so there should be
314         // real panel opened along with it.
315         bool showConsole = m_showAfterVisible == ConsolePanel;
316         if (m_showAfterVisible == CurrentPanel || showConsole) {
317           Setting lastActivePanelSetting = setting(lastActivePanelSettingName);
318           if (lastActivePanelSetting.type() == Setting::StringType)
319               m_showAfterVisible = specialPanelForJSName(lastActivePanelSetting.string());
320           else
321               m_showAfterVisible = ElementsPanel;
322         }
323 
324         if (m_nodeToFocus)
325             focusNode();
326 #if ENABLE(JAVASCRIPT_DEBUGGER)
327         if (m_attachDebuggerWhenShown)
328             enableDebugger();
329 #endif
330         showPanel(m_showAfterVisible);
331         if (showConsole)
332             showPanel(ConsolePanel);
333     } else {
334 #if ENABLE(JAVASCRIPT_DEBUGGER)
335         // If the window is being closed with the debugger enabled,
336         // remember this state to re-enable debugger on the next window
337         // opening.
338         bool debuggerWasEnabled = m_debuggerEnabled;
339         disableDebugger();
340         if (debuggerWasEnabled)
341             m_attachDebuggerWhenShown = true;
342 #endif
343         resetScriptObjects();
344     }
345     m_showAfterVisible = CurrentPanel;
346 }
347 
addMessageToConsole(MessageSource source,MessageType type,MessageLevel level,ScriptCallStack * callStack)348 void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, ScriptCallStack* callStack)
349 {
350     if (!enabled())
351         return;
352 
353     addConsoleMessage(callStack->state(), new ConsoleMessage(source, type, level, callStack, m_groupLevel, type == TraceMessageType));
354 }
355 
addMessageToConsole(MessageSource source,MessageType type,MessageLevel level,const String & message,unsigned lineNumber,const String & sourceID)356 void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID)
357 {
358     if (!enabled())
359         return;
360 
361     addConsoleMessage(0, new ConsoleMessage(source, type, level, message, lineNumber, sourceID, m_groupLevel));
362 }
363 
addConsoleMessage(ScriptState * scriptState,ConsoleMessage * consoleMessage)364 void InspectorController::addConsoleMessage(ScriptState* scriptState, ConsoleMessage* consoleMessage)
365 {
366     ASSERT(enabled());
367     ASSERT_ARG(consoleMessage, consoleMessage);
368 
369     if (m_previousMessage && m_previousMessage->isEqual(scriptState, consoleMessage)) {
370         m_previousMessage->incrementCount();
371         delete consoleMessage;
372     } else {
373         m_previousMessage = consoleMessage;
374         m_consoleMessages.append(consoleMessage);
375     }
376 
377     if (windowVisible())
378         m_previousMessage->addToConsole(m_frontend.get());
379 }
380 
clearConsoleMessages()381 void InspectorController::clearConsoleMessages()
382 {
383     deleteAllValues(m_consoleMessages);
384     m_consoleMessages.clear();
385     m_previousMessage = 0;
386     m_groupLevel = 0;
387 }
388 
startGroup(MessageSource source,ScriptCallStack * callStack)389 void InspectorController::startGroup(MessageSource source, ScriptCallStack* callStack)
390 {
391     ++m_groupLevel;
392 
393     addConsoleMessage(callStack->state(), new ConsoleMessage(source, StartGroupMessageType, LogMessageLevel, callStack, m_groupLevel));
394 }
395 
endGroup(MessageSource source,unsigned lineNumber,const String & sourceURL)396 void InspectorController::endGroup(MessageSource source, unsigned lineNumber, const String& sourceURL)
397 {
398     if (m_groupLevel == 0)
399         return;
400 
401     --m_groupLevel;
402 
403     addConsoleMessage(0, new ConsoleMessage(source, EndGroupMessageType, LogMessageLevel, String(), lineNumber, sourceURL, m_groupLevel));
404 }
405 
constrainedAttachedWindowHeight(unsigned preferredHeight,unsigned totalWindowHeight)406 static unsigned constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
407 {
408     return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
409 }
410 
attachWindow()411 void InspectorController::attachWindow()
412 {
413     if (!enabled())
414         return;
415 
416     unsigned inspectedPageHeight = m_inspectedPage->mainFrame()->view()->visibleHeight();
417 
418     m_client->attachWindow();
419 
420     Setting attachedHeight = setting(inspectorAttachedHeightName);
421     unsigned preferredHeight = attachedHeight.type() == Setting::IntegerType ? attachedHeight.integerValue() : defaultAttachedHeight;
422 
423     // We need to constrain the window height here in case the user has resized the inspected page's window so that
424     // the user's preferred height would be too big to display.
425     m_client->setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
426 }
427 
detachWindow()428 void InspectorController::detachWindow()
429 {
430     if (!enabled())
431         return;
432     m_client->detachWindow();
433 }
434 
setAttachedWindow(bool attached)435 void InspectorController::setAttachedWindow(bool attached)
436 {
437     if (!enabled() || !m_frontend)
438         return;
439 
440     m_frontend->setAttachedWindow(attached);
441 }
442 
setAttachedWindowHeight(unsigned height)443 void InspectorController::setAttachedWindowHeight(unsigned height)
444 {
445     if (!enabled())
446         return;
447 
448     unsigned totalHeight = m_page->mainFrame()->view()->visibleHeight() + m_inspectedPage->mainFrame()->view()->visibleHeight();
449     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
450 
451     setSetting(inspectorAttachedHeightName, Setting(attachedHeight));
452 
453     m_client->setAttachedWindowHeight(attachedHeight);
454 }
455 
storeLastActivePanel(const String & panelName)456 void InspectorController::storeLastActivePanel(const String& panelName)
457 {
458     setSetting(lastActivePanelSettingName, Setting(panelName));
459 }
460 
toggleSearchForNodeInPage()461 void InspectorController::toggleSearchForNodeInPage()
462 {
463     if (!enabled())
464         return;
465 
466     m_searchingForNode = !m_searchingForNode;
467     if (!m_searchingForNode)
468         hideHighlight();
469 }
470 
mouseDidMoveOverElement(const HitTestResult & result,unsigned)471 void InspectorController::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
472 {
473     if (!enabled() || !m_searchingForNode)
474         return;
475 
476     Node* node = result.innerNode();
477     if (node)
478         highlight(node);
479 }
480 
handleMousePressOnNode(Node * node)481 void InspectorController::handleMousePressOnNode(Node* node)
482 {
483     if (!enabled())
484         return;
485 
486     ASSERT(m_searchingForNode);
487     ASSERT(node);
488     if (!node)
489         return;
490 
491     // inspect() will implicitly call ElementsPanel's focusedNodeChanged() and the hover feedback will be stopped there.
492     inspect(node);
493 }
494 
inspectedWindowScriptObjectCleared(Frame * frame)495 void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame)
496 {
497     if (!enabled() || !m_frontend)
498         return;
499 
500     m_frontend->inspectedWindowScriptObjectCleared(frame);
501 }
502 
windowScriptObjectAvailable()503 void InspectorController::windowScriptObjectAvailable()
504 {
505     if (!m_page || !enabled())
506         return;
507 
508     // Grant the inspector the ability to script the inspected page.
509     m_page->mainFrame()->document()->securityOrigin()->grantUniversalAccess();
510     m_scriptState = scriptStateFromPage(m_page);
511     ScriptGlobalObject::set(m_scriptState, "InspectorController", m_inspectorBackend.get());
512 }
513 
scriptObjectReady(bool enableDOMAgent)514 void InspectorController::scriptObjectReady(bool enableDOMAgent)
515 {
516     ASSERT(m_scriptState);
517     if (!m_scriptState)
518         return;
519 
520     ScriptObject webInspectorObj;
521     if (!ScriptGlobalObject::get(m_scriptState, "WebInspector", webInspectorObj))
522         return;
523     setFrontendProxyObject(m_scriptState, webInspectorObj);
524     if (enableDOMAgent)
525         m_domAgent = new InspectorDOMAgent(m_frontend.get());
526 
527 #if ENABLE(JAVASCRIPT_DEBUGGER)
528     Setting debuggerEnabled = setting(debuggerEnabledSettingName);
529     if (debuggerEnabled.type() == Setting::BooleanType && debuggerEnabled.booleanValue())
530         enableDebugger();
531     Setting profilerEnabled = setting(profilerEnabledSettingName);
532     if (profilerEnabled.type() == Setting::BooleanType && profilerEnabled.booleanValue())
533         enableProfiler();
534 #endif
535 
536     // Make sure our window is visible now that the page loaded
537     showWindow();
538 
539     m_client->inspectorWindowObjectCleared();
540 }
541 
setFrontendProxyObject(ScriptState * scriptState,ScriptObject webInspectorObj)542 void InspectorController::setFrontendProxyObject(ScriptState* scriptState, ScriptObject webInspectorObj)
543 {
544     m_frontend.set(new InspectorFrontend(scriptState, webInspectorObj));
545 }
546 
show()547 void InspectorController::show()
548 {
549     if (!enabled())
550         return;
551 
552     if (!m_page) {
553         if (m_frontend)
554             return;  // We are using custom frontend - no need to create page.
555 
556         m_page = m_client->createPage();
557         if (!m_page)
558             return;
559         m_page->setParentInspectorController(this);
560 
561         // showWindow() will be called after the page loads in scriptObjectReady()
562         return;
563     }
564 
565     showWindow();
566 }
567 
showPanel(SpecialPanels panel)568 void InspectorController::showPanel(SpecialPanels panel)
569 {
570     if (!enabled())
571         return;
572 
573     show();
574 
575     if (!m_frontend) {
576         m_showAfterVisible = panel;
577         return;
578     }
579 
580     if (panel == CurrentPanel)
581         return;
582 
583     m_frontend->showPanel(panel);
584 }
585 
close()586 void InspectorController::close()
587 {
588     if (!enabled())
589         return;
590 
591 #if ENABLE(JAVASCRIPT_DEBUGGER)
592     stopUserInitiatedProfiling();
593     disableDebugger();
594 #endif
595     closeWindow();
596 
597     m_frontend.set(0);
598     if (m_domAgent) {
599         m_domAgent->setDocument(0);
600         m_domAgent = 0;
601     }
602     m_scriptState = 0;
603 }
604 
showWindow()605 void InspectorController::showWindow()
606 {
607     ASSERT(enabled());
608 
609     unsigned inspectedPageHeight = m_inspectedPage->mainFrame()->view()->visibleHeight();
610 
611     m_client->showWindow();
612 
613     Setting attachedHeight = setting(inspectorAttachedHeightName);
614     unsigned preferredHeight = attachedHeight.type() == Setting::IntegerType ? attachedHeight.integerValue() : defaultAttachedHeight;
615 
616     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
617     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
618     // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
619     m_client->setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
620 }
621 
closeWindow()622 void InspectorController::closeWindow()
623 {
624     m_client->closeWindow();
625 }
626 
populateScriptObjects()627 void InspectorController::populateScriptObjects()
628 {
629     ASSERT(m_frontend);
630     if (!m_frontend)
631         return;
632 
633     ResourcesMap::iterator resourcesEnd = m_resources.end();
634     for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it)
635         it->second->createScriptObject(m_frontend.get());
636 
637     unsigned messageCount = m_consoleMessages.size();
638     for (unsigned i = 0; i < messageCount; ++i)
639         m_consoleMessages[i]->addToConsole(m_frontend.get());
640 
641 #if ENABLE(DATABASE)
642     DatabaseResourcesSet::iterator databasesEnd = m_databaseResources.end();
643     for (DatabaseResourcesSet::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
644         (*it)->bind(m_frontend.get());
645 #endif
646 #if ENABLE(DOM_STORAGE)
647     DOMStorageResourcesSet::iterator domStorageEnd = m_domStorageResources.end();
648     for (DOMStorageResourcesSet::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
649         (*it)->bind(m_frontend.get());
650 #endif
651 
652     if (m_domAgent)
653         m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
654     m_frontend->populateInterface();
655 }
656 
resetScriptObjects()657 void InspectorController::resetScriptObjects()
658 {
659     if (!m_frontend)
660         return;
661 
662     ResourcesMap::iterator resourcesEnd = m_resources.end();
663     for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it)
664         it->second->releaseScriptObject(m_frontend.get(), false);
665 
666 #if ENABLE(DATABASE)
667     DatabaseResourcesSet::iterator databasesEnd = m_databaseResources.end();
668     for (DatabaseResourcesSet::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
669         (*it)->unbind();
670 #endif
671 #if ENABLE(DOM_STORAGE)
672     DOMStorageResourcesSet::iterator domStorageEnd = m_domStorageResources.end();
673     for (DOMStorageResourcesSet::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
674         (*it)->unbind();
675 #endif
676 
677     m_frontend->reset();
678 }
679 
pruneResources(ResourcesMap * resourceMap,DocumentLoader * loaderToKeep)680 void InspectorController::pruneResources(ResourcesMap* resourceMap, DocumentLoader* loaderToKeep)
681 {
682     ASSERT_ARG(resourceMap, resourceMap);
683 
684     ResourcesMap mapCopy(*resourceMap);
685     ResourcesMap::iterator end = mapCopy.end();
686     for (ResourcesMap::iterator it = mapCopy.begin(); it != end; ++it) {
687         InspectorResource* resource = (*it).second.get();
688         if (resource == m_mainResource)
689             continue;
690 
691         if (!loaderToKeep || !resource->isSameLoader(loaderToKeep)) {
692             removeResource(resource);
693             if (windowVisible())
694                 resource->releaseScriptObject(m_frontend.get(), true);
695         }
696     }
697 }
698 
didCommitLoad(DocumentLoader * loader)699 void InspectorController::didCommitLoad(DocumentLoader* loader)
700 {
701     if (!enabled())
702         return;
703 
704     ASSERT(m_inspectedPage);
705 
706     if (loader->frame() == m_inspectedPage->mainFrame()) {
707         m_client->inspectedURLChanged(loader->url().string());
708 
709         clearConsoleMessages();
710 
711         m_times.clear();
712         m_counts.clear();
713 #if ENABLE(JAVASCRIPT_DEBUGGER)
714         m_profiles.clear();
715         m_currentUserInitiatedProfileNumber = 1;
716         m_nextUserInitiatedProfileNumber = 1;
717 #endif
718 #if ENABLE(DATABASE)
719         m_databaseResources.clear();
720 #endif
721 #if ENABLE(DOM_STORAGE)
722         m_domStorageResources.clear();
723 #endif
724 
725         if (windowVisible()) {
726             resetScriptObjects();
727 
728             if (!loader->isLoadingFromCachedPage()) {
729                 ASSERT(m_mainResource && m_mainResource->isSameLoader(loader));
730                 // We don't add the main resource until its load is committed. This is
731                 // needed to keep the load for a user-entered URL from showing up in the
732                 // list of resources for the page they are navigating away from.
733                 m_mainResource->createScriptObject(m_frontend.get());
734             } else {
735                 // Pages loaded from the page cache are committed before
736                 // m_mainResource is the right resource for this load, so we
737                 // clear it here. It will be re-assigned in
738                 // identifierForInitialRequest.
739                 m_mainResource = 0;
740             }
741         }
742 
743         if (m_domAgent)
744             m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
745     }
746 
747     for (Frame* frame = loader->frame(); frame; frame = frame->tree()->traverseNext(loader->frame()))
748         if (ResourcesMap* resourceMap = m_frameResources.get(frame))
749             pruneResources(resourceMap, loader);
750 }
751 
frameDetachedFromParent(Frame * frame)752 void InspectorController::frameDetachedFromParent(Frame* frame)
753 {
754     if (!enabled())
755         return;
756     if (ResourcesMap* resourceMap = m_frameResources.get(frame))
757         removeAllResources(resourceMap);
758 }
759 
addResource(InspectorResource * resource)760 void InspectorController::addResource(InspectorResource* resource)
761 {
762     m_resources.set(resource->identifier(), resource);
763     m_knownResources.add(resource->requestURL());
764 
765     Frame* frame = resource->frame();
766     ResourcesMap* resourceMap = m_frameResources.get(frame);
767     if (resourceMap)
768         resourceMap->set(resource->identifier(), resource);
769     else {
770         resourceMap = new ResourcesMap;
771         resourceMap->set(resource->identifier(), resource);
772         m_frameResources.set(frame, resourceMap);
773     }
774 }
775 
removeResource(InspectorResource * resource)776 void InspectorController::removeResource(InspectorResource* resource)
777 {
778     m_resources.remove(resource->identifier());
779     String requestURL = resource->requestURL();
780     if (!requestURL.isNull())
781         m_knownResources.remove(requestURL);
782 
783     Frame* frame = resource->frame();
784     ResourcesMap* resourceMap = m_frameResources.get(frame);
785     if (!resourceMap) {
786         ASSERT_NOT_REACHED();
787         return;
788     }
789 
790     resourceMap->remove(resource->identifier());
791     if (resourceMap->isEmpty()) {
792         m_frameResources.remove(frame);
793         delete resourceMap;
794     }
795 }
796 
getTrackedResource(long long identifier)797 InspectorResource* InspectorController::getTrackedResource(long long identifier)
798 {
799     if (!enabled())
800         return 0;
801 
802     if (m_resourceTrackingEnabled)
803         return m_resources.get(identifier).get();
804 
805     bool isMainResource = m_mainResource && m_mainResource->identifier() == identifier;
806     if (isMainResource)
807         return m_mainResource.get();
808 
809     return 0;
810 }
811 
didLoadResourceFromMemoryCache(DocumentLoader * loader,const CachedResource * cachedResource)812 void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* cachedResource)
813 {
814     if (!enabled())
815         return;
816 
817     // If the resource URL is already known, we don't need to add it again since this is just a cached load.
818     if (m_knownResources.contains(cachedResource->url()))
819         return;
820 
821     ASSERT(m_inspectedPage);
822     bool isMainResource = isMainResourceLoader(loader, KURL(cachedResource->url()));
823     ensureResourceTrackingSettingsLoaded();
824     if (!isMainResource && !m_resourceTrackingEnabled)
825         return;
826 
827     RefPtr<InspectorResource> resource = InspectorResource::createCached(m_nextIdentifier--, loader, cachedResource);
828 
829     if (isMainResource) {
830         m_mainResource = resource;
831         resource->markMainResource();
832     }
833 
834     addResource(resource.get());
835 
836     if (windowVisible())
837         resource->createScriptObject(m_frontend.get());
838 }
839 
identifierForInitialRequest(unsigned long identifier,DocumentLoader * loader,const ResourceRequest & request)840 void InspectorController::identifierForInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
841 {
842     if (!enabled())
843         return;
844     ASSERT(m_inspectedPage);
845 
846     bool isMainResource = isMainResourceLoader(loader, request.url());
847     ensureResourceTrackingSettingsLoaded();
848     if (!isMainResource && !m_resourceTrackingEnabled)
849         return;
850 
851     RefPtr<InspectorResource> resource = InspectorResource::create(identifier, loader);
852 
853     resource->updateRequest(request);
854 
855     if (isMainResource) {
856         m_mainResource = resource;
857         resource->markMainResource();
858     }
859 
860     addResource(resource.get());
861 
862     if (windowVisible() && loader->isLoadingFromCachedPage() && resource == m_mainResource)
863         resource->createScriptObject(m_frontend.get());
864 }
865 
isMainResourceLoader(DocumentLoader * loader,const KURL & requestUrl)866 bool InspectorController::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
867 {
868     return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
869 }
870 
willSendRequest(DocumentLoader *,unsigned long identifier,ResourceRequest & request,const ResourceResponse & redirectResponse)871 void InspectorController::willSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
872 {
873     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
874     if (!resource)
875         return;
876 
877     resource->startTiming();
878 
879     if (!redirectResponse.isNull()) {
880         resource->updateRequest(request);
881         resource->updateResponse(redirectResponse);
882     }
883 
884     if (resource != m_mainResource && windowVisible())
885         resource->createScriptObject(m_frontend.get());
886 }
887 
didReceiveResponse(DocumentLoader *,unsigned long identifier,const ResourceResponse & response)888 void InspectorController::didReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse& response)
889 {
890     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
891     if (!resource)
892         return;
893 
894     resource->updateResponse(response);
895     resource->markResponseReceivedTime();
896 
897     if (windowVisible())
898         resource->updateScriptObject(m_frontend.get());
899 }
900 
didReceiveContentLength(DocumentLoader *,unsigned long identifier,int lengthReceived)901 void InspectorController::didReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived)
902 {
903     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
904     if (!resource)
905         return;
906 
907     resource->addLength(lengthReceived);
908 
909     if (windowVisible())
910         resource->updateScriptObject(m_frontend.get());
911 }
912 
didFinishLoading(DocumentLoader *,unsigned long identifier)913 void InspectorController::didFinishLoading(DocumentLoader*, unsigned long identifier)
914 {
915     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
916     if (!resource)
917         return;
918 
919     removeResource(resource.get());
920 
921     resource->endTiming();
922 
923     addResource(resource.get());
924 
925     if (windowVisible())
926         resource->updateScriptObject(m_frontend.get());
927 }
928 
didFailLoading(DocumentLoader *,unsigned long identifier,const ResourceError &)929 void InspectorController::didFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError& /*error*/)
930 {
931     RefPtr<InspectorResource> resource = getTrackedResource(identifier);
932     if (!resource)
933         return;
934 
935     removeResource(resource.get());
936 
937     resource->markFailed();
938     resource->endTiming();
939 
940     addResource(resource.get());
941 
942     if (windowVisible())
943         resource->updateScriptObject(m_frontend.get());
944 }
945 
resourceRetrievedByXMLHttpRequest(unsigned long identifier,const ScriptString & sourceString)946 void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString)
947 {
948     if (!enabled() || !m_resourceTrackingEnabled)
949         return;
950 
951     InspectorResource* resource = m_resources.get(identifier).get();
952     if (!resource)
953         return;
954 
955     resource->setXMLHttpResponseText(sourceString);
956 
957     if (windowVisible())
958         resource->updateScriptObject(m_frontend.get());
959 }
960 
scriptImported(unsigned long identifier,const String & sourceString)961 void InspectorController::scriptImported(unsigned long identifier, const String& sourceString)
962 {
963     if (!enabled() || !m_resourceTrackingEnabled)
964         return;
965 
966     InspectorResource* resource = m_resources.get(identifier).get();
967     if (!resource)
968         return;
969 
970     // FIXME: imported script and XHR response are currently viewed as the same
971     // thing by the Inspector. They should be made into distinct types.
972     resource->setXMLHttpResponseText(ScriptString(sourceString));
973 
974     if (windowVisible())
975         resource->updateScriptObject(m_frontend.get());
976 }
977 
enableResourceTracking(bool always)978 void InspectorController::enableResourceTracking(bool always)
979 {
980     if (!enabled())
981         return;
982 
983     if (always)
984         setSetting(resourceTrackingEnabledSettingName, Setting(true));
985 
986     if (m_resourceTrackingEnabled)
987         return;
988 
989     ASSERT(m_inspectedPage);
990     m_resourceTrackingEnabled = true;
991     if (m_frontend)
992         m_frontend->resourceTrackingWasEnabled();
993 
994     m_inspectedPage->mainFrame()->loader()->reload();
995 }
996 
disableResourceTracking(bool always)997 void InspectorController::disableResourceTracking(bool always)
998 {
999     if (!enabled())
1000         return;
1001 
1002     if (always)
1003         setSetting(resourceTrackingEnabledSettingName, Setting(false));
1004 
1005     ASSERT(m_inspectedPage);
1006     m_resourceTrackingEnabled = false;
1007     if (m_frontend)
1008         m_frontend->resourceTrackingWasDisabled();
1009 }
1010 
ensureResourceTrackingSettingsLoaded()1011 void InspectorController::ensureResourceTrackingSettingsLoaded()
1012 {
1013     if (m_resourceTrackingSettingsLoaded)
1014         return;
1015     m_resourceTrackingSettingsLoaded = true;
1016 
1017     Setting resourceTracking = setting(resourceTrackingEnabledSettingName);
1018     if (resourceTracking.type() == Setting::BooleanType && resourceTracking.booleanValue())
1019         m_resourceTrackingEnabled = true;
1020 }
1021 
1022 #if ENABLE(DATABASE)
didOpenDatabase(Database * database,const String & domain,const String & name,const String & version)1023 void InspectorController::didOpenDatabase(Database* database, const String& domain, const String& name, const String& version)
1024 {
1025     if (!enabled())
1026         return;
1027 
1028     RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
1029 
1030     m_databaseResources.add(resource);
1031 
1032     if (windowVisible())
1033         resource->bind(m_frontend.get());
1034 }
1035 #endif
1036 
1037 #if ENABLE(DOM_STORAGE)
didUseDOMStorage(StorageArea * storageArea,bool isLocalStorage,Frame * frame)1038 void InspectorController::didUseDOMStorage(StorageArea* storageArea, bool isLocalStorage, Frame* frame)
1039 {
1040     if (!enabled())
1041         return;
1042 
1043     DOMStorageResourcesSet::iterator domStorageEnd = m_domStorageResources.end();
1044     for (DOMStorageResourcesSet::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
1045         if ((*it)->isSameHostAndType(frame, isLocalStorage))
1046             return;
1047 
1048     RefPtr<Storage> domStorage = Storage::create(frame, storageArea);
1049     RefPtr<InspectorDOMStorageResource> resource = InspectorDOMStorageResource::create(domStorage.get(), isLocalStorage, frame);
1050 
1051     m_domStorageResources.add(resource);
1052     if (windowVisible())
1053         resource->bind(m_frontend.get());
1054 }
1055 #endif
1056 
moveWindowBy(float x,float y) const1057 void InspectorController::moveWindowBy(float x, float y) const
1058 {
1059     if (!m_page || !enabled())
1060         return;
1061 
1062     FloatRect frameRect = m_page->chrome()->windowRect();
1063     frameRect.move(x, y);
1064     m_page->chrome()->setWindowRect(frameRect);
1065 }
1066 
1067 #if ENABLE(JAVASCRIPT_DEBUGGER)
addProfile(PassRefPtr<Profile> prpProfile,unsigned lineNumber,const UString & sourceURL)1068 void InspectorController::addProfile(PassRefPtr<Profile> prpProfile, unsigned lineNumber, const UString& sourceURL)
1069 {
1070     if (!enabled())
1071         return;
1072 
1073     RefPtr<Profile> profile = prpProfile;
1074     m_profiles.append(profile);
1075 
1076     if (windowVisible())
1077         addScriptProfile(profile.get());
1078 
1079     addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL);
1080 }
1081 
addProfileFinishedMessageToConsole(PassRefPtr<Profile> prpProfile,unsigned lineNumber,const UString & sourceURL)1082 void InspectorController::addProfileFinishedMessageToConsole(PassRefPtr<Profile> prpProfile, unsigned lineNumber, const UString& sourceURL)
1083 {
1084     RefPtr<Profile> profile = prpProfile;
1085 
1086     UString message = "Profile \"webkit-profile://";
1087     message += encodeWithURLEscapeSequences(profile->title());
1088     message += "/";
1089     message += UString::from(profile->uid());
1090     message += "\" finished.";
1091     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
1092 }
1093 
addStartProfilingMessageToConsole(const UString & title,unsigned lineNumber,const UString & sourceURL)1094 void InspectorController::addStartProfilingMessageToConsole(const UString& title, unsigned lineNumber, const UString& sourceURL)
1095 {
1096     UString message = "Profile \"webkit-profile://";
1097     message += encodeWithURLEscapeSequences(title);
1098     message += "/0\" started.";
1099     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
1100 }
1101 
addScriptProfile(Profile * profile)1102 void InspectorController::addScriptProfile(Profile* profile)
1103 {
1104     if (!m_frontend)
1105         return;
1106 
1107     JSLock lock(SilenceAssertionsOnly);
1108     m_frontend->addProfile(toJS(m_scriptState, profile));
1109 }
1110 
getCurrentUserInitiatedProfileName(bool incrementProfileNumber=false)1111 UString InspectorController::getCurrentUserInitiatedProfileName(bool incrementProfileNumber = false)
1112 {
1113     if (incrementProfileNumber)
1114         m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;
1115 
1116     UString title = UserInitiatedProfileName;
1117     title += ".";
1118     title += UString::from(m_currentUserInitiatedProfileNumber);
1119 
1120     return title;
1121 }
1122 
startUserInitiatedProfilingSoon()1123 void InspectorController::startUserInitiatedProfilingSoon()
1124 {
1125     m_startProfiling.startOneShot(0);
1126 }
1127 
startUserInitiatedProfiling(Timer<InspectorController> *)1128 void InspectorController::startUserInitiatedProfiling(Timer<InspectorController>*)
1129 {
1130     if (!enabled())
1131         return;
1132 
1133     if (!profilerEnabled()) {
1134         enableProfiler(false, true);
1135         JavaScriptDebugServer::shared().recompileAllJSFunctions();
1136     }
1137 
1138     m_recordingUserInitiatedProfile = true;
1139 
1140     UString title = getCurrentUserInitiatedProfileName(true);
1141 
1142     ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame())->globalExec();
1143     Profiler::profiler()->startProfiling(scriptState, title);
1144 
1145     addStartProfilingMessageToConsole(title, 0, UString());
1146 
1147     toggleRecordButton(true);
1148 }
1149 
stopUserInitiatedProfiling()1150 void InspectorController::stopUserInitiatedProfiling()
1151 {
1152     if (!enabled())
1153         return;
1154 
1155     m_recordingUserInitiatedProfile = false;
1156 
1157     UString title = getCurrentUserInitiatedProfileName();
1158 
1159     ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame())->globalExec();
1160     RefPtr<Profile> profile = Profiler::profiler()->stopProfiling(scriptState, title);
1161     if (profile)
1162         addProfile(profile, 0, UString());
1163 
1164     toggleRecordButton(false);
1165 }
1166 
toggleRecordButton(bool isProfiling)1167 void InspectorController::toggleRecordButton(bool isProfiling)
1168 {
1169     if (!m_frontend)
1170         return;
1171     m_frontend->setRecordingProfile(isProfiling);
1172 }
1173 
enableProfiler(bool always,bool skipRecompile)1174 void InspectorController::enableProfiler(bool always, bool skipRecompile)
1175 {
1176     if (always)
1177         setSetting(profilerEnabledSettingName, Setting(true));
1178 
1179     if (m_profilerEnabled)
1180         return;
1181 
1182     m_profilerEnabled = true;
1183 
1184     if (!skipRecompile)
1185         JavaScriptDebugServer::shared().recompileAllJSFunctionsSoon();
1186 
1187     if (m_frontend)
1188         m_frontend->profilerWasEnabled();
1189 }
1190 
disableProfiler(bool always)1191 void InspectorController::disableProfiler(bool always)
1192 {
1193     if (always)
1194         setSetting(profilerEnabledSettingName, Setting(false));
1195 
1196     if (!m_profilerEnabled)
1197         return;
1198 
1199     m_profilerEnabled = false;
1200 
1201     JavaScriptDebugServer::shared().recompileAllJSFunctionsSoon();
1202 
1203     if (m_frontend)
1204         m_frontend->profilerWasDisabled();
1205 }
1206 
enableDebuggerFromFrontend(bool always)1207 void InspectorController::enableDebuggerFromFrontend(bool always)
1208 {
1209     if (always)
1210         setSetting(debuggerEnabledSettingName, Setting(true));
1211 
1212     ASSERT(m_inspectedPage);
1213 
1214     JavaScriptDebugServer::shared().addListener(this, m_inspectedPage);
1215     JavaScriptDebugServer::shared().clearBreakpoints();
1216 
1217     m_debuggerEnabled = true;
1218     m_frontend->debuggerWasEnabled();
1219 }
1220 
enableDebugger()1221 void InspectorController::enableDebugger()
1222 {
1223     if (!enabled())
1224         return;
1225 
1226     if (m_debuggerEnabled)
1227         return;
1228 
1229     if (!m_scriptState || !m_frontend) {
1230         m_attachDebuggerWhenShown = true;
1231     } else {
1232         m_frontend->attachDebuggerWhenShown();
1233         m_attachDebuggerWhenShown = false;
1234     }
1235 }
1236 
disableDebugger(bool always)1237 void InspectorController::disableDebugger(bool always)
1238 {
1239     if (!enabled())
1240         return;
1241 
1242     if (always)
1243         setSetting(debuggerEnabledSettingName, Setting(false));
1244 
1245     ASSERT(m_inspectedPage);
1246 
1247     JavaScriptDebugServer::shared().removeListener(this, m_inspectedPage);
1248 
1249     m_debuggerEnabled = false;
1250     m_attachDebuggerWhenShown = false;
1251 
1252     if (m_frontend)
1253         m_frontend->debuggerWasDisabled();
1254 }
1255 
resumeDebugger()1256 void InspectorController::resumeDebugger()
1257 {
1258     if (!m_debuggerEnabled)
1259         return;
1260     JavaScriptDebugServer::shared().continueProgram();
1261 }
1262 
1263 // JavaScriptDebugListener functions
1264 
didParseSource(ExecState *,const SourceCode & source)1265 void InspectorController::didParseSource(ExecState*, const SourceCode& source)
1266 {
1267     m_frontend->parsedScriptSource(source);
1268 }
1269 
failedToParseSource(ExecState *,const SourceCode & source,int errorLine,const UString & errorMessage)1270 void InspectorController::failedToParseSource(ExecState*, const SourceCode& source, int errorLine, const UString& errorMessage)
1271 {
1272     m_frontend->failedToParseScriptSource(source, errorLine, errorMessage);
1273 }
1274 
didPause()1275 void InspectorController::didPause()
1276 {
1277     m_frontend->pausedScript();
1278 }
1279 
didContinue()1280 void InspectorController::didContinue()
1281 {
1282     m_frontend->resumedScript();
1283 }
1284 
1285 #endif
1286 
quadToPath(const FloatQuad & quad)1287 static Path quadToPath(const FloatQuad& quad)
1288 {
1289     Path quadPath;
1290     quadPath.moveTo(quad.p1());
1291     quadPath.addLineTo(quad.p2());
1292     quadPath.addLineTo(quad.p3());
1293     quadPath.addLineTo(quad.p4());
1294     quadPath.closeSubpath();
1295     return quadPath;
1296 }
1297 
drawOutlinedQuad(GraphicsContext & context,const FloatQuad & quad,const Color & fillColor)1298 static void drawOutlinedQuad(GraphicsContext& context, const FloatQuad& quad, const Color& fillColor)
1299 {
1300     static const int outlineThickness = 2;
1301     static const Color outlineColor(62, 86, 180, 228);
1302 
1303     Path quadPath = quadToPath(quad);
1304 
1305     // Clip out the quad, then draw with a 2px stroke to get a pixel
1306     // of outline (because inflating a quad is hard)
1307     {
1308         context.save();
1309         context.addPath(quadPath);
1310         context.clipOut(quadPath);
1311 
1312         context.addPath(quadPath);
1313         context.setStrokeThickness(outlineThickness);
1314         context.setStrokeColor(outlineColor);
1315         context.strokePath();
1316 
1317         context.restore();
1318     }
1319 
1320     // Now do the fill
1321     context.addPath(quadPath);
1322     context.setFillColor(fillColor);
1323     context.fillPath();
1324 }
1325 
drawOutlinedQuadWithClip(GraphicsContext & context,const FloatQuad & quad,const FloatQuad & clipQuad,const Color & fillColor)1326 static void drawOutlinedQuadWithClip(GraphicsContext& context, const FloatQuad& quad, const FloatQuad& clipQuad, const Color& fillColor)
1327 {
1328     context.save();
1329     Path clipQuadPath = quadToPath(clipQuad);
1330     context.clipOut(clipQuadPath);
1331     drawOutlinedQuad(context, quad, fillColor);
1332     context.restore();
1333 }
1334 
drawHighlightForBox(GraphicsContext & context,const FloatQuad & contentQuad,const FloatQuad & paddingQuad,const FloatQuad & borderQuad,const FloatQuad & marginQuad)1335 static void drawHighlightForBox(GraphicsContext& context, const FloatQuad& contentQuad, const FloatQuad& paddingQuad, const FloatQuad& borderQuad, const FloatQuad& marginQuad)
1336 {
1337     static const Color contentBoxColor(125, 173, 217, 128);
1338     static const Color paddingBoxColor(125, 173, 217, 160);
1339     static const Color borderBoxColor(125, 173, 217, 192);
1340     static const Color marginBoxColor(125, 173, 217, 228);
1341 
1342     if (marginQuad != borderQuad)
1343         drawOutlinedQuadWithClip(context, marginQuad, borderQuad, marginBoxColor);
1344     if (borderQuad != paddingQuad)
1345         drawOutlinedQuadWithClip(context, borderQuad, paddingQuad, borderBoxColor);
1346     if (paddingQuad != contentQuad)
1347         drawOutlinedQuadWithClip(context, paddingQuad, contentQuad, paddingBoxColor);
1348 
1349     drawOutlinedQuad(context, contentQuad, contentBoxColor);
1350 }
1351 
drawHighlightForLineBoxes(GraphicsContext & context,const Vector<FloatQuad> & lineBoxQuads)1352 static void drawHighlightForLineBoxes(GraphicsContext& context, const Vector<FloatQuad>& lineBoxQuads)
1353 {
1354     static const Color lineBoxColor(125, 173, 217, 128);
1355 
1356     for (size_t i = 0; i < lineBoxQuads.size(); ++i)
1357         drawOutlinedQuad(context, lineBoxQuads[i], lineBoxColor);
1358 }
1359 
convertFromFrameToMainFrame(Frame * frame,IntRect & rect)1360 static inline void convertFromFrameToMainFrame(Frame* frame, IntRect& rect)
1361 {
1362     rect = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(rect));
1363 }
1364 
frameToMainFrameOffset(Frame * frame)1365 static inline IntSize frameToMainFrameOffset(Frame* frame)
1366 {
1367     IntPoint mainFramePoint = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(IntPoint()));
1368     return mainFramePoint - IntPoint();
1369 }
1370 
drawNodeHighlight(GraphicsContext & context) const1371 void InspectorController::drawNodeHighlight(GraphicsContext& context) const
1372 {
1373     if (!m_highlightedNode)
1374         return;
1375 
1376     RenderObject* renderer = m_highlightedNode->renderer();
1377     Frame* containingFrame = m_highlightedNode->document()->frame();
1378     if (!renderer || !containingFrame)
1379         return;
1380 
1381     IntSize mainFrameOffset = frameToMainFrameOffset(containingFrame);
1382     IntRect boundingBox = renderer->absoluteBoundingBoxRect(true);
1383     boundingBox.move(mainFrameOffset);
1384 
1385     ASSERT(m_inspectedPage);
1386 
1387     FrameView* view = m_inspectedPage->mainFrame()->view();
1388     FloatRect overlayRect = view->visibleContentRect();
1389     if (!overlayRect.contains(boundingBox) && !boundingBox.contains(enclosingIntRect(overlayRect)))
1390         overlayRect = view->visibleContentRect();
1391     context.translate(-overlayRect.x(), -overlayRect.y());
1392 
1393     if (renderer->isBox()) {
1394         RenderBox* renderBox = toRenderBox(renderer);
1395 
1396         IntRect contentBox = renderBox->contentBoxRect();
1397 
1398         IntRect paddingBox(contentBox.x() - renderBox->paddingLeft(), contentBox.y() - renderBox->paddingTop(),
1399                            contentBox.width() + renderBox->paddingLeft() + renderBox->paddingRight(), contentBox.height() + renderBox->paddingTop() + renderBox->paddingBottom());
1400         IntRect borderBox(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(),
1401                           paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom());
1402         IntRect marginBox(borderBox.x() - renderBox->marginLeft(), borderBox.y() - renderBox->marginTop(),
1403                           borderBox.width() + renderBox->marginLeft() + renderBox->marginRight(), borderBox.height() + renderBox->marginTop() + renderBox->marginBottom());
1404 
1405         FloatQuad absContentQuad = renderBox->localToAbsoluteQuad(FloatRect(contentBox));
1406         FloatQuad absPaddingQuad = renderBox->localToAbsoluteQuad(FloatRect(paddingBox));
1407         FloatQuad absBorderQuad = renderBox->localToAbsoluteQuad(FloatRect(borderBox));
1408         FloatQuad absMarginQuad = renderBox->localToAbsoluteQuad(FloatRect(marginBox));
1409 
1410         absContentQuad.move(mainFrameOffset);
1411         absPaddingQuad.move(mainFrameOffset);
1412         absBorderQuad.move(mainFrameOffset);
1413         absMarginQuad.move(mainFrameOffset);
1414 
1415         drawHighlightForBox(context, absContentQuad, absPaddingQuad, absBorderQuad, absMarginQuad);
1416     } else if (renderer->isRenderInline()) {
1417         RenderInline* renderInline = toRenderInline(renderer);
1418 
1419         // FIXME: We should show margins/padding/border for inlines.
1420         Vector<FloatQuad> lineBoxQuads;
1421         renderInline->absoluteQuads(lineBoxQuads);
1422         for (unsigned i = 0; i < lineBoxQuads.size(); ++i)
1423             lineBoxQuads[i] += mainFrameOffset;
1424 
1425         drawHighlightForLineBoxes(context, lineBoxQuads);
1426     }
1427 }
1428 
count(const String & title,unsigned lineNumber,const String & sourceID)1429 void InspectorController::count(const String& title, unsigned lineNumber, const String& sourceID)
1430 {
1431     String identifier = title + String::format("@%s:%d", sourceID.utf8().data(), lineNumber);
1432     HashMap<String, unsigned>::iterator it = m_counts.find(identifier);
1433     int count;
1434     if (it == m_counts.end())
1435         count = 1;
1436     else {
1437         count = it->second + 1;
1438         m_counts.remove(it);
1439     }
1440 
1441     m_counts.add(identifier, count);
1442 
1443     String message = String::format("%s: %d", title.utf8().data(), count);
1444     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceID);
1445 }
1446 
startTiming(const String & title)1447 void InspectorController::startTiming(const String& title)
1448 {
1449     m_times.add(title, currentTime() * 1000);
1450 }
1451 
stopTiming(const String & title,double & elapsed)1452 bool InspectorController::stopTiming(const String& title, double& elapsed)
1453 {
1454     HashMap<String, double>::iterator it = m_times.find(title);
1455     if (it == m_times.end())
1456         return false;
1457 
1458     double startTime = it->second;
1459     m_times.remove(it);
1460 
1461     elapsed = currentTime() * 1000 - startTime;
1462     return true;
1463 }
1464 
specialPanelForJSName(const String & panelName)1465 InspectorController::SpecialPanels InspectorController::specialPanelForJSName(const String& panelName)
1466 {
1467     if (panelName == "elements")
1468         return ElementsPanel;
1469     else if (panelName == "resources")
1470         return ResourcesPanel;
1471     else if (panelName == "scripts")
1472         return ScriptsPanel;
1473     else if (panelName == "profiles")
1474         return ProfilesPanel;
1475     else if (panelName == "databases")
1476         return DatabasesPanel;
1477     else
1478         return ElementsPanel;
1479 }
1480 
1481 } // namespace WebCore
1482