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