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