diff options
Diffstat (limited to 'WebCore/inspector/InspectorController.cpp')
-rw-r--r-- | WebCore/inspector/InspectorController.cpp | 369 |
1 files changed, 302 insertions, 67 deletions
diff --git a/WebCore/inspector/InspectorController.cpp b/WebCore/inspector/InspectorController.cpp index 1e131f7..5f8f190 100644 --- a/WebCore/inspector/InspectorController.cpp +++ b/WebCore/inspector/InspectorController.cpp @@ -33,6 +33,7 @@ #if ENABLE(INSPECTOR) #include "CachedResource.h" +#include "CachedResourceLoader.h" #include "Chrome.h" #include "Console.h" #include "ConsoleMessage.h" @@ -71,6 +72,7 @@ #include "InspectorTimelineAgent.h" #include "InspectorValues.h" #include "InspectorWorkerResource.h" +#include "IntRect.h" #include "Page.h" #include "ProgressTracker.h" #include "Range.h" @@ -117,26 +119,28 @@ using namespace std; namespace WebCore { -static const char* const resourceTrackingEnabledSettingName = "resourceTrackingEnabled"; -static const char* const debuggerEnabledSettingName = "debuggerEnabled"; -static const char* const profilerEnabledSettingName = "profilerEnabled"; -static const char* const inspectorAttachedHeightName = "inspectorAttachedHeight"; +static const char* const frontendSettingsSettingName = "frontendSettings"; + +static const char* const debuggerAlwaysEnabledSettingName = "debuggerEnabled"; static const char* const lastActivePanel = "lastActivePanel"; +static const char* const monitoringXHRSettingName = "xhrMonitor"; +static const char* const resourceTrackingAlwaysEnabledSettingName = "resourceTrackingEnabled"; +static const char* const profilerAlwaysEnabledSettingName = "profilerEnabled"; + +static const char* const timelineProfilerEnabledStateName = "timelineProfilerEnabled"; +static const char* const resourceTrackingEnabledStateName = "resourceTrackingEnabled"; +static const char* const monitoringXHRStateName = "monitoringXHREnabled"; + +static const char* const inspectorAttachedHeightName = "inspectorAttachedHeight"; + const char* const InspectorController::ElementsPanel = "elements"; const char* const InspectorController::ConsolePanel = "console"; const char* const InspectorController::ScriptsPanel = "scripts"; const char* const InspectorController::ProfilesPanel = "profiles"; -static const char* const monitoringXHRSettingName = "xhrMonitor"; static int connectedFrontendCount = 0; -const String& InspectorController::frontendSettingsSettingName() -{ - DEFINE_STATIC_LOCAL(String, settingName, ("frontendSettings")); - return settingName; -} - const String& InspectorController::inspectorStartsAttachedSettingName() { DEFINE_STATIC_LOCAL(String, settingName, ("inspectorStartsAttached")); @@ -240,7 +244,7 @@ void InspectorController::setSetting(const String& key, const String& value) void InspectorController::saveApplicationSettings(const String& settings) { - setSetting(InspectorController::frontendSettingsSettingName(), settings); + setSetting(frontendSettingsSettingName, settings); } void InspectorController::saveSessionSettings(const String& settingsJSON) @@ -248,10 +252,46 @@ void InspectorController::saveSessionSettings(const String& settingsJSON) m_sessionSettings = InspectorValue::parseJSON(settingsJSON); } +void InspectorController::getInspectorState(RefPtr<InspectorObject>* state) +{ + (*state)->setBoolean(monitoringXHRStateName, m_monitoringXHR); + (*state)->setBoolean(resourceTrackingEnabledStateName, m_resourceTrackingEnabled); +} + +void InspectorController::updateInspectorStateCookie() +{ + RefPtr<InspectorObject> state = InspectorObject::create(); + state->setBoolean(monitoringXHRStateName, m_monitoringXHR); + state->setBoolean(resourceTrackingEnabledStateName, m_resourceTrackingEnabled); + state->setBoolean(timelineProfilerEnabledStateName, m_timelineAgent); + m_client->updateInspectorStateCookie(state->toJSONString()); +} + +void InspectorController::restoreInspectorStateFromCookie(const String& inspectorStateString) +{ + RefPtr<InspectorValue> inspectorStateValue = InspectorValue::parseJSON(inspectorStateString); + if (!inspectorStateValue) + return; + + RefPtr<InspectorObject> inspectorState = inspectorStateValue->asObject(); + if (!inspectorState) + return; + + inspectorState->getBoolean(monitoringXHRStateName, &m_monitoringXHR); + inspectorState->getBoolean(resourceTrackingEnabledStateName, &m_resourceTrackingEnabled); + + bool timelineProfilerEnabled = false; + inspectorState->getBoolean(timelineProfilerEnabledStateName, &timelineProfilerEnabled); + if (timelineProfilerEnabled) + startTimelineProfiler(); + else + stopTimelineProfiler(); +} + void InspectorController::getSettings(RefPtr<InspectorObject>* settings) { *settings = InspectorObject::create(); - (*settings)->setString("application", setting(frontendSettingsSettingName())); + (*settings)->setString("application", setting(frontendSettingsSettingName)); (*settings)->setString("session", m_sessionSettings->toJSONString()); } @@ -460,18 +500,14 @@ void InspectorController::setSearchingForNode(bool enabled) } } -void InspectorController::setMonitoringXHR(bool enabled) +void InspectorController::setMonitoringXHREnabled(bool enabled, bool* newState) { + *newState = enabled; if (m_monitoringXHR == enabled) return; m_monitoringXHR = enabled; setSetting(monitoringXHRSettingName, enabled ? "true" : "false"); - if (m_frontend) { - if (enabled) - m_frontend->monitoringXHRWasEnabled(); - else - m_frontend->monitoringXHRWasDisabled(); - } + updateInspectorStateCookie(); } void InspectorController::connectFrontend() @@ -500,6 +536,13 @@ void InspectorController::connectFrontend() connectedFrontendCount++; } +void InspectorController::reuseFrontend() +{ + connectFrontend(); + restoreDebugger(); + restoreProfiler(); +} + void InspectorController::show() { if (!enabled()) @@ -604,18 +647,12 @@ void InspectorController::populateScriptObjects() if (m_showAfterVisible == lastActivePanel) m_showAfterVisible = setting(lastActivePanel); - if (m_nodeToFocus) - focusNode(); - showPanel(m_showAfterVisible); - if (m_resourceTrackingEnabled) - m_frontend->resourceTrackingWasEnabled(); + showPanel(m_showAfterVisible); if (m_searchingForNode) m_frontend->searchingForNodeWasEnabled(); - if (m_monitoringXHR) - m_frontend->monitoringXHRWasEnabled(); #if ENABLE(JAVASCRIPT_DEBUGGER) if (m_profilerAgent->enabled()) @@ -628,6 +665,9 @@ void InspectorController::populateScriptObjects() m_domAgent->setDocument(m_inspectedPage->mainFrame()->document()); + if (m_nodeToFocus) + focusNode(); + if (m_expiredConsoleMessageCount) m_frontend->updateConsoleMessageExpiredCount(m_expiredConsoleMessageCount); unsigned messageCount = m_consoleMessages.size(); @@ -661,17 +701,31 @@ void InspectorController::populateScriptObjects() m_frontend->evaluateForTestInFrontend((*it).first, (*it).second); m_pendingEvaluateTestCommands.clear(); + restoreDebugger(); + restoreProfiler(); +} + +void InspectorController::restoreDebugger() +{ + ASSERT(m_frontend); #if ENABLE(JAVASCRIPT_DEBUGGER) if (InspectorDebuggerAgent::isDebuggerAlwaysEnabled()) enableDebuggerFromFrontend(false); else { - String debuggerEnabled = setting(debuggerEnabledSettingName); + String debuggerEnabled = setting(debuggerAlwaysEnabledSettingName); if (debuggerEnabled == "true" || m_attachDebuggerWhenShown) enableDebugger(); } +#endif +} + +void InspectorController::restoreProfiler() +{ + ASSERT(m_frontend); +#if ENABLE(JAVASCRIPT_DEBUGGER) m_profilerAgent->setFrontend(m_frontend.get()); if (!ScriptProfiler::isProfilerAlwaysEnabled()) { - String profilerEnabledSetting = setting(profilerEnabledSettingName); + String profilerEnabledSetting = setting(profilerAlwaysEnabledSettingName); if (profilerEnabledSetting == "true") enableProfiler(); } @@ -798,6 +852,8 @@ void InspectorController::addResource(InspectorResource* resource) m_knownResources.add(resource->requestURL()); Frame* frame = resource->frame(); + if (!frame) + return; ResourcesMap* resourceMap = m_frameResources.get(frame); if (resourceMap) resourceMap->set(resource->identifier(), resource); @@ -816,6 +872,8 @@ void InspectorController::removeResource(InspectorResource* resource) m_knownResources.remove(requestURL); Frame* frame = resource->frame(); + if (!frame) + return; ResourcesMap* resourceMap = m_frameResources.get(frame); if (!resourceMap) { ASSERT_NOT_REACHED(); @@ -957,7 +1015,7 @@ void InspectorController::willSendRequest(unsigned long identifier, ResourceRequ // Redirect may have empty URL and we'd like to not crash with invalid HashMap entry. // See http/tests/misc/will-send-request-returns-null-on-redirect.html if (!request.url().isEmpty()) { - resource->endTiming(); + resource->endTiming(0); resource->updateResponse(redirectResponse); // We always store last redirect by the original id key. Rest of the redirects are stored within the last one. @@ -980,6 +1038,15 @@ void InspectorController::willSendRequest(unsigned long identifier, ResourceRequ resource->updateScriptObject(m_frontend.get()); } +void InspectorController::markResourceAsCached(unsigned long identifier) +{ + if (!enabled()) + return; + + if (RefPtr<InspectorResource> resource = getTrackedResource(identifier)) + resource->markAsCached(); +} + void InspectorController::didReceiveResponse(unsigned long identifier, const ResourceResponse& response) { if (!enabled()) @@ -1014,7 +1081,7 @@ void InspectorController::didReceiveContentLength(unsigned long identifier, int resource->updateScriptObject(m_frontend.get()); } -void InspectorController::didFinishLoading(unsigned long identifier) +void InspectorController::didFinishLoading(unsigned long identifier, double finishTime) { if (!enabled()) return; @@ -1026,7 +1093,7 @@ void InspectorController::didFinishLoading(unsigned long identifier) if (!resource) return; - resource->endTiming(); + resource->endTiming(finishTime); // No need to mute this event for main resource since it happens after did commit load. if (m_frontend) @@ -1051,7 +1118,7 @@ void InspectorController::didFailLoading(unsigned long identifier, const Resourc return; resource->markFailed(); - resource->endTiming(); + resource->endTiming(0); // No need to mute this event for main resource since it happens after did commit load. if (m_frontend) @@ -1094,40 +1161,33 @@ void InspectorController::scriptImported(unsigned long identifier, const String& resource->updateScriptObject(m_frontend.get()); } -void InspectorController::enableResourceTracking(bool always, bool reload) +void InspectorController::setResourceTrackingEnabled(bool enable) { if (!enabled()) return; - if (always) - setSetting(resourceTrackingEnabledSettingName, "true"); - - if (m_resourceTrackingEnabled) - return; - ASSERT(m_inspectedPage); - m_resourceTrackingEnabled = true; - if (m_frontend) - m_frontend->resourceTrackingWasEnabled(); - m_client->resourceTrackingWasEnabled(); - - if (reload) - m_inspectedPage->mainFrame()->redirectScheduler()->scheduleRefresh(true); + m_resourceTrackingEnabled = enable; + updateInspectorStateCookie(); } -void InspectorController::disableResourceTracking(bool always) +void InspectorController::setResourceTrackingEnabled(bool enable, bool always, bool* newState) { - if (!enabled()) - return; + *newState = enable; if (always) - setSetting(resourceTrackingEnabledSettingName, "false"); + setSetting(resourceTrackingAlwaysEnabledSettingName, enable ? "true" : "false"); + + if (m_resourceTrackingEnabled == enable) + return; ASSERT(m_inspectedPage); - m_resourceTrackingEnabled = false; - if (m_frontend) - m_frontend->resourceTrackingWasDisabled(); - m_client->resourceTrackingWasDisabled(); + m_resourceTrackingEnabled = enable; + + if (enable) + m_inspectedPage->mainFrame()->redirectScheduler()->scheduleRefresh(true); + + updateInspectorStateCookie(); } void InspectorController::ensureSettingsLoaded() @@ -1136,14 +1196,15 @@ void InspectorController::ensureSettingsLoaded() return; m_settingsLoaded = true; - String resourceTracking = setting(resourceTrackingEnabledSettingName); - if (resourceTracking == "true") + String resourceTrackingAlwaysEnabled = setting(resourceTrackingAlwaysEnabledSettingName); + if (resourceTrackingAlwaysEnabled == "true") m_resourceTrackingEnabled = true; - m_client->resourceTrackingWasEnabled(); - String monitoringXHR = setting(monitoringXHRSettingName); - if (monitoringXHR == "true") + String monitoringXHRAlwaysEnabled = setting(monitoringXHRSettingName); + if (monitoringXHRAlwaysEnabled == "true") m_monitoringXHR = true; + + updateInspectorStateCookie(); } void InspectorController::startTimelineProfiler() @@ -1157,7 +1218,8 @@ void InspectorController::startTimelineProfiler() m_timelineAgent = new InspectorTimelineAgent(m_frontend.get()); if (m_frontend) m_frontend->timelineProfilerWasStarted(); - m_client->timelineProfilerWasStarted(); + + updateInspectorStateCookie(); } void InspectorController::stopTimelineProfiler() @@ -1171,7 +1233,8 @@ void InspectorController::stopTimelineProfiler() m_timelineAgent = 0; if (m_frontend) m_frontend->timelineProfilerWasStopped(); - m_client->timelineProfilerWasStopped(); + + updateInspectorStateCookie(); } #if ENABLE(WORKERS) @@ -1442,6 +1505,56 @@ InspectorDOMStorageResource* InspectorController::getDOMStorageResourceForId(lon } #endif +#if ENABLE(WEB_SOCKETS) +void InspectorController::didCreateWebSocket(unsigned long identifier, const KURL& requestURL, const KURL& documentURL) +{ + if (!enabled()) + return; + ASSERT(m_inspectedPage); + + RefPtr<InspectorResource> resource = InspectorResource::createWebSocket(identifier, requestURL, documentURL); + addResource(resource.get()); + + if (m_frontend) + resource->updateScriptObject(m_frontend.get()); +} + +void InspectorController::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request) +{ + RefPtr<InspectorResource> resource = getTrackedResource(identifier); + if (!resource) + return; + resource->startTiming(); + resource->updateWebSocketRequest(request); + if (m_frontend) + resource->updateScriptObject(m_frontend.get()); +} + +void InspectorController::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response) +{ + RefPtr<InspectorResource> resource = getTrackedResource(identifier); + if (!resource) + return; + // Calling resource->markResponseReceivedTime() here makes resources bar chart confusing, because + // we cannot apply the "latency + download" model of regular resources to WebSocket connections. + // FIXME: Design a new UI for bar charts of WebSocket resources, and record timing here. + resource->updateWebSocketResponse(response); + if (m_frontend) + resource->updateScriptObject(m_frontend.get()); +} + +void InspectorController::didCloseWebSocket(unsigned long identifier) +{ + RefPtr<InspectorResource> resource = getTrackedResource(identifier); + if (!resource) + return; + + resource->endTiming(0); + if (m_frontend) + resource->updateScriptObject(m_frontend.get()); +} +#endif // ENABLE(WEB_SOCKETS) + #if ENABLE(JAVASCRIPT_DEBUGGER) void InspectorController::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL) { @@ -1493,14 +1606,14 @@ bool InspectorController::profilerEnabled() const void InspectorController::enableProfiler(bool always, bool skipRecompile) { if (always) - setSetting(profilerEnabledSettingName, "true"); + setSetting(profilerAlwaysEnabledSettingName, "true"); m_profilerAgent->enable(skipRecompile); } void InspectorController::disableProfiler(bool always) { if (always) - setSetting(profilerEnabledSettingName, "false"); + setSetting(profilerAlwaysEnabledSettingName, "false"); m_profilerAgent->disable(); } #endif @@ -1510,7 +1623,7 @@ void InspectorController::enableDebuggerFromFrontend(bool always) { ASSERT(!debuggerEnabled()); if (always) - setSetting(debuggerEnabledSettingName, "true"); + setSetting(debuggerAlwaysEnabledSettingName, "true"); ASSERT(m_inspectedPage); @@ -1541,7 +1654,7 @@ void InspectorController::disableDebugger(bool always) return; if (always) - setSetting(debuggerEnabledSettingName, "false"); + setSetting(debuggerAlwaysEnabledSettingName, "false"); ASSERT(m_inspectedPage); @@ -1558,7 +1671,6 @@ void InspectorController::resume() if (m_debuggerAgent) m_debuggerAgent->resume(); } -// JavaScriptDebugListener functions #endif @@ -1585,6 +1697,8 @@ void InspectorController::didEvaluateForTestInFrontend(long callId, const String String InspectorController::breakpointsSettingKey() { DEFINE_STATIC_LOCAL(String, keyPrefix, ("breakpoints:")); + if (!m_mainResource) + return ""; return keyPrefix + InspectorDebuggerAgent::md5Base16(m_mainResource->requestURL()); } @@ -1744,6 +1858,84 @@ void InspectorController::drawNodeHighlight(GraphicsContext& context) const drawHighlightForLineBoxesOrSVGRenderer(context, lineBoxQuads); } + + // Draw node title if necessary. + + if (!m_highlightedNode->isElementNode()) + return; + + WebCore::Settings* settings = containingFrame->settings(); + drawElementTitle(context, boundingBox, overlayRect, settings); +} + +void InspectorController::drawElementTitle(GraphicsContext& context, const IntRect& boundingBox, const FloatRect& overlayRect, WebCore::Settings* settings) const +{ + static const int rectInflatePx = 4; + static const int fontHeightPx = 12; + static const int borderWidthPx = 1; + static const Color tooltipBackgroundColor(255, 255, 194, 255); + static const Color tooltipBorderColor(Color::black); + static const Color tooltipFontColor(Color::black); + + Element* element = static_cast<Element*>(m_highlightedNode.get()); + bool isXHTML = element->document()->isXHTMLDocument(); + String nodeTitle = isXHTML ? element->nodeName() : element->nodeName().lower(); + const AtomicString& idValue = element->getIdAttribute(); + if (!idValue.isNull() && !idValue.isEmpty()) { + nodeTitle += "#"; + nodeTitle += idValue; + } + if (element->hasClass() && element->isStyledElement()) { + const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames(); + size_t classNameCount = classNamesString.size(); + if (classNameCount) { + HashSet<AtomicString> usedClassNames; + for (size_t i = 0; i < classNameCount; ++i) { + const AtomicString& className = classNamesString[i]; + if (usedClassNames.contains(className)) + continue; + usedClassNames.add(className); + nodeTitle += "."; + nodeTitle += className; + } + } + } + nodeTitle += " ["; + nodeTitle += String::number(boundingBox.width()); + nodeTitle.append(static_cast<UChar>(0x00D7)); // × + nodeTitle += String::number(boundingBox.height()); + nodeTitle += "]"; + + FontDescription desc; + FontFamily family; + family.setFamily(settings->fixedFontFamily()); + desc.setFamily(family); + desc.setComputedSize(fontHeightPx); + Font font = Font(desc, 0, 0); + font.update(0); + + TextRun nodeTitleRun(nodeTitle); + IntPoint titleBasePoint = boundingBox.bottomLeft(); + titleBasePoint.move(rectInflatePx, rectInflatePx); + IntRect titleRect = enclosingIntRect(font.selectionRectForText(nodeTitleRun, titleBasePoint, fontHeightPx)); + titleRect.inflate(rectInflatePx); + + // The initial offsets needed to compensate for a 1px-thick border stroke (which is not a part of the rectangle). + int dx = -borderWidthPx; + int dy = borderWidthPx; + if (titleRect.right() > overlayRect.right()) + dx += overlayRect.right() - titleRect.right(); + if (titleRect.x() + dx < overlayRect.x()) + dx = overlayRect.x() - titleRect.x(); + if (titleRect.bottom() > overlayRect.bottom()) + dy += overlayRect.bottom() - titleRect.bottom() - borderWidthPx; + titleRect.move(dx, dy); + context.setStrokeColor(tooltipBorderColor, DeviceColorSpace); + context.setStrokeThickness(borderWidthPx); + context.setFillColor(tooltipBackgroundColor, DeviceColorSpace); + context.drawRect(titleRect); + context.setFillColor(tooltipFontColor, DeviceColorSpace); + context.drawText(font, nodeTitleRun, IntPoint(titleRect.x() + rectInflatePx, titleRect.y() + font.height())); } void InspectorController::openInInspectedWindow(const String& url) @@ -1840,6 +2032,49 @@ void InspectorController::getResourceContent(unsigned long identifier, String* c *content = resource ? resource->sourceString() : String(); } +bool InspectorController::resourceContentForURL(const KURL& url, Document* frameDocument, String* result) +{ + if (!frameDocument) + return false; + + String textEncodingName; + RefPtr<SharedBuffer> buffer; + if (equalIgnoringFragmentIdentifier(url, frameDocument->frame()->loader()->documentLoader()->requestURL())) { + textEncodingName = frameDocument->inputEncoding(); + buffer = frameDocument->frame()->loader()->provisionalDocumentLoader()->mainResourceData(); + } else { + const String& urlString = url.string(); + CachedResource* cachedResource = frameDocument->cachedResourceLoader()->cachedResource(urlString); + if (!cachedResource) + cachedResource = cache()->resourceForURL(urlString); + + ASSERT(cachedResource); // FIXME(apavlov): This might be too aggressive. + + bool isUnpurgeable = true; + if (cachedResource->isPurgeable()) { + // If the resource is purgeable then make it unpurgeable to get + // its data. This might fail, in which case we return an + // empty String. + if (!cachedResource->makePurgeable(false)) + isUnpurgeable = false; + } + if (isUnpurgeable) { + textEncodingName = cachedResource->encoding(); + buffer = cachedResource->data(); + } + } + + if (buffer) { + TextEncoding encoding(textEncodingName); + if (!encoding.isValid()) + encoding = WindowsLatin1Encoding(); + *result = encoding.decode(buffer->data(), buffer->size()); + return true; + } + + return false; +} + void InspectorController::reloadPage() { m_inspectedPage->mainFrame()->redirectScheduler()->scheduleRefresh(true); |