diff options
Diffstat (limited to 'WebCore/inspector')
73 files changed, 6705 insertions, 1978 deletions
diff --git a/WebCore/inspector/CodeGeneratorInspector.pm b/WebCore/inspector/CodeGeneratorInspector.pm index 80e243b..a566576 100644 --- a/WebCore/inspector/CodeGeneratorInspector.pm +++ b/WebCore/inspector/CodeGeneratorInspector.pm @@ -28,6 +28,11 @@ $typeTransform{"Debug"} = { "header" => "InspectorDebuggerAgent.h", "domainAccessor" => "m_inspectorController->debuggerAgent()", }; +$typeTransform{"Resource"} = { + "forward" => "InspectorResourceAgent", + "header" => "InspectorResourceAgent.h", + "domainAccessor" => "m_inspectorController->m_resourceAgent", +}; $typeTransform{"DOM"} = { "forward" => "InspectorDOMAgent", "header" => "InspectorDOMAgent.h", @@ -114,6 +119,14 @@ $typeTransform{"unsigned int"} = { "header" => "", "JSONType" => "Number" }; +$typeTransform{"double"} = { + "param" => "double", + "variable" => "double", + "defaultValue" => "0.0", + "forward" => "", + "header" => "", + "JSONType" => "Number" +}; $typeTransform{"boolean"} = { "param" => "bool", "variable"=> "bool", @@ -306,7 +319,7 @@ sub generateBackendFunction my $domainAccessor = $typeTransform{$domain}->{"domainAccessor"}; $backendTypes{$domain} = 1; push(@function, " if (!$domainAccessor)"); - push(@function, " protocolErrors->pushString(String::format(\"Protocol Error: %s handler is not available.\", \"$domain\"));"); + push(@function, " protocolErrors->pushString(\"Protocol Error: $domain handler is not available.\");"); push(@function, ""); if (scalar(@inArgs)) { @@ -316,7 +329,7 @@ sub generateBackendFunction push(@function, ""); push(@function, " RefPtr<InspectorObject> argumentsContainer;"); push(@function, " if (!(argumentsContainer = requestMessageObject->getObject(\"arguments\"))) {"); - push(@function, " protocolErrors->pushString(String::format(\"Protocol Error: 'arguments' property with type 'object' was not found.\"));"); + push(@function, " protocolErrors->pushString(\"Protocol Error: 'arguments' property with type 'object' was not found.\");"); push(@function, " } else {"); push(@function, " InspectorObject::const_iterator argumentsEndIterator = argumentsContainer->end();"); @@ -329,10 +342,10 @@ sub generateBackendFunction push(@function, ""); push(@function, " InspectorObject::const_iterator ${name}ValueIterator = argumentsContainer->find(\"$name\");"); push(@function, " if (${name}ValueIterator == argumentsEndIterator) {"); - push(@function, " protocolErrors->pushString(String::format(\"Protocol Error: Argument '%s' with type '%s' was not found.\", \"$name\", \"$JSONType\"));"); + push(@function, " protocolErrors->pushString(\"Protocol Error: Argument '$name' with type '$JSONType' was not found.\");"); push(@function, " } else {"); push(@function, " if (!${name}ValueIterator->second->as$JSONType(&$name)) {"); - push(@function, " protocolErrors->pushString(String::format(\"Protocol Error: Argument '%s' has wrong type. It should be '%s'.\", \"$name\", \"$JSONType\"));"); + push(@function, " protocolErrors->pushString(\"Protocol Error: Argument '$name' has wrong type. It should be '$JSONType'.\");"); push(@function, " }"); push(@function, " }"); } @@ -446,7 +459,7 @@ $mapEntries HashMap<String, CallHandler>::iterator it = dispatchMap.find(command); if (it == dispatchMap.end()) { - reportProtocolError(callId, String::format("Protocol Error: Invalid command was received. '%s' wasn't found.", command.utf8().data())); + reportProtocolError(callId, makeString("Protocol Error: Invalid command was received. '", command, "' wasn't found.")); return; } @@ -606,7 +619,7 @@ sub generateSource my @sourceContent = split("\r", $licenseTemplate); push(@sourceContent, "\n#include \"config.h\""); push(@sourceContent, "#include \"$className.h\""); - push(@sourceContent, "#include <wtf/text/CString.h>"); + push(@sourceContent, "#include <wtf/text/StringConcatenate.h>"); push(@sourceContent, ""); push(@sourceContent, "#if ENABLE(INSPECTOR)"); push(@sourceContent, ""); diff --git a/WebCore/inspector/ConsoleMessage.cpp b/WebCore/inspector/ConsoleMessage.cpp index f3bd6bc..67930cd 100644 --- a/WebCore/inspector/ConsoleMessage.cpp +++ b/WebCore/inspector/ConsoleMessage.cpp @@ -67,7 +67,7 @@ PassRefPtr<InspectorObject> ConsoleMessage::CallFrame::buildInspectorObject() co { RefPtr<InspectorObject> frame = InspectorObject::create(); frame->setString("functionName", m_functionName); - frame->setString("sourceURL", m_sourceURL.string()); + frame->setString("sourceURL", m_sourceURL); frame->setNumber("lineNumber", m_lineNumber); return frame; } @@ -100,7 +100,7 @@ ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, c { const ScriptCallFrame& lastCaller = callStack->at(0); m_line = lastCaller.lineNumber(); - m_url = lastCaller.sourceURL().string(); + m_url = lastCaller.sourceURL(); if (storeTrace) { for (unsigned i = 0; i < callStack->size(); ++i) @@ -155,22 +155,20 @@ void ConsoleMessage::updateRepeatCountInConsole(InspectorFrontend* frontend) } #endif // ENABLE(INSPECTOR) -bool ConsoleMessage::isEqual(ScriptState* state, ConsoleMessage* msg) const +bool ConsoleMessage::isEqual(ConsoleMessage* msg) const { #if ENABLE(INSPECTOR) if (msg->m_arguments.size() != m_arguments.size()) return false; - if (!state && msg->m_arguments.size()) + if (!msg->m_scriptState.get() && msg->m_arguments.size()) { + ASSERT_NOT_REACHED(); return false; + } - ASSERT_ARG(state, state || msg->m_arguments.isEmpty()); - - for (size_t i = 0; i < msg->m_arguments.size(); ++i) { - if (!m_arguments[i].isEqual(state, msg->m_arguments[i])) + for (size_t i = 0; i < m_arguments.size(); ++i) { + if (!m_arguments[i].isEqual(msg->m_scriptState.get(), msg->m_arguments[i])) return false; } -#else - UNUSED_PARAM(state); #endif // ENABLE(INSPECTOR) size_t frameCount = msg->m_frames.size(); diff --git a/WebCore/inspector/ConsoleMessage.h b/WebCore/inspector/ConsoleMessage.h index d10fa3d..6c3f2c7 100644 --- a/WebCore/inspector/ConsoleMessage.h +++ b/WebCore/inspector/ConsoleMessage.h @@ -55,7 +55,7 @@ public: void updateRepeatCountInConsole(InspectorFrontend* frontend); #endif void incrementCount() { ++m_repeatCount; } - bool isEqual(ScriptState*, ConsoleMessage* msg) const; + bool isEqual(ConsoleMessage* msg) const; MessageSource source() const { return m_source; } const String& message() const { return m_message; } @@ -72,7 +72,7 @@ private: private: String m_functionName; - KURL m_sourceURL; + String m_sourceURL; unsigned m_lineNumber; }; diff --git a/WebCore/inspector/Inspector.idl b/WebCore/inspector/Inspector.idl index a08a622..85c30a8 100644 --- a/WebCore/inspector/Inspector.idl +++ b/WebCore/inspector/Inspector.idl @@ -32,7 +32,6 @@ module core { interface [Conditional=INSPECTOR] Inspector { - [notify] void addConsoleMessage(out Object messageObj); [notify] void addRecordToTimeline(out Object record); [notify] void addNodesToSearchResult(out Array nodeIds); [notify] void attributesUpdated(out long id, out Array attributes); @@ -41,11 +40,12 @@ module core { [notify] void childNodeCountUpdated(out long id, out int newValue); [notify] void childNodeInserted(out long parentId, out long prevId, out Object node); [notify] void childNodeRemoved(out long parentId, out long id); - [notify] void consoleMessagesCleared(); [notify] void didCommitLoad(); [notify] void evaluateForTestInFrontend(out long testCallId, out String script); [notify] void disconnectFromBackend(); + [notify] void domContentEventFired(out double time); [notify] void inspectedURLChanged(out String url); + [notify] void loadEventFired(out double time); [notify] void removeResource(out unsigned long identifier); [notify] void reset(); [notify] void resetProfilesPanel(); @@ -55,8 +55,6 @@ module core { [notify] void showPanel(out String panel); [notify] void timelineProfilerWasStarted(); [notify] void timelineProfilerWasStopped(); - [notify] void updateConsoleMessageExpiredCount(out unsigned long count); - [notify] void updateConsoleMessageRepeatCount(out unsigned long count); [notify] void updateFocusedNode(out long nodeId); [notify] void updateResource(out Value resource); @@ -108,12 +106,31 @@ module core { [handler=Controller] void setMonitoringXHREnabled(in boolean enable, out boolean newState); [handler=Controller] void setResourceTrackingEnabled(in boolean enabled, in boolean always, out boolean newState); - [handler=Controller] void getResourceContent(in unsigned long identifier, out String content); + [handler=Controller] void getResourceContent(in unsigned long identifier, in boolean encode, out String content); [handler=Controller] void reloadPage(); [handler=Controller] void startTimelineProfiler(); [handler=Controller] void stopTimelineProfiler(); + [handler=Resource] void cachedResources(out Array resources); + [handler=Resource] void resourceContent(in unsigned long frameID, in String url, out String content); + [notify] void identifierForInitialRequest(out long identifier, out String url, out unsigned long frameID, out boolean isMainResource); + [notify] void willSendRequest(out long identifier, out double time, out Object request, out Object redirectResponse); + [notify] void markResourceAsCached(out long identifier); + [notify] void didReceiveResponse(out long identifier, out double time, out String resourceType, out Object response); + [notify] void didReceiveContentLength(out long identifier, out double time, out long lengthReceived); + [notify] void didFinishLoading(out long identifier, out double finishTime); + [notify] void didFailLoading(out long identifier, out double time, out String localizedDescription); + [notify] void didLoadResourceFromMemoryCache(out double time, out unsigned long frameID, out Object resource); + [notify] void setOverrideContent(out long identifier, out String sourceString, out String type); + [notify] void didCommitLoad(out unsigned long frameID); + [notify] void frameDetachedFromParent(out unsigned long frameID); + + [notify] void didCreateWebSocket(out unsigned long identifier, out String requestURL); + [notify] void willSendWebSocketHandshakeRequest(out unsigned long identifier, out double time, out Object request); + [notify] void didReceiveWebSocketHandshakeResponse(out unsigned long identifier, out double time, out Object response); + [notify] void didCloseWebSocket(out unsigned long identifier, out double time); + #if defined(ENABLE_JAVASCRIPT_DEBUGGER) && ENABLE_JAVASCRIPT_DEBUGGER [handler=Backend] void enableDebugger(in boolean always); [handler=Controller] void disableDebugger(in boolean always); @@ -135,8 +152,8 @@ module core { [handler=Debug] void editScriptSource(in String sourceID, in String newContent, out boolean success, out String result, out Value newCallFrames); [handler=Debug] void getScriptSource(in String sourceID, out String scriptSource); - [handler=Controller] void setNativeBreakpoint(in Object breakpoint, out unsigned int breakpointId); - [handler=Controller] void removeNativeBreakpoint(in unsigned int breakpointId); + [handler=Controller] void setNativeBreakpoint(in Object breakpoint, out String breakpointId); + [handler=Controller] void removeNativeBreakpoint(in String breakpointId); [handler=Controller] void enableProfiler(in boolean always); [handler=Controller] void disableProfiler(in boolean always); @@ -171,10 +188,15 @@ module core { [handler=DOM] void performSearch(in String query, in boolean runSynchronously); [handler=DOM] void searchCanceled(); [handler=DOM] void pushNodeByPathToFrontend(in String path, out long nodeId); - [handler=DOM] void setDOMBreakpoint(in long nodeId, in long type); - [handler=DOM] void removeDOMBreakpoint(in long nodeId, in long type); + + [handler=Controller] void setConsoleMessagesEnabled(in boolean enabled, out boolean newState); + [notify] void addConsoleMessage(out Object messageObj); + [notify] void updateConsoleMessageExpiredCount(out unsigned long count); + [notify] void updateConsoleMessageRepeatCount(out unsigned long count); [handler=Controller] void clearConsoleMessages(); + [notify] void consoleMessagesCleared(); + [handler=Controller] void highlightDOMNode(in long nodeId); [handler=Controller] void hideDOMNodeHighlight(); [handler=Controller] void openInInspectedWindow(in String url); @@ -185,7 +207,7 @@ module core { [handler=DOM] void getComputedStyle(in long nodeId, out Value style); [handler=DOM] void getStyleSheet(in long styleSheetId, out Object styleSheet); [handler=DOM] void getStyleSourceData(in long styleSheetId, out Object styleSourceData); - [handler=DOM] void applyStyleText(in long styleId, in String styleText, in String propertyName, out boolean success, out Value style, out Array changedProperties); + [handler=DOM] void applyStyleText(in long styleId, in String styleText, in String propertyName, out boolean success, out Value style); [handler=DOM] void setStyleText(in long styleId, in String styleText, out boolean success); [handler=DOM] void setStyleProperty(in long styleId, in String name, in String value, out boolean success); [handler=DOM] void toggleStyleEnabled(in long styleId, in String propertyName, in boolean disabled, out Value style); diff --git a/WebCore/inspector/InspectorApplicationCacheAgent.cpp b/WebCore/inspector/InspectorApplicationCacheAgent.cpp index 918643b..ced61c5 100644 --- a/WebCore/inspector/InspectorApplicationCacheAgent.cpp +++ b/WebCore/inspector/InspectorApplicationCacheAgent.cpp @@ -48,7 +48,7 @@ InspectorApplicationCacheAgent::InspectorApplicationCacheAgent(InspectorControll void InspectorApplicationCacheAgent::didReceiveManifestResponse(unsigned long identifier, const ResourceResponse& response) { - m_inspectorController->didReceiveResponse(identifier, response); + m_inspectorController->didReceiveResponse(identifier, 0, response); } void InspectorApplicationCacheAgent::updateApplicationCacheStatus(ApplicationCacheHost::Status status) diff --git a/WebCore/inspector/InspectorCSSAgent.cpp b/WebCore/inspector/InspectorCSSAgent.cpp new file mode 100644 index 0000000..e0fab85 --- /dev/null +++ b/WebCore/inspector/InspectorCSSAgent.cpp @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorCSSAgent.h" + +#if ENABLE(INSPECTOR) + +#include "CSSComputedStyleDeclaration.h" +#include "CSSMutableStyleDeclaration.h" +#include "CSSPropertyNames.h" +#include "CSSPropertySourceData.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSStyleRule.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "DOMWindow.h" +#include "HTMLHeadElement.h" +#include "InspectorDOMAgent.h" +#include "InspectorFrontend.h" +#include "InspectorValues.h" +#include "Node.h" +#include "StyleSheetList.h" + +#include <wtf/HashSet.h> +#include <wtf/ListHashSet.h> +#include <wtf/Vector.h> +#include <wtf/text/CString.h> + + +// cssProperty = { +// name : <string>, +// value : <string>, +// priority : <string>, // "" for non-parsedOk properties +// implicit : <boolean>, +// parsedOk : <boolean>, // whether property is understood by WebCore +// status : <string>, // "disabled" | "active" | "inactive" | "style" +// shorthandName : <string>, +// startOffset : <number>, // Optional - property text start offset in enclosing style declaration. Absent for computed styles and such. +// endOffset : <number>, // Optional - property text end offset in enclosing style declaration. Absent for computed styles and such. +// } +// +// status: +// "disabled" == property disabled by user +// "active" == property participates in the computed style calculation +// "inactive" == property does no participate in the computed style calculation (i.e. overridden by a subsequent property with the same name) +// "style" == property is active and originates from the WebCore CSSStyleDeclaration rather than CSS source code (e.g. implicit longhand properties) +// +// +// cssStyle = { +// styleId : <number>, // Optional +// styleSheetId : <number>, // Parent: -1 for inline styles (<foo style="..">) +// cssProperties : [ +// #cssProperty, +// ... +// #cssProperty +// ], +// shorthandValues : { +// shorthandName1 : shorthandValue1, +// shorthandName2 : shorthandValue2 +// }, +// cssText : <string>, // declaration text +// properties : { } // ??? +// } +// +// // TODO: +// // - convert disabledProperties to enabled flag. +// // - convert width, height to properties +// +// cssRule = { +// ruleId : <number>, +// selectorText : <string> +// sourceURL : <string> +// sourceLine : <string> +// styleSheetId : <number> // also found in style +// origin : <string> // "" || "user-agent" || "user" || "inspector" +// style : #cssStyle +// } +// +// // TODO: +// // - fix origin +// // - add sourceURL +// // - fix parentStyleSheetId +// +// cssStyleSheet = { +// styleSheetId : <number> +// href : <string> +// title : <string> +// disabled : <boolean> +// documentNodeId : <number> +// rules : [ +// #cssRule, +// ... +// #cssRule +// ] +// } + +namespace WebCore { + +// static +PassRefPtr<InspectorObject> InspectorCSSAgent::buildObjectForStyle(CSSStyleDeclaration* style, const String& fullStyleId, CSSStyleSourceData* sourceData) +{ + RefPtr<InspectorObject> result = InspectorObject::create(); + if (!fullStyleId.isEmpty()) + result->setString("id", fullStyleId); + + result->setString("width", style->getPropertyValue("width")); + result->setString("height", style->getPropertyValue("height")); + Vector<CSSPropertySourceData>* propertyData = 0; + + if (sourceData) { + result->setNumber("startOffset", sourceData->styleBodyRange.start); + result->setNumber("endOffset", sourceData->styleBodyRange.end); + propertyData = &sourceData->propertyData; + } + populateObjectWithStyleProperties(style, result.get(), propertyData); + + return result.release(); +} + +// static +CSSStyleSheet* InspectorCSSAgent::parentStyleSheet(StyleBase* styleBase) +{ + if (!styleBase) + return 0; + + StyleSheet* styleSheet = styleBase->stylesheet(); + if (styleSheet && styleSheet->isCSSStyleSheet()) + return static_cast<CSSStyleSheet*>(styleSheet); + + return 0; +} + +// static +CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(StyleBase* styleBase) +{ + if (!styleBase->isStyleRule()) + return 0; + CSSRule* rule = static_cast<CSSRule*>(styleBase); + if (rule->type() != CSSRule::STYLE_RULE) + return 0; + return static_cast<CSSStyleRule*>(rule); +} + + +InspectorCSSAgent::InspectorCSSAgent(InspectorDOMAgent* domAgent, InspectorFrontend* frontend) + : m_domAgent(domAgent) + , m_frontend(frontend) + , m_lastStyleSheetId(1) + , m_lastRuleId(1) + , m_lastStyleId(1) +{ + m_domAgent->setDOMListener(this); +} + +InspectorCSSAgent::~InspectorCSSAgent() +{ + reset(); +} + +void InspectorCSSAgent::reset() +{ + m_domAgent->setDOMListener(0); +} + +void InspectorCSSAgent::getMatchedRulesForNode2(long nodeId, RefPtr<InspectorArray>* result) +{ + Element* element = elementForId(nodeId); + if (!element) + return; + + CSSStyleSelector* selector = element->ownerDocument()->styleSelector(); + RefPtr<CSSRuleList> matchedRules = selector->styleRulesForElement(element, false, true); + *result = buildArrayForRuleList(matchedRules.get()); +} + +void InspectorCSSAgent::getMatchedPseudoRulesForNode2(long nodeId, RefPtr<InspectorArray>* result) +{ + Element* element = elementForId(nodeId); + if (!element) + return; + + CSSStyleSelector* selector = element->ownerDocument()->styleSelector(); + for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) { + RefPtr<CSSRuleList> matchedRules = selector->pseudoStyleRulesForElement(element, pseudoId, false, true); + if (matchedRules && matchedRules->length()) { + RefPtr<InspectorObject> pseudoStyles = InspectorObject::create(); + pseudoStyles->setNumber("pseudoId", static_cast<int>(pseudoId)); + pseudoStyles->setArray("rules", buildArrayForRuleList(matchedRules.get())); + (*result)->pushObject(pseudoStyles.release()); + } + } +} + +void InspectorCSSAgent::getAttributeStylesForNode2(long nodeId, RefPtr<InspectorValue>* result) +{ + Element* element = elementForId(nodeId); + if (!element) + return; + + *result = buildObjectForAttributeStyles(element); +} + +void InspectorCSSAgent::getInlineStyleForNode2(long nodeId, RefPtr<InspectorValue>* style) +{ + Element* element = elementForId(nodeId); + if (!element) + return; + + InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element); + if (!styleSheet) + return; + + *style = styleSheet->buildObjectForStyle(element->style()); +} + +void InspectorCSSAgent::getComputedStyleForNode2(long nodeId, RefPtr<InspectorValue>* style) +{ + Element* element = elementForId(nodeId); + if (!element) + return; + + DOMWindow* defaultView = element->ownerDocument()->defaultView(); + if (!defaultView) + return; + + *style = buildObjectForStyle(defaultView->getComputedStyle(element, "").get(), ""); +} + +void InspectorCSSAgent::getInheritedStylesForNode2(long nodeId, RefPtr<InspectorArray>* style) +{ + Element* element = elementForId(nodeId); + if (!element) { + *style = InspectorArray::create(); + return; + } + RefPtr<InspectorArray> inheritedStyles = InspectorArray::create(); + Element* parentElement = element->parentElement(); + while (parentElement) { + RefPtr<InspectorObject> parentStyle = InspectorObject::create(); + if (parentElement->style() && parentElement->style()->length()) { + InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element); + if (styleSheet) + parentStyle->setObject("inlineStyle", styleSheet->buildObjectForStyle(styleSheet->styleForId("0"))); + } + + CSSStyleSelector* parentSelector = parentElement->ownerDocument()->styleSelector(); + RefPtr<CSSRuleList> parentMatchedRules = parentSelector->styleRulesForElement(parentElement, false, true); + parentStyle->setArray("matchedCSSRules", buildArrayForRuleList(parentMatchedRules.get())); + inheritedStyles->pushObject(parentStyle.release()); + parentElement = parentElement->parentElement(); + } + *style = inheritedStyles.release(); +} + +void InspectorCSSAgent::getAllStyles2(RefPtr<InspectorArray>* styles) +{ + const ListHashSet<RefPtr<Document> >& documents = m_domAgent->documents(); + for (ListHashSet<RefPtr<Document> >::const_iterator it = documents.begin(); it != documents.end(); ++it) { + StyleSheetList* list = (*it)->styleSheets(); + for (unsigned i = 0; i < list->length(); ++i) { + StyleSheet* styleSheet = list->item(i); + if (styleSheet->isCSSStyleSheet()) { + InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(static_cast<CSSStyleSheet*>(styleSheet)); + (*styles)->pushString(inspectorStyleSheet->id()); + } + } + } +} + +void InspectorCSSAgent::getStyleSheet2(const String& styleSheetId, RefPtr<InspectorValue>* styleSheetObject) +{ + InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(styleSheetId); + if (!inspectorStyleSheet) + return; + + *styleSheetObject = inspectorStyleSheet->buildObjectForStyleSheet(); +} + +void InspectorCSSAgent::setStyleSheetText2(const String& styleSheetId, const String& text) +{ + InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(styleSheetId); + if (!inspectorStyleSheet) + return; + + inspectorStyleSheet->setText(text); +} + +void InspectorCSSAgent::setStyleText2(const String& fullStyleId, const String& text, RefPtr<InspectorValue>* result) +{ + Vector<String> idParts; + fullStyleId.split(':', idParts); + ASSERT(idParts.size() == 2); + + InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(idParts.at(0)); + if (!inspectorStyleSheet) + return; + + if (!inspectorStyleSheet->setStyleText(idParts.at(1), text)) + return; + + *result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(idParts.at(1))); +} + +void InspectorCSSAgent::toggleProperty2(const String&, long, bool) +{ + // FIXME(apavlov): implement +} + +void InspectorCSSAgent::setRuleSelector2(const String& fullRuleId, const String& selector, RefPtr<InspectorValue>* result) +{ + Vector<String> idParts; + fullRuleId.split(':', idParts); + ASSERT(idParts.size() == 2); + + InspectorStyleSheet* inspectorStyleSheet = styleSheetForId(idParts.at(0)); + if (!inspectorStyleSheet) + return; + + const String& ruleId = idParts.at(1); + bool success = inspectorStyleSheet->setRuleSelector(ruleId, selector); + if (!success) + return; + + *result = inspectorStyleSheet->buildObjectForRule(inspectorStyleSheet->ruleForId(ruleId)); +} + +void InspectorCSSAgent::addRule2(const long contextNodeId, const String& selector, RefPtr<InspectorValue>* result) +{ + Node* node = m_domAgent->nodeForId(contextNodeId); + if (!node) + return; + + InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(node->document(), true); + CSSStyleRule* newRule = inspectorStyleSheet->addRule(selector); + if (newRule) + *result = inspectorStyleSheet->buildObjectForRule(newRule); +} + +void InspectorCSSAgent::getSupportedCSSProperties(RefPtr<InspectorArray>* cssProperties) +{ + RefPtr<InspectorArray> properties = InspectorArray::create(); + for (int i = 0; i < numCSSProperties; ++i) + properties->pushString(propertyNameStrings[i]); + + *cssProperties = properties.release(); +} + +// static +Element* InspectorCSSAgent::inlineStyleElement(CSSStyleDeclaration* style) +{ + if (!style || !style->isMutableStyleDeclaration()) + return 0; + Node* node = static_cast<CSSMutableStyleDeclaration*>(style)->node(); + if (!node || !node->isStyledElement() || static_cast<StyledElement*>(node)->getInlineStyleDecl() != style) + return 0; + return static_cast<Element*>(node); +} + +// static +void InspectorCSSAgent::populateObjectWithStyleProperties(CSSStyleDeclaration* style, InspectorObject* result, Vector<CSSPropertySourceData>* propertyData) +{ + RefPtr<InspectorArray> properties = InspectorArray::create(); + RefPtr<InspectorObject> shorthandValues = InspectorObject::create(); + HashMap<String, RefPtr<InspectorObject> > propertyNameToPreviousActiveProperty; + HashSet<String> foundShorthands; + HashSet<String> sourcePropertyNames; + if (propertyData) { + for (Vector<CSSPropertySourceData>::const_iterator it = propertyData->begin(); it != propertyData->end(); ++it) { + const CSSPropertySourceData& propertyEntry = *it; + RefPtr<InspectorObject> property = InspectorObject::create(); + properties->pushObject(property); + const String& name = propertyEntry.name; + sourcePropertyNames.add(name); + property->setString("name", name); + property->setString("value", propertyEntry.value); + property->setString("priority", propertyEntry.important ? "important" : ""); + property->setString("status", "active"); + property->setBoolean("parsedOk", propertyEntry.parsedOk); + property->setNumber("startOffset", propertyEntry.range.start); + property->setNumber("endOffset", propertyEntry.range.end); + if (propertyEntry.parsedOk) { + property->setBoolean("implicit", false); + String shorthand = style->getPropertyShorthand(name); + property->setString("shorthandName", shorthand); + if (!shorthand.isEmpty() && !foundShorthands.contains(shorthand)) { + foundShorthands.add(shorthand); + shorthandValues->setString(shorthand, shorthandValue(style, shorthand)); + } + } else { + property->setBoolean("implicit", false); + property->setString("shorthandName", ""); + } + HashMap<String, RefPtr<InspectorObject> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(name); + if (activeIt != propertyNameToPreviousActiveProperty.end()) { + activeIt->second->setString("status", "inactive"); + activeIt->second->setString("shorthandName", ""); + } + propertyNameToPreviousActiveProperty.set(name, property); + } + } + + for (int i = 0, size = style->length(); i < size; ++i) { + String name = style->item(i); + if (sourcePropertyNames.contains(name)) + continue; + + sourcePropertyNames.add(name); + RefPtr<InspectorObject> property = InspectorObject::create(); + properties->pushObject(property); + property->setString("name", name); + property->setString("value", style->getPropertyValue(name)); + property->setString("priority", style->getPropertyPriority("name")); + property->setBoolean("implicit", style->isPropertyImplicit(name)); + property->setBoolean("parsedOk", true); + property->setString("status", "style"); + String shorthand = style->getPropertyShorthand(name); + property->setString("shorthandName", shorthand); + if (!shorthand.isEmpty() && !foundShorthands.contains(shorthand)) { + foundShorthands.add(shorthand); + shorthandValues->setString(shorthand, shorthandValue(style, shorthand)); + } + } + + result->setArray("properties", properties); + result->setObject("shorthandValues", shorthandValues); +} + +// static +String InspectorCSSAgent::shorthandValue(CSSStyleDeclaration* style, const String& shorthandProperty) +{ + String value = style->getPropertyValue(shorthandProperty); + if (value.isEmpty()) { + for (unsigned i = 0; i < style->length(); ++i) { + String individualProperty = style->item(i); + if (style->getPropertyShorthand(individualProperty) != shorthandProperty) + continue; + if (style->isPropertyImplicit(individualProperty)) + continue; + String individualValue = style->getPropertyValue(individualProperty); + if (individualValue == "initial") + continue; + if (value.length()) + value.append(" "); + value.append(individualValue); + } + } + return value; +} + +// static +String InspectorCSSAgent::shorthandPriority(CSSStyleDeclaration* style, const String& shorthandProperty) +{ + String priority = style->getPropertyPriority(shorthandProperty); + if (priority.isEmpty()) { + for (unsigned i = 0; i < style->length(); ++i) { + String individualProperty = style->item(i); + if (style->getPropertyShorthand(individualProperty) != shorthandProperty) + continue; + priority = style->getPropertyPriority(individualProperty); + break; + } + } + return priority; +} + + +// static +Vector<String> InspectorCSSAgent::longhandProperties(CSSStyleDeclaration* style, const String& shorthandProperty) +{ + Vector<String> properties; + HashSet<String> foundProperties; + for (unsigned i = 0; i < style->length(); ++i) { + String individualProperty = style->item(i); + if (foundProperties.contains(individualProperty) || style->getPropertyShorthand(individualProperty) != shorthandProperty) + continue; + + foundProperties.add(individualProperty); + properties.append(individualProperty); + } + return properties; +} + +InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element) +{ + NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element); + if (it == m_nodeToInspectorStyleSheet.end()) { + CSSStyleDeclaration* style = element->isStyledElement() ? element->style() : 0; + if (!style) + return 0; + + String newStyleSheetId = String::number(m_lastStyleSheetId++); + RefPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, ""); + m_idToInspectorStyleSheet.set(newStyleSheetId, inspectorStyleSheet); + m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet); + return inspectorStyleSheet.get(); + } + + return it->second.get(); +} + +Element* InspectorCSSAgent::elementForId(long nodeId) +{ + Node* node = m_domAgent->nodeForId(nodeId); + return (!node || node->nodeType() != Node::ELEMENT_NODE) ? 0 : static_cast<Element*>(node); +} + +InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet) +{ + RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet); + if (!inspectorStyleSheet) { + String id = String::number(m_lastStyleSheetId++); + inspectorStyleSheet = InspectorStyleSheet::create(id, styleSheet, detectOrigin(styleSheet, styleSheet->document()), m_domAgent->documentURLString(styleSheet->document())); + m_idToInspectorStyleSheet.set(id, inspectorStyleSheet); + m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet); + } + return inspectorStyleSheet.get(); +} + +InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent) +{ + if (!document) { + ASSERT(!createIfAbsent); + return 0; + } + + RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToInspectorStyleSheet.get(document); + if (inspectorStyleSheet || !createIfAbsent) + return inspectorStyleSheet.get(); + + ExceptionCode ec = 0; + RefPtr<Element> styleElement = document->createElement("style", ec); + if (!ec) + styleElement->setAttribute("type", "text/css", ec); + if (!ec) + document->head()->appendChild(styleElement, ec); + if (ec) + return 0; + StyleSheetList* styleSheets = document->styleSheets(); + StyleSheet* styleSheet = styleSheets->item(styleSheets->length() - 1); + if (!styleSheet->isCSSStyleSheet()) + return 0; + CSSStyleSheet* cssStyleSheet = static_cast<CSSStyleSheet*>(styleSheet); + String id = String::number(m_lastStyleSheetId++); + inspectorStyleSheet = InspectorStyleSheet::create(id, cssStyleSheet, "inspector", m_domAgent->documentURLString(document)); + m_idToInspectorStyleSheet.set(id, inspectorStyleSheet); + m_cssStyleSheetToInspectorStyleSheet.set(cssStyleSheet, inspectorStyleSheet); + m_documentToInspectorStyleSheet.set(document, inspectorStyleSheet); + return inspectorStyleSheet.get(); +} + +InspectorStyleSheet* InspectorCSSAgent::styleSheetForId(const String& styleSheetId) +{ + IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId); + return it == m_idToInspectorStyleSheet.end() ? 0 : it->second.get(); +} + +String InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument) +{ + DEFINE_STATIC_LOCAL(String, userAgent, ("user-agent")); + DEFINE_STATIC_LOCAL(String, user, ("user")); + DEFINE_STATIC_LOCAL(String, inspector, ("inspector")); + + String origin(""); + if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty()) + origin = userAgent; + else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->nodeName() == "#document") + origin = user; + else { + InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false); + if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet()) + origin = inspector; + } + return origin; +} + +PassRefPtr<InspectorArray> InspectorCSSAgent::buildArrayForRuleList(CSSRuleList* ruleList) +{ + RefPtr<InspectorArray> result = InspectorArray::create(); + if (!ruleList) + return result.release(); + + for (unsigned i = 0, size = ruleList->length(); i < size; ++i) { + CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i)); + if (!rule) + continue; + + InspectorStyleSheet* styleSheet = bindStyleSheet(parentStyleSheet(rule)); + if (styleSheet) + result->pushObject(styleSheet->buildObjectForRule(rule)); + } + return result.release(); +} + +PassRefPtr<InspectorObject> InspectorCSSAgent::buildObjectForAttributeStyles(Element* element) +{ + RefPtr<InspectorObject> styleAttributes = InspectorObject::create(); + NamedNodeMap* attributes = element->attributes(); + for (unsigned i = 0; attributes && i < attributes->length(); ++i) { + Attribute* attribute = attributes->attributeItem(i); + if (attribute->style()) { + String attributeName = attribute->localName(); + styleAttributes->setObject(attributeName.utf8().data(), buildObjectForStyle(attribute->style(), "")); + } + } + + return styleAttributes; +} + +void InspectorCSSAgent::didRemoveDocument(Document* document) +{ + m_documentToInspectorStyleSheet.remove(document); +} + +void InspectorCSSAgent::didRemoveDOMNode(Node* node) +{ + NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node); + if (it == m_nodeToInspectorStyleSheet.end()) + return; + + m_idToInspectorStyleSheet.remove(it->second->id()); + m_nodeToInspectorStyleSheet.remove(node); +} + +} // namespace WebCore + +#endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorCSSAgent.h b/WebCore/inspector/InspectorCSSAgent.h new file mode 100644 index 0000000..eb6013b --- /dev/null +++ b/WebCore/inspector/InspectorCSSAgent.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef InspectorCSSAgent_h +#define InspectorCSSAgent_h + +#include "Document.h" +#include "InspectorDOMAgent.h" +#include "InspectorStyleSheet.h" +#include "InspectorValues.h" +#include "PlatformString.h" + +#include <wtf/HashMap.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class CSSRule; +class CSSRuleList; +class CSSStyleDeclaration; +class CSSStyleRule; +class CSSStyleSheet; +class Element; +class InspectorFrontend; +class NameNodeMap; +class Node; + +#if ENABLE(INSPECTOR) + +class InspectorCSSAgent : public RefCounted<InspectorCSSAgent>, public InspectorDOMAgent::DOMListener { +public: + static PassRefPtr<InspectorCSSAgent> create(InspectorDOMAgent* domAgent, InspectorFrontend* frontend) + { + return adoptRef(new InspectorCSSAgent(domAgent, frontend)); + } + + static PassRefPtr<InspectorObject> buildObjectForStyle(CSSStyleDeclaration*, const String& fullStyleId, CSSStyleSourceData* = 0); + static CSSStyleSheet* parentStyleSheet(StyleBase*); + static CSSStyleRule* asCSSStyleRule(StyleBase*); + + InspectorCSSAgent(InspectorDOMAgent* domAgent, InspectorFrontend* frontend); + ~InspectorCSSAgent(); + + void reset(); + void getMatchedRulesForNode2(long nodeId, RefPtr<InspectorArray>* rules); + void getMatchedPseudoRulesForNode2(long nodeId, RefPtr<InspectorArray>* rules); + void getAttributeStylesForNode2(long nodeId, RefPtr<InspectorValue>* styles); + void getInlineStyleForNode2(long nodeId, RefPtr<InspectorValue>* style); + void getComputedStyleForNode2(long nodeId, RefPtr<InspectorValue>* style); + void getInheritedStylesForNode2(long nodeId, RefPtr<InspectorArray>* result); + void getAllStyles2(RefPtr<InspectorArray>* styles); + void getStyleSheet2(const String& styleSheetId, RefPtr<InspectorValue>* result); + void setStyleSheetText2(const String& styleSheetId, const String& text); + void setStyleText2(const String& styleId, const String& text, RefPtr<InspectorValue>* result); + void toggleProperty2(const String& styleId, long propertyOrdinal, bool disabled); + void setRuleSelector2(const String& ruleId, const String& selector, RefPtr<InspectorValue>* result); + void addRule2(const long contextNodeId, const String& selector, RefPtr<InspectorValue>* result); + void getSupportedCSSProperties(RefPtr<InspectorArray>* result); + +private: + typedef HashMap<String, RefPtr<InspectorStyleSheet> > IdToInspectorStyleSheet; + typedef HashMap<CSSStyleSheet*, RefPtr<InspectorStyleSheet> > CSSStyleSheetToInspectorStyleSheet; + typedef HashMap<Node*, RefPtr<InspectorStyleSheetForInlineStyle> > NodeToInspectorStyleSheet; // for bogus "stylesheets" with inline node styles + typedef HashMap<RefPtr<Document>, RefPtr<InspectorStyleSheet> > DocumentToViaInspectorStyleSheet; // "via inspector" stylesheets + + static Element* inlineStyleElement(CSSStyleDeclaration*); + static void populateObjectWithStyleProperties(CSSStyleDeclaration*, InspectorObject* result, Vector<CSSPropertySourceData>* propertyData); + static String shorthandValue(CSSStyleDeclaration*, const String& shorthandProperty); + static String shorthandPriority(CSSStyleDeclaration*, const String& shorthandProperty); + static Vector<String> longhandProperties(CSSStyleDeclaration*, const String& shorthandProperty); + + InspectorStyleSheetForInlineStyle* asInspectorStyleSheet(Element* element); + Element* elementForId(long nodeId); + + InspectorStyleSheet* bindStyleSheet(CSSStyleSheet*); + InspectorStyleSheet* viaInspectorStyleSheet(Document*, bool createIfAbsent); + InspectorStyleSheet* styleSheetForId(const String& styleSheetId); + String detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument); + + PassRefPtr<InspectorArray> buildArrayForRuleList(CSSRuleList* ruleList); + PassRefPtr<InspectorObject> buildObjectForAttributeStyles(Element* element); + + // InspectorDOMAgent::DOMListener interface + virtual void didRemoveDocument(Document*); + virtual void didRemoveDOMNode(Node*); + + RefPtr<InspectorDOMAgent> m_domAgent; + InspectorFrontend* m_frontend; + + IdToInspectorStyleSheet m_idToInspectorStyleSheet; + CSSStyleSheetToInspectorStyleSheet m_cssStyleSheetToInspectorStyleSheet; + NodeToInspectorStyleSheet m_nodeToInspectorStyleSheet; + DocumentToViaInspectorStyleSheet m_documentToInspectorStyleSheet; + + long m_lastStyleSheetId; + long m_lastRuleId; + long m_lastStyleId; +}; + +#endif + +} // namespace WebCore + +#endif // !defined(InspectorCSSAgent_h) diff --git a/WebCore/inspector/InspectorCSSStore.cpp b/WebCore/inspector/InspectorCSSStore.cpp index b019860..e0b658f 100644 --- a/WebCore/inspector/InspectorCSSStore.cpp +++ b/WebCore/inspector/InspectorCSSStore.cpp @@ -158,7 +158,7 @@ String InspectorCSSStore::inlineStyleSheetText(CSSStyleSheet* styleSheet) // All ranges are: [start; end) (start - inclusive, end - exclusive). -bool InspectorCSSStore::getStyleSourceData(CSSStyleDeclaration* style, RefPtr<CSSStyleSourceData>* result) +bool InspectorCSSStore::getRuleSourceData(CSSStyleDeclaration* style, RefPtr<CSSRuleSourceData>* result) { if (!style) return false; @@ -166,11 +166,13 @@ bool InspectorCSSStore::getStyleSourceData(CSSStyleDeclaration* style, RefPtr<CS Element* element = inlineStyleElement(style); if (element) { // Inline: style="...". + RefPtr<CSSRuleSourceData> ruleSourceData = CSSRuleSourceData::create(); RefPtr<CSSStyleSourceData> styleSourceData = CSSStyleSourceData::create(); bool success = getStyleAttributeRanges(element, &styleSourceData); if (!success) return false; - *result = styleSourceData; + ruleSourceData->styleSourceData = styleSourceData.release(); + *result = ruleSourceData; return true; } @@ -178,7 +180,7 @@ bool InspectorCSSStore::getStyleSourceData(CSSStyleDeclaration* style, RefPtr<CS if (!styleSheet) return false; - Vector<RefPtr<CSSStyleSourceData> >* rangesVector = 0; + Vector<RefPtr<CSSRuleSourceData> >* rangesVector = 0; StyleSheetToOffsetsMap::iterator it = m_styleSheetToOffsets.find(styleSheet); if (it == m_styleSheetToOffsets.end()) { String text = styleSheetText(bindStyleSheet(styleSheet)); @@ -187,7 +189,7 @@ bool InspectorCSSStore::getStyleSourceData(CSSStyleDeclaration* style, RefPtr<CS CSSParser p; StyleRuleRangeMap ruleRangeMap; p.parseSheet(newStyleSheet.get(), text, 0, &ruleRangeMap); - rangesVector = new Vector<RefPtr<CSSStyleSourceData> >; + rangesVector = new Vector<RefPtr<CSSRuleSourceData> >; extractRanges(newStyleSheet.get(), ruleRangeMap, rangesVector); m_styleSheetToOffsets.set(styleSheet, rangesVector); } @@ -211,7 +213,7 @@ bool InspectorCSSStore::getStyleSourceData(CSSStyleDeclaration* style, RefPtr<CS return false; } -void InspectorCSSStore::extractRanges(CSSStyleSheet* styleSheet, const StyleRuleRangeMap& ruleRangeMap, Vector<RefPtr<CSSStyleSourceData> >* rangesVector) +void InspectorCSSStore::extractRanges(CSSStyleSheet* styleSheet, const StyleRuleRangeMap& ruleRangeMap, Vector<RefPtr<CSSRuleSourceData> >* rangesVector) { for (unsigned i = 0, length = styleSheet->length(); i < length; ++i) { CSSStyleRule* rule = asCSSStyleRule(styleSheet->item(i)); @@ -237,7 +239,7 @@ bool InspectorCSSStore::getStyleAttributeRanges(Node* node, RefPtr<CSSStyleSourc RefPtr<CSSMutableStyleDeclaration> tempDeclaration = CSSMutableStyleDeclaration::create(); CSSParser p; - p.parseDeclaration(tempDeclaration.get(), styleText, result->get()); + p.parseDeclaration(tempDeclaration.get(), styleText, result); return true; } diff --git a/WebCore/inspector/InspectorCSSStore.h b/WebCore/inspector/InspectorCSSStore.h index 9b329df..25a9d15 100644 --- a/WebCore/inspector/InspectorCSSStore.h +++ b/WebCore/inspector/InspectorCSSStore.h @@ -55,7 +55,7 @@ typedef HashMap<CSSStyleDeclaration*, long> StyleToIdMap; typedef HashMap<long, RefPtr<CSSStyleDeclaration> > IdToStyleMap; typedef HashMap<CSSStyleRule*, long> RuleToIdMap; typedef HashMap<long, RefPtr<CSSStyleRule> > IdToRuleMap; -typedef HashMap<CSSStyleSheet*, Vector<RefPtr<CSSStyleSourceData> >* > StyleSheetToOffsetsMap; +typedef HashMap<CSSStyleSheet*, Vector<RefPtr<CSSRuleSourceData> >* > StyleSheetToOffsetsMap; typedef HashMap<CSSStyleSheet*, long> StyleSheetToIdMap; typedef HashMap<long, RefPtr<CSSStyleSheet> > IdToStyleSheetMap; typedef HashMap<long, String> IdToStyleSheetTextMap; @@ -71,7 +71,7 @@ public: InspectorCSSStore(InspectorController* inspectorController); ~InspectorCSSStore(); void reset(); - bool getStyleSourceData(CSSStyleDeclaration*, RefPtr<CSSStyleSourceData>* result); + bool getRuleSourceData(CSSStyleDeclaration*, RefPtr<CSSRuleSourceData>* result); CSSStyleDeclaration* styleForId(long styleId); CSSStyleSheet* styleSheetForId(long styleSheetId); CSSStyleRule* ruleForId(long styleRuleId); @@ -88,7 +88,7 @@ private: static CSSStyleRule* asCSSStyleRule(StyleBase*); String inlineStyleSheetText(CSSStyleSheet*); bool resourceStyleSheetText(CSSStyleSheet*, String* result); - void extractRanges(CSSStyleSheet*, const StyleRuleRangeMap&, Vector<RefPtr<CSSStyleSourceData> >* rangesVector); + void extractRanges(CSSStyleSheet*, const StyleRuleRangeMap&, Vector<RefPtr<CSSRuleSourceData> >* rangesVector); bool getStyleAttributeRanges(Node* parentNode, RefPtr<CSSStyleSourceData>* result); StyleToIdMap m_styleToId; diff --git a/WebCore/inspector/InspectorClient.cpp b/WebCore/inspector/InspectorClient.cpp new file mode 100644 index 0000000..434d697 --- /dev/null +++ b/WebCore/inspector/InspectorClient.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorClient.h" + +#if ENABLE(INSPECTOR) + +#include "Frame.h" +#include "Page.h" +#include "ScriptController.h" +#include "ScriptSourceCode.h" + +namespace WebCore { + +bool InspectorClient::doDispatchMessageOnFrontendPage(Page* frontendPage, const String& message) +{ + if (!frontendPage) + return false; + + Frame* frame = frontendPage->mainFrame(); + if (!frame) + return false; + + ScriptController* scriptController = frame->script(); + if (!scriptController) + return false; + + String dispatchToFrontend("WebInspector.dispatchMessageFromBackend("); + dispatchToFrontend += message; + dispatchToFrontend += ");"; + + // Do not call executeInWorld here since it will end up calling Document::updateStyleForAllDocuments(). + // As a result we might re-enter CSSStyleSelector::styleForElement() which is terrible. + scriptController->evaluate(ScriptSourceCode(dispatchToFrontend)); + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorClient.h b/WebCore/inspector/InspectorClient.h index bb71b13..0c78246 100644 --- a/WebCore/inspector/InspectorClient.h +++ b/WebCore/inspector/InspectorClient.h @@ -37,7 +37,7 @@ class Page; class InspectorClient { public: - virtual ~InspectorClient() { } + virtual ~InspectorClient() { } virtual void inspectorDestroyed() = 0; @@ -55,6 +55,8 @@ public: // However, there are some inspector controller states that should survive navigation (such as tracking resources // or recording timeline). Following callbacks allow embedders to track these states. virtual void updateInspectorStateCookie(const String&) { }; + + bool doDispatchMessageOnFrontendPage(Page* frontendPage, const String& message); }; } // namespace WebCore diff --git a/WebCore/inspector/InspectorController.cpp b/WebCore/inspector/InspectorController.cpp index f2ee2c1..f99d487 100644 --- a/WebCore/inspector/InspectorController.cpp +++ b/WebCore/inspector/InspectorController.cpp @@ -66,8 +66,11 @@ #include "InspectorDebuggerAgent.h" #include "InspectorFrontend.h" #include "InspectorFrontendClient.h" +#include "InspectorInstrumentation.h" #include "InspectorProfilerAgent.h" #include "InspectorResource.h" +#include "InspectorResourceAgent.h" +#include "InspectorState.h" #include "InspectorStorageAgent.h" #include "InspectorTimelineAgent.h" #include "InspectorValues.h" @@ -85,14 +88,15 @@ #include "ScriptProfile.h" #include "ScriptProfiler.h" #include "ScriptSourceCode.h" -#include "ScriptString.h" +#include "ScriptState.h" #include "SecurityOrigin.h" #include "Settings.h" #include "SharedBuffer.h" #include "TextEncoding.h" #include "TextIterator.h" +#include "UserGestureIndicator.h" #include "WindowFeatures.h" -#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <wtf/CurrentTime.h> #include <wtf/ListHashSet.h> #include <wtf/RefCounted.h> @@ -115,57 +119,34 @@ using namespace std; namespace WebCore { -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 monitoringXHRStateName = "monitoringXHREnabled"; -static const char* const resourceTrackingEnabledStateName = "resourceTrackingEnabled"; -static const char* const searchingForNodeEnabledStateName = "searchingForNodeEnabled"; -static const char* const timelineProfilerEnabledStateName = "timelineProfilerEnabled"; -static const char* const pauseOnExceptionsStateStateName = "pauseOnExceptionsState"; - -static const char* const inspectorAttachedHeightName = "inspectorAttachedHeight"; +static const char* const domNativeBreakpointType = "DOM"; +static const char* const eventListenerNativeBreakpointType = "EventListener"; +static const char* const xhrNativeBreakpointType = "XHR"; +// FIXME: move last panel setting to the front-end +const char* const InspectorController::LastActivePanel = "lastActivePanel"; const char* const InspectorController::ElementsPanel = "elements"; const char* const InspectorController::ConsolePanel = "console"; const char* const InspectorController::ScriptsPanel = "scripts"; const char* const InspectorController::ProfilesPanel = "profiles"; +const unsigned InspectorController::defaultAttachedHeight = 300; -static int connectedFrontendCount = 0; - -const String& InspectorController::inspectorStartsAttachedSettingName() -{ - DEFINE_STATIC_LOCAL(String, settingName, ("inspectorStartsAttached")); - return settingName; -} - -static const unsigned defaultAttachedHeight = 300; -static const float minimumAttachedHeight = 250.0f; -static const float maximumAttachedHeightRatio = 0.75f; static const unsigned maximumConsoleMessages = 1000; static const unsigned expireConsoleMessagesStep = 100; -unsigned InspectorController::s_inspectorControllerCount = 0; - InspectorController::InspectorController(Page* page, InspectorClient* client) : m_inspectedPage(page) , m_client(client) , m_openingFrontend(false) , m_cssStore(new InspectorCSSStore(this)) + , m_loadEventTime(-1.0) + , m_domContentEventTime(-1.0) , m_expiredConsoleMessageCount(0) - , m_showAfterVisible(lastActivePanel) + , m_showAfterVisible(LastActivePanel) , m_sessionSettings(InspectorObject::create()) , m_groupLevel(0) - , m_searchingForNode(false) - , m_monitoringXHR(false) , m_previousMessage(0) - , m_resourceTrackingEnabled(false) , m_settingsLoaded(false) , m_inspectorBackend(InspectorBackend::create(this)) , m_inspectorBackendDispatcher(new InspectorBackendDispatcher(this)) @@ -176,9 +157,9 @@ InspectorController::InspectorController(Page* page, InspectorClient* client) , m_profilerAgent(InspectorProfilerAgent::create(this)) #endif { + m_state = new InspectorState(client); ASSERT_ARG(page, page); ASSERT_ARG(client, client); - ++s_inspectorControllerCount; } InspectorController::~InspectorController() @@ -190,9 +171,6 @@ InspectorController::~InspectorController() deleteAllValues(m_frameResources); - ASSERT(s_inspectorControllerCount); - --s_inspectorControllerCount; - releaseFrontendLifetimeAgents(); m_inspectorBackend->disconnectController(); @@ -223,27 +201,39 @@ bool InspectorController::enabled() const return m_inspectedPage->settings()->developerExtrasEnabled(); } -String InspectorController::setting(const String& key) const +bool InspectorController::inspectorStartsAttached() +{ + return m_state->getBoolean(InspectorState::inspectorStartsAttached); +} + +void InspectorController::setInspectorStartsAttached(bool attached) { - Settings::iterator it = m_settings.find(key); - if (it != m_settings.end()) - return it->second; + m_state->setBoolean(InspectorState::inspectorStartsAttached, attached); +} - String value; - m_client->populateSetting(key, &value); - m_settings.set(key, value); - return value; +void InspectorController::setInspectorAttachedHeight(long height) +{ + m_state->setLong(InspectorState::inspectorAttachedHeight, height); } -void InspectorController::setSetting(const String& key, const String& value) +int InspectorController::inspectorAttachedHeight() const { - m_settings.set(key, value); - m_client->storeSetting(key, value); + return m_state->getBoolean(InspectorState::inspectorAttachedHeight); +} + +bool InspectorController::searchingForNodeInPage() const +{ + return m_state->getBoolean(InspectorState::searchingForNode); +} + +bool InspectorController::resourceTrackingEnabled() const +{ + return m_state->getBoolean(InspectorState::resourceTrackingEnabled); } void InspectorController::saveApplicationSettings(const String& settings) { - setSetting(frontendSettingsSettingName, settings); + m_state->setString(InspectorState::frontendSettings, settings); } void InspectorController::saveSessionSettings(const String& settingsJSON) @@ -253,50 +243,24 @@ void InspectorController::saveSessionSettings(const String& settingsJSON) void InspectorController::getInspectorState(RefPtr<InspectorObject>* state) { - (*state)->setBoolean(monitoringXHRStateName, m_monitoringXHR); - (*state)->setBoolean(resourceTrackingEnabledStateName, m_resourceTrackingEnabled); #if ENABLE(JAVASCRIPT_DEBUGGER) if (m_debuggerAgent) - (*state)->setNumber(pauseOnExceptionsStateStateName, m_debuggerAgent->pauseOnExceptionsState()); + m_state->setLong(InspectorState::pauseOnExceptionsState, m_debuggerAgent->pauseOnExceptionsState()); #endif + *state = m_state->generateStateObjectForFrontend(); } -void InspectorController::updateInspectorStateCookie() +void InspectorController::restoreInspectorStateFromCookie(const String& inspectorStateCookie) { - RefPtr<InspectorObject> state = InspectorObject::create(); - state->setBoolean(monitoringXHRStateName, m_monitoringXHR); - state->setBoolean(resourceTrackingEnabledStateName, m_resourceTrackingEnabled); - state->setBoolean(timelineProfilerEnabledStateName, m_timelineAgent); - state->setBoolean(searchingForNodeEnabledStateName, m_searchingForNode); - 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); - inspectorState->getBoolean(searchingForNodeEnabledStateName, &m_searchingForNode); - - bool timelineProfilerEnabled = false; - inspectorState->getBoolean(timelineProfilerEnabledStateName, &timelineProfilerEnabled); - if (timelineProfilerEnabled) + m_state->restoreFromInspectorCookie(inspectorStateCookie); + if (m_state->getBoolean(InspectorState::timelineProfilerEnabled)) startTimelineProfiler(); - else - stopTimelineProfiler(); } void InspectorController::getSettings(RefPtr<InspectorObject>* settings) { *settings = InspectorObject::create(); - (*settings)->setString("application", setting(frontendSettingsSettingName)); + (*settings)->setString("application", m_state->getString(InspectorState::frontendSettings)); (*settings)->setString("session", m_sessionSettings->toJSONString()); } @@ -356,13 +320,32 @@ void InspectorController::hideHighlight() m_client->hideHighlight(); } +void InspectorController::setConsoleMessagesEnabled(bool enabled, bool* newState) +{ + *newState = enabled; + setConsoleMessagesEnabled(enabled); +} + +void InspectorController::setConsoleMessagesEnabled(bool enabled) +{ + m_state->setBoolean(InspectorState::consoleMessagesEnabled, enabled); + if (!enabled) + return; + + if (m_expiredConsoleMessageCount) + m_frontend->updateConsoleMessageExpiredCount(m_expiredConsoleMessageCount); + unsigned messageCount = m_consoleMessages.size(); + for (unsigned i = 0; i < messageCount; ++i) + m_consoleMessages[i]->addToFrontend(m_frontend.get(), m_injectedScriptHost.get()); +} + void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, ScriptCallStack* callStack, const String& message) { if (!enabled()) return; bool storeStackTrace = type == TraceMessageType || type == UncaughtExceptionMessageType || type == AssertMessageType; - addConsoleMessage(callStack->state(), new ConsoleMessage(source, type, level, message, callStack, m_groupLevel, storeStackTrace)); + addConsoleMessage(new ConsoleMessage(source, type, level, message, callStack, m_groupLevel, storeStackTrace)); } void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID) @@ -370,22 +353,22 @@ void InspectorController::addMessageToConsole(MessageSource source, MessageType if (!enabled()) return; - addConsoleMessage(0, new ConsoleMessage(source, type, level, message, lineNumber, sourceID, m_groupLevel)); + addConsoleMessage(new ConsoleMessage(source, type, level, message, lineNumber, sourceID, m_groupLevel)); } -void InspectorController::addConsoleMessage(ScriptState* scriptState, PassOwnPtr<ConsoleMessage> consoleMessage) +void InspectorController::addConsoleMessage(PassOwnPtr<ConsoleMessage> consoleMessage) { ASSERT(enabled()); ASSERT_ARG(consoleMessage, consoleMessage); - if (m_previousMessage && m_previousMessage->isEqual(scriptState, consoleMessage.get())) { + if (m_previousMessage && m_previousMessage->isEqual(consoleMessage.get())) { m_previousMessage->incrementCount(); - if (m_frontend) + if (m_state->getBoolean(InspectorState::consoleMessagesEnabled) && m_frontend) m_previousMessage->updateRepeatCountInConsole(m_frontend.get()); } else { m_previousMessage = consoleMessage.get(); m_consoleMessages.append(consoleMessage); - if (m_frontend) + if (m_state->getBoolean(InspectorState::consoleMessagesEnabled) && m_frontend) m_previousMessage->addToFrontend(m_frontend.get(), m_injectedScriptHost.get()); } @@ -412,7 +395,7 @@ void InspectorController::startGroup(MessageSource source, ScriptCallStack* call { ++m_groupLevel; - addConsoleMessage(callStack->state(), new ConsoleMessage(source, collapsed ? StartGroupCollapsedMessageType : StartGroupMessageType, LogMessageLevel, String(), callStack, m_groupLevel)); + addConsoleMessage(new ConsoleMessage(source, collapsed ? StartGroupCollapsedMessageType : StartGroupMessageType, LogMessageLevel, String(), callStack, m_groupLevel)); } void InspectorController::endGroup(MessageSource source, unsigned lineNumber, const String& sourceURL) @@ -422,7 +405,7 @@ void InspectorController::endGroup(MessageSource source, unsigned lineNumber, co --m_groupLevel; - addConsoleMessage(0, new ConsoleMessage(source, EndGroupMessageType, LogMessageLevel, String(), lineNumber, sourceURL, m_groupLevel)); + addConsoleMessage(new ConsoleMessage(source, EndGroupMessageType, LogMessageLevel, String(), lineNumber, sourceURL, m_groupLevel)); } void InspectorController::markTimeline(const String& message) @@ -433,12 +416,12 @@ void InspectorController::markTimeline(const String& message) void InspectorController::storeLastActivePanel(const String& panelName) { - setSetting(lastActivePanel, panelName); + m_state->setString(InspectorState::lastActivePanel, panelName); } void InspectorController::mouseDidMoveOverElement(const HitTestResult& result, unsigned) { - if (!enabled() || !m_searchingForNode) + if (!enabled() || !searchingForNodeInPage()) return; Node* node = result.innerNode(); @@ -453,7 +436,7 @@ void InspectorController::handleMousePress() if (!enabled()) return; - ASSERT(m_searchingForNode); + ASSERT(searchingForNodeInPage()); if (!m_highlightedNode) return; @@ -492,12 +475,11 @@ void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame) void InspectorController::setSearchingForNode(bool enabled) { - if (m_searchingForNode == enabled) + if (searchingForNodeInPage() == enabled) return; - m_searchingForNode = enabled; - if (!m_searchingForNode) + m_state->setBoolean(InspectorState::searchingForNode, enabled); + if (!enabled) hideHighlight(); - updateInspectorStateCookie(); } void InspectorController::setSearchingForNode(bool enabled, bool* newState) @@ -509,11 +491,7 @@ void InspectorController::setSearchingForNode(bool enabled, bool* newState) void InspectorController::setMonitoringXHREnabled(bool enabled, bool* newState) { *newState = enabled; - if (m_monitoringXHR == enabled) - return; - m_monitoringXHR = enabled; - setSetting(monitoringXHRSettingName, enabled ? "true" : "false"); - updateInspectorStateCookie(); + m_state->setBoolean(InspectorState::monitoringXHR, enabled); } void InspectorController::connectFrontend() @@ -522,6 +500,8 @@ void InspectorController::connectFrontend() releaseFrontendLifetimeAgents(); m_frontend = new InspectorFrontend(m_client); m_domAgent = InspectorDOMAgent::create(m_cssStore.get(), m_frontend.get()); + // FIXME: enable resource agent once front-end is ready. + // m_resourceAgent = InspectorResourceAgent::create(m_inspectedPage, m_frontend.get()); #if ENABLE(DATABASE) m_storageAgent = InspectorStorageAgent::create(m_frontend.get()); @@ -537,9 +517,9 @@ void InspectorController::connectFrontend() m_applicationCacheAgent = new InspectorApplicationCacheAgent(this, m_frontend.get()); #endif - if (!connectedFrontendCount) + if (!InspectorInstrumentation::hasFrontends()) ScriptController::setCaptureCallStackForUncaughtExceptions(true); - connectedFrontendCount++; + InspectorInstrumentation::frontendCreated(); } void InspectorController::reuseFrontend() @@ -577,7 +557,7 @@ void InspectorController::showPanel(const String& panel) return; } - if (panel == lastActivePanel) + if (panel == LastActivePanel) return; m_frontend->showPanel(panel); @@ -595,10 +575,13 @@ void InspectorController::disconnectFrontend() { if (!m_frontend) return; + + setConsoleMessagesEnabled(false); + m_frontend.clear(); - connectedFrontendCount--; - if (!connectedFrontendCount) + InspectorInstrumentation::frontendDeleted(); + if (!InspectorInstrumentation::hasFrontends()) ScriptController::setCaptureCallStackForUncaughtExceptions(false); #if ENABLE(JAVASCRIPT_DEBUGGER) @@ -613,7 +596,7 @@ void InspectorController::disconnectFrontend() unbindAllResources(); stopTimelineProfiler(); - m_showAfterVisible = lastActivePanel; + m_showAfterVisible = LastActivePanel; hideHighlight(); @@ -628,6 +611,8 @@ void InspectorController::disconnectFrontend() void InspectorController::releaseFrontendLifetimeAgents() { + m_resourceAgent.clear(); + // m_domAgent is RefPtr. Remove DOM listeners first to ensure that there are // no references to the DOM agent from the DOM tree. if (m_domAgent) @@ -651,8 +636,8 @@ void InspectorController::populateScriptObjects() if (!m_frontend) return; - if (m_showAfterVisible == lastActivePanel) - m_showAfterVisible = setting(lastActivePanel); + if (m_showAfterVisible == LastActivePanel) + m_showAfterVisible = m_state->getString(InspectorState::lastActivePanel); showPanel(m_showAfterVisible); @@ -664,18 +649,16 @@ void InspectorController::populateScriptObjects() ResourcesMap::iterator resourcesEnd = m_resources.end(); for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) it->second->updateScriptObject(m_frontend.get()); + if (m_domContentEventTime != -1.0) + m_frontend->domContentEventFired(m_domContentEventTime); + if (m_loadEventTime != -1.0) + m_frontend->loadEventFired(m_loadEventTime); m_domAgent->setDocument(m_inspectedPage->mainFrame()->document()); if (m_nodeToFocus) focusNode(); - if (m_expiredConsoleMessageCount) - m_frontend->updateConsoleMessageExpiredCount(m_expiredConsoleMessageCount); - unsigned messageCount = m_consoleMessages.size(); - for (unsigned i = 0; i < messageCount; ++i) - m_consoleMessages[i]->addToFrontend(m_frontend.get(), m_injectedScriptHost.get()); - #if ENABLE(DATABASE) DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end(); for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it) @@ -710,8 +693,7 @@ void InspectorController::restoreDebugger() if (InspectorDebuggerAgent::isDebuggerAlwaysEnabled()) enableDebuggerFromFrontend(false); else { - String debuggerEnabled = setting(debuggerAlwaysEnabledSettingName); - if (debuggerEnabled == "true" || m_attachDebuggerWhenShown) + if (m_state->getBoolean(InspectorState::debuggerAlwaysEnabled) || m_attachDebuggerWhenShown) enableDebugger(); } #endif @@ -722,11 +704,8 @@ void InspectorController::restoreProfiler() ASSERT(m_frontend); #if ENABLE(JAVASCRIPT_DEBUGGER) m_profilerAgent->setFrontend(m_frontend.get()); - if (!ScriptProfiler::isProfilerAlwaysEnabled()) { - String profilerEnabledSetting = setting(profilerAlwaysEnabledSettingName); - if (profilerEnabledSetting == "true") - enableProfiler(); - } + if (!ScriptProfiler::isProfilerAlwaysEnabled() && m_state->getBoolean(InspectorState::profilerAlwaysEnabled)) + enableProfiler(); #endif } @@ -774,6 +753,9 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) if (!enabled()) return; + if (m_resourceAgent) + m_resourceAgent->didCommitLoad(loader); + ASSERT(m_inspectedPage); if (loader->frame() == m_inspectedPage->mainFrame()) { @@ -785,15 +767,21 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) m_times.clear(); m_counts.clear(); + #if ENABLE(JAVASCRIPT_DEBUGGER) if (m_debuggerAgent) m_debuggerAgent->clearForPageNavigation(); + m_nativeBreakpoints.clear(); + m_eventListenerBreakpoints.clear(); m_XHRBreakpoints.clear(); + m_lastBreakpointId = 0; #endif + #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) m_profilerAgent->resetState(); #endif + // unbindAllResources should be called before database and DOM storage // resources are cleared so that it has a chance to unbind them. unbindAllResources(); @@ -842,6 +830,10 @@ void InspectorController::frameDetachedFromParent(Frame* frame) { if (!enabled()) return; + + if (m_resourceAgent) + m_resourceAgent->frameDetachedFromParent(frame); + if (ResourcesMap* resourceMap = m_frameResources.get(frame)) removeAllResources(resourceMap); } @@ -892,7 +884,7 @@ InspectorResource* InspectorController::getTrackedResource(unsigned long identif if (!enabled()) return 0; - if (m_resourceTrackingEnabled) + if (resourceTrackingEnabled()) return m_resources.get(identifier).get(); bool isMainResource = m_mainResource && m_mainResource->identifier() == identifier; @@ -916,6 +908,9 @@ void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, if (!enabled()) return; + if (m_resourceAgent) + m_resourceAgent->didLoadResourceFromMemoryCache(loader, cachedResource); + // If the resource URL is already known, we don't need to add it again since this is just a cached load. if (m_knownResources.contains(cachedResource->url())) return; @@ -923,7 +918,7 @@ void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, ASSERT(m_inspectedPage); bool isMainResource = isMainResourceLoader(loader, KURL(ParsedURLString, cachedResource->url())); ensureSettingsLoaded(); - if (!isMainResource && !m_resourceTrackingEnabled) + if (!isMainResource && !resourceTrackingEnabled()) return; RefPtr<InspectorResource> resource = InspectorResource::createCached(m_inspectedPage->progress()->createUniqueIdentifier(), loader, cachedResource); @@ -946,8 +941,12 @@ void InspectorController::identifierForInitialRequest(unsigned long identifier, ASSERT(m_inspectedPage); bool isMainResource = isMainResourceLoader(loader, request.url()); + + if (m_resourceAgent) + m_resourceAgent->identifierForInitialRequest(identifier, request.url(), loader, isMainResource); + ensureSettingsLoaded(); - if (!isMainResource && !m_resourceTrackingEnabled) + if (!isMainResource && !resourceTrackingEnabled()) return; RefPtr<InspectorResource> resource = InspectorResource::create(identifier, loader, request.url()); @@ -968,13 +967,11 @@ void InspectorController::mainResourceFiredDOMContentEvent(DocumentLoader* loade if (!enabled() || !isMainResourceLoader(loader, url)) return; - if (m_mainResource) { - m_mainResource->markDOMContentEventTime(); - if (m_timelineAgent) - m_timelineAgent->didMarkDOMContentEvent(); - if (m_frontend) - m_mainResource->updateScriptObject(m_frontend.get()); - } + m_domContentEventTime = currentTime(); + if (m_timelineAgent) + m_timelineAgent->didMarkDOMContentEvent(); + if (m_frontend) + m_frontend->domContentEventFired(m_domContentEventTime); } void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, const KURL& url) @@ -982,13 +979,11 @@ void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, con if (!enabled() || !isMainResourceLoader(loader, url)) return; - if (m_mainResource) { - m_mainResource->markLoadEventTime(); - if (m_timelineAgent) - m_timelineAgent->didMarkLoadEvent(); - if (m_frontend) - m_mainResource->updateScriptObject(m_frontend.get()); - } + m_loadEventTime = currentTime(); + if (m_timelineAgent) + m_timelineAgent->didMarkLoadEvent(); + if (m_frontend) + m_frontend->loadEventFired(m_loadEventTime); } bool InspectorController::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl) @@ -1001,6 +996,9 @@ void InspectorController::willSendRequest(unsigned long identifier, ResourceRequ if (!enabled()) return; + if (m_resourceAgent) + m_resourceAgent->willSendRequest(identifier, request, redirectResponse); + bool isMainResource = (m_mainResource && m_mainResource->identifier() == identifier); if (m_timelineAgent) m_timelineAgent->willSendResourceRequest(identifier, isMainResource, request); @@ -1010,6 +1008,10 @@ void InspectorController::willSendRequest(unsigned long identifier, ResourceRequ return; request.setReportLoadTiming(true); + // Only enable raw headers if front-end is attached, as otherwise we may lack + // permissions to fetch the headers. + if (m_frontend) + request.setReportRawHeaders(true); if (!redirectResponse.isNull()) { // Redirect may have empty URL and we'd like to not crash with invalid HashMap entry. @@ -1043,15 +1045,21 @@ void InspectorController::markResourceAsCached(unsigned long identifier) if (!enabled()) return; + if (m_resourceAgent) + m_resourceAgent->markResourceAsCached(identifier); + if (RefPtr<InspectorResource> resource = getTrackedResource(identifier)) resource->markAsCached(); } -void InspectorController::didReceiveResponse(unsigned long identifier, const ResourceResponse& response) +void InspectorController::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response) { if (!enabled()) return; + if (m_resourceAgent) + m_resourceAgent->didReceiveResponse(identifier, loader, response); + if (RefPtr<InspectorResource> resource = getTrackedResource(identifier)) { resource->updateResponse(response); @@ -1059,9 +1067,7 @@ void InspectorController::didReceiveResponse(unsigned long identifier, const Res resource->updateScriptObject(m_frontend.get()); } if (response.httpStatusCode() >= 400) { - // The ugly code below is due to that String::format() is not utf8-safe at the moment. - String message = String::format("Failed to load resource: the server responded with a status of %u (", response.httpStatusCode()) + response.httpStatusText() + ")"; - + String message = makeString("Failed to load resource: the server responded with a status of ", String::number(response.httpStatusCode()), " (", response.httpStatusText(), ')'); addMessageToConsole(OtherMessageSource, LogMessageType, ErrorMessageLevel, message, 0, response.url().string()); } } @@ -1071,6 +1077,9 @@ void InspectorController::didReceiveContentLength(unsigned long identifier, int if (!enabled()) return; + if (m_resourceAgent) + m_resourceAgent->didReceiveContentLength(identifier, lengthReceived); + RefPtr<InspectorResource> resource = getTrackedResource(identifier); if (!resource) return; @@ -1086,8 +1095,11 @@ void InspectorController::didFinishLoading(unsigned long identifier, double fini if (!enabled()) return; + if (m_resourceAgent) + m_resourceAgent->didFinishLoading(identifier, finishTime); + if (m_timelineAgent) - m_timelineAgent->didFinishLoadingResource(identifier, false); + m_timelineAgent->didFinishLoadingResource(identifier, false, finishTime); RefPtr<InspectorResource> resource = getTrackedResource(identifier); if (!resource) @@ -1105,8 +1117,11 @@ void InspectorController::didFailLoading(unsigned long identifier, const Resourc if (!enabled()) return; + if (m_resourceAgent) + m_resourceAgent->didFailLoading(identifier, error); + if (m_timelineAgent) - m_timelineAgent->didFinishLoadingResource(identifier, true); + m_timelineAgent->didFinishLoadingResource(identifier, true, 0); String message = "Failed to load resource"; if (!error.localizedDescription().isEmpty()) @@ -1125,15 +1140,18 @@ void InspectorController::didFailLoading(unsigned long identifier, const Resourc resource->updateScriptObject(m_frontend.get()); } -void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString, const String& url, const String& sendURL, unsigned sendLineNumber) +void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const String& sourceString, const String& url, const String& sendURL, unsigned sendLineNumber) { if (!enabled()) return; - if (m_monitoringXHR) + if (m_resourceAgent) + m_resourceAgent->setOverrideContent(identifier, sourceString, InspectorResource::XHR); + + if (m_state->getBoolean(InspectorState::monitoringXHR)) addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, "XHR finished loading: \"" + url + "\".", sendLineNumber, sendURL); - if (!m_resourceTrackingEnabled) + if (!resourceTrackingEnabled()) return; InspectorResource* resource = m_resources.get(identifier).get(); @@ -1148,14 +1166,20 @@ void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identi void InspectorController::scriptImported(unsigned long identifier, const String& sourceString) { - if (!enabled() || !m_resourceTrackingEnabled) + if (!enabled()) + return; + + if (m_resourceAgent) + m_resourceAgent->setOverrideContent(identifier, sourceString, InspectorResource::Script); + + if (!resourceTrackingEnabled()) return; InspectorResource* resource = m_resources.get(identifier).get(); if (!resource) return; - resource->setOverrideContent(ScriptString(sourceString), InspectorResource::Script); + resource->setOverrideContent(sourceString, InspectorResource::Script); if (m_frontend) resource->updateScriptObject(m_frontend.get()); @@ -1167,8 +1191,7 @@ void InspectorController::setResourceTrackingEnabled(bool enable) return; ASSERT(m_inspectedPage); - m_resourceTrackingEnabled = enable; - updateInspectorStateCookie(); + m_state->setBoolean(InspectorState::resourceTrackingEnabled, enable); } void InspectorController::setResourceTrackingEnabled(bool enable, bool always, bool* newState) @@ -1176,18 +1199,16 @@ void InspectorController::setResourceTrackingEnabled(bool enable, bool always, b *newState = enable; if (always) - setSetting(resourceTrackingAlwaysEnabledSettingName, enable ? "true" : "false"); + m_state->setBoolean(InspectorState::resourceTrackingAlwaysEnabled, enable); - if (m_resourceTrackingEnabled == enable) + if (resourceTrackingEnabled() == enable) return; ASSERT(m_inspectedPage); - m_resourceTrackingEnabled = enable; + m_state->setBoolean(InspectorState::resourceTrackingEnabled, enable); if (enable) - m_inspectedPage->mainFrame()->redirectScheduler()->scheduleRefresh(true); - - updateInspectorStateCookie(); + reloadPage(); } void InspectorController::ensureSettingsLoaded() @@ -1196,15 +1217,10 @@ void InspectorController::ensureSettingsLoaded() return; m_settingsLoaded = true; - String resourceTrackingAlwaysEnabled = setting(resourceTrackingAlwaysEnabledSettingName); - if (resourceTrackingAlwaysEnabled == "true") - m_resourceTrackingEnabled = true; - - String monitoringXHRAlwaysEnabled = setting(monitoringXHRSettingName); - if (monitoringXHRAlwaysEnabled == "true") - m_monitoringXHR = true; + m_state->loadFromSettings(); - updateInspectorStateCookie(); + if (m_state->getBoolean(InspectorState::resourceTrackingAlwaysEnabled)) + m_state->setBoolean(InspectorState::resourceTrackingEnabled, true); } void InspectorController::startTimelineProfiler() @@ -1219,7 +1235,7 @@ void InspectorController::startTimelineProfiler() if (m_frontend) m_frontend->timelineProfilerWasStarted(); - updateInspectorStateCookie(); + m_state->setBoolean(InspectorState::timelineProfilerEnabled, true); } void InspectorController::stopTimelineProfiler() @@ -1234,7 +1250,7 @@ void InspectorController::stopTimelineProfiler() if (m_frontend) m_frontend->timelineProfilerWasStopped(); - updateInspectorStateCookie(); + m_state->setBoolean(InspectorState::timelineProfilerEnabled, false); } #if ENABLE(WORKERS) @@ -1512,6 +1528,9 @@ void InspectorController::didCreateWebSocket(unsigned long identifier, const KUR return; ASSERT(m_inspectedPage); + if (m_resourceAgent) + m_resourceAgent->didCreateWebSocket(identifier, requestURL); + RefPtr<InspectorResource> resource = InspectorResource::createWebSocket(identifier, requestURL, documentURL); addResource(resource.get()); @@ -1521,6 +1540,9 @@ void InspectorController::didCreateWebSocket(unsigned long identifier, const KUR void InspectorController::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request) { + if (m_resourceAgent) + m_resourceAgent->willSendWebSocketHandshakeRequest(identifier, request); + RefPtr<InspectorResource> resource = getTrackedResource(identifier); if (!resource) return; @@ -1532,6 +1554,9 @@ void InspectorController::willSendWebSocketHandshakeRequest(unsigned long identi void InspectorController::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response) { + if (m_resourceAgent) + m_resourceAgent->didReceiveWebSocketHandshakeResponse(identifier, response); + RefPtr<InspectorResource> resource = getTrackedResource(identifier); if (!resource) return; @@ -1545,6 +1570,9 @@ void InspectorController::didReceiveWebSocketHandshakeResponse(unsigned long ide void InspectorController::didCloseWebSocket(unsigned long identifier) { + if (m_resourceAgent) + m_resourceAgent->didCloseWebSocket(identifier); + RefPtr<InspectorResource> resource = getTrackedResource(identifier); if (!resource) return; @@ -1606,14 +1634,14 @@ bool InspectorController::profilerEnabled() const void InspectorController::enableProfiler(bool always, bool skipRecompile) { if (always) - setSetting(profilerAlwaysEnabledSettingName, "true"); + m_state->setBoolean(InspectorState::profilerAlwaysEnabled, true); m_profilerAgent->enable(skipRecompile); } void InspectorController::disableProfiler(bool always) { if (always) - setSetting(profilerAlwaysEnabledSettingName, "false"); + m_state->setBoolean(InspectorState::profilerAlwaysEnabled, false); m_profilerAgent->disable(); } #endif @@ -1623,7 +1651,7 @@ void InspectorController::enableDebuggerFromFrontend(bool always) { ASSERT(!debuggerEnabled()); if (always) - setSetting(debuggerAlwaysEnabledSettingName, "true"); + m_state->setBoolean(InspectorState::debuggerAlwaysEnabled, true); ASSERT(m_inspectedPage); @@ -1654,7 +1682,7 @@ void InspectorController::disableDebugger(bool always) return; if (always) - setSetting(debuggerAlwaysEnabledSettingName, "false"); + m_state->setBoolean(InspectorState::debuggerAlwaysEnabled, false); ASSERT(m_inspectedPage); @@ -1672,29 +1700,74 @@ void InspectorController::resume() m_debuggerAgent->resume(); } -void InspectorController::setNativeBreakpoint(PassRefPtr<InspectorObject> breakpoint, unsigned int* breakpointId) +void InspectorController::setNativeBreakpoint(PassRefPtr<InspectorObject> breakpoint, String* breakpointId) { - *breakpointId = 0; + *breakpointId = ""; String type; if (!breakpoint->getString("type", &type)) return; - if (type == "XHR") { - RefPtr<InspectorObject> condition = breakpoint->getObject("condition"); - if (!condition) - return; + RefPtr<InspectorObject> condition = breakpoint->getObject("condition"); + if (!condition) + return; + if (type == xhrNativeBreakpointType) { String url; if (!condition->getString("url", &url)) return; - *breakpointId = ++m_lastBreakpointId; + *breakpointId = String::number(++m_lastBreakpointId); m_XHRBreakpoints.set(*breakpointId, url); + m_nativeBreakpoints.set(*breakpointId, type); + } else if (type == eventListenerNativeBreakpointType) { + String eventName; + if (!condition->getString("eventName", &eventName)) + return; + if (m_eventListenerBreakpoints.contains(eventName)) + return; + *breakpointId = eventName; + m_eventListenerBreakpoints.add(eventName); + m_nativeBreakpoints.set(*breakpointId, type); + } else if (type == domNativeBreakpointType) { + if (!m_domAgent) + return; + double nodeIdNumber; + if (!condition->getNumber("nodeId", &nodeIdNumber)) + return; + double domBreakpointTypeNumber; + if (!condition->getNumber("type", &domBreakpointTypeNumber)) + return; + long nodeId = (long) nodeIdNumber; + long domBreakpointType = (long) domBreakpointTypeNumber; + *breakpointId = m_domAgent->setDOMBreakpoint(nodeId, domBreakpointType); + if (!breakpointId->isEmpty()) + m_nativeBreakpoints.set(*breakpointId, type); + } +} + +void InspectorController::removeNativeBreakpoint(const String& breakpointId) +{ + String type = m_nativeBreakpoints.take(breakpointId); + if (type == xhrNativeBreakpointType) + m_XHRBreakpoints.remove(breakpointId); + else if (type == eventListenerNativeBreakpointType) + m_eventListenerBreakpoints.remove(breakpointId); + else if (type == domNativeBreakpointType) { + if (m_domAgent) + m_domAgent->removeDOMBreakpoint(breakpointId); } } -void InspectorController::removeNativeBreakpoint(unsigned int breakpointId) +String InspectorController::findEventListenerBreakpoint(const String& eventName) { - m_XHRBreakpoints.remove(breakpointId); + return m_eventListenerBreakpoints.contains(eventName) ? eventName : ""; } +String InspectorController::findXHRBreakpoint(const String& url) +{ + for (HashMap<String, String>::iterator it = m_XHRBreakpoints.begin(); it != m_XHRBreakpoints.end(); ++it) { + if (url.contains(it->second)) + return it->first; + } + return ""; +} #endif void InspectorController::evaluateForTestInFrontend(long callId, const String& script) @@ -1727,12 +1800,14 @@ String InspectorController::breakpointsSettingKey() PassRefPtr<InspectorValue> InspectorController::loadBreakpoints() { - return InspectorValue::parseJSON(setting(breakpointsSettingKey())); + String jsonString; + m_client->populateSetting(breakpointsSettingKey(), &jsonString); + return InspectorValue::parseJSON(jsonString); } void InspectorController::saveBreakpoints(PassRefPtr<InspectorObject> breakpoints) { - setSetting(breakpointsSettingKey(), breakpoints->toJSONString()); + m_client->storeSetting(breakpointsSettingKey(), breakpoints->toJSONString()); } #endif @@ -1763,7 +1838,7 @@ static void drawOutlinedQuad(GraphicsContext& context, const FloatQuad& quad, co context.addPath(quadPath); context.setStrokeThickness(outlineThickness); - context.setStrokeColor(outlineColor, DeviceColorSpace); + context.setStrokeColor(outlineColor, ColorSpaceDeviceRGB); context.strokePath(); context.restore(); @@ -1771,7 +1846,7 @@ static void drawOutlinedQuad(GraphicsContext& context, const FloatQuad& quad, co // Now do the fill context.addPath(quadPath); - context.setFillColor(fillColor, DeviceColorSpace); + context.setFillColor(fillColor, ColorSpaceDeviceRGB); context.fillPath(); } @@ -1953,11 +2028,11 @@ void InspectorController::drawElementTitle(GraphicsContext& context, const IntRe if (titleRect.bottom() > overlayRect.bottom()) dy += overlayRect.bottom() - titleRect.bottom() - borderWidthPx; titleRect.move(dx, dy); - context.setStrokeColor(tooltipBorderColor, DeviceColorSpace); + context.setStrokeColor(tooltipBorderColor, ColorSpaceDeviceRGB); context.setStrokeThickness(borderWidthPx); - context.setFillColor(tooltipBackgroundColor, DeviceColorSpace); + context.setFillColor(tooltipBackgroundColor, ColorSpaceDeviceRGB); context.drawRect(titleRect); - context.setFillColor(tooltipFontColor, DeviceColorSpace); + context.setFillColor(tooltipFontColor, ColorSpaceDeviceRGB); context.drawText(font, nodeTitleRun, IntPoint(titleRect.x() + rectInflatePx, titleRect.y() + font.height())); } @@ -1972,14 +2047,15 @@ void InspectorController::openInInspectedWindow(const String& url) if (!newFrame) return; + UserGestureIndicator indicator(DefinitelyProcessingUserGesture); newFrame->loader()->setOpener(mainFrame); newFrame->page()->setOpenedByDOM(); - newFrame->loader()->changeLocation(newFrame->loader()->completeURL(url), "", false, false, true); + newFrame->loader()->changeLocation(newFrame->loader()->completeURL(url), "", false, false); } void InspectorController::count(const String& title, unsigned lineNumber, const String& sourceID) { - String identifier = title + String::format("@%s:%d", sourceID.utf8().data(), lineNumber); + String identifier = makeString(title, '@', sourceID, ':', String::number(lineNumber)); HashMap<String, unsigned>::iterator it = m_counts.find(identifier); int count; if (it == m_counts.end()) @@ -1991,7 +2067,7 @@ void InspectorController::count(const String& title, unsigned lineNumber, const m_counts.add(identifier, count); - String message = String::format("%s: %d", title.utf8().data(), count); + String message = makeString(title, ": ", String::number(count)); addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceID); } @@ -2049,10 +2125,14 @@ void InspectorController::setInspectorExtensionAPI(const String& source) m_inspectorExtensionAPI = source; } -void InspectorController::getResourceContent(unsigned long identifier, String* content) +void InspectorController::getResourceContent(unsigned long identifier, bool encode, String* content) { RefPtr<InspectorResource> resource = m_resources.get(identifier); - *content = resource ? resource->sourceString() : String(); + if (!resource) { + *content = String(); + return; + } + *content = encode ? resource->sourceBytes() : resource->sourceString(); } bool InspectorController::resourceContentForURL(const KURL& url, Document* frameDocument, String* result) @@ -2100,86 +2180,11 @@ bool InspectorController::resourceContentForURL(const KURL& url, Document* frame void InspectorController::reloadPage() { - m_inspectedPage->mainFrame()->redirectScheduler()->scheduleRefresh(true); -} - -void InspectorController::willInsertDOMNodeImpl(Node* node, Node* parent) -{ -#if ENABLE(JAVASCRIPT_DEBUGGER) - if (!m_debuggerAgent || !m_domAgent) - return; - PassRefPtr<InspectorValue> eventData; - if (m_domAgent->shouldBreakOnNodeInsertion(node, parent, &eventData)) - m_debuggerAgent->breakProgram(DOMBreakpointDebuggerEventType, eventData); -#endif -} - -void InspectorController::didInsertDOMNodeImpl(Node* node) -{ - if (m_domAgent) - m_domAgent->didInsertDOMNode(node); -} - -void InspectorController::willRemoveDOMNodeImpl(Node* node) -{ -#if ENABLE(JAVASCRIPT_DEBUGGER) - if (!m_debuggerAgent || !m_domAgent) - return; - PassRefPtr<InspectorValue> eventData; - if (m_domAgent->shouldBreakOnNodeRemoval(node, &eventData)) - m_debuggerAgent->breakProgram(DOMBreakpointDebuggerEventType, eventData); -#endif -} - -void InspectorController::didRemoveDOMNodeImpl(Node* node) -{ - if (m_domAgent) - m_domAgent->didRemoveDOMNode(node); -} - -void InspectorController::willModifyDOMAttrImpl(Element* element) -{ -#if ENABLE(JAVASCRIPT_DEBUGGER) - if (!m_debuggerAgent || !m_domAgent) - return; - PassRefPtr<InspectorValue> eventData; - if (m_domAgent->shouldBreakOnAttributeModification(element, &eventData)) - m_debuggerAgent->breakProgram(DOMBreakpointDebuggerEventType, eventData); -#endif -} - -void InspectorController::didModifyDOMAttrImpl(Element* element) -{ - if (m_domAgent) - m_domAgent->didModifyDOMAttr(element); -} - -void InspectorController::characterDataModifiedImpl(CharacterData* characterData) -{ - if (m_domAgent) - m_domAgent->characterDataModified(characterData); + // FIXME: Why do we set the user gesture indicator here? + UserGestureIndicator indicator(DefinitelyProcessingUserGesture); + m_inspectedPage->mainFrame()->navigationScheduler()->scheduleRefresh(); } -void InspectorController::instrumentWillSendXMLHttpRequestImpl(const KURL& url) -{ -#if ENABLE(JAVASCRIPT_DEBUGGER) - if (m_debuggerAgent) { - if (!m_XHRBreakpoints.size()) - return; - for (HashMap<unsigned int, String>::iterator it = m_XHRBreakpoints.begin(); it != m_XHRBreakpoints.end(); ++it) { - if (!url.string().contains(it->second)) - continue; - RefPtr<InspectorObject> eventData = InspectorObject::create(); - eventData->setString("type", "XHR"); - eventData->setString("url", url); - m_debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData); - break; - } - } -#endif -} - - } // namespace WebCore #endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorController.h b/WebCore/inspector/InspectorController.h index c6f516d..a047962 100644 --- a/WebCore/inspector/InspectorController.h +++ b/WebCore/inspector/InspectorController.h @@ -32,10 +32,8 @@ #include "CharacterData.h" #include "Console.h" #include "Cookie.h" -#include "Element.h" #include "Page.h" #include "PlatformString.h" -#include "ScriptState.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/ListHashSet.h> @@ -51,7 +49,6 @@ class ConsoleMessage; class Database; class Document; class DocumentLoader; -class Element; class FloatRect; class GraphicsContext; class HitTestResult; @@ -71,6 +68,8 @@ class InspectorFrontendClient; class InspectorObject; class InspectorProfilerAgent; class InspectorResource; +class InspectorResourceAgent; +class InspectorState; class InspectorStorageAgent; class InspectorTimelineAgent; class InspectorValue; @@ -84,7 +83,6 @@ class ResourceResponse; class ResourceError; class ScriptCallStack; class ScriptProfile; -class ScriptString; class SharedBuffer; class Storage; class StorageArea; @@ -105,6 +103,7 @@ public: typedef HashMap<int, RefPtr<InspectorDatabaseResource> > DatabaseResourcesMap; typedef HashMap<int, RefPtr<InspectorDOMStorageResource> > DOMStorageResourcesMap; + static const char* const LastActivePanel; static const char* const ConsolePanel; static const char* const ElementsPanel; static const char* const ProfilesPanel; @@ -125,13 +124,11 @@ public: Page* inspectedPage() const { return m_inspectedPage; } void reloadPage(); - String setting(const String& key) const; - void setSetting(const String& key, const String& value); void saveApplicationSettings(const String& settings); void saveSessionSettings(const String&); void getSettings(RefPtr<InspectorObject>*); - void restoreInspectorStateFromCookie(const String& inspectorState); + void restoreInspectorStateFromCookie(const String& inspectorCookie); void inspect(Node*); void highlight(Node*); @@ -143,19 +140,17 @@ public: void showPanel(const String&); void close(); - // We are in transition from JS transport via webInspector to native - // transport via InspectorClient. After migration, webInspector parameter should - // be removed. void connectFrontend(); void reuseFrontend(); void disconnectFrontend(); + void setConsoleMessagesEnabled(bool enabled, bool* newState); void addMessageToConsole(MessageSource, MessageType, MessageLevel, ScriptCallStack*, const String& message); void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, unsigned lineNumber, const String& sourceID); void clearConsoleMessages(); const Vector<OwnPtr<ConsoleMessage> >& consoleMessages() const { return m_consoleMessages; } - bool searchingForNodeInPage() const { return m_searchingForNode; } + bool searchingForNodeInPage() const; void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags); void handleMousePress(); @@ -171,16 +166,16 @@ public: void identifierForInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&); void willSendRequest(unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse); void markResourceAsCached(unsigned long identifier); - void didReceiveResponse(unsigned long identifier, const ResourceResponse&); + void didReceiveResponse(unsigned long identifier, DocumentLoader*, const ResourceResponse&); void didReceiveContentLength(unsigned long identifier, int lengthReceived); void didFinishLoading(unsigned long identifier, double finishTime); void didFailLoading(unsigned long identifier, const ResourceError&); - void resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString, const String& url, const String& sendURL, unsigned sendLineNumber); + void resourceRetrievedByXMLHttpRequest(unsigned long identifier, const String& sourceString, const String& url, const String& sendURL, unsigned sendLineNumber); void scriptImported(unsigned long identifier, const String& sourceString); void setResourceTrackingEnabled(bool enabled); void setResourceTrackingEnabled(bool enabled, bool always, bool* newState); - bool resourceTrackingEnabled() const { return m_resourceTrackingEnabled; } + bool resourceTrackingEnabled() const; void ensureSettingsLoaded(); @@ -198,15 +193,6 @@ public: void mainResourceFiredLoadEvent(DocumentLoader*, const KURL&); void mainResourceFiredDOMContentEvent(DocumentLoader*, const KURL&); - static void willInsertDOMNode(Node* node, Node* parent); - static void didInsertDOMNode(Node*); - static void willRemoveDOMNode(Node*); - static void willModifyDOMAttr(Element*); - static void didModifyDOMAttr(Element*); - static void characterDataModified(CharacterData*); - - static void instrumentWillSendXMLHttpRequest(ScriptExecutionContext*, const KURL&); - #if ENABLE(WORKERS) enum WorkerAction { WorkerCreated, WorkerDestroyed }; @@ -270,9 +256,8 @@ public: InspectorDebuggerAgent* debuggerAgent() const { return m_debuggerAgent.get(); } void resume(); - void setNativeBreakpoint(PassRefPtr<InspectorObject> breakpoint, unsigned int* breakpointId); - void removeNativeBreakpoint(unsigned int breakpointId); - + void setNativeBreakpoint(PassRefPtr<InspectorObject> breakpoint, String* breakpointId); + void removeNativeBreakpoint(const String& breakpointId); #endif void evaluateForTestInFrontend(long testCallId, const String& script); @@ -283,14 +268,20 @@ public: void removeAllScriptsToEvaluateOnLoad(); void setInspectorExtensionAPI(const String& source); - static const String& inspectorStartsAttachedSettingName(); + bool inspectorStartsAttached(); + void setInspectorStartsAttached(bool); + void setInspectorAttachedHeight(long height); + int inspectorAttachedHeight() const; + + static const unsigned defaultAttachedHeight; private: - void updateInspectorStateCookie(); void getInspectorState(RefPtr<InspectorObject>* state); + void setConsoleMessagesEnabled(bool enabled); friend class InspectorBackend; friend class InspectorBackendDispatcher; + friend class InspectorInstrumentation; friend class InjectedScriptHost; void populateScriptObjects(); @@ -308,9 +299,11 @@ private: void releaseFrontendLifetimeAgents(); #if ENABLE(JAVASCRIPT_DEBUGGER) - void toggleRecordButton(bool); void enableDebuggerFromFrontend(bool always); + + String findEventListenerBreakpoint(const String& eventName); + String findXHRBreakpoint(const String& url); #endif #if ENABLE(DATABASE) void selectDatabase(Database* database); @@ -325,12 +318,12 @@ private: void focusNode(); - void addConsoleMessage(ScriptState*, PassOwnPtr<ConsoleMessage>); + void addConsoleMessage(PassOwnPtr<ConsoleMessage>); void addResource(InspectorResource*); void removeResource(InspectorResource*); InspectorResource* getTrackedResource(unsigned long identifier); - void getResourceContent(unsigned long identifier, String* content); + void getResourceContent(unsigned long identifier, bool encode, String* content); void pruneResources(ResourcesMap*, DocumentLoader* loaderToKeep = 0); void removeAllResources(ResourcesMap* map) { pruneResources(map); } @@ -339,20 +332,6 @@ private: void didEvaluateForTestInFrontend(long callId, const String& jsonResult); - static InspectorController* inspectorControllerForScriptExecutionContext(ScriptExecutionContext* context); - static InspectorController* inspectorControllerForNode(Node*); - static InspectorController* inspectorControllerForDocument(Document* document); - - void willInsertDOMNodeImpl(Node* node, Node* parent); - void didInsertDOMNodeImpl(Node*); - void willRemoveDOMNodeImpl(Node*); - void didRemoveDOMNodeImpl(Node*); - void willModifyDOMAttrImpl(Element*); - void didModifyDOMAttrImpl(Element*); - void characterDataModifiedImpl(CharacterData*); - - void instrumentWillSendXMLHttpRequestImpl(const KURL&); - #if ENABLE(JAVASCRIPT_DEBUGGER) friend class InspectorDebuggerAgent; String breakpointsSettingKey(); @@ -360,17 +339,17 @@ private: void saveBreakpoints(PassRefPtr<InspectorObject> breakpoints); #endif - static unsigned s_inspectorControllerCount; - Page* m_inspectedPage; InspectorClient* m_client; OwnPtr<InspectorFrontendClient> m_inspectorFrontendClient; bool m_openingFrontend; OwnPtr<InspectorFrontend> m_frontend; RefPtr<InspectorDOMAgent> m_domAgent; + RefPtr<InspectorResourceAgent> m_resourceAgent; RefPtr<InspectorStorageAgent> m_storageAgent; OwnPtr<InspectorCSSStore> m_cssStore; OwnPtr<InspectorTimelineAgent> m_timelineAgent; + OwnPtr<InspectorState> m_state; #if ENABLE(OFFLINE_WEB_APPLICATIONS) OwnPtr<InspectorApplicationCacheAgent> m_applicationCacheAgent; @@ -381,6 +360,8 @@ private: ResourcesMap m_resources; HashSet<String> m_knownResources; FrameResourcesMap m_frameResources; + double m_loadEventTime; + double m_domContentEventTime; Vector<OwnPtr<ConsoleMessage> > m_consoleMessages; unsigned m_expiredConsoleMessageCount; HashMap<String, double> m_times; @@ -397,10 +378,7 @@ private: RefPtr<InspectorValue> m_sessionSettings; #endif unsigned m_groupLevel; - bool m_searchingForNode; - bool m_monitoringXHR; ConsoleMessage* m_previousMessage; - bool m_resourceTrackingEnabled; bool m_settingsLoaded; RefPtr<InspectorBackend> m_inspectorBackend; OwnPtr<InspectorBackendDispatcher> m_inspectorBackendDispatcher; @@ -416,7 +394,10 @@ private: bool m_attachDebuggerWhenShown; OwnPtr<InspectorDebuggerAgent> m_debuggerAgent; - HashMap<unsigned int, String> m_XHRBreakpoints; + HashMap<String, String> m_nativeBreakpoints; + HashSet<String> m_eventListenerBreakpoints; + HashMap<String, String> m_XHRBreakpoints; + unsigned int m_lastBreakpointId; OwnPtr<InspectorProfilerAgent> m_profilerAgent; @@ -428,6 +409,7 @@ private: #endif }; +<<<<<<< HEAD inline void InspectorController::willInsertDOMNode(Node* node, Node* parent) { #if ENABLE(INSPECTOR) @@ -518,6 +500,8 @@ inline InspectorController* InspectorController::inspectorControllerForDocument( #endif } +======= +>>>>>>> webkit.org at r70209 } // namespace WebCore #endif // !defined(InspectorController_h) diff --git a/WebCore/inspector/InspectorDOMAgent.cpp b/WebCore/inspector/InspectorDOMAgent.cpp index f51da73..f5c5f8b 100644 --- a/WebCore/inspector/InspectorDOMAgent.cpp +++ b/WebCore/inspector/InspectorDOMAgent.cpp @@ -77,6 +77,7 @@ #include "markup.h" #include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <wtf/HashSet.h> #include <wtf/ListHashSet.h> #include <wtf/OwnPtr.h> @@ -187,6 +188,8 @@ public: if (!ec) resultCollector.add(node); } +#else + UNUSED_PARAM(resultCollector); #endif } }; @@ -203,7 +206,8 @@ public: enum DOMBreakpointType { SubtreeModified = 0, AttributeModified, - NodeRemoved + NodeRemoved, + DOMBreakpointTypesCount }; const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified); @@ -215,6 +219,7 @@ InspectorDOMAgent::InspectorDOMAgent(InspectorCSSStore* cssStore, InspectorFront : EventListener(InspectorDOMAgentType) , m_cssStore(cssStore) , m_frontend(frontend) + , m_domListener(0) , m_lastNodeId(1) , m_matchJobsTimer(this, &InspectorDOMAgent::onMatchJobsTimer) { @@ -237,6 +242,11 @@ void InspectorDOMAgent::reset() ASSERT(!m_documents.size()); } +void InspectorDOMAgent::setDOMListener(DOMListener* listener) +{ + m_domListener = listener; +} + void InspectorDOMAgent::setDocument(Document* doc) { if (doc == mainFrameDocument()) @@ -326,6 +336,8 @@ void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap) if (node->isFrameOwnerElement()) { const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node); stopListening(frameOwner->contentDocument()); + if (m_domListener) + m_domListener->didRemoveDocument(frameOwner->contentDocument()); cssStore()->removeDocument(frameOwner->contentDocument()); } @@ -473,7 +485,7 @@ void InspectorDOMAgent::removeNode(long nodeId, long* outNodeId) if (!node) return; - Node* parentNode = node->parentNode(); + ContainerNode* parentNode = node->parentNode(); if (!parentNode) return; @@ -508,7 +520,7 @@ void InspectorDOMAgent::changeTagName(long nodeId, const String& tagName, long* newElem->appendChild(child, ec); // Replace the old node with the new node - Node* parent = oldNode->parentNode(); + ContainerNode* parent = oldNode->parentNode(); parent->insertBefore(newElem, oldNode->nextSibling(), ec); parent->removeChild(oldNode, ec); @@ -539,7 +551,7 @@ void InspectorDOMAgent::setOuterHTML(long nodeId, const String& outerHTML, long* bool childrenRequested = m_childrenRequested.contains(nodeId); Node* previousSibling = node->previousSibling(); - Node* parentNode = node->parentNode(); + ContainerNode* parentNode = node->parentNode(); HTMLElement* htmlElement = static_cast<HTMLElement*>(node); ExceptionCode ec = 0; @@ -743,11 +755,17 @@ void InspectorDOMAgent::searchCanceled() m_searchResults.clear(); } -void InspectorDOMAgent::setDOMBreakpoint(long nodeId, long type) +String InspectorDOMAgent::setDOMBreakpoint(long nodeId, long type) { Node* node = nodeForId(nodeId); if (!node) - return; + return ""; + + String breakpointId = createBreakpointId(nodeId, type); + if (m_idToBreakpoint.contains(breakpointId)) + return ""; + + m_idToBreakpoint.set(breakpointId, std::make_pair(nodeId, type)); uint32_t rootBit = 1 << type; m_breakpoints.set(node, m_breakpoints.get(node) | rootBit); @@ -755,15 +773,19 @@ void InspectorDOMAgent::setDOMBreakpoint(long nodeId, long type) for (Node* child = innerFirstChild(node); child; child = innerNextSibling(child)) updateSubtreeBreakpoints(child, rootBit, true); } + + return breakpointId; } -void InspectorDOMAgent::removeDOMBreakpoint(long nodeId, long type) +void InspectorDOMAgent::removeDOMBreakpoint(const String& breakpointId) { - Node* node = nodeForId(nodeId); + Breakpoint breakpoint = m_idToBreakpoint.take(breakpointId); + + Node* node = nodeForId(breakpoint.first); if (!node) return; - uint32_t rootBit = 1 << type; + uint32_t rootBit = 1 << breakpoint.second; uint32_t mask = m_breakpoints.get(node) & ~rootBit; if (mask) m_breakpoints.set(node, mask); @@ -835,11 +857,7 @@ PassRefPtr<InspectorValue> InspectorDOMAgent::descriptionForDOMEvent(Node* targe long breakpointOwnerNodeId = m_documentNodeToIdMap.get(breakpointOwner); ASSERT(breakpointOwnerNodeId); - - RefPtr<InspectorObject> breakpoint = InspectorObject::create(); - breakpoint->setNumber("nodeId", breakpointOwnerNodeId); - breakpoint->setNumber("type", breakpointType); - description->setObject("breakpoint", breakpoint); + description->setString("breakpointId", createBreakpointId(breakpointOwnerNodeId, breakpointType)); return description; } @@ -1016,8 +1034,8 @@ unsigned InspectorDOMAgent::innerChildNodeCount(Node* node) Node* InspectorDOMAgent::innerParentNode(Node* node) { - Node* parent = node->parentNode(); - if (parent && parent->nodeType() == Node::DOCUMENT_NODE) + ContainerNode* parent = node->parentNode(); + if (parent && parent->isDocumentNode()) return static_cast<Document*>(parent)->ownerElement(); return parent; } @@ -1058,7 +1076,7 @@ void InspectorDOMAgent::didInsertDOMNode(Node* node) // We could be attaching existing subtree. Forget the bindings. unbind(node, &m_documentNodeToIdMap); - Node* parent = node->parentNode(); + ContainerNode* parent = node->parentNode(); long parentId = m_documentNodeToIdMap.get(parent); // Return if parent is not mapped yet. if (!parentId) @@ -1083,25 +1101,28 @@ void InspectorDOMAgent::didRemoveDOMNode(Node* node) if (m_breakpoints.size()) { // Remove subtree breakpoints. - m_breakpoints.remove(node); + removeBreakpointsForNode(node); Vector<Node*> stack(1, innerFirstChild(node)); do { Node* node = stack.last(); stack.removeLast(); if (!node) continue; - m_breakpoints.remove(node); + removeBreakpointsForNode(node); stack.append(innerFirstChild(node)); stack.append(innerNextSibling(node)); } while (!stack.isEmpty()); } - Node* parent = node->parentNode(); + ContainerNode* parent = node->parentNode(); long parentId = m_documentNodeToIdMap.get(parent); // If parent is not mapped yet -> ignore the event. if (!parentId) return; + if (m_domListener) + m_domListener->didRemoveDOMNode(node); + if (!m_childrenRequested.contains(parentId)) { // No children are mapped yet -> only notify on changes of hasChildren. if (innerChildNodeCount(parent) == 1) @@ -1154,6 +1175,25 @@ void InspectorDOMAgent::updateSubtreeBreakpoints(Node* node, uint32_t rootMask, updateSubtreeBreakpoints(child, newRootMask, set); } +void InspectorDOMAgent::removeBreakpointsForNode(Node* node) +{ + uint32_t mask = m_breakpoints.take(node); + if (!mask) + return; + long nodeId = m_documentNodeToIdMap.get(node); + if (!nodeId) + return; + for (int type = 0; type < DOMBreakpointTypesCount; ++type) { + if (mask && (1 << type)) + m_idToBreakpoint.remove(createBreakpointId(nodeId, type)); + } +} + +String InspectorDOMAgent::createBreakpointId(long nodeId, long type) +{ + return makeString("dom:", String::number(nodeId), ':', String::number(type)); +} + void InspectorDOMAgent::getStyles(long nodeId, bool authorOnly, RefPtr<InspectorValue>* styles) { Node* node = nodeForId(nodeId); @@ -1221,18 +1261,25 @@ void InspectorDOMAgent::getStyleSourceData(long styleId, RefPtr<InspectorObject> CSSStyleDeclaration* style = cssStore()->styleForId(styleId); if (!style) return; - RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create(); - bool success = cssStore()->getStyleSourceData(style, &sourceData); + RefPtr<CSSRuleSourceData> sourceData = CSSRuleSourceData::create(); + bool success = cssStore()->getRuleSourceData(style, &sourceData); if (!success) return; RefPtr<InspectorObject> result = InspectorObject::create(); + RefPtr<InspectorObject> bodyRange = InspectorObject::create(); result->setObject("bodyRange", bodyRange); - bodyRange->setNumber("start", sourceData->styleBodyRange.start); - bodyRange->setNumber("end", sourceData->styleBodyRange.end); + bodyRange->setNumber("start", sourceData->styleSourceData->styleBodyRange.start); + bodyRange->setNumber("end", sourceData->styleSourceData->styleBodyRange.end); + + RefPtr<InspectorObject> selectorRange = InspectorObject::create(); + result->setObject("selectorRange", selectorRange); + selectorRange->setNumber("start", sourceData->selectorListRange.start); + selectorRange->setNumber("end", sourceData->selectorListRange.end); + RefPtr<InspectorArray> propertyRanges = InspectorArray::create(); result->setArray("propertyData", propertyRanges); - Vector<CSSPropertySourceData>& propertyData = sourceData->propertyData; + Vector<CSSPropertySourceData>& propertyData = sourceData->styleSourceData->propertyData; for (Vector<CSSPropertySourceData>::iterator it = propertyData.begin(); it != propertyData.end(); ++it) { RefPtr<InspectorObject> propertyRange = InspectorObject::create(); propertyRange->setString("name", it->name); @@ -1311,7 +1358,7 @@ PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForPseudoElements(Elemen return result.release(); } -void InspectorDOMAgent::applyStyleText(long styleId, const String& styleText, const String& propertyName, bool* success, RefPtr<InspectorValue>* styleObject, RefPtr<InspectorArray>* changedPropertiesArray) +void InspectorDOMAgent::applyStyleText(long styleId, const String& styleText, const String& propertyName, bool* success, RefPtr<InspectorValue>* styleObject) { CSSStyleDeclaration* style = cssStore()->styleForId(styleId); if (!style) @@ -1348,7 +1395,6 @@ void InspectorDOMAgent::applyStyleText(long styleId, const String& styleText, co // Notify caller that the property was successfully deleted. if (!styleTextLength) { - (*changedPropertiesArray)->pushString(propertyName); *success = true; return; } @@ -1359,7 +1405,6 @@ void InspectorDOMAgent::applyStyleText(long styleId, const String& styleText, co // Iterate of the properties on the test element's style declaration and // add them to the real style declaration. We take care to move shorthands. HashSet<String> foundShorthands; - Vector<String> changedProperties; for (unsigned i = 0; i < tempStyle->length(); ++i) { String name = tempStyle->item(i); @@ -1386,11 +1431,9 @@ void InspectorDOMAgent::applyStyleText(long styleId, const String& styleText, co // Remove disabled property entry for property with this name. if (disabledStyle) disabledStyle->remove(name); - changedProperties.append(name); } *success = true; *styleObject = buildObjectForStyle(style, true); - *changedPropertiesArray = toArray(changedProperties); } void InspectorDOMAgent::setStyleText(long styleId, const String& cssText, bool* success) @@ -1493,6 +1536,7 @@ void InspectorDOMAgent::getSupportedCSSProperties(RefPtr<InspectorArray>* cssPro RefPtr<InspectorArray> properties = InspectorArray::create(); for (int i = 0; i < numCSSProperties; ++i) properties->pushString(propertyNameStrings[i]); + *cssProperties = properties.release(); } diff --git a/WebCore/inspector/InspectorDOMAgent.h b/WebCore/inspector/InspectorDOMAgent.h index 89c6fd0..03e4d8d 100644 --- a/WebCore/inspector/InspectorDOMAgent.h +++ b/WebCore/inspector/InspectorDOMAgent.h @@ -80,6 +80,14 @@ namespace WebCore { class InspectorDOMAgent : public EventListener { public: + struct DOMListener { + virtual ~DOMListener() + { + } + virtual void didRemoveDocument(Document*) = 0; + virtual void didRemoveDOMNode(Node*) = 0; + }; + static PassRefPtr<InspectorDOMAgent> create(InspectorCSSStore* cssStore, InspectorFrontend* frontend) { return adoptRef(new InspectorDOMAgent(cssStore, frontend)); @@ -112,8 +120,6 @@ namespace WebCore { void addInspectedNode(long nodeId); void performSearch(const String& whitespaceTrimmedQuery, bool runSynchronously); void searchCanceled(); - void setDOMBreakpoint(long nodeId, long type); - void removeDOMBreakpoint(long nodeId, long type); bool shouldBreakOnNodeInsertion(Node* node, Node* parent, PassRefPtr<InspectorValue>* details); bool shouldBreakOnNodeRemoval(Node* node, PassRefPtr<InspectorValue>* details); bool shouldBreakOnAttributeModification(Element* element, PassRefPtr<InspectorValue>* details); @@ -125,7 +131,7 @@ namespace WebCore { void getComputedStyle(long nodeId, RefPtr<InspectorValue>* styles); void getStyleSheet(long styleSheetId, RefPtr<InspectorObject>* styleSheetObject); void getStyleSourceData(long styleId, RefPtr<InspectorObject>* dataObject); - void applyStyleText(long styleId, const String& styleText, const String& propertyName, bool* success, RefPtr<InspectorValue>* styleObject, RefPtr<InspectorArray>* changedProperties); + void applyStyleText(long styleId, const String& styleText, const String& propertyName, bool* success, RefPtr<InspectorValue>* styleObject); void setStyleText(long styleId, const String& cssText, bool* success); void setStyleProperty(long styleId, const String& name, const String& value, bool* success); void toggleStyleEnabled(long styleId, const String& propertyName, bool disabled, RefPtr<InspectorValue>* styleObject); @@ -148,6 +154,13 @@ namespace WebCore { void pushNodeByPathToFrontend(const String& path, long* nodeId); long inspectedNode(unsigned long num); void copyNode(long nodeId); + const ListHashSet<RefPtr<Document> >& documents() { return m_documents; } + void setDOMListener(DOMListener*); + + String documentURLString(Document* document) const; + + String setDOMBreakpoint(long nodeId, long type); + void removeDOMBreakpoint(const String& breakpointId); private: void startListening(Document* document); @@ -164,7 +177,9 @@ namespace WebCore { bool hasBreakpoint(Node* node, long type); void updateSubtreeBreakpoints(Node* root, uint32_t rootMask, bool value); + void removeBreakpointsForNode(Node* node); PassRefPtr<InspectorValue> descriptionForDOMEvent(Node* target, long breakpointType, bool insertion); + String createBreakpointId(long nodeId, long type); PassRefPtr<InspectorObject> buildObjectForAttributeStyles(Element* element); PassRefPtr<InspectorArray> buildArrayForCSSRules(Document* ownerDocument, CSSRuleList*); @@ -185,7 +200,6 @@ namespace WebCore { bool isWhitespace(Node* node); Document* mainFrameDocument() const; - String documentURLString(Document* document) const; InspectorCSSStore* cssStore() { return m_cssStore; } void onMatchJobsTimer(Timer<InspectorDOMAgent>*); @@ -207,6 +221,7 @@ namespace WebCore { InspectorCSSStore* m_cssStore; InspectorFrontend* m_frontend; + DOMListener* m_domListener; NodeToIdMap m_documentNodeToIdMap; // Owns node mappings for dangling nodes. Vector<NodeToIdMap*> m_danglingNodeToIdMaps; @@ -220,6 +235,8 @@ namespace WebCore { HashSet<RefPtr<Node> > m_searchResults; Vector<long> m_inspectedNodes; HashMap<Node*, uint32_t> m_breakpoints; + typedef pair<long, long> Breakpoint; + HashMap<String, Breakpoint> m_idToBreakpoint; }; #endif diff --git a/WebCore/inspector/InspectorDebuggerAgent.cpp b/WebCore/inspector/InspectorDebuggerAgent.cpp index 4964c09..a7eacf6 100644 --- a/WebCore/inspector/InspectorDebuggerAgent.cpp +++ b/WebCore/inspector/InspectorDebuggerAgent.cpp @@ -38,13 +38,13 @@ #include "PlatformString.h" #include "ScriptDebugServer.h" #include <wtf/MD5.h> -#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> namespace WebCore { static String formatBreakpointId(const String& sourceID, unsigned lineNumber) { - return String::format("%s:%d", sourceID.utf8().data(), lineNumber); + return makeString(sourceID, ':', String::number(lineNumber)); } PassOwnPtr<InspectorDebuggerAgent> InspectorDebuggerAgent::create(InspectorController* inspectorController, InspectorFrontend* frontend) @@ -57,8 +57,6 @@ PassOwnPtr<InspectorDebuggerAgent> InspectorDebuggerAgent::create(InspectorContr return agent.release(); } -InspectorDebuggerAgent* InspectorDebuggerAgent::s_debuggerAgentOnBreakpoint = 0; - InspectorDebuggerAgent::InspectorDebuggerAgent(InspectorController* inspectorController, InspectorFrontend* frontend) : m_inspectorController(inspectorController) , m_frontend(frontend) @@ -71,9 +69,6 @@ InspectorDebuggerAgent::~InspectorDebuggerAgent() { ScriptDebugServer::shared().removeListener(this, m_inspectorController->inspectedPage()); m_pausedScriptState = 0; - - if (this == s_debuggerAgentOnBreakpoint) - s_debuggerAgentOnBreakpoint = 0; } bool InspectorDebuggerAgent::isDebuggerAlwaysEnabled() @@ -147,9 +142,23 @@ void InspectorDebuggerAgent::getScriptSource(const String& sourceID, String* scr *scriptSource = m_scriptIDToContent.get(sourceID); } +void InspectorDebuggerAgent::schedulePauseOnNextStatement(DebuggerEventType type, PassRefPtr<InspectorValue> data) +{ + m_breakProgramDetails = InspectorObject::create(); + m_breakProgramDetails->setNumber("eventType", type); + m_breakProgramDetails->setValue("eventData", data); + ScriptDebugServer::shared().setPauseOnNextStatement(true); +} + +void InspectorDebuggerAgent::cancelPauseOnNextStatement() +{ + m_breakProgramDetails = 0; + ScriptDebugServer::shared().setPauseOnNextStatement(false); +} + void InspectorDebuggerAgent::pause() { - ScriptDebugServer::shared().pause(); + schedulePauseOnNextStatement(JavaScriptPauseEventType, InspectorObject::create()); } void InspectorDebuggerAgent::resume() @@ -305,6 +314,7 @@ void InspectorDebuggerAgent::didPause(ScriptState* scriptState) void InspectorDebuggerAgent::didContinue() { m_pausedScriptState = 0; + m_breakProgramDetails = 0; m_frontend->resumedScript(); } @@ -313,14 +323,7 @@ void InspectorDebuggerAgent::breakProgram(DebuggerEventType type, PassRefPtr<Ins m_breakProgramDetails = InspectorObject::create(); m_breakProgramDetails->setNumber("eventType", type); m_breakProgramDetails->setValue("eventData", data); - s_debuggerAgentOnBreakpoint = this; - ScriptDebugServer::shared().breakProgram(); - if (!s_debuggerAgentOnBreakpoint) - return; - - s_debuggerAgentOnBreakpoint = 0; - m_breakProgramDetails = 0; } } // namespace WebCore diff --git a/WebCore/inspector/InspectorDebuggerAgent.h b/WebCore/inspector/InspectorDebuggerAgent.h index 8a5379d..3825042 100644 --- a/WebCore/inspector/InspectorDebuggerAgent.h +++ b/WebCore/inspector/InspectorDebuggerAgent.h @@ -47,7 +47,8 @@ class InspectorObject; class InspectorValue; enum DebuggerEventType { - DOMBreakpointDebuggerEventType, + JavaScriptPauseEventType, + JavaScriptBreakpointEventType, NativeBreakpointDebuggerEventType }; @@ -66,8 +67,10 @@ public: void editScriptSource(const String& sourceID, const String& newContent, bool* success, String* result, RefPtr<InspectorValue>* newCallFrames); void getScriptSource(const String& sourceID, String* scriptSource); - void pause(); + void schedulePauseOnNextStatement(DebuggerEventType type, PassRefPtr<InspectorValue> data); + void cancelPauseOnNextStatement(); void breakProgram(DebuggerEventType type, PassRefPtr<InspectorValue> data); + void pause(); void resume(); void stepOverStatement(); void stepIntoStatement(); diff --git a/WebCore/inspector/InspectorFrontendClientLocal.cpp b/WebCore/inspector/InspectorFrontendClientLocal.cpp index 77616da..4812b2a 100644 --- a/WebCore/inspector/InspectorFrontendClientLocal.cpp +++ b/WebCore/inspector/InspectorFrontendClientLocal.cpp @@ -47,7 +47,6 @@ namespace WebCore { -static const char* const inspectorAttachedHeightName = "inspectorAttachedHeight"; static const unsigned defaultAttachedHeight = 300; static const float minimumAttachedHeight = 250.0f; static const float maximumAttachedHeightRatio = 0.75f; @@ -108,7 +107,7 @@ void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height) { unsigned totalHeight = m_frontendPage->mainFrame()->view()->visibleHeight() + m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight(); unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight); - m_inspectorController->setSetting(inspectorAttachedHeightName, String::number(attachedHeight)); + m_inspectorController->setInspectorAttachedHeight(attachedHeight); setAttachedWindowHeight(attachedHeight); } @@ -134,10 +133,9 @@ void InspectorFrontendClientLocal::setAttachedWindow(bool attached) void InspectorFrontendClientLocal::restoreAttachedWindowHeight() { unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight(); - String attachedHeight = m_inspectorController->setting(inspectorAttachedHeightName); + int attachedHeight = m_inspectorController->inspectorAttachedHeight(); bool success = true; - int height = attachedHeight.toInt(&success); - unsigned preferredHeight = success ? static_cast<unsigned>(height) : defaultAttachedHeight; + unsigned preferredHeight = success ? static_cast<unsigned>(attachedHeight) : defaultAttachedHeight; // This call might not go through (if the window starts out detached), but if the window is initially created attached, // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight. diff --git a/WebCore/inspector/InspectorInstrumentation.cpp b/WebCore/inspector/InspectorInstrumentation.cpp new file mode 100644 index 0000000..ae89266 --- /dev/null +++ b/WebCore/inspector/InspectorInstrumentation.cpp @@ -0,0 +1,462 @@ +/* +* Copyright (C) 2010 Google Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* * Neither the name of Google Inc. nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" +#include "InspectorInstrumentation.h" + +#if ENABLE(INSPECTOR) + +#include "DOMWindow.h" +#include "Event.h" +#include "InspectorController.h" +#include "InspectorDOMAgent.h" +#include "InspectorDebuggerAgent.h" +#include "InspectorTimelineAgent.h" +#include "XMLHttpRequest.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +static const char* const listenerEventCategoryType = "listener"; +static const char* const instrumentationEventCategoryType = "instrumentation"; + +static const char* const setTimerEventName = "setTimer"; +static const char* const clearTimerEventName = "clearTimer"; +static const char* const timerFiredEventName = "timerFired"; + +int InspectorInstrumentation::s_frontendCounter = 0; + +static bool eventHasListeners(const AtomicString& eventType, DOMWindow* window, Node* node, const Vector<RefPtr<ContainerNode> >& ancestors) +{ + if (window && window->hasEventListeners(eventType)) + return true; + + if (node->hasEventListeners(eventType)) + return true; + + for (size_t i = 0; i < ancestors.size(); i++) { + ContainerNode* ancestor = ancestors[i].get(); + if (ancestor->hasEventListeners(eventType)) + return true; + } + + return false; +} + +void InspectorInstrumentation::willInsertDOMNodeImpl(InspectorController* inspectorController, Node* node, Node* parent) +{ +#if ENABLE(JAVASCRIPT_DEBUGGER) + InspectorDebuggerAgent* debuggerAgent = inspectorController->m_debuggerAgent.get(); + if (!debuggerAgent) + return; + InspectorDOMAgent* domAgent = inspectorController->m_domAgent.get(); + if (!domAgent) + return; + PassRefPtr<InspectorValue> eventData; + if (domAgent->shouldBreakOnNodeInsertion(node, parent, &eventData)) + debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData); +#endif +} + +void InspectorInstrumentation::didInsertDOMNodeImpl(InspectorController* inspectorController, Node* node) +{ + if (InspectorDOMAgent* domAgent = inspectorController->m_domAgent.get()) + domAgent->didInsertDOMNode(node); +} + +void InspectorInstrumentation::willRemoveDOMNodeImpl(InspectorController* inspectorController, Node* node) +{ +#if ENABLE(JAVASCRIPT_DEBUGGER) + InspectorDebuggerAgent* debuggerAgent = inspectorController->m_debuggerAgent.get(); + if (!debuggerAgent) + return; + InspectorDOMAgent* domAgent = inspectorController->m_domAgent.get(); + if (!domAgent) + return; + PassRefPtr<InspectorValue> eventData; + if (domAgent->shouldBreakOnNodeRemoval(node, &eventData)) + debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData); +#endif +} + +void InspectorInstrumentation::didRemoveDOMNodeImpl(InspectorController* inspectorController, Node* node) +{ + if (InspectorDOMAgent* domAgent = inspectorController->m_domAgent.get()) + domAgent->didRemoveDOMNode(node); +} + +void InspectorInstrumentation::willModifyDOMAttrImpl(InspectorController* inspectorController, Element* element) +{ +#if ENABLE(JAVASCRIPT_DEBUGGER) + InspectorDebuggerAgent* debuggerAgent = inspectorController->m_debuggerAgent.get(); + if (!debuggerAgent) + return; + InspectorDOMAgent* domAgent = inspectorController->m_domAgent.get(); + if (!domAgent) + return; + PassRefPtr<InspectorValue> eventData; + if (domAgent->shouldBreakOnAttributeModification(element, &eventData)) + debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData); +#endif +} + +void InspectorInstrumentation::didModifyDOMAttrImpl(InspectorController* inspectorController, Element* element) +{ + if (InspectorDOMAgent* domAgent = inspectorController->m_domAgent.get()) + domAgent->didModifyDOMAttr(element); +} + +void InspectorInstrumentation::characterDataModifiedImpl(InspectorController* inspectorController, CharacterData* characterData) +{ + if (InspectorDOMAgent* domAgent = inspectorController->m_domAgent.get()) + domAgent->characterDataModified(characterData); +} + + +void InspectorInstrumentation::willSendXMLHttpRequestImpl(InspectorController* inspectorController, const String& url) +{ +#if ENABLE(JAVASCRIPT_DEBUGGER) + InspectorDebuggerAgent* debuggerAgent = inspectorController->m_debuggerAgent.get(); + if (!debuggerAgent) + return; + + String breakpointId = inspectorController->findXHRBreakpoint(url); + if (breakpointId.isEmpty()) + return; + + RefPtr<InspectorObject> eventData = InspectorObject::create(); + eventData->setString("breakpointId", breakpointId); + eventData->setString("url", url); + debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData); +#endif +} + +void InspectorInstrumentation::didScheduleResourceRequestImpl(InspectorController* inspectorController, const String& url) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController)) + timelineAgent->didScheduleResourceRequest(url); +} + +void InspectorInstrumentation::didInstallTimerImpl(InspectorController* inspectorController, int timerId, int timeout, bool singleShot) +{ + pauseOnNativeEventIfNeeded(inspectorController, instrumentationEventCategoryType, setTimerEventName, true); + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController)) + timelineAgent->didInstallTimer(timerId, timeout, singleShot); +} + +void InspectorInstrumentation::didRemoveTimerImpl(InspectorController* inspectorController, int timerId) +{ + pauseOnNativeEventIfNeeded(inspectorController, instrumentationEventCategoryType, clearTimerEventName, true); + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController)) + timelineAgent->didRemoveTimer(timerId); +} + + +InspectorInstrumentationCookie InspectorInstrumentation::willCallFunctionImpl(InspectorController* inspectorController, const String& scriptName, int scriptLine) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willCallFunction(scriptName, scriptLine); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didCallFunctionImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didCallFunction(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willChangeXHRReadyStateImpl(InspectorController* inspectorController, XMLHttpRequest* request) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent && request->hasEventListeners(eventNames().readystatechangeEvent)) { + timelineAgent->willChangeXHRReadyState(request->url().string(), request->readyState()); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didChangeXHRReadyStateImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didChangeXHRReadyState(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willDispatchEventImpl(InspectorController* inspectorController, const Event& event, DOMWindow* window, Node* node, const Vector<RefPtr<ContainerNode> >& ancestors) +{ + pauseOnNativeEventIfNeeded(inspectorController, listenerEventCategoryType, event.type(), false); + + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent && eventHasListeners(event.type(), window, node, ancestors)) { + timelineAgent->willDispatchEvent(event); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didDispatchEventImpl(const InspectorInstrumentationCookie& cookie) +{ + cancelPauseOnNativeEvent(cookie.first); + + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didDispatchEvent(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willDispatchEventOnWindowImpl(InspectorController* inspectorController, const Event& event, DOMWindow* window) +{ + pauseOnNativeEventIfNeeded(inspectorController, listenerEventCategoryType, event.type(), false); + + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent && window->hasEventListeners(event.type())) { + timelineAgent->willDispatchEvent(event); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didDispatchEventOnWindowImpl(const InspectorInstrumentationCookie& cookie) +{ + cancelPauseOnNativeEvent(cookie.first); + + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didDispatchEvent(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willEvaluateScriptImpl(InspectorController* inspectorController, const String& url, int lineNumber) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willEvaluateScript(url, lineNumber); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didEvaluateScriptImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didEvaluateScript(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willFireTimerImpl(InspectorController* inspectorController, int timerId) +{ + pauseOnNativeEventIfNeeded(inspectorController, instrumentationEventCategoryType, timerFiredEventName, false); + + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willFireTimer(timerId); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didFireTimerImpl(const InspectorInstrumentationCookie& cookie) +{ + cancelPauseOnNativeEvent(cookie.first); + + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didFireTimer(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willLayoutImpl(InspectorController* inspectorController) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willLayout(); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didLayoutImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didLayout(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willLoadXHRImpl(InspectorController* inspectorController, XMLHttpRequest* request) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent && request->hasEventListeners(eventNames().loadEvent)) { + timelineAgent->willLoadXHR(request->url()); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didLoadXHRImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didLoadXHR(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willPaintImpl(InspectorController* inspectorController, const IntRect& rect) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willPaint(rect); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didPaintImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didPaint(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willRecalculateStyleImpl(InspectorController* inspectorController) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willRecalculateStyle(); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didRecalculateStyleImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didRecalculateStyle(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willReceiveResourceDataImpl(InspectorController* inspectorController, unsigned long identifier) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willReceiveResourceData(identifier); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didReceiveResourceDataImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didReceiveResourceData(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willReceiveResourceResponseImpl(InspectorController* inspectorController, unsigned long identifier, const ResourceResponse& response) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willReceiveResourceResponse(identifier, response); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didReceiveResourceResponseImpl(const InspectorInstrumentationCookie& cookie) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didReceiveResourceResponse(); +} + +InspectorInstrumentationCookie InspectorInstrumentation::willWriteHTMLImpl(InspectorController* inspectorController, unsigned int length, unsigned int startLine) +{ + int timelineAgentId = 0; + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(inspectorController); + if (timelineAgent) { + timelineAgent->willWriteHTML(length, startLine); + timelineAgentId = timelineAgent->id(); + } + return InspectorInstrumentationCookie(inspectorController, timelineAgentId); +} + +void InspectorInstrumentation::didWriteHTMLImpl(const InspectorInstrumentationCookie& cookie, unsigned int endLine) +{ + if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) + timelineAgent->didWriteHTML(endLine); +} + +bool InspectorInstrumentation::hasFrontend(InspectorController* inspectorController) +{ + return inspectorController->hasFrontend(); +} + +void InspectorInstrumentation::pauseOnNativeEventIfNeeded(InspectorController* inspectorController, const String& categoryType, const String& eventName, bool synchronous) +{ +#if ENABLE(JAVASCRIPT_DEBUGGER) + InspectorDebuggerAgent* debuggerAgent = inspectorController->m_debuggerAgent.get(); + if (!debuggerAgent) + return; + String fullEventName = String::format("%s:%s", categoryType.utf8().data(), eventName.utf8().data()); + String breakpointId = inspectorController->findEventListenerBreakpoint(fullEventName); + if (breakpointId.isEmpty()) + return; + RefPtr<InspectorObject> eventData = InspectorObject::create(); + eventData->setString("breakpointId", breakpointId); + if (synchronous) + debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData); + else + debuggerAgent->schedulePauseOnNextStatement(NativeBreakpointDebuggerEventType, eventData); +#endif +} + +void InspectorInstrumentation::cancelPauseOnNativeEvent(InspectorController* inspectorController) +{ +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (InspectorDebuggerAgent* debuggerAgent = inspectorController->m_debuggerAgent.get()) + debuggerAgent->cancelPauseOnNextStatement(); +#endif +} + +InspectorTimelineAgent* InspectorInstrumentation::retrieveTimelineAgent(InspectorController* inspectorController) +{ + return inspectorController->m_timelineAgent.get(); +} + +InspectorTimelineAgent* InspectorInstrumentation::retrieveTimelineAgent(const InspectorInstrumentationCookie& cookie) +{ + InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie.first); + if (timelineAgent && timelineAgent->id() == cookie.second) + return timelineAgent; + return 0; +} + +} // namespace WebCore + +#endif // !ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorInstrumentation.h b/WebCore/inspector/InspectorInstrumentation.h new file mode 100644 index 0000000..e728a5e --- /dev/null +++ b/WebCore/inspector/InspectorInstrumentation.h @@ -0,0 +1,500 @@ +/* +* Copyright (C) 2010 Google Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* * Neither the name of Google Inc. nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef InspectorInstrumentation_h +#define InspectorInstrumentation_h + +#include "Document.h" +#include "Frame.h" +#include "Page.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +class CharacterData; +class Element; +class InspectorController; +class InspectorTimelineAgent; +class Node; +class ResourceRequest; +class ResourceResponse; +class XMLHttpRequest; + +typedef pair<InspectorController*, int> InspectorInstrumentationCookie; + +class InspectorInstrumentation { +public: + static void willInsertDOMNode(Document*, Node*, Node* parent); + static void didInsertDOMNode(Document*, Node*); + static void willRemoveDOMNode(Document*, Node*); + static void willModifyDOMAttr(Document*, Element*); + static void didModifyDOMAttr(Document*, Element*); + static void characterDataModified(Document*, CharacterData*); + + static void willSendXMLHttpRequest(ScriptExecutionContext*, const String& url); + static void didScheduleResourceRequest(Document*, const String& url); + static void didInstallTimer(ScriptExecutionContext*, int timerId, int timeout, bool singleShot); + static void didRemoveTimer(ScriptExecutionContext*, int timerId); + + static InspectorInstrumentationCookie willCallFunction(Frame*, const String& scriptName, int scriptLine); + static void didCallFunction(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willChangeXHRReadyState(ScriptExecutionContext*, XMLHttpRequest* request); + static void didChangeXHRReadyState(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willDispatchEvent(Document*, const Event& event, DOMWindow* window, Node* node, const Vector<RefPtr<ContainerNode> >& ancestors); + static void didDispatchEvent(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willDispatchEventOnWindow(Frame*, const Event& event, DOMWindow* window); + static void didDispatchEventOnWindow(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willEvaluateScript(Frame*, const String& url, int lineNumber); + static void didEvaluateScript(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willFireTimer(ScriptExecutionContext*, int timerId); + static void didFireTimer(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willLayout(Frame*); + static void didLayout(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willLoadXHR(ScriptExecutionContext*, XMLHttpRequest*); + static void didLoadXHR(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willPaint(Frame*, const IntRect& rect); + static void didPaint(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willRecalculateStyle(Document*); + static void didRecalculateStyle(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willReceiveResourceData(Frame*, unsigned long identifier); + static void didReceiveResourceData(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willReceiveResourceResponse(Frame*, unsigned long identifier, const ResourceResponse& response); + static void didReceiveResourceResponse(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willWriteHTML(Document*, unsigned int length, unsigned int startLine); + static void didWriteHTML(const InspectorInstrumentationCookie&, unsigned int endLine); + +#if ENABLE(INSPECTOR) + static void frontendCreated() { s_frontendCounter += 1; } + static void frontendDeleted() { s_frontendCounter -= 1; } + static bool hasFrontends() { return s_frontendCounter; } +#else + static bool hasFrontends() { return false; } +#endif + +private: +#if ENABLE(INSPECTOR) + static void willInsertDOMNodeImpl(InspectorController*, Node* node, Node* parent); + static void didInsertDOMNodeImpl(InspectorController*, Node*); + static void willRemoveDOMNodeImpl(InspectorController*, Node*); + static void didRemoveDOMNodeImpl(InspectorController*, Node*); + static void willModifyDOMAttrImpl(InspectorController*, Element*); + static void didModifyDOMAttrImpl(InspectorController*, Element*); + static void characterDataModifiedImpl(InspectorController*, CharacterData*); + + static void willSendXMLHttpRequestImpl(InspectorController*, const String& url); + static void didScheduleResourceRequestImpl(InspectorController*, const String& url); + static void didInstallTimerImpl(InspectorController*, int timerId, int timeout, bool singleShot); + static void didRemoveTimerImpl(InspectorController*, int timerId); + + static InspectorInstrumentationCookie willCallFunctionImpl(InspectorController*, const String& scriptName, int scriptLine); + static void didCallFunctionImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willChangeXHRReadyStateImpl(InspectorController*, XMLHttpRequest* request); + static void didChangeXHRReadyStateImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willDispatchEventImpl(InspectorController*, const Event& event, DOMWindow* window, Node* node, const Vector<RefPtr<ContainerNode> >& ancestors); + static void didDispatchEventImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willDispatchEventOnWindowImpl(InspectorController*, const Event& event, DOMWindow* window); + static void didDispatchEventOnWindowImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willEvaluateScriptImpl(InspectorController*, const String& url, int lineNumber); + static void didEvaluateScriptImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willFireTimerImpl(InspectorController*, int timerId); + static void didFireTimerImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willLayoutImpl(InspectorController*); + static void didLayoutImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willLoadXHRImpl(InspectorController*, XMLHttpRequest* request); + static void didLoadXHRImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willPaintImpl(InspectorController*, const IntRect& rect); + static void didPaintImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willRecalculateStyleImpl(InspectorController*); + static void didRecalculateStyleImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willReceiveResourceDataImpl(InspectorController*, unsigned long identifier); + static void didReceiveResourceDataImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willReceiveResourceResponseImpl(InspectorController*, unsigned long identifier, const ResourceResponse& response); + static void didReceiveResourceResponseImpl(const InspectorInstrumentationCookie&); + static InspectorInstrumentationCookie willWriteHTMLImpl(InspectorController*, unsigned int length, unsigned int startLine); + static void didWriteHTMLImpl(const InspectorInstrumentationCookie&, unsigned int endLine); + + static InspectorController* inspectorControllerForContext(ScriptExecutionContext*); + static InspectorController* inspectorControllerForDocument(Document*); + static InspectorController* inspectorControllerForFrame(Frame*); + static InspectorController* inspectorControllerForPage(Page*); + + static bool hasFrontend(InspectorController*); + static void pauseOnNativeEventIfNeeded(InspectorController*, const String& categoryType, const String& eventName, bool synchronous); + static void cancelPauseOnNativeEvent(InspectorController*); + static InspectorTimelineAgent* retrieveTimelineAgent(InspectorController*); + static InspectorTimelineAgent* retrieveTimelineAgent(const InspectorInstrumentationCookie&); + + static int s_frontendCounter; +#endif +}; + +inline void InspectorInstrumentation::willInsertDOMNode(Document* document, Node* node, Node* parent) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + willInsertDOMNodeImpl(inspectorController, node, parent); +#endif +} + +inline void InspectorInstrumentation::didInsertDOMNode(Document* document, Node* node) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + didInsertDOMNodeImpl(inspectorController, node); +#endif +} + +inline void InspectorInstrumentation::willRemoveDOMNode(Document* document, Node* node) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) { + willRemoveDOMNodeImpl(inspectorController, node); + didRemoveDOMNodeImpl(inspectorController, node); + } +#endif +} + +inline void InspectorInstrumentation::willModifyDOMAttr(Document* document, Element* element) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + willModifyDOMAttrImpl(inspectorController, element); +#endif +} + +inline void InspectorInstrumentation::didModifyDOMAttr(Document* document, Element* element) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + didModifyDOMAttrImpl(inspectorController, element); +#endif +} + +inline void InspectorInstrumentation::characterDataModified(Document* document, CharacterData* characterData) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + characterDataModifiedImpl(inspectorController, characterData); +#endif +} + + +inline void InspectorInstrumentation::willSendXMLHttpRequest(ScriptExecutionContext* context, const String& url) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForContext(context)) + willSendXMLHttpRequestImpl(inspectorController, url); +#endif +} + +inline void InspectorInstrumentation::didScheduleResourceRequest(Document* document, const String& url) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + didScheduleResourceRequestImpl(inspectorController, url); +#endif +} + +inline void InspectorInstrumentation::didInstallTimer(ScriptExecutionContext* context, int timerId, int timeout, bool singleShot) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForContext(context)) + didInstallTimerImpl(inspectorController, timerId, timeout, singleShot); +#endif +} + +inline void InspectorInstrumentation::didRemoveTimer(ScriptExecutionContext* context, int timerId) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForContext(context)) + didRemoveTimerImpl(inspectorController, timerId); +#endif +} + + +inline InspectorInstrumentationCookie InspectorInstrumentation::willCallFunction(Frame* frame, const String& scriptName, int scriptLine) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForFrame(frame)) + return willCallFunctionImpl(inspectorController, scriptName, scriptLine); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didCallFunction(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didCallFunctionImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willChangeXHRReadyState(ScriptExecutionContext* context, XMLHttpRequest* request) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForContext(context)) + return willChangeXHRReadyStateImpl(inspectorController, request); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didChangeXHRReadyState(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didChangeXHRReadyStateImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willDispatchEvent(Document* document, const Event& event, DOMWindow* window, Node* node, const Vector<RefPtr<ContainerNode> >& ancestors) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + return willDispatchEventImpl(inspectorController, event, window, node, ancestors); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didDispatchEvent(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didDispatchEventImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willDispatchEventOnWindow(Frame* frame, const Event& event, DOMWindow* window) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForFrame(frame)) + return willDispatchEventOnWindowImpl(inspectorController, event, window); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didDispatchEventOnWindow(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didDispatchEventOnWindowImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willEvaluateScript(Frame* frame, const String& url, int lineNumber) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForFrame(frame)) + return willEvaluateScriptImpl(inspectorController, url, lineNumber); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didEvaluateScript(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didEvaluateScriptImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willFireTimer(ScriptExecutionContext* context, int timerId) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForContext(context)) + return willFireTimerImpl(inspectorController, timerId); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didFireTimer(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didFireTimerImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willLayout(Frame* frame) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForFrame(frame)) + return willLayoutImpl(inspectorController); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didLayout(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didLayoutImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willLoadXHR(ScriptExecutionContext* context, XMLHttpRequest* request) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForContext(context)) + return willLoadXHRImpl(inspectorController, request); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didLoadXHR(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didLoadXHRImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willPaint(Frame* frame, const IntRect& rect) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForFrame(frame)) + return willPaintImpl(inspectorController, rect); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didPaint(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didPaintImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willRecalculateStyle(Document* document) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + return willRecalculateStyleImpl(inspectorController); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didRecalculateStyle(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didRecalculateStyleImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willReceiveResourceData(Frame* frame, unsigned long identifier) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForFrame(frame)) + return willReceiveResourceDataImpl(inspectorController, identifier); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didReceiveResourceData(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didReceiveResourceDataImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willReceiveResourceResponse(Frame* frame, unsigned long identifier, const ResourceResponse& response) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForFrame(frame)) + return willReceiveResourceResponseImpl(inspectorController, identifier, response); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didReceiveResourceResponse(const InspectorInstrumentationCookie& cookie) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didReceiveResourceResponseImpl(cookie); +#endif +} + +inline InspectorInstrumentationCookie InspectorInstrumentation::willWriteHTML(Document* document, unsigned int length, unsigned int startLine) +{ +#if ENABLE(INSPECTOR) + if (InspectorController* inspectorController = inspectorControllerForDocument(document)) + return willWriteHTMLImpl(inspectorController, length, startLine); +#endif + return InspectorInstrumentationCookie(); +} + +inline void InspectorInstrumentation::didWriteHTML(const InspectorInstrumentationCookie& cookie, unsigned int endLine) +{ +#if ENABLE(INSPECTOR) + if (hasFrontends() && cookie.first) + didWriteHTMLImpl(cookie, endLine); +#endif +} + + +#if ENABLE(INSPECTOR) +inline InspectorController* InspectorInstrumentation::inspectorControllerForContext(ScriptExecutionContext* context) +{ + if (hasFrontends() && context && context->isDocument()) + return inspectorControllerForPage(static_cast<Document*>(context)->page()); + return 0; +} + +inline InspectorController* InspectorInstrumentation::inspectorControllerForDocument(Document* document) +{ + if (hasFrontends() && document) + return inspectorControllerForPage(document->page()); + return 0; +} + +inline InspectorController* InspectorInstrumentation::inspectorControllerForFrame(Frame* frame) +{ + if (hasFrontends() && frame) + return inspectorControllerForPage(frame->page()); + return 0; +} + +inline InspectorController* InspectorInstrumentation::inspectorControllerForPage(Page* page) +{ + if (page) { + if (InspectorController* inspectorController = page->inspectorController()) { + if (hasFrontend(inspectorController)) + return inspectorController; + } + } + return 0; +} +#endif + +} // namespace WebCore + +#endif // !defined(InspectorInstrumentation_h) diff --git a/WebCore/inspector/InspectorProfilerAgent.cpp b/WebCore/inspector/InspectorProfilerAgent.cpp index 09b2b32..71c7231 100644 --- a/WebCore/inspector/InspectorProfilerAgent.cpp +++ b/WebCore/inspector/InspectorProfilerAgent.cpp @@ -43,7 +43,7 @@ #include "ScriptProfile.h" #include "ScriptProfiler.h" #include <wtf/OwnPtr.h> -#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #if USE(JSC) #include "JSDOMWindow.h" @@ -89,13 +89,13 @@ void InspectorProfilerAgent::addProfileFinishedMessageToConsole(PassRefPtr<Scrip { RefPtr<ScriptProfile> profile = prpProfile; String title = profile->title(); - String message = String::format("Profile \"webkit-profile://%s/%s#%d\" finished.", CPUProfileType, encodeWithURLEscapeSequences(title).utf8().data(), profile->uid()); + String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), '#', String::number(profile->uid()), "\" finished."); m_inspectorController->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL); } void InspectorProfilerAgent::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL) { - String message = String::format("Profile \"webkit-profile://%s/%s#0\" started.", CPUProfileType, encodeWithURLEscapeSequences(title).utf8().data()); + String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), "#0 \" started."); m_inspectorController->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL); } @@ -143,7 +143,7 @@ String InspectorProfilerAgent::getCurrentUserInitiatedProfileName(bool increment if (incrementProfileNumber) m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++; - return String::format("%s.%d", UserInitiatedProfileName, m_currentUserInitiatedProfileNumber); + return makeString(UserInitiatedProfileName, '.', String::number(m_currentUserInitiatedProfileNumber)); } void InspectorProfilerAgent::getProfileHeaders(RefPtr<InspectorArray>* headers) @@ -232,7 +232,9 @@ void InspectorProfilerAgent::stopUserInitiatedProfiling() void InspectorProfilerAgent::takeHeapSnapshot() { - String title = String::format("%s.%d", UserInitiatedProfileName, m_nextUserInitiatedHeapSnapshotNumber++); + String title = makeString(UserInitiatedProfileName, '.', String::number(m_nextUserInitiatedHeapSnapshotNumber)); + ++m_nextUserInitiatedHeapSnapshotNumber; + RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title); if (snapshot) { m_snapshots.add(snapshot->uid(), snapshot); diff --git a/WebCore/inspector/InspectorResource.cpp b/WebCore/inspector/InspectorResource.cpp index ac2c9a0..05b1bff 100644 --- a/WebCore/inspector/InspectorResource.cpp +++ b/WebCore/inspector/InspectorResource.cpp @@ -33,22 +33,24 @@ #if ENABLE(INSPECTOR) +#include "Base64.h" #include "Cache.h" #include "CachedResource.h" #include "CachedResourceLoader.h" #include "DocumentLoader.h" #include "Frame.h" #include "InspectorFrontend.h" +#include "InspectorResourceAgent.h" #include "InspectorValues.h" #include "ResourceLoadTiming.h" #include "ResourceRequest.h" #include "ResourceResponse.h" -#include "StringBuffer.h" #include "TextEncoding.h" #include "WebSocketHandshakeRequest.h" #include "WebSocketHandshakeResponse.h" #include <wtf/Assertions.h> +#include <wtf/text/StringBuffer.h> namespace WebCore { @@ -86,8 +88,6 @@ InspectorResource::InspectorResource(unsigned long identifier, DocumentLoader* l , m_startTime(-1.0) , m_responseReceivedTime(-1.0) , m_endTime(-1.0) - , m_loadEventTime(-1.0) - , m_domContentEventTime(-1.0) , m_connectionID(0) , m_connectionReused(false) , m_isMainResource(false) @@ -147,6 +147,7 @@ PassRefPtr<InspectorResource> InspectorResource::createWebSocket(unsigned long i RefPtr<InspectorResource> resource = adoptRef(new InspectorResource(identifier, 0, requestURL)); resource->markWebSocket(); resource->m_documentURL = documentURL; + resource->m_changes.setAll(); return resource.release(); } #endif @@ -175,7 +176,12 @@ void InspectorResource::updateResponse(const ResourceResponse& response) if (cachedResource) m_mimeType = cachedResource->response().mimeType(); } - m_responseHeaderFields = response.httpHeaderFields(); + if (ResourceRawHeaders* headers = response.resourceRawHeaders().get()) { + m_requestHeaderFields = headers->requestHeaders; + m_responseHeaderFields = headers->responseHeaders; + m_changes.set(RequestChange); + } else + m_responseHeaderFields = response.httpHeaderFields(); m_responseStatusCode = response.httpStatusCode(); m_responseStatusText = response.httpStatusText(); m_suggestedFilename = response.suggestedFilename(); @@ -184,12 +190,7 @@ void InspectorResource::updateResponse(const ResourceResponse& response) m_connectionReused = response.connectionReused(); m_loadTiming = response.resourceLoadTiming(); m_cached = m_cached || response.wasCached(); - - if (!m_cached && m_loadTiming && m_loadTiming->requestTime) - m_responseReceivedTime = m_loadTiming->requestTime + m_loadTiming->receiveHeadersEnd / 1000.0; - else - m_responseReceivedTime = currentTime(); - + m_responseReceivedTime = currentTime(); m_changes.set(TimingChange); m_changes.set(ResponseChange); m_changes.set(TypeChange); @@ -257,9 +258,6 @@ void InspectorResource::updateScriptObject(InspectorFrontend* frontend) m_documentURL = m_frame->document()->url(); jsonObject->setString("url", m_requestURL.string()); jsonObject->setString("documentURL", m_documentURL.string()); - jsonObject->setString("host", m_requestURL.host()); - jsonObject->setString("path", m_requestURL.path()); - jsonObject->setString("lastPathComponent", m_requestURL.lastPathComponent()); RefPtr<InspectorObject> requestHeaders = buildHeadersObject(m_requestHeaderFields); jsonObject->setObject("requestHeaders", requestHeaders); jsonObject->setBoolean("mainResource", m_isMainResource); @@ -315,10 +313,6 @@ void InspectorResource::updateScriptObject(InspectorFrontend* frontend) jsonObject->setNumber("responseReceivedTime", m_responseReceivedTime); if (m_endTime > 0) jsonObject->setNumber("endTime", m_endTime); - if (m_loadEventTime > 0) - jsonObject->setNumber("loadEventTime", m_loadEventTime); - if (m_domContentEventTime > 0) - jsonObject->setNumber("domContentEventTime", m_domContentEventTime); jsonObject->setBoolean("didTimingChange", true); } @@ -342,41 +336,25 @@ void InspectorResource::releaseScriptObject(InspectorFrontend* frontend) frontend->removeResource(m_identifier); } -CachedResource* InspectorResource::cachedResource() const +static InspectorResource::Type cachedResourceType(CachedResource* cachedResource) { - // Try hard to find a corresponding CachedResource. During preloading, CachedResourceLoader may not have the resource in document resources set yet, - // but Inspector will already try to fetch data that is only available via CachedResource (and it won't update once the resource is added, - // because m_changes will not have the appropriate bits set). - if (!m_frame) - return 0; - const String& url = m_requestURL.string(); - CachedResource* cachedResource = m_frame->document()->cachedResourceLoader()->cachedResource(url); if (!cachedResource) - cachedResource = cache()->resourceForURL(url); - return cachedResource; -} - -InspectorResource::Type InspectorResource::cachedResourceType() const -{ - CachedResource* cachedResource = this->cachedResource(); - - if (!cachedResource) - return Other; + return InspectorResource::Other; switch (cachedResource->type()) { - case CachedResource::ImageResource: - return Image; - case CachedResource::FontResource: - return Font; - case CachedResource::CSSStyleSheet: + case CachedResource::ImageResource: + return InspectorResource::Image; + case CachedResource::FontResource: + return InspectorResource::Font; + case CachedResource::CSSStyleSheet: #if ENABLE(XSLT) - case CachedResource::XSLStyleSheet: + case CachedResource::XSLStyleSheet: #endif - return Stylesheet; - case CachedResource::Script: - return Script; - default: - return Other; + return InspectorResource::Stylesheet; + case CachedResource::Script: + return InspectorResource::Script; + default: + return InspectorResource::Other; } } @@ -391,21 +369,21 @@ InspectorResource::Type InspectorResource::type() const #endif ASSERT(m_loader); - if (equalIgnoringFragmentIdentifier(m_requestURL, m_loader->requestURL())) { - InspectorResource::Type resourceType = cachedResourceType(); - if (resourceType == Other) - return Doc; - - return resourceType; - } if (m_loader->frameLoader() && equalIgnoringFragmentIdentifier(m_requestURL, m_loader->frameLoader()->iconURL())) return Image; - return cachedResourceType(); + if (!m_frame) + return Other; + + InspectorResource::Type resourceType = cachedResourceType(InspectorResourceAgent::cachedResource(m_frame.get(), m_requestURL)); + if (equalIgnoringFragmentIdentifier(m_requestURL, m_loader->requestURL()) && resourceType == Other) + return Doc; + + return resourceType; } -void InspectorResource::setOverrideContent(const ScriptString& data, Type type) +void InspectorResource::setOverrideContent(const String& data, Type type) { m_overrideContent = data; m_overrideContentType = type; @@ -417,41 +395,27 @@ String InspectorResource::sourceString() const if (!m_overrideContent.isNull()) return String(m_overrideContent); - String textEncodingName; - RefPtr<SharedBuffer> buffer = resourceData(&textEncodingName); - if (!buffer) + String result; + if (!InspectorResourceAgent::resourceContent(m_frame.get(), m_requestURL, &result)) return String(); - - TextEncoding encoding(textEncodingName); - if (!encoding.isValid()) - encoding = WindowsLatin1Encoding(); - return encoding.decode(buffer->data(), buffer->size()); + return result; } -PassRefPtr<SharedBuffer> InspectorResource::resourceData(String* textEncodingName) const +String InspectorResource::sourceBytes() const { - if (m_loader && equalIgnoringFragmentIdentifier(m_requestURL, m_loader->requestURL())) { - *textEncodingName = m_frame->document()->inputEncoding(); - return m_loader->mainResourceData(); - } - - CachedResource* cachedResource = this->cachedResource(); - if (!cachedResource) - return 0; - - if (cachedResource->isPurgeable()) { - // If the resource is purgeable then make it unpurgeable to get - // get its data. This might fail, in which case we return an - // empty String. - // FIXME: should we do something else in the case of a purged - // resource that informs the user why there is no data in the - // inspector? - if (!cachedResource->makePurgeable(false)) - return 0; + Vector<char> out; + if (!m_overrideContent.isNull()) { + Vector<char> data; + String overrideContent = m_overrideContent; + data.append(overrideContent.characters(), overrideContent.length()); + base64Encode(data, out); + return String(out.data(), out.size()); } - *textEncodingName = cachedResource->encoding(); - return cachedResource->data(); + String result; + if (!InspectorResourceAgent::resourceContentBase64(m_frame.get(), m_requestURL, &result)) + return String(); + return result; } void InspectorResource::startTiming() @@ -462,14 +426,9 @@ void InspectorResource::startTiming() void InspectorResource::endTiming(double actualEndTime) { - if (actualEndTime) { + if (actualEndTime) m_endTime = actualEndTime; - // In case of fast load (or in case of cached resources), endTime on network stack - // can be less then m_responseReceivedTime measured in WebCore. Normalize it here, - // prefer actualEndTime to m_responseReceivedTime. - if (m_endTime < m_responseReceivedTime) - m_responseReceivedTime = m_endTime; - } else + else m_endTime = currentTime(); m_finished = true; @@ -477,18 +436,6 @@ void InspectorResource::endTiming(double actualEndTime) m_changes.set(CompletionChange); } -void InspectorResource::markDOMContentEventTime() -{ - m_domContentEventTime = currentTime(); - m_changes.set(TimingChange); -} - -void InspectorResource::markLoadEventTime() -{ - m_loadEventTime = currentTime(); - m_changes.set(TimingChange); -} - void InspectorResource::markFailed() { m_failed = true; diff --git a/WebCore/inspector/InspectorResource.h b/WebCore/inspector/InspectorResource.h index 914232e..aa6001f 100644 --- a/WebCore/inspector/InspectorResource.h +++ b/WebCore/inspector/InspectorResource.h @@ -33,7 +33,6 @@ #include "HTTPHeaderMap.h" #include "KURL.h" -#include "ScriptString.h" #include "WebSocketHandshakeRequest.h" #include "WebSocketHandshakeResponse.h" @@ -98,10 +97,9 @@ namespace WebCore { void updateWebSocketResponse(const WebSocketHandshakeResponse&); #endif - void setOverrideContent(const ScriptString& data, Type); - + void setOverrideContent(const String& data, Type); String sourceString() const; - PassRefPtr<SharedBuffer> resourceData(String* textEncodingName) const; + String sourceBytes() const; bool isSameLoader(DocumentLoader* loader) const { return loader == m_loader; } void markMainResource() { m_isMainResource = true; } @@ -116,8 +114,6 @@ namespace WebCore { String requestFormData() const { return m_requestFormData; } void startTiming(); - void markLoadEventTime(); - void markDOMContentEventTime(); void endTiming(double actualEndTime); void markFailed(); @@ -162,9 +158,6 @@ namespace WebCore { InspectorResource(unsigned long identifier, DocumentLoader*, const KURL& requestURL); Type type() const; - Type cachedResourceType() const; - CachedResource* cachedResource() const; - #if ENABLE(WEB_SOCKETS) void markWebSocket() { m_isWebSocket = true; } #endif @@ -188,12 +181,10 @@ namespace WebCore { double m_startTime; double m_responseReceivedTime; double m_endTime; - double m_loadEventTime; - double m_domContentEventTime; unsigned m_connectionID; bool m_connectionReused; RefPtr<ResourceLoadTiming> m_loadTiming; - ScriptString m_overrideContent; + String m_overrideContent; Type m_overrideContentType; Changes m_changes; bool m_isMainResource; diff --git a/WebCore/inspector/InspectorResourceAgent.cpp b/WebCore/inspector/InspectorResourceAgent.cpp new file mode 100644 index 0000000..f49e1d7 --- /dev/null +++ b/WebCore/inspector/InspectorResourceAgent.cpp @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorResourceAgent.h" + +#include "Base64.h" +#include "Cache.h" +#include "CachedResource.h" +#include "CachedResourceLoader.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTTPHeaderMap.h" +#include "InspectorFrontend.h" +#include "InspectorValues.h" +#include "KURL.h" +#include "Page.h" +#include "ProgressTracker.h" +#include "ResourceError.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "SharedBuffer.h" +#include "TextEncoding.h" +#include "WebSocketHandshakeRequest.h" +#include "WebSocketHandshakeResponse.h" + +#include <wtf/ListHashSet.h> +#include <wtf/RefPtr.h> +#include <wtf/text/StringBuffer.h> + +#if ENABLE(INSPECTOR) + +namespace WebCore { + +bool InspectorResourceAgent::resourceContent(Frame* frame, const KURL& url, String* result) +{ + if (!frame) + return false; + + String textEncodingName; + RefPtr<SharedBuffer> buffer = InspectorResourceAgent::resourceData(frame, url, &textEncodingName); + + if (buffer) { + TextEncoding encoding(textEncodingName); + if (!encoding.isValid()) + encoding = WindowsLatin1Encoding(); + *result = encoding.decode(buffer->data(), buffer->size()); + return true; + } + + return false; +} + +bool InspectorResourceAgent::resourceContentBase64(Frame* frame, const KURL& url, String* result) +{ + Vector<char> out; + String textEncodingName; + RefPtr<SharedBuffer> data = InspectorResourceAgent::resourceData(frame, url, &textEncodingName); + if (!data) { + *result = String(); + return false; + } + + base64Encode(data->buffer(), out); + *result = String(out.data(), out.size()); + return true; +} + +PassRefPtr<SharedBuffer> InspectorResourceAgent::resourceData(Frame* frame, const KURL& url, String* textEncodingName) +{ + FrameLoader* frameLoader = frame->loader(); + DocumentLoader* loader = frameLoader->documentLoader(); + if (equalIgnoringFragmentIdentifier(url, loader->url())) { + *textEncodingName = frame->document()->inputEncoding(); + return frameLoader->documentLoader()->mainResourceData(); + } + + CachedResource* cachedResource = InspectorResourceAgent::cachedResource(frame, url); + if (!cachedResource) + return 0; + + if (cachedResource->isPurgeable()) { + // If the resource is purgeable then make it unpurgeable to get + // get its data. This might fail, in which case we return an + // empty String. + // FIXME: should we do something else in the case of a purged + // resource that informs the user why there is no data in the + // inspector? + if (!cachedResource->makePurgeable(false)) + return 0; + } + + *textEncodingName = cachedResource->encoding(); + return cachedResource->data(); +} + +CachedResource* InspectorResourceAgent::cachedResource(Frame* frame, const KURL& url) +{ + const String& urlString = url.string(); + CachedResource* cachedResource = frame->document()->cachedResourceLoader()->cachedResource(urlString); + if (!cachedResource) + cachedResource = cache()->resourceForURL(urlString); + return cachedResource; +} + +static PassRefPtr<InspectorObject> buildObjectForHeaders(const HTTPHeaderMap& headers) +{ + RefPtr<InspectorObject> headersObject = InspectorObject::create(); + HTTPHeaderMap::const_iterator end = headers.end(); + for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) + headersObject->setString(it->first.string(), it->second); + return headersObject; +} + +static PassRefPtr<InspectorObject> buildObjectForTiming(const ResourceLoadTiming& timing) +{ + RefPtr<InspectorObject> timingObject = InspectorObject::create(); + timingObject->setNumber("requestTime", timing.requestTime); + timingObject->setNumber("proxyStart", timing.proxyStart); + timingObject->setNumber("proxyEnd", timing.proxyEnd); + timingObject->setNumber("dnsStart", timing.dnsStart); + timingObject->setNumber("dnsEnd", timing.dnsEnd); + timingObject->setNumber("connectStart", timing.connectStart); + timingObject->setNumber("connectEnd", timing.connectEnd); + timingObject->setNumber("sslStart", timing.sslStart); + timingObject->setNumber("sslEnd", timing.sslEnd); + timingObject->setNumber("sendStart", timing.sendStart); + timingObject->setNumber("sendEnd", timing.sendEnd); + timingObject->setNumber("receiveHeadersEnd", timing.receiveHeadersEnd); + return timingObject; +} + +static PassRefPtr<InspectorObject> buildObjectForResourceRequest(const ResourceRequest& request) +{ + RefPtr<InspectorObject> requestObject = InspectorObject::create(); + requestObject->setString("url", request.url().string()); + requestObject->setString("httpMethod", request.httpMethod()); + requestObject->setObject("httpHeaderFields", buildObjectForHeaders(request.httpHeaderFields())); + if (request.httpBody() && !request.httpBody()->isEmpty()) + requestObject->setString("requestFormData", request.httpBody()->flattenToString()); + return requestObject; +} + +static PassRefPtr<InspectorObject> buildObjectForResourceResponse(const ResourceResponse& response) +{ + RefPtr<InspectorObject> responseObject = InspectorObject::create(); + if (response.isNull()) { + responseObject->setBoolean("isNull", true); + return responseObject; + } + responseObject->setString("url", response.url().string()); + responseObject->setString("mimeType", response.mimeType()); + responseObject->setNumber("expectedContentLength", response.expectedContentLength()); + responseObject->setString("textEncodingName", response.textEncodingName()); + responseObject->setString("suggestedFilename", response.suggestedFilename()); + responseObject->setNumber("httpStatusCode", response.httpStatusCode()); + responseObject->setString("httpStatusText", response.httpStatusText()); + responseObject->setObject("httpHeaderFields", buildObjectForHeaders(response.httpHeaderFields())); + responseObject->setBoolean("connectionReused", response.connectionReused()); + responseObject->setNumber("connectionID", response.connectionID()); + responseObject->setBoolean("wasCached", response.wasCached()); + if (response.resourceLoadTiming()) + responseObject->setObject("timing", buildObjectForTiming(*response.resourceLoadTiming())); + if (response.resourceRawHeaders()) { + RefPtr<InspectorObject> rawHeadersObject = InspectorObject::create(); + rawHeadersObject->setObject("requestHeaders", buildObjectForHeaders(response.resourceRawHeaders()->requestHeaders)); + rawHeadersObject->setObject("responseHeaders", buildObjectForHeaders(response.resourceRawHeaders()->responseHeaders)); + responseObject->setObject("rawHeaders", rawHeadersObject); + } + return responseObject; +} + +static PassRefPtr<InspectorObject> buildObjectForMainResource(Frame* frame) +{ + FrameLoader* frameLoader = frame->loader(); + DocumentLoader* loader = frameLoader->documentLoader(); + + RefPtr<InspectorObject> resourceObject = InspectorObject::create(); + resourceObject->setString("url", loader->url().string()); + resourceObject->setString("host", loader->url().host()); + resourceObject->setString("path", loader->url().path()); + resourceObject->setString("lastPathComponent", loader->url().lastPathComponent()); + resourceObject->setString("type", "Document"); + resourceObject->setObject("request", buildObjectForResourceRequest(loader->request())); + resourceObject->setObject("response", buildObjectForResourceResponse(loader->response())); + return resourceObject; +} + +static String cachedResourceTypeString(const CachedResource& cachedResource) +{ + switch (cachedResource.type()) { + case CachedResource::ImageResource: + return "Image"; + break; + case CachedResource::FontResource: + return "Font"; + case CachedResource::CSSStyleSheet: + // Fall through. +#if ENABLE(XSLT) + case CachedResource::XSLStyleSheet: +#endif + return "Stylesheet"; + case CachedResource::Script: + return "Script"; + default: + return "Other"; + } +} + +static PassRefPtr<InspectorObject> buildObjectForCachedResource(const CachedResource& cachedResource) +{ + RefPtr<InspectorObject> resourceObject = InspectorObject::create(); + resourceObject->setString("url", cachedResource.url()); + resourceObject->setString("type", cachedResourceTypeString(cachedResource)); + resourceObject->setNumber("encodedSize", cachedResource.encodedSize()); + resourceObject->setObject("response", buildObjectForResourceResponse(cachedResource.response())); + return resourceObject; +} + +static PassRefPtr<InspectorObject> buildObjectForFrameResources(Frame* frame) +{ + RefPtr<InspectorObject> frameResources = InspectorObject::create(); + frameResources->setNumber("frameID", reinterpret_cast<uintptr_t>(frame)); + frameResources->setObject("mainResource", buildObjectForMainResource(frame)); + RefPtr<InspectorArray> subresources = InspectorArray::create(); + frameResources->setArray("subresources", subresources); + + const CachedResourceLoader::DocumentResourceMap& allResources = frame->document()->cachedResourceLoader()->allCachedResources(); + CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end(); + for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) { + CachedResource* cachedResource = it->second.get(); + if (cachedResource) + subresources->pushValue(buildObjectForCachedResource(*cachedResource)); + } + return frameResources; +} + +InspectorResourceAgent::~InspectorResourceAgent() +{ +} + +void InspectorResourceAgent::identifierForInitialRequest(unsigned long identifier, const KURL& url, DocumentLoader* loader, bool isMainResource) +{ + m_frontend->identifierForInitialRequest(identifier, url.string(), reinterpret_cast<uintptr_t>(loader->frame()), isMainResource); +} + +void InspectorResourceAgent::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + m_frontend->willSendRequest(identifier, currentTime(), buildObjectForResourceRequest(request), buildObjectForResourceResponse(redirectResponse)); +} + +void InspectorResourceAgent::markResourceAsCached(unsigned long identifier) +{ + m_frontend->markResourceAsCached(identifier); +} + +void InspectorResourceAgent::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response) +{ + String type = "Other"; + if (loader) { + if (equalIgnoringFragmentIdentifier(response.url(), loader->frameLoader()->iconURL())) + type = "Image"; + else { + CachedResource* cachedResource = InspectorResourceAgent::cachedResource(loader->frame(), response.url()); + if (cachedResource) + type = cachedResourceTypeString(*cachedResource); + + if (equalIgnoringFragmentIdentifier(response.url(), loader->url()) && type == "Other") + type = "Document"; + } + } + m_frontend->didReceiveResponse(identifier, currentTime(), type, buildObjectForResourceResponse(response)); +} + +void InspectorResourceAgent::didReceiveContentLength(unsigned long identifier, int lengthReceived) +{ + m_frontend->didReceiveContentLength(identifier, currentTime(), lengthReceived); +} + +void InspectorResourceAgent::didFinishLoading(unsigned long identifier, double finishTime) +{ + if (!finishTime) + finishTime = currentTime(); + + m_frontend->didFinishLoading(identifier, finishTime); +} + +void InspectorResourceAgent::didFailLoading(unsigned long identifier, const ResourceError& error) +{ + m_frontend->didFailLoading(identifier, currentTime(), error.localizedDescription()); +} + +void InspectorResourceAgent::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* resource) +{ + Frame* frame = loader->frame(); + m_frontend->didLoadResourceFromMemoryCache(currentTime(), reinterpret_cast<uintptr_t>(frame), buildObjectForCachedResource(*resource)); +} + +void InspectorResourceAgent::setOverrideContent(unsigned long identifier, const String& sourceString, InspectorResource::Type type) +{ + String typeString; + switch (type) { + case InspectorResource::XHR: + typeString = "XHR"; + break; + case InspectorResource::Script: + typeString = "Script"; + break; + default: + typeString = "Other"; + } + + m_frontend->setOverrideContent(identifier, sourceString, typeString); +} + +void InspectorResourceAgent::didCommitLoad(DocumentLoader* loader) +{ + Frame* frame = loader->frame(); + m_frontend->didCommitLoad(reinterpret_cast<uintptr_t>(frame)); +} + +void InspectorResourceAgent::frameDetachedFromParent(Frame* frame) +{ + m_frontend->frameDetachedFromParent(reinterpret_cast<uintptr_t>(frame)); +} + + +#if ENABLE(WEB_SOCKETS) + +// FIXME: More this into the front-end? +// Create human-readable binary representation, like "01:23:45:67:89:AB:CD:EF". +static String createReadableStringFromBinary(const unsigned char* value, size_t length) +{ + ASSERT(length > 0); + static const char hexDigits[17] = "0123456789ABCDEF"; + size_t bufferSize = length * 3 - 1; + StringBuffer buffer(bufferSize); + size_t index = 0; + for (size_t i = 0; i < length; ++i) { + if (i > 0) + buffer[index++] = ':'; + buffer[index++] = hexDigits[value[i] >> 4]; + buffer[index++] = hexDigits[value[i] & 0xF]; + } + ASSERT(index == bufferSize); + return String::adopt(buffer); +} + +void InspectorResourceAgent::didCreateWebSocket(unsigned long identifier, const KURL& requestURL) +{ + m_frontend->didCreateWebSocket(identifier, requestURL.string()); +} + +void InspectorResourceAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request) +{ + RefPtr<InspectorObject> requestObject = InspectorObject::create(); + requestObject->setObject("webSocketHeaderFields", buildObjectForHeaders(request.headerFields())); + requestObject->setString("webSocketRequestKey3", createReadableStringFromBinary(request.key3().value, sizeof(request.key3().value))); + m_frontend->willSendWebSocketHandshakeRequest(identifier, currentTime(), requestObject); +} + +void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response) +{ + RefPtr<InspectorObject> responseObject = InspectorObject::create(); + responseObject->setNumber("statusCode", response.statusCode()); + responseObject->setString("statusText", response.statusText()); + responseObject->setObject("webSocketHeaderFields", buildObjectForHeaders(response.headerFields())); + responseObject->setString("webSocketChallengeResponse", createReadableStringFromBinary(response.challengeResponse().value, sizeof(response.challengeResponse().value))); + m_frontend->didReceiveWebSocketHandshakeResponse(identifier, currentTime(), responseObject); +} + +void InspectorResourceAgent::didCloseWebSocket(unsigned long identifier) +{ + m_frontend->didCloseWebSocket(identifier, currentTime()); +} +#endif // ENABLE(WEB_SOCKETS) + +void InspectorResourceAgent::cachedResources(RefPtr<InspectorArray>* resources) +{ + for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext(m_page->mainFrame())) + (*resources)->pushObject(buildObjectForFrameResources(frame)); +} + +void InspectorResourceAgent::resourceContent(unsigned long frameID, const String& url, String* content) +{ + RefPtr<InspectorArray> frameResources = InspectorArray::create(); + for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext(m_page->mainFrame())) { + if (reinterpret_cast<uintptr_t>(frame) != frameID) + continue; + InspectorResourceAgent::resourceContent(frame, KURL(ParsedURLString, url), content); + break; + } +} + +InspectorResourceAgent::InspectorResourceAgent(Page* page, InspectorFrontend* frontend) + : m_page(page) + , m_frontend(frontend) +{ +} + +} // namespace WebCore + +#endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorResourceAgent.h b/WebCore/inspector/InspectorResourceAgent.h new file mode 100644 index 0000000..f0bead8 --- /dev/null +++ b/WebCore/inspector/InspectorResourceAgent.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef InspectorResourceAgent_h +#define InspectorResourceAgent_h + +#include "InspectorResource.h" +#include "PlatformString.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +#if ENABLE(INSPECTOR) + +namespace WTF { +class String; +} + +namespace WebCore { + +class CachedResource; +class Document; +class DocumentLoader; +class InspectorArray; +class InspectorObject; +class InspectorFrontend; +class KURL; +class Page; +class ResourceError; +class ResourceRequest; +class ResourceResponse; +class SharedBuffer; + +#if ENABLE(WEB_SOCKETS) +class WebSocketHandshakeRequest; +class WebSocketHandshakeResponse; +#endif + +class InspectorResourceAgent : public RefCounted<InspectorResourceAgent> { +public: + static PassRefPtr<InspectorResourceAgent> create(Page* page, InspectorFrontend* frontend) + { + return adoptRef(new InspectorResourceAgent(page, frontend)); + } + + static bool resourceContent(Frame*, const KURL&, String* result); + static bool resourceContentBase64(Frame*, const KURL&, String* result); + static PassRefPtr<SharedBuffer> resourceData(Frame*, const KURL&, String* textEncodingName); + static CachedResource* cachedResource(Frame*, const KURL&); + + ~InspectorResourceAgent(); + + void identifierForInitialRequest(unsigned long identifier, const KURL&, DocumentLoader*, bool isMainResource); + void willSendRequest(unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse); + void markResourceAsCached(unsigned long identifier); + void didReceiveResponse(unsigned long identifier, DocumentLoader* laoder, const ResourceResponse&); + void didReceiveContentLength(unsigned long identifier, int lengthReceived); + void didFinishLoading(unsigned long identifier, double finishTime); + void didFailLoading(unsigned long identifier, const ResourceError&); + void didLoadResourceFromMemoryCache(DocumentLoader*, const CachedResource*); + void setOverrideContent(unsigned long identifier, const String& sourceString, InspectorResource::Type); + void didCommitLoad(DocumentLoader*); + void frameDetachedFromParent(Frame*); + +#if ENABLE(WEB_SOCKETS) + void didCreateWebSocket(unsigned long identifier, const KURL& requestURL); + void willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest&); + void didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse&); + void didCloseWebSocket(unsigned long identifier); +#endif + + // Called from frontend + void cachedResources(RefPtr<InspectorArray>*); + void resourceContent(unsigned long frameID, const String& url, String* content); + +private: + InspectorResourceAgent(Page* page, InspectorFrontend* frontend); + + Page* m_page; + InspectorFrontend* m_frontend; +}; + +} // namespace WebCore + +#endif // ENABLE(INSPECTOR) + +#endif // !defined(InspectorResourceAgent_h) diff --git a/WebCore/inspector/InspectorState.cpp b/WebCore/inspector/InspectorState.cpp new file mode 100644 index 0000000..44065c0 --- /dev/null +++ b/WebCore/inspector/InspectorState.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorState.h" + +#include "InspectorClient.h" +#include "InspectorController.h" + +#if ENABLE(INSPECTOR) + +namespace WebCore { + +InspectorState::InspectorState(InspectorClient* client) + : m_client(client) +{ + registerBoolean(monitoringXHR, false, "monitoringXHR", "xhrMonitor"); + registerBoolean(resourceTrackingEnabled, false, "resourceTrackingEnabled", (const char*)0); + registerBoolean(resourceTrackingAlwaysEnabled, false, (const char*)0, "resourceTrackingEnabled"); + registerBoolean(timelineProfilerEnabled, false, "timelineProfilerEnabled", (const char*)0); + registerBoolean(searchingForNode, false, "searchingForNodeEnabled", (const char*)0); + registerBoolean(profilerAlwaysEnabled, false, (const char*)0, "profilerEnabled"); + registerString(frontendSettings, "", (const char*)0, "frontendSettings"); + registerBoolean(debuggerAlwaysEnabled, false, (const char*)0, "debuggerEnabled"); + registerString(lastActivePanel, InspectorController::LastActivePanel, (const char*)0, "lastActivePanel"); + registerBoolean(inspectorStartsAttached, true, (const char*)0, "InspectorStartsAttached"); + registerLong(inspectorAttachedHeight, InspectorController::defaultAttachedHeight, (const char*)0, "inspectorAttachedHeight"); + registerLong(pauseOnExceptionsState, 0, "pauseOnExceptionsState", (const char*)0); + registerBoolean(consoleMessagesEnabled, false, "consoleMessagesEnabled", (const char*)0); +} + +void InspectorState::restoreFromInspectorCookie(const String& json) +{ + RefPtr<InspectorValue> jsonValue = InspectorValue::parseJSON(json); + if (!jsonValue) + return; + + RefPtr<InspectorObject> jsonObject = jsonValue->asObject(); + if (!jsonObject) + return; + + for (InspectorObject::iterator i = jsonObject->begin(); i != jsonObject->end(); ++i) { + InspectorPropertyId id = (InspectorPropertyId)i->first.toInt(); + ASSERT(id > 0 && id < lastPropertyId); + PropertyMap::iterator j = m_properties.find(id); + ASSERT(j != m_properties.end()); + ASSERT(j->second.m_value->type() == i->second->type()); + j->second.m_value = i->second; + } +} + +PassRefPtr<InspectorObject> InspectorState::generateStateObjectForFrontend() +{ + RefPtr<InspectorObject> stateObject = InspectorObject::create(); + for (PropertyMap::iterator i = m_properties.begin(); i != m_properties.end(); ++i) { + if (i->second.m_frontendAlias.length()) + stateObject->setValue(i->second.m_frontendAlias, i->second.m_value); + } + return stateObject.release(); +} + +void InspectorState::loadFromSettings() +{ + for (PropertyMap::iterator i = m_properties.begin(); i != m_properties.end(); ++i) { + if (i->second.m_preferenceName.length()) { + String value; + m_client->populateSetting(i->second.m_preferenceName, &value); + switch (i->second.m_value->type()) { + case InspectorValue::TypeBoolean: + if (value.length()) + i->second.m_value = InspectorBasicValue::create(value == "true"); + break; + case InspectorValue::TypeString: + i->second.m_value = InspectorString::create(value); + break; + case InspectorValue::TypeNumber: + if (value.length()) + i->second.m_value = InspectorBasicValue::create((double)value.toInt()); + break; + default: + ASSERT(false); + break; + } + } + } +} + +void InspectorState::updateCookie() +{ + RefPtr<InspectorObject> cookieObject = InspectorObject::create(); + for (PropertyMap::iterator i = m_properties.begin(); i != m_properties.end(); ++i) + cookieObject->setValue(String::number(i->first), i->second.m_value); + m_client->updateInspectorStateCookie(cookieObject->toJSONString()); +} + +void InspectorState::setValue(InspectorPropertyId id, PassRefPtr<InspectorValue> value, const String& stringValue) +{ + PropertyMap::iterator i = m_properties.find(id); + ASSERT(i != m_properties.end()); + i->second.m_value = value; + if (i->second.m_preferenceName.length()) + m_client->storeSetting(i->second.m_preferenceName, stringValue); + updateCookie(); +} + +bool InspectorState::getBoolean(InspectorPropertyId id) +{ + PropertyMap::iterator i = m_properties.find(id); + ASSERT(i != m_properties.end()); + bool value = false; + i->second.m_value->asBoolean(&value); + return value; +} + +String InspectorState::getString(InspectorPropertyId id) +{ + PropertyMap::iterator i = m_properties.find(id); + ASSERT(i != m_properties.end()); + String value; + i->second.m_value->asString(&value); + return value; +} + +long InspectorState::getLong(InspectorPropertyId id) +{ + PropertyMap::iterator i = m_properties.find(id); + ASSERT(i != m_properties.end()); + long value = 0; + i->second.m_value->asNumber(&value); + return value; +} + +void InspectorState::registerBoolean(InspectorPropertyId propertyId, bool value, const String& frontendAlias, const String& preferenceName) +{ + m_properties.set(propertyId, Property::create(InspectorBasicValue::create(value), frontendAlias, preferenceName)); +} + +void InspectorState::registerString(InspectorPropertyId propertyId, const String& value, const String& frontendAlias, const String& preferenceName) +{ + m_properties.set(propertyId, Property::create(InspectorString::create(value), frontendAlias, preferenceName)); +} + +void InspectorState::registerLong(InspectorPropertyId propertyId, long value, const String& frontendAlias, const String& preferenceName) +{ + m_properties.set(propertyId, Property::create(InspectorBasicValue::create((double)value), frontendAlias, preferenceName)); +} + +InspectorState::Property InspectorState::Property::create(PassRefPtr<InspectorValue> value, const String& frontendAlias, const String& preferenceName) +{ + Property property; + property.m_value = value; + property.m_frontendAlias = frontendAlias; + property.m_preferenceName = preferenceName; + return property; +} + +} // namespace WebCore + +#endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorState.h b/WebCore/inspector/InspectorState.h new file mode 100644 index 0000000..5707600 --- /dev/null +++ b/WebCore/inspector/InspectorState.h @@ -0,0 +1,101 @@ +/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InspectorState_h
+#define InspectorState_h
+
+#if ENABLE(INSPECTOR)
+
+#include "InspectorValues.h"
+#include "PlatformString.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class InspectorClient;
+
+class InspectorState {
+public:
+ enum InspectorPropertyId {
+ monitoringXHR = 1,
+ resourceTrackingEnabled,
+ resourceTrackingAlwaysEnabled,
+ timelineProfilerEnabled,
+ searchingForNode,
+ profilerAlwaysEnabled,
+ frontendSettings,
+ debuggerAlwaysEnabled,
+ lastActivePanel,
+ inspectorStartsAttached,
+ inspectorAttachedHeight,
+ pauseOnExceptionsState,
+ consoleMessagesEnabled,
+ lastPropertyId
+ };
+
+ InspectorState(InspectorClient* client);
+
+ PassRefPtr<InspectorObject> generateStateObjectForFrontend();
+ void restoreFromInspectorCookie(const String& jsonString);
+ void loadFromSettings();
+ String getFrontendAlias(InspectorPropertyId propertyId);
+
+ bool getBoolean(InspectorPropertyId propertyId);
+ String getString(InspectorPropertyId propertyId);
+ long getLong(InspectorPropertyId propertyId);
+
+ void setBoolean(InspectorPropertyId propertyId, bool value) { setValue(propertyId, InspectorBasicValue::create(value), value ? "true" : "false"); }
+ void setString(InspectorPropertyId propertyId, const String& value) { setValue(propertyId, InspectorString::create(value), value); }
+ void setLong(InspectorPropertyId propertyId, long value) { setValue(propertyId, InspectorBasicValue::create((double)value), String::number(value)); }
+
+private:
+ void updateCookie();
+ void setValue(InspectorPropertyId propertyId, PassRefPtr<InspectorValue> value, const String& stringValue);
+
+ struct Property {
+ static Property create(PassRefPtr<InspectorValue> value, const String& frontendAlias, const String& preferenceName);
+ String m_frontendAlias;
+ String m_preferenceName;
+ RefPtr<InspectorValue> m_value;
+ };
+ typedef HashMap<long, Property> PropertyMap;
+ PropertyMap m_properties;
+
+ void registerBoolean(InspectorPropertyId propertyId, bool value, const String& frontendAlias, const String& preferenceName);
+ void registerString(InspectorPropertyId propertyId, const String& value, const String& frontendAlias, const String& preferenceName);
+ void registerLong(InspectorPropertyId propertyId, long value, const String& frontendAlias, const String& preferenceName);
+
+ InspectorClient* m_client;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(INSPECTOR)
+#endif // !defined(InspectorState_h)
diff --git a/WebCore/inspector/InspectorStyleSheet.cpp b/WebCore/inspector/InspectorStyleSheet.cpp new file mode 100644 index 0000000..11cd649 --- /dev/null +++ b/WebCore/inspector/InspectorStyleSheet.cpp @@ -0,0 +1,571 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorStyleSheet.h" + +#if ENABLE(INSPECTOR) + +#include "CSSParser.h" +#include "CSSPropertySourceData.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSStyleRule.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "Document.h" +#include "Element.h" +#include "HTMLHeadElement.h" +#include "InspectorCSSAgent.h" +#include "InspectorResourceAgent.h" +#include "InspectorValues.h" +#include "Node.h" +#include "StyleSheetList.h" + +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> + +class ParsedStyleSheet { +public: + typedef Vector<RefPtr<WebCore::CSSRuleSourceData> > SourceData; + ParsedStyleSheet(); + + WebCore::CSSStyleSheet* cssStyleSheet() const { return m_parserOutput; } + const String& text() const { return m_text; } + void setText(const String& text); + bool hasText() const { return m_hasText; } + SourceData* sourceData() const { return m_sourceData.get(); } + void setSourceData(PassOwnPtr<SourceData> sourceData); + bool hasSourceData() const { return m_sourceData; } + RefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned index) const; + +private: + + // StyleSheet constructed while parsing m_text. + WebCore::CSSStyleSheet* m_parserOutput; + String m_text; + bool m_hasText; + OwnPtr<SourceData> m_sourceData; +}; + +ParsedStyleSheet::ParsedStyleSheet() + : m_parserOutput(0) + , m_hasText(false) +{ +} + +void ParsedStyleSheet::setText(const String& text) +{ + m_hasText = true; + m_text = text; + setSourceData(0); +} + +void ParsedStyleSheet::setSourceData(PassOwnPtr<SourceData> sourceData) +{ + m_sourceData = sourceData; +} + +RefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const +{ + if (!hasSourceData() || index >= m_sourceData->size()) + return 0; + + return m_sourceData->at(index); +} + +namespace WebCore { + +InspectorStyleSheet::InspectorStyleSheet(const String& id, CSSStyleSheet* pageStyleSheet, const String& origin, const String& documentURL) + : m_id(id) + , m_pageStyleSheet(pageStyleSheet) + , m_origin(origin) + , m_documentURL(documentURL) + , m_isRevalidating(false) +{ + m_parsedStyleSheet = new ParsedStyleSheet(); +} + +InspectorStyleSheet::~InspectorStyleSheet() +{ + delete m_parsedStyleSheet; +} + +bool InspectorStyleSheet::setText(const String& text) +{ + if (!m_parsedStyleSheet) + return false; + + m_parsedStyleSheet->setText(text); + for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i) + m_pageStyleSheet->remove(i); + + m_pageStyleSheet->parseString(text, m_pageStyleSheet->useStrictParsing()); + return true; +} + +bool InspectorStyleSheet::setRuleSelector(const String& ruleId, const String& selector) +{ + CSSStyleRule* rule = ruleForId(ruleId); + if (!rule) + return false; + CSSStyleSheet* styleSheet = InspectorCSSAgent::parentStyleSheet(rule); + if (!styleSheet || !ensureParsedDataReady()) + return false; + + rule->setSelectorText(selector); + RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style()); + if (!sourceData) + return false; + + const String& sheetText = m_parsedStyleSheet->text(); + String newStyleSheetText = sheetText.substring(0, sourceData->selectorListRange.start); + newStyleSheetText += selector; + newStyleSheetText += sheetText.right(sheetText.length() - sourceData->selectorListRange.end); + m_parsedStyleSheet->setText(newStyleSheetText); + return true; +} + +CSSStyleRule* InspectorStyleSheet::addRule(const String& selector) +{ + String text; + bool success = styleSheetText(&text); + if (!success) + return 0; + + ExceptionCode ec = 0; + m_pageStyleSheet->addRule(selector, "", ec); + if (ec) + return 0; + RefPtr<CSSRuleList> rules = m_pageStyleSheet->cssRules(); + ASSERT(rules->length()); + CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(rules->item(rules->length() - 1)); + ASSERT(rule); + + if (text.length()) + text += "\n"; + + text += selector; + text += " {}"; + m_parsedStyleSheet->setText(text); + + return rule; +} + +CSSStyleRule* InspectorStyleSheet::ruleForId(const String& id) const +{ + if (!m_pageStyleSheet) + return 0; + + bool ok; + unsigned index = id.toUInt(&ok); + if (!ok) + return 0; + + unsigned currentIndex = 0; + for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i) { + CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(m_pageStyleSheet->item(i)); + if (!rule) + continue; + if (index == currentIndex) + return rule; + + ++currentIndex; + } + return 0; +} + +PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheet() +{ + CSSStyleSheet* styleSheet = pageStyleSheet(); + if (!styleSheet) + return 0; + + RefPtr<InspectorObject> result = InspectorObject::create(); + result->setString("id", id()); + result->setBoolean("disabled", styleSheet->disabled()); + result->setString("href", styleSheet->href()); + result->setString("title", styleSheet->title()); + RefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet, true); + RefPtr<InspectorArray> cssRules = buildArrayForRuleList(cssRuleList.get()); + result->setArray("cssRules", cssRules.release()); + + String styleSheetText; + bool success = text(&styleSheetText); + if (success) + result->setString("text", styleSheetText); + + return result.release(); +} + +PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule) +{ + CSSStyleSheet* styleSheet = pageStyleSheet(); + if (!styleSheet) + return 0; + + RefPtr<InspectorObject> result = InspectorObject::create(); + result->setString("selectorText", rule->selectorText()); + result->setString("cssText", rule->cssText()); + result->setNumber("sourceLine", rule->sourceLine()); + result->setString("documentURL", m_documentURL); + + RefPtr<InspectorObject> parentStyleSheetValue = InspectorObject::create(); + parentStyleSheetValue->setString("id", id()); + parentStyleSheetValue->setString("href", styleSheet->href()); + result->setObject("parentStyleSheet", parentStyleSheetValue.release()); + result->setString("origin", m_origin); + + RefPtr<CSSRuleSourceData> sourceData; + if (ensureParsedDataReady()) + sourceData = ruleSourceDataFor(rule->style()); + if (sourceData) { + result->setNumber("selectorStartOffset", sourceData->selectorListRange.start); + result->setNumber("selectorEndOffset", sourceData->selectorListRange.end); + } + + result->setObject("style", buildObjectForStyle(rule->style())); + if (canBind()) + result->setString("id", fullRuleId(rule)); + + return result.release(); +} + +PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style) +{ + RefPtr<CSSRuleSourceData> sourceData; + if (ensureParsedDataReady()) + sourceData = ruleSourceDataFor(style); + + RefPtr<InspectorObject> result = InspectorCSSAgent::buildObjectForStyle(style, fullStyleId(style), sourceData ? sourceData->styleSourceData.get() : 0); + result->setString("parentStyleSheetId", id()); + return result.release(); +} + +CSSStyleDeclaration* InspectorStyleSheet::styleForId(const String& id) const +{ + CSSStyleRule* rule = ruleForId(id); + if (!rule) + return 0; + + return rule->style(); +} + +bool InspectorStyleSheet::setStyleText(const String& styleId, const String& newText) +{ + CSSStyleDeclaration* style = styleForId(styleId); + if (!style) + return false; + + return innerSetStyleText(style, newText); +} + +Document* InspectorStyleSheet::ownerDocument() const +{ + return m_pageStyleSheet->document(); +} + +RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const +{ + return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style)); +} + +unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const +{ + unsigned index = 0; + for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i) { + CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(m_pageStyleSheet->item(i)); + if (!rule) + continue; + if (rule->style() == pageStyle) + return index; + + ++index; + } + return UINT_MAX; +} + +bool InspectorStyleSheet::ensureParsedDataReady() +{ + return ensureText() && ensureSourceData(pageStyleSheet()->ownerNode()); +} + +bool InspectorStyleSheet::text(String* result) const +{ + if (!ensureText()) + return false; + *result = m_parsedStyleSheet->text(); + return true; +} + +bool InspectorStyleSheet::ensureText() const +{ + if (!m_parsedStyleSheet) + return false; + if (m_parsedStyleSheet->hasText()) + return true; + + String text; + bool success = styleSheetText(&text); + if (success) + m_parsedStyleSheet->setText(text); + + return success; +} + +bool InspectorStyleSheet::ensureSourceData(Node* ownerNode) +{ + if (m_parsedStyleSheet->hasSourceData()) + return true; + + if (!m_parsedStyleSheet->hasText()) + return false; + + RefPtr<CSSStyleSheet> newStyleSheet = CSSStyleSheet::create(ownerNode); + CSSParser p; + StyleRuleRangeMap ruleRangeMap; + p.parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, &ruleRangeMap); + OwnPtr<ParsedStyleSheet::SourceData> rangesVector(new ParsedStyleSheet::SourceData()); + + for (unsigned i = 0, length = newStyleSheet->length(); i < length; ++i) { + CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(newStyleSheet->item(i)); + if (!rule) + continue; + StyleRuleRangeMap::iterator it = ruleRangeMap.find(rule); + if (it != ruleRangeMap.end()) + rangesVector->append(it->second); + } + + m_parsedStyleSheet->setSourceData(rangesVector.release()); + return m_parsedStyleSheet->hasSourceData(); +} + +void InspectorStyleSheet::innerSetStyleSheetText(const String& newText) +{ + m_parsedStyleSheet->setText(newText); +} + +bool InspectorStyleSheet::innerSetStyleText(CSSStyleDeclaration* style, const String& text) +{ + if (!pageStyleSheet()) + return false; + if (!ensureParsedDataReady()) + return false; + + String patchedStyleSheetText; + bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText); + if (!success) + return false; + + String id = ruleOrStyleId(style); + if (id.isEmpty()) + return false; + + ExceptionCode ec = 0; + style->setCssText(text, ec); + if (!ec) + innerSetStyleSheetText(patchedStyleSheetText); + + return !ec; +} + +bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result) +{ + if (!style) + return false; + + if (!ensureParsedDataReady()) + return false; + + RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style); + unsigned bodyStart = sourceData->styleSourceData->styleBodyRange.start; + unsigned bodyEnd = sourceData->styleSourceData->styleBodyRange.end; + ASSERT(bodyStart <= bodyEnd); + + String text = m_parsedStyleSheet->text(); + ASSERT(bodyEnd <= text.length()); // bodyEnd is exclusive + + String patchedText = text.substring(0, bodyStart); + patchedText += newStyleText; + patchedText += text.substring(bodyEnd, text.length()); + *result = patchedText; + return true; +} + +CSSStyleRule* InspectorStyleSheet::findPageRuleWithStyle(CSSStyleDeclaration* style) +{ + for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i) { + CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(m_pageStyleSheet->item(i)); + if (!rule) + continue; + if (rule->style() == style) + return rule; + } + return 0; +} + +String InspectorStyleSheet::fullRuleId(CSSStyleRule* rule) const +{ + return fullRuleOrStyleId(rule->style()); +} + +void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle) +{ + if (m_isRevalidating) + return; + + m_isRevalidating = true; + CSSStyleSheet* parsedSheet = m_parsedStyleSheet->cssStyleSheet(); + for (unsigned i = 0, size = parsedSheet->length(); i < size; ++i) { + StyleBase* styleBase = parsedSheet->item(i); + CSSStyleRule* parsedRule = InspectorCSSAgent::asCSSStyleRule(styleBase); + if (!parsedRule) + continue; + if (parsedRule->style() == pageStyle) { + if (parsedRule->style()->cssText() != pageStyle->cssText()) + innerSetStyleText(pageStyle, pageStyle->cssText()); + break; + } + } + m_isRevalidating = false; +} + +bool InspectorStyleSheet::styleSheetText(String* result) const +{ + String text; + bool success = inlineStyleSheetText(&text); + if (!success) + success = resourceStyleSheetText(&text); + if (success) + *result = text; + return success; +} + +bool InspectorStyleSheet::resourceStyleSheetText(String* result) const +{ + if (!m_pageStyleSheet) + return false; + + return InspectorResourceAgent::resourceContent(ownerDocument()->frame(), m_pageStyleSheet->finalURL(), result); +} + +bool InspectorStyleSheet::inlineStyleSheetText(String* result) const +{ + if (!m_pageStyleSheet) + return false; + + Node* ownerNode = m_pageStyleSheet->ownerNode(); + if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE) + return false; + Element* ownerElement = static_cast<Element*>(ownerNode); + if (ownerElement->tagName().lower() != "style") + return false; + *result = ownerElement->innerText(); + return true; +} + +PassRefPtr<InspectorArray> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList) +{ + RefPtr<InspectorArray> result = InspectorArray::create(); + if (!ruleList) + return result.release(); + + for (unsigned i = 0, size = ruleList->length(); i < size; ++i) { + CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(ruleList->item(i)); + if (!rule) + continue; + + result->pushObject(buildObjectForRule(rule)); + } + return result.release(); +} + + +InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, Element* element, const String& origin) + : InspectorStyleSheet(id, 0, origin, "") + , m_element(element) + , m_ruleSourceData(0) +{ + ASSERT(element); +} + +bool InspectorStyleSheetForInlineStyle::setStyleText(const String& styleId, const String& text) +{ + ASSERT(styleId == "0"); + UNUSED_PARAM(styleId); + ExceptionCode ec = 0; + m_element->setAttribute("style", text, ec); + m_ruleSourceData.clear(); + return !ec; +} + +Document* InspectorStyleSheetForInlineStyle::ownerDocument() const +{ + return m_element->document(); +} + +bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady() +{ + if (m_ruleSourceData) + return true; + + m_ruleSourceData = CSSRuleSourceData::create(); + RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create(); + bool success = getStyleAttributeRanges(&sourceData); + if (!success) + return false; + + m_ruleSourceData->styleSourceData = sourceData.release(); + return true; +} + +CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const +{ + return m_element->style(); +} + +bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(RefPtr<CSSStyleSourceData>* result) +{ + DEFINE_STATIC_LOCAL(String, styleAttributeName, ("style")); + + if (!m_element->isStyledElement()) + return false; + + String styleText = static_cast<StyledElement*>(m_element)->getAttribute(styleAttributeName); + if (styleText.isEmpty()) { + (*result)->styleBodyRange.start = 0; + (*result)->styleBodyRange.end = 0; + return true; + } + + RefPtr<CSSMutableStyleDeclaration> tempDeclaration = CSSMutableStyleDeclaration::create(); + CSSParser p; + p.parseDeclaration(tempDeclaration.get(), styleText, result); + return true; +} + +} // namespace WebCore + +#endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorStyleSheet.h b/WebCore/inspector/InspectorStyleSheet.h new file mode 100644 index 0000000..38ab777 --- /dev/null +++ b/WebCore/inspector/InspectorStyleSheet.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef InspectorStyleSheet_h +#define InspectorStyleSheet_h + +#include "CSSPropertySourceData.h" +#include "Document.h" +#include "InspectorValues.h" +#include "PlatformString.h" + +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/UnusedParam.h> + +class ParsedStyleSheet; + +namespace WebCore { + +class CSSRuleList; +class CSSStyleDeclaration; +class Element; +class Node; + +#if ENABLE(INSPECTOR) + +class InspectorStyleSheet : public RefCounted<InspectorStyleSheet> { +public: + static PassRefPtr<InspectorStyleSheet> create(const String& id, CSSStyleSheet* pageStyleSheet, const String& origin, const String& documentURL) + { + return adoptRef(new InspectorStyleSheet(id, pageStyleSheet, origin, documentURL)); + } + + InspectorStyleSheet(const String& id, CSSStyleSheet* pageStyleSheet, const String& origin, const String& documentURL); + virtual ~InspectorStyleSheet(); + + const String& id() const { return m_id; } + CSSStyleSheet* pageStyleSheet() const { return m_pageStyleSheet; } + bool setText(const String&); + bool setRuleSelector(const String& ruleId, const String& selector); + CSSStyleRule* addRule(const String& selector); + CSSStyleRule* ruleForId(const String&) const; + PassRefPtr<InspectorObject> buildObjectForStyleSheet(); + PassRefPtr<InspectorObject> buildObjectForRule(CSSStyleRule*); + PassRefPtr<InspectorObject> buildObjectForStyle(CSSStyleDeclaration*); + virtual CSSStyleDeclaration* styleForId(const String&) const; + virtual bool setStyleText(const String& styleId, const String& text); + +protected: + bool canBind() const { return m_origin != "userAgent" && m_origin != "user"; } + String fullRuleOrStyleId(CSSStyleDeclaration* style) const { return id() + ":" + ruleOrStyleId(style); } + String ruleOrStyleId(CSSStyleDeclaration* style) const { unsigned index = ruleIndexByStyle(style); return index == UINT_MAX ? "" : String::number(index); } + virtual Document* ownerDocument() const; + virtual RefPtr<CSSRuleSourceData> ruleSourceDataFor(CSSStyleDeclaration* style) const; + virtual unsigned ruleIndexByStyle(CSSStyleDeclaration*) const; + virtual bool ensureParsedDataReady(); + +private: + bool text(String* result) const; + bool ensureText() const; + bool ensureSourceData(Node* ownerNode); + void innerSetStyleSheetText(const String& newText); + bool innerSetStyleText(CSSStyleDeclaration*, const String&); + bool styleSheetTextWithChangedStyle(CSSStyleDeclaration*, const String& newStyleText, String* result); + CSSStyleRule* findPageRuleWithStyle(CSSStyleDeclaration*); + String fullRuleId(CSSStyleRule* rule) const; + String fullStyleId(CSSStyleDeclaration* style) const { return fullRuleOrStyleId(style); } + void revalidateStyle(CSSStyleDeclaration*); + bool styleSheetText(String* result) const; + bool resourceStyleSheetText(String* result) const; + bool inlineStyleSheetText(String* result) const; + PassRefPtr<InspectorArray> buildArrayForRuleList(CSSRuleList*); + + String m_id; + CSSStyleSheet* m_pageStyleSheet; + String m_origin; + String m_documentURL; + bool m_isRevalidating; + ParsedStyleSheet* m_parsedStyleSheet; +}; + +class InspectorStyleSheetForInlineStyle : public InspectorStyleSheet { +public: + static PassRefPtr<InspectorStyleSheetForInlineStyle> create(const String& id, Element* element, const String& origin) + { + return adoptRef(new InspectorStyleSheetForInlineStyle(id, element, origin)); + } + + InspectorStyleSheetForInlineStyle(const String& id, Element* element, const String& origin); + virtual CSSStyleDeclaration* styleForId(const String& id) const { ASSERT(id == "0"); UNUSED_PARAM(id); return inlineStyle(); } + virtual bool setStyleText(const String& styleId, const String& text); + +protected: + virtual Document* ownerDocument() const; + virtual RefPtr<CSSRuleSourceData> ruleSourceDataFor(CSSStyleDeclaration* style) const { ASSERT(style == inlineStyle()); UNUSED_PARAM(style); return m_ruleSourceData; } + virtual unsigned ruleIndexByStyle(CSSStyleDeclaration*) const { return 0; } + virtual bool ensureParsedDataReady(); + +private: + CSSStyleDeclaration* inlineStyle() const; + bool getStyleAttributeRanges(RefPtr<CSSStyleSourceData>* result); + + Element* m_element; + RefPtr<CSSRuleSourceData> m_ruleSourceData; +}; + +#endif + +} // namespace WebCore + +#endif // !defined(InspectorStyleSheet_h) diff --git a/WebCore/inspector/InspectorTimelineAgent.cpp b/WebCore/inspector/InspectorTimelineAgent.cpp index fbb17c4..645b692 100644 --- a/WebCore/inspector/InspectorTimelineAgent.cpp +++ b/WebCore/inspector/InspectorTimelineAgent.cpp @@ -44,12 +44,12 @@ namespace WebCore { -int InspectorTimelineAgent::s_instanceCount = 0; +int InspectorTimelineAgent::s_id = 0; InspectorTimelineAgent::InspectorTimelineAgent(InspectorFrontend* frontend) : m_frontend(frontend) + , m_id(++s_id) { - ++s_instanceCount; ScriptGCEvent::addEventListener(this); ASSERT(m_frontend); } @@ -76,8 +76,6 @@ void InspectorTimelineAgent::didGC(double startTime, double endTime, size_t coll InspectorTimelineAgent::~InspectorTimelineAgent() { - ASSERT(s_instanceCount); - --s_instanceCount; ScriptGCEvent::removeEventListener(this); } @@ -242,10 +240,11 @@ void InspectorTimelineAgent::didReceiveResourceResponse() didCompleteCurrentRecord(ResourceReceiveResponseTimelineRecordType); } -void InspectorTimelineAgent::didFinishLoadingResource(unsigned long identifier, bool didFail) +void InspectorTimelineAgent::didFinishLoadingResource(unsigned long identifier, bool didFail, double finishTime) { pushGCEventRecords(); - RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(WTF::currentTimeMS()); + // Sometimes network stack can provide for us exact finish loading time. In the other case we will use currentTime. + RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(finishTime ? finishTime * 1000 : WTF::currentTimeMS()); record->setObject("data", TimelineRecordFactory::createResourceFinishData(identifier, didFail)); record->setNumber("type", ResourceFinishTimelineRecordType); setHeapSizeStatistic(record.get()); diff --git a/WebCore/inspector/InspectorTimelineAgent.h b/WebCore/inspector/InspectorTimelineAgent.h index 6b3324b..bc1e027 100644 --- a/WebCore/inspector/InspectorTimelineAgent.h +++ b/WebCore/inspector/InspectorTimelineAgent.h @@ -33,9 +33,7 @@ #if ENABLE(INSPECTOR) -#include "Document.h" #include "InspectorValues.h" -#include "ScriptExecutionContext.h" #include "ScriptGCEvent.h" #include "ScriptGCEventListener.h" #include <wtf/Vector.h> @@ -77,6 +75,8 @@ public: InspectorTimelineAgent(InspectorFrontend* frontend); ~InspectorTimelineAgent(); + int id() const { return m_id; } + void reset(); void resetFrontendProxyObject(InspectorFrontend*); @@ -122,15 +122,12 @@ public: void willSendResourceRequest(unsigned long, bool isMainResource, const ResourceRequest&); void willReceiveResourceResponse(unsigned long, const ResourceResponse&); void didReceiveResourceResponse(); - void didFinishLoadingResource(unsigned long, bool didFail); + void didFinishLoadingResource(unsigned long, bool didFail, double finishTime); void willReceiveResourceData(unsigned long identifier); void didReceiveResourceData(); virtual void didGC(double, double, size_t); - static int instanceCount() { return s_instanceCount; } - static InspectorTimelineAgent* retrieve(ScriptExecutionContext*); - private: struct TimelineRecordEntry { TimelineRecordEntry(PassRefPtr<InspectorObject> record, PassRefPtr<InspectorObject> data, PassRefPtr<InspectorArray> children, TimelineRecordType type) @@ -155,7 +152,9 @@ private: InspectorFrontend* m_frontend; Vector<TimelineRecordEntry> m_recordStack; - static int s_instanceCount; + + static int s_id; + const int m_id; struct GCEvent { GCEvent(double startTime, double endTime, size_t collectedBytes) : startTime(startTime), endTime(endTime), collectedBytes(collectedBytes) @@ -169,13 +168,6 @@ private: GCEvents m_gcEvents; }; -inline InspectorTimelineAgent* InspectorTimelineAgent::retrieve(ScriptExecutionContext* context) -{ - if (context && context->isDocument()) - return static_cast<Document*>(context)->inspectorTimelineAgent(); - return 0; -} - } // namespace WebCore #endif // !ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorValues.cpp b/WebCore/inspector/InspectorValues.cpp index c96e953..89e9b7c 100644 --- a/WebCore/inspector/InspectorValues.cpp +++ b/WebCore/inspector/InspectorValues.cpp @@ -33,6 +33,8 @@ #if ENABLE(INSPECTOR) +#include <wtf/DecimalNumber.h> + namespace WebCore { namespace { @@ -604,9 +606,9 @@ void InspectorBasicValue::writeJSON(Vector<UChar>* output) const else output->append(falseString, 5); } else if (type() == TypeNumber) { - String value = String::format("%f", m_doubleValue); - value.replace(',', '.'); - output->append(value.characters(), value.length()); + NumberToStringBuffer buffer; + unsigned length = DecimalNumber(m_doubleValue).toStringDecimal(buffer, WTF::NumberToStringBufferLength); + output->append(buffer, length); } } @@ -622,6 +624,10 @@ void InspectorString::writeJSON(Vector<UChar>* output) const doubleQuoteString(m_stringValue, output); } +InspectorObject::~InspectorObject() +{ +} + bool InspectorObject::asObject(RefPtr<InspectorObject>* output) { *output = this; @@ -696,6 +702,17 @@ void InspectorObject::writeJSON(Vector<UChar>* output) const output->append('}'); } +InspectorObject::InspectorObject() + : InspectorValue(TypeObject) + , m_data() + , m_order() +{ +} + +InspectorArray::~InspectorArray() +{ +} + bool InspectorArray::asArray(RefPtr<InspectorArray>* output) { *output = this; @@ -718,6 +735,12 @@ void InspectorArray::writeJSON(Vector<UChar>* output) const output->append(']'); } +InspectorArray::InspectorArray() + : InspectorValue(TypeArray) + , m_data() +{ +} + PassRefPtr<InspectorValue> InspectorArray::get(size_t index) { ASSERT(index < m_data.size()); diff --git a/WebCore/inspector/InspectorValues.h b/WebCore/inspector/InspectorValues.h index 4036f55..391372e 100644 --- a/WebCore/inspector/InspectorValues.h +++ b/WebCore/inspector/InspectorValues.h @@ -166,7 +166,7 @@ public: { return adoptRef(new InspectorObject()); } - ~InspectorObject() { } + ~InspectorObject(); virtual bool asObject(RefPtr<InspectorObject>* output); virtual PassRefPtr<InspectorObject> asObject(); @@ -194,7 +194,7 @@ public: const_iterator end() const { return m_data.end(); } private: - InspectorObject() : InspectorValue(TypeObject) { } + InspectorObject(); Dictionary m_data; Vector<String> m_order; }; @@ -205,7 +205,7 @@ public: { return adoptRef(new InspectorArray()); } - ~InspectorArray() { } + ~InspectorArray(); virtual bool asArray(RefPtr<InspectorArray>* output); virtual PassRefPtr<InspectorArray> asArray(); @@ -223,7 +223,7 @@ public: virtual void writeJSON(Vector<UChar>* output) const; private: - InspectorArray() : InspectorValue(TypeArray) { } + InspectorArray(); Vector<RefPtr<InspectorValue> > m_data; }; diff --git a/WebCore/inspector/front-end/AuditRules.js b/WebCore/inspector/front-end/AuditRules.js index a72de56..cd9f13e 100644 --- a/WebCore/inspector/front-end/AuditRules.js +++ b/WebCore/inspector/front-end/AuditRules.js @@ -42,17 +42,17 @@ WebInspector.AuditRules.CacheableResponseCodes = 304: true // Underlying resource is cacheable } -WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, regexp, needFullResources) +WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, needFullResources) { var domainToResourcesMap = {}; for (var i = 0, size = resources.length; i < size; ++i) { var resource = resources[i]; if (types && types.indexOf(resource.type) === -1) continue; - var match = resource.url.match(regexp); - if (!match) + var parsedURL = resource.url.asParsedURL(); + if (!parsedURL) continue; - var domain = match[2]; + var domain = parsedURL.host; var domainResources = domainToResourcesMap[domain]; if (domainResources === undefined) { domainResources = []; @@ -128,7 +128,7 @@ WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, WebInspector.AuditRules.CombineExternalResourcesRule.prototype = { doRun: function(resources, result, callback) { - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [this._type], WebInspector.URLRegExp); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [this._type]); var penalizedResourceCount = 0; // TODO: refactor according to the chosen i18n approach var summary = result.addChild("", true); @@ -175,14 +175,14 @@ WebInspector.AuditRules.MinimizeDnsLookupsRule.prototype = { doRun: function(resources, result, callback) { var summary = result.addChild(""); - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, undefined, WebInspector.URLRegExp); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, undefined); for (var domain in domainToResourcesMap) { if (domainToResourcesMap[domain].length > 1) continue; - var match = domain.match(WebInspector.URLRegExp); - if (!match) + var parsedURL = domain.asParsedURL(); + if (!parsedURL) continue; - if (!match[2].search(WebInspector.AuditRules.IPAddressRegexp)) + if (!parsedURL.host.search(WebInspector.AuditRules.IPAddressRegexp)) continue; // an IP address summary.addSnippet(match[2]); result.violationCount++; @@ -220,7 +220,6 @@ WebInspector.AuditRules.ParallelizeDownloadRule.prototype = { var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap( resources, [WebInspector.Resource.Type.Stylesheet, WebInspector.Resource.Type.Image], - WebInspector.URLRegExp, true); var hosts = []; @@ -647,7 +646,7 @@ WebInspector.AuditRules.ImageDimensionsRule.prototype = { const node = WebInspector.domAgent.nodeForId(imageId); var src = node.getAttribute("src"); - if (!WebInspector.URLRegExp.test(src)) { + if (!src.asParsedURL()) { for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) { if (frameOwnerCandidate.documentURL) { var completeSrc = WebInspector.completeURL(frameOwnerCandidate.documentURL, src); @@ -934,7 +933,6 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, null, - WebInspector.URLRegExp, true); var matchingResourceData = {}; this.mapResourceCookies(domainToResourcesMap, allCookies, collectorCallback.bind(this)); @@ -998,7 +996,6 @@ WebInspector.AuditRules.StaticCookielessRule.prototype = { var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [WebInspector.Resource.Type.Stylesheet, WebInspector.Resource.Type.Image], - WebInspector.URLRegExp, true); var totalStaticResources = 0; for (var domain in domainToResourcesMap) diff --git a/WebCore/inspector/front-end/BreakpointManager.js b/WebCore/inspector/front-end/BreakpointManager.js index ec4e7cf..77ba89d 100644 --- a/WebCore/inspector/front-end/BreakpointManager.js +++ b/WebCore/inspector/front-end/BreakpointManager.js @@ -27,7 +27,8 @@ WebInspector.BreakpointManager = function() { this._breakpoints = {}; - this._xhrBreakpoints = {}; + this._nativeBreakpoints = {}; + this._domBreakpoints = {}; } WebInspector.BreakpointManager.prototype = { @@ -87,6 +88,7 @@ WebInspector.BreakpointManager.prototype = { { this._breakpoints = {}; delete this._oneTimeBreakpoint; + this._nativeBreakpoints = {}; }, _setBreakpoint: function(sourceID, url, line, enabled, condition) @@ -127,20 +129,144 @@ WebInspector.BreakpointManager.prototype = { InspectorBackend.setBreakpoint(breakpoint.sourceID, breakpoint.line, breakpoint.enabled, breakpoint.condition, didSetBreakpoint.bind(this)); }, - createXHRBreakpoint: function(url) + createDOMBreakpoint: function(nodeId, domEventType, disabled) { - if (url in this._xhrBreakpoints) + var frontendId = "dom:" + nodeId + ":" + domEventType; + if (frontendId in this._nativeBreakpoints) return; - this._xhrBreakpoints[url] = true; - var breakpoint = new WebInspector.XHRBreakpoint(url); - breakpoint.addEventListener("removed", this._xhrBreakpointRemoved.bind(this)); + var breakpoint = new WebInspector.DOMBreakpoint(this, frontendId, nodeId, domEventType); + this._nativeBreakpoints[frontendId] = breakpoint; + this._domBreakpoints[frontendId] = breakpoint; + this.dispatchEventToListeners("dom-breakpoint-added", breakpoint); + breakpoint.enabled = !disabled; + return breakpoint; + }, + + createEventListenerBreakpoint: function(eventName, disabled) + { + var frontendId = eventName; + if (frontendId in this._nativeBreakpoints) + return; + + var breakpoint = new WebInspector.EventListenerBreakpoint(this, frontendId, eventName); + this._nativeBreakpoints[frontendId] = breakpoint; + breakpoint.enabled = !disabled; + return breakpoint; + }, + + createXHRBreakpoint: function(url, disabled) + { + var frontendId = url; + if (frontendId in this._nativeBreakpoints) + return; + + var breakpoint = new WebInspector.XHRBreakpoint(this, frontendId, url); + this._nativeBreakpoints[frontendId] = breakpoint; this.dispatchEventToListeners("xhr-breakpoint-added", breakpoint); + breakpoint.enabled = !disabled + return breakpoint; }, - _xhrBreakpointRemoved: function(event) + _removeNativeBreakpoint: function(breakpoint) { - delete this._xhrBreakpoints[event.target.url]; + if (breakpoint._beingSetOnBackend) + return; + if (breakpoint.enabled) + this._removeNativeBreakpointFromBackend(breakpoint); + delete this._nativeBreakpoints[breakpoint._frontendId]; + if (breakpoint._type === "DOM") + delete this._domBreakpoints[breakpoint._frontendId]; + breakpoint.dispatchEventToListeners("removed"); + }, + + _setNativeBreakpointEnabled: function(breakpoint, enabled) + { + if (breakpoint._beingSetOnBackend) + return; + if (breakpoint.enabled === enabled) + return; + if (enabled) + this._setNativeBreakpointOnBackend(breakpoint); + else + this._removeNativeBreakpointFromBackend(breakpoint); + }, + + _setNativeBreakpointOnBackend: function(breakpoint) + { + breakpoint._beingSetOnBackend = true; + var data = { type: breakpoint._type, condition: breakpoint._condition() }; + InspectorBackend.setNativeBreakpoint(data, didSetNativeBreakpoint.bind(this)); + + function didSetNativeBreakpoint(backendBreakpointId) + { + breakpoint._beingSetOnBackend = false; + if (backendBreakpointId !== "") { + breakpoint._backendId = backendBreakpointId; + this._breakpoints[backendBreakpointId] = breakpoint; + } + breakpoint.dispatchEventToListeners("enable-changed"); + } + }, + + _removeNativeBreakpointFromBackend: function(breakpoint) + { + InspectorBackend.removeNativeBreakpoint(breakpoint._backendId); + delete this._breakpoints[breakpoint._backendId] + delete breakpoint._backendId; + breakpoint.dispatchEventToListeners("enable-changed"); + }, + + debuggerPaused: function(details) + { + if (details.eventType !== WebInspector.DebuggerEventTypes.NativeBreakpoint) + return; + + var breakpoint = this._breakpoints[details.eventData.breakpointId]; + if (!breakpoint) + return; + + breakpoint.hit = true; + breakpoint.dispatchEventToListeners("hit-state-changed"); + this._lastHitBreakpoint = breakpoint; + + this.dispatchEventToListeners("breakpoint-hit", { breakpoint: breakpoint, eventData: details.eventData }); + }, + + debuggerResumed: function() + { + if (!this._lastHitBreakpoint) + return; + this._lastHitBreakpoint.hit = false; + this._lastHitBreakpoint.dispatchEventToListeners("hit-state-changed"); + delete this._lastHitBreakpoint; + }, + + restoreDOMBreakpoints: function() + { + var domBreakpoints = this._domBreakpoints; + this._domBreakpoints = {}; + + var breakpointsToRestore = {}; + for (var frontendId in domBreakpoints) { + var breakpoint = domBreakpoints[frontendId]; + var path = breakpoint._path; + if (!path) + continue; + if (!breakpointsToRestore[path]) { + breakpointsToRestore[path] = []; + InspectorBackend.pushNodeByPathToFrontend(path, restoreBreakpointsForNode.bind(this, breakpointsToRestore[path])); + } + breakpointsToRestore[path].push(breakpoint); + } + + function restoreBreakpointsForNode(breakpoints, nodeId) + { + if (!nodeId) + return; + for (var i = 0; i < breakpoints.length; ++i) + this.createDOMBreakpoint(nodeId, breakpoints[i]._domEventType, !breakpoints[i].enabled); + } } } @@ -226,82 +352,237 @@ WebInspector.Breakpoint.prototype = { WebInspector.Breakpoint.prototype.__proto__ = WebInspector.Object.prototype; -WebInspector.XHRBreakpoint = function(url) +WebInspector.NativeBreakpoint = function(manager, frontendId, type) { - this._url = url; - this._locked = false; - this.enabled = true; + this._manager = manager; + this.__frontendId = frontendId; + this.__type = type; } -WebInspector.XHRBreakpoint.prototype = { +WebInspector.NativeBreakpoint.prototype = { get enabled() { - return "_id" in this; + return "_backendId" in this; }, set enabled(enabled) { - if (this._locked) - return; - if (this.enabled === enabled) - return; - if (enabled) - this._setOnBackend(); - else - this._removeFromBackend(); + this._manager._setNativeBreakpointEnabled(this, enabled); }, - get url() + remove: function() { - return this._url; + this._manager._removeNativeBreakpoint(this); + this._onRemove(); }, - formatLabel: function() + get _frontendId() { - var label = ""; - if (!this.url.length) - label = WebInspector.UIString("Any XHR"); - else - label = WebInspector.UIString("URL contains \"%s\"", this.url); - return label; + return this.__frontendId; }, - compareTo: function(other) + get _type() { - if (this.url != other.url) - return this.url < other.url ? -1 : 1; + return this.__type; + }, + + _compare: function(x, y) + { + if (x !== y) + return x < y ? -1 : 1; return 0; }, - remove: function() + _onRemove: function() { - if (this._locked) - return; - if (this.enabled) - this._removeFromBackend(); - this.dispatchEventToListeners("removed"); + } +} + +WebInspector.NativeBreakpoint.prototype.__proto__ = WebInspector.Object.prototype; + +WebInspector.DOMBreakpoint = function(manager, frontendId, nodeId, domEventType) +{ + WebInspector.NativeBreakpoint.call(this, manager, frontendId, "DOM"); + this._nodeId = nodeId; + this._domEventType = domEventType; + + var node = WebInspector.domAgent.nodeForId(this._nodeId); + if (node) { + node.breakpoints[this._domEventType] = this; + this._path = node.path(); + } +} + +WebInspector.DOMBreakpoint.prototype = { + click: function() + { + WebInspector.updateFocusedNode(this._nodeId); }, - _setOnBackend: function() + compareTo: function(other) { - this._locked = true; - var data = { type: "XHR", condition: { url: this.url } }; - InspectorBackend.setNativeBreakpoint(data, didSet.bind(this)); + return this._compare(this._domEventType, other._domEventType); + }, - function didSet(breakpointId) + populateLabelElement: function(element) + { + element.appendChild(WebInspector.panels.elements.linkifyNodeById(this._nodeId)); + element.appendChild(document.createTextNode(" - ")); + element.appendChild(document.createTextNode(WebInspector.domBreakpointTypeLabel(this._domEventType))); + }, + + populateStatusMessageElement: function(element, eventData) + { + var substitutions = [WebInspector.domBreakpointTypeLabel(this._domEventType), WebInspector.panels.elements.linkifyNodeById(this._nodeId)]; + var formatters = { + s: function(substitution) + { + return substitution; + } + }; + function append(a, b) { - this._locked = false; - this._id = breakpointId; - this.dispatchEventToListeners("enable-changed"); + if (typeof b === "string") + b = document.createTextNode(b); + element.appendChild(b); } + if (this._domEventType === WebInspector.DOMBreakpointTypes.SubtreeModified) { + var targetNode = WebInspector.panels.elements.linkifyNodeById(eventData.targetNodeId); + if (eventData.insertion) { + if (eventData.targetNodeId !== this._nodeId) + WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s.", substitutions.concat(targetNode), formatters, "", append); + else + WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node.", substitutions, formatters, "", append); + } else + WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.", substitutions.concat(targetNode), formatters, "", append); + } else + WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s.", substitutions, formatters, "", append); }, - _removeFromBackend: function() + _condition: function() { - InspectorBackend.removeNativeBreakpoint(this._id); - delete this._id; - this.dispatchEventToListeners("enable-changed"); + return { nodeId: this._nodeId, type: this._domEventType }; + }, + + _onRemove: function() + { + var node = WebInspector.domAgent.nodeForId(this._nodeId); + if (node) + delete node.breakpoints[this._domEventType]; } } -WebInspector.XHRBreakpoint.prototype.__proto__ = WebInspector.Object.prototype; +WebInspector.DOMBreakpoint.prototype.__proto__ = WebInspector.NativeBreakpoint.prototype; + +WebInspector.EventListenerBreakpoint = function(manager, frontendId, eventName) +{ + WebInspector.NativeBreakpoint.call(this, manager, frontendId, "EventListener"); + this._eventName = eventName; +} + +WebInspector.EventListenerBreakpoint.prototype = { + compareTo: function(other) + { + return this._compare(this._eventName, other._eventName); + }, + + populateLabelElement: function(element) + { + element.appendChild(document.createTextNode(this._uiEventName())); + }, + + populateStatusMessageElement: function(element, eventData) + { + var status = WebInspector.UIString("Paused on a \"%s\" Event Listener.", this._uiEventName()); + element.appendChild(document.createTextNode(status)); + }, + + _condition: function() + { + return { eventName: this._eventName }; + }, + + _uiEventName: function() + { + if (!WebInspector.EventListenerBreakpoint._uiEventNames) { + WebInspector.EventListenerBreakpoint._uiEventNames = { + "instrumentation:setTimer": WebInspector.UIString("Set Timer"), + "instrumentation:clearTimer": WebInspector.UIString("Clear Timer"), + "instrumentation:timerFired": WebInspector.UIString("Timer Fired") + }; + } + return WebInspector.EventListenerBreakpoint._uiEventNames[this._eventName] || this._eventName.substring(this._eventName.indexOf(":") + 1); + } +} + +WebInspector.EventListenerBreakpoint.prototype.__proto__ = WebInspector.NativeBreakpoint.prototype; + +WebInspector.XHRBreakpoint = function(manager, frontendId, url) +{ + WebInspector.NativeBreakpoint.call(this, manager, frontendId, "XHR"); + this._url = url; +} + +WebInspector.XHRBreakpoint.prototype = { + compareTo: function(other) + { + return this._compare(this._url, other._url); + }, + + populateLabelElement: function(element) + { + var label; + if (!this._url.length) + label = WebInspector.UIString("Any XHR"); + else + label = WebInspector.UIString("URL contains \"%s\"", this._url); + element.appendChild(document.createTextNode(label)); + }, + + populateStatusMessageElement: function(element) + { + var status = WebInspector.UIString("Paused on a XMLHttpRequest."); + element.appendChild(document.createTextNode(status)); + }, + + _condition: function() + { + return { url: this._url }; + } +} + +WebInspector.XHRBreakpoint.prototype.__proto__ = WebInspector.NativeBreakpoint.prototype; + +WebInspector.DebuggerEventTypes = { + JavaScriptPause: 0, + JavaScriptBreakpoint: 1, + NativeBreakpoint: 2 +}; + +WebInspector.DOMBreakpointTypes = { + SubtreeModified: 0, + AttributeModified: 1, + NodeRemoved: 2 +}; + +WebInspector.domBreakpointTypeLabel = function(type) +{ + if (!WebInspector._DOMBreakpointTypeLabels) { + WebInspector._DOMBreakpointTypeLabels = {}; + WebInspector._DOMBreakpointTypeLabels[WebInspector.DOMBreakpointTypes.SubtreeModified] = WebInspector.UIString("Subtree Modified"); + WebInspector._DOMBreakpointTypeLabels[WebInspector.DOMBreakpointTypes.AttributeModified] = WebInspector.UIString("Attribute Modified"); + WebInspector._DOMBreakpointTypeLabels[WebInspector.DOMBreakpointTypes.NodeRemoved] = WebInspector.UIString("Node Removed"); + } + return WebInspector._DOMBreakpointTypeLabels[type]; +} + +WebInspector.domBreakpointTypeContextMenuLabel = function(type) +{ + if (!WebInspector._DOMBreakpointTypeContextMenuLabels) { + WebInspector._DOMBreakpointTypeContextMenuLabels = {}; + WebInspector._DOMBreakpointTypeContextMenuLabels[WebInspector.DOMBreakpointTypes.SubtreeModified] = WebInspector.UIString("Break on Subtree Modifications"); + WebInspector._DOMBreakpointTypeContextMenuLabels[WebInspector.DOMBreakpointTypes.AttributeModified] = WebInspector.UIString("Break on Attributes Modifications"); + WebInspector._DOMBreakpointTypeContextMenuLabels[WebInspector.DOMBreakpointTypes.NodeRemoved] = WebInspector.UIString("Break on Node Removal"); + } + return WebInspector._DOMBreakpointTypeContextMenuLabels[type]; +} diff --git a/WebCore/inspector/front-end/BreakpointsSidebarPane.js b/WebCore/inspector/front-end/BreakpointsSidebarPane.js index 16ab041..2151137 100644 --- a/WebCore/inspector/front-end/BreakpointsSidebarPane.js +++ b/WebCore/inspector/front-end/BreakpointsSidebarPane.js @@ -113,6 +113,9 @@ WebInspector.XHRBreakpointsSidebarPane.prototype = { if (this.urlInputElement.parentElement) return; + if (!this.expanded) + this.expanded = true; + this.urlInputElement.textContent = ""; this.bodyElement.insertBefore(this.urlInputElement, this.bodyElement.firstChild); WebInspector.startEditing(this.urlInputElement, this._hideEditBreakpointDialog.bind(this, false), this._hideEditBreakpointDialog.bind(this, true)); @@ -142,7 +145,11 @@ WebInspector.BreakpointItem = function(breakpoint) checkboxElement.addEventListener("click", this._checkboxClicked.bind(this), false); this._element.appendChild(checkboxElement); + if ("populateLabelElement" in this._breakpoint) + this._breakpoint.populateLabelElement(this._element); + this._breakpoint.addEventListener("enable-changed", this._enableChanged, this); + this._breakpoint.addEventListener("hit-state-changed", this._hitStateChanged, this); this._breakpoint.addEventListener("removed", this.dispatchEventToListeners.bind(this, "removed")); } @@ -162,6 +169,12 @@ WebInspector.BreakpointItem.prototype = { this._breakpoint.remove(); }, + _breakpointClicked: function(event) + { + if ("click" in this._breakpoint) + this._breakpoint.click(); + }, + _checkboxClicked: function(event) { this._breakpoint.enabled = !this._breakpoint.enabled; @@ -176,8 +189,12 @@ WebInspector.BreakpointItem.prototype = { checkbox.checked = this._breakpoint.enabled; }, - _breakpointClicked: function(event) + _hitStateChanged: function(event) { + if (event.target.hit) + this._element.addStyleClass("breakpoint-hit"); + else + this._element.removeStyleClass("breakpoint-hit"); } } @@ -214,33 +231,119 @@ WebInspector.JSBreakpointItem.prototype = { WebInspector.JSBreakpointItem.prototype.__proto__ = WebInspector.BreakpointItem.prototype; -WebInspector.DOMBreakpointItem = function(breakpoint) +WebInspector.EventListenerBreakpointsSidebarPane = function() { - WebInspector.BreakpointItem.call(this, breakpoint); + WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listener Breakpoints")); - var link = WebInspector.panels.elements.linkifyNodeById(this._breakpoint.nodeId); - this._element.appendChild(link); - - var type = WebInspector.DOMBreakpoint.labelForType(this._breakpoint.type); - var typeElement = document.createTextNode(" - " + type); - this._element.appendChild(typeElement); + this.categoriesElement = document.createElement("ol"); + this.categoriesElement.tabIndex = 0; + this.categoriesElement.addStyleClass("properties-tree event-listener-breakpoints"); + this.categoriesTreeOutline = new TreeOutline(this.categoriesElement); + this.bodyElement.appendChild(this.categoriesElement); } -WebInspector.DOMBreakpointItem.prototype = { - _breakpointClicked: function() +WebInspector.EventListenerBreakpointsSidebarPane.prototype = { + _populate: function() { - WebInspector.updateFocusedNode(this._breakpoint.nodeId); - } -} + var categories = { + "Mouse": { type: "listener", eventNames: ["click", "dblclick", "mousedown", "mouseup", "mouseover", "mousemove", "mouseout", "mousewheel"] }, + "Keyboard": { type: "listener", eventNames: ["keydown", "keypress", "keyup"] }, + "HTML frame/object": { type: "listener", eventNames: ["load", "error", "resize", "scroll"] }, + "Timer": { type: "instrumentation", eventNames: ["setTimer", "clearTimer", "timerFired"] } + }; + + for (var category in categories) { + var categoryTreeElement = new TreeElement(WebInspector.UIString(category)); + this.categoriesTreeOutline.appendChild(categoryTreeElement); + categoryTreeElement.listItemElement.addStyleClass("event-category"); + categoryTreeElement.selectable = true; + + var categoryItem = {}; + categoryItem.checkbox = this._createCheckbox(categoryTreeElement, this._categoryCheckboxClicked.bind(this, categoryItem)); + categoryItem.children = {}; + + var categoryType = categories[category].type; + var eventNames = categories[category].eventNames; + for (var i = 0; i < eventNames.length; ++i) { + var eventName = categoryType + ":" + eventNames[i]; + + var breakpoint = WebInspector.breakpointManager.createEventListenerBreakpoint(eventName, true); + if (!breakpoint) + continue; + + var labelElement = document.createElement("div"); + breakpoint.populateLabelElement(labelElement); + var eventNameTreeElement = new TreeElement(labelElement); + categoryTreeElement.appendChild(eventNameTreeElement); + eventNameTreeElement.listItemElement.addStyleClass("source-code"); + eventNameTreeElement.selectable = true; + + var eventNameItem = {}; + eventNameItem.checkbox = this._createCheckbox(eventNameTreeElement, this._eventNameCheckboxClicked.bind(this, categoryItem, eventNameItem)); + eventNameItem.breakpoint = breakpoint; + + breakpoint.addEventListener("enable-changed", this._breakpointEnableChanged.bind(this, categoryItem, eventNameItem), true); + + categoryItem.children[eventName] = eventNameItem; + } + } + }, -WebInspector.DOMBreakpointItem.prototype.__proto__ = WebInspector.BreakpointItem.prototype; + _createCheckbox: function(treeElement, checkboxClickedDelegate) + { + var checkbox = document.createElement("input"); + checkbox.className = "checkbox-elem"; + checkbox.type = "checkbox"; + checkbox.addEventListener("click", checkboxClickedDelegate, true); + treeElement.listItemElement.insertBefore(checkbox, treeElement.listItemElement.firstChild); + return checkbox; + }, -WebInspector.XHRBreakpointItem = function(breakpoint) -{ - WebInspector.BreakpointItem.call(this, breakpoint); + _categoryCheckboxClicked: function(categoryItem) + { + var checkbox = categoryItem.checkbox; + checkbox.indeterminate = false; + for (var eventName in categoryItem.children) { + var eventNameItem = categoryItem.children[eventName]; + eventNameItem.checkbox.checked = checkbox.checked; + eventNameItem.breakpoint.enabled = checkbox.checked; + } + }, + + _eventNameCheckboxClicked: function(categoryItem, eventNameItem) + { + this._updateCategoryCheckbox(categoryItem); + eventNameItem.breakpoint.enabled = eventNameItem.checkbox.checked; + }, + + _breakpointEnableChanged: function(categoryItem, eventNameItem) + { + if (eventNameItem.checkbox.checked === eventNameItem.breakpoint.enabled) + return; - var label = document.createTextNode(this._breakpoint.formatLabel()); - this._element.appendChild(label); + eventNameItem.checkbox.checked = eventNameItem.breakpoint.enabled; + this._updateCategoryCheckbox(categoryItem); + }, + + _updateCategoryCheckbox: function(categoryItem) + { + var hasEnabled = false, hasDisabled = false; + for (var eventName in categoryItem.children) { + var eventNameItem = categoryItem.children[eventName]; + if (eventNameItem.checkbox.checked) + hasEnabled = true; + else + hasDisabled = true; + } + categoryItem.checkbox.checked = hasEnabled; + categoryItem.checkbox.indeterminate = hasEnabled && hasDisabled; + }, + + reset: function() + { + this.categoriesTreeOutline.removeChildren(); + this._populate(); + } } -WebInspector.XHRBreakpointItem.prototype.__proto__ = WebInspector.BreakpointItem.prototype; +WebInspector.EventListenerBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; diff --git a/WebCore/inspector/front-end/CSSStyleModel.js b/WebCore/inspector/front-end/CSSStyleModel.js index bda4064..e3e9b4f 100644 --- a/WebCore/inspector/front-end/CSSStyleModel.js +++ b/WebCore/inspector/front-end/CSSStyleModel.js @@ -96,13 +96,13 @@ WebInspector.CSSStyleModel.prototype = { applyStyleText: function(styleId, styleText, propertyName, successCallback, failureCallback) { - function callback(success, newPayload, changedProperties) + function callback(success, newPayload) { if (!success) failureCallback(); else { var newStyle = newPayload ? WebInspector.CSSStyleDeclaration.parseStyle(newPayload) : null; - successCallback(newStyle, changedProperties); + successCallback(newStyle); } } diff --git a/WebCore/inspector/front-end/CallStackSidebarPane.js b/WebCore/inspector/front-end/CallStackSidebarPane.js index 6212ea1..08c1942 100644 --- a/WebCore/inspector/front-end/CallStackSidebarPane.js +++ b/WebCore/inspector/front-end/CallStackSidebarPane.js @@ -26,13 +26,9 @@ WebInspector.CallStackSidebarPane = function() { WebInspector.SidebarPane.call(this, WebInspector.UIString("Call Stack")); + WebInspector.breakpointManager.addEventListener("breakpoint-hit", this._breakpointHit, this); } -WebInspector.CallStackSidebarPane.DebuggerEventType = { - DOMBreakpoint: 0, - NativeBreakpoint: 1 -}; - WebInspector.CallStackSidebarPane.prototype = { update: function(callFrames, sourceIDMap) { @@ -87,44 +83,6 @@ WebInspector.CallStackSidebarPane.prototype = { } }, - updateStatus: function(eventType, eventData) - { - var statusElement = document.createElement("div"); - if (eventType === WebInspector.CallStackSidebarPane.DebuggerEventType.DOMBreakpoint) { - var breakpoint = eventData.breakpoint; - var substitutions = [WebInspector.DOMBreakpoint.labelForType(breakpoint.type), WebInspector.panels.elements.linkifyNodeById(breakpoint.nodeId)]; - var formatters = { - s: function(substitution) - { - return substitution; - } - }; - function append(a, b) - { - if (typeof b === "string") - b = document.createTextNode(b); - statusElement.appendChild(b); - } - if (breakpoint.type === WebInspector.DOMBreakpoint.Types.SubtreeModified) { - var targetNode = WebInspector.panels.elements.linkifyNodeById(eventData.targetNodeId); - if (eventData.insertion) { - if (eventData.targetNodeId !== breakpoint.nodeId) - WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s.", substitutions.concat(targetNode), formatters, "", append); - else - WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node.", substitutions, formatters, "", append); - } else - WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.", substitutions.concat(targetNode), formatters, "", append); - } else - WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s.", substitutions, formatters, "", append); - } else if (eventType === WebInspector.CallStackSidebarPane.DebuggerEventType.NativeBreakpoint && eventData.type === "XHR") - statusElement.appendChild(document.createTextNode(WebInspector.UIString("Paused on XMLHttpRequest."))); - else - return; - - statusElement.className = "info"; - this.bodyElement.appendChild(statusElement); - }, - get selectedCallFrame() { return this._selectedCallFrame; @@ -210,6 +168,16 @@ WebInspector.CallStackSidebarPane.prototype = { this._shortcuts[prevCallFrame.key] = this._selectPreviousCallFrameOnStack.bind(this); section.addRelatedKeys([ nextCallFrame.name, prevCallFrame.name ], WebInspector.UIString("Next/previous call frame")); + }, + + _breakpointHit: function(event) + { + var breakpoint = event.data.breakpoint; + + var statusMessageElement = document.createElement("div"); + statusMessageElement.className = "info"; + breakpoint.populateStatusMessageElement(statusMessageElement, event.data.eventData); + this.bodyElement.appendChild(statusMessageElement); } } diff --git a/WebCore/inspector/front-end/ConsoleView.js b/WebCore/inspector/front-end/ConsoleView.js index 6f8bd8b..8cd5d52 100644 --- a/WebCore/inspector/front-end/ConsoleView.js +++ b/WebCore/inspector/front-end/ConsoleView.js @@ -64,7 +64,7 @@ WebInspector.ConsoleView = function(drawer) function createDividerElement() { var dividerElement = document.createElement("div"); - dividerElement.addStyleClass("divider"); + dividerElement.addStyleClass("scope-bar-divider"); this.filterBarElement.appendChild(dividerElement); } @@ -360,7 +360,7 @@ WebInspector.ConsoleView.prototype = { } var results = []; - var properties = Object.sortedProperties(result); + var properties = Object.keys(result).sort(); for (var i = 0; i < properties.length; ++i) { var property = properties[i]; @@ -679,25 +679,22 @@ WebInspector.ConsoleMessage.prototype = { case WebInspector.ConsoleMessage.MessageType.UncaughtException: var ol = document.createElement("ol"); ol.addStyleClass("stack-trace"); - if (this.type === WebInspector.ConsoleMessage.MessageType.Trace) - ol.addStyleClass("trace-message"); var treeOutline = new TreeOutline(ol); + var messageText; + if (this.type === WebInspector.ConsoleMessage.MessageType.Assert) + messageText = this._format(this._parameters); + else if (this.type === WebInspector.ConsoleMessage.MessageType.Trace) + messageText = document.createTextNode("console.trace()"); + else + messageText = document.createTextNode(this._messageText); - var root = treeOutline; - if (this.type === WebInspector.ConsoleMessage.MessageType.UncaughtException || - this.type === WebInspector.ConsoleMessage.MessageType.Assert) { - var messageText; - if (this.type === WebInspector.ConsoleMessage.MessageType.Assert) - messageText = this._format(this._parameters); - else - messageText = document.createTextNode(this._messageText); - - var content = document.createElement("div"); - this._addMessageHeader(content, messageText); - root = new TreeElement(content, null, true); - content.treeElementForTest = root; - treeOutline.appendChild(root); - } + var content = document.createElement("div"); + this._addMessageHeader(content, messageText); + var root = new TreeElement(content, null, true); + content.treeElementForTest = root; + treeOutline.appendChild(root); + if (this.type === WebInspector.ConsoleMessage.MessageType.Trace) + root.expand(); this._populateStackTraceTreeElement(root); this.formattedMessage = ol; diff --git a/WebCore/inspector/front-end/CookieItemsView.js b/WebCore/inspector/front-end/CookieItemsView.js index 1baf4a6..88cbe05 100644 --- a/WebCore/inspector/front-end/CookieItemsView.js +++ b/WebCore/inspector/front-end/CookieItemsView.js @@ -122,8 +122,8 @@ WebInspector.CookieItemsView.prototype = { for (var id in WebInspector.resources) { var resource = WebInspector.resources[id]; - var match = resource.documentURL.match(WebInspector.GenericURLRegExp); - if (match && match[2] === this._cookieDomain) + var url = resource.documentURL.asParsedURL(); + if (url && url.host == this._cookieDomain) resourceURLsForDocumentURL.push(resource.url); } diff --git a/WebCore/inspector/front-end/CookieParser.js b/WebCore/inspector/front-end/CookieParser.js new file mode 100755 index 0000000..2be5df7 --- /dev/null +++ b/WebCore/inspector/front-end/CookieParser.js @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Ideally, we would rely on platform support for parsing a cookie, since +// this would save us from any potential inconsistency. However, exposing +// platform cookie parsing logic would require quite a bit of additional +// plumbing, and at least some platforms lack support for parsing Cookie, +// which is in a format slightly different from Set-Cookie and is normally +// only required on the server side. + +WebInspector.CookieParser = function() +{ +} + +WebInspector.CookieParser.prototype = { + get cookies() + { + return this._cookies; + }, + + parseCookie: function(cookieHeader) + { + if (!this._initialize(cookieHeader)) + return; + + for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) { + if (kv.key.charAt(0) === "$" && this._lastCookie) + this._lastCookie.addAttribute(kv.key.slice(1), kv.value); + else if (kv.key.toLowerCase() !== "$version" && typeof kv.value === "string") + this._addCookie(kv); + this._advanceAndCheckCookieDelimiter(); + } + this._flushCookie(); + return this._cookies; + }, + + parseSetCookie: function(setCookieHeader) + { + if (!this._initialize(setCookieHeader)) + return; + for (var kv = this._extractKeyValue(); kv; kv = this._extractKeyValue()) { + if (this._lastCookie) + this._lastCookie.addAttribute(kv.key, kv.value); + else + this._addCookie(kv); + if (this._advanceAndCheckCookieDelimiter()) + this._flushCookie(); + } + this._flushCookie(); + return this._cookies; + }, + + _initialize: function(headerValue) + { + this._input = headerValue; + if (typeof headerValue !== "string") + return false; + this._cookies = []; + this._lastCookie = null; + this._originalInputLength = this._input.length; + return true; + }, + + _flushCookie: function() + { + if (this._lastCookie) + this._lastCookie.size = this._originalInputLength - this._input.length - this._lastCookiePosition; + this._lastCookie = null; + }, + + _extractKeyValue: function() + { + if (!this._input || !this._input.length) + return null; + // Note: RFCs offer an option for quoted values that may contain commas and semicolons. + // Many browsers/platforms do not support this, however (see http://webkit.org/b/16699 + // and http://crbug.com/12361). The logic below matches latest versions of IE, Firefox, + // Chrome and Safari on some old platforms. The latest version of Safari supports quoted + // cookie values, though. + var keyValueMatch = /^[ \t]*([^\s=;]+)[ \t]*(?:=[ \t]*([^;\n]*))?/.exec(this._input); + if (!keyValueMatch) { + console.log("Failed parsing cookie header before: " + this._input); + return null; + } + + var result = { + key: keyValueMatch[1], + value: keyValueMatch[2] && keyValueMatch[2].trim(), + position: this._originalInputLength - this._input.length + }; + this._input = this._input.slice(keyValueMatch[0].length); + return result; + }, + + _advanceAndCheckCookieDelimiter: function() + { + var match = /^\s*[\n;]\s*/.exec(this._input); + if (!match) + return false; + this._input = this._input.slice(match[0].length); + return match[0].match("\n") !== null; + }, + + _addCookie: function(keyValue) + { + if (this._lastCookie) + this._lastCookie.size = keyValue.position - this._lastCookiePosition; + // Mozilla bug 169091: Mozilla, IE and Chrome treat signle token (w/o "=") as + // specifying a value for a cookie with empty name. + this._lastCookie = keyValue.value ? new WebInspector.Cookie(keyValue.key, keyValue.value) : + new WebInspector.Cookie("", keyValue.key); + this._lastCookiePosition = keyValue.position; + this._cookies.push(this._lastCookie); + } +}; + +WebInspector.CookieParser.parseCookie = function(header) +{ + return (new WebInspector.CookieParser()).parseCookie(header); +} + +WebInspector.CookieParser.parseSetCookie = function(header) +{ + return (new WebInspector.CookieParser()).parseSetCookie(header); +} + +WebInspector.Cookie = function(name, value) +{ + this.name = name; + this.value = value; + this._attributes = {}; +} + +WebInspector.Cookie.prototype = { + get httpOnly() + { + return "httponly" in this._attributes; + }, + + get secure() + { + return "secure" in this._attributes; + }, + + get session() + { + // RFC 2965 suggests using Discard attribute to mark session cookies, but this does not seem to be widely used. + // Check for absence of explicity max-age or expiry date instead. + return !("expries" in this._attributes || "max-age" in this._attributes); + }, + + get path() + { + return this._attributes.path; + }, + + get domain() + { + return this._attributes.domain; + }, + + expires: function(requestDate) + { + return this._attributes.expires ? new Date(this._attributes.expires) : + (this._attributes["max-age"] ? new Date(requestDate.getTime() + 1000 * this._attributes["max-age"]) : null); + }, + + get attributes() + { + return this._attributes; + }, + + addAttribute: function(key, value) + { + this._attributes[key.toLowerCase()] = value; + } +} diff --git a/WebCore/inspector/front-end/DOMAgent.js b/WebCore/inspector/front-end/DOMAgent.js index 279852e..5153fb1 100644 --- a/WebCore/inspector/front-end/DOMAgent.js +++ b/WebCore/inspector/front-end/DOMAgent.js @@ -60,6 +60,8 @@ WebInspector.DOMNode = function(doc, payload) { this.style = null; this._matchedCSSRules = []; + this.breakpoints = {}; + if (this.nodeType === Node.ELEMENT_NODE) { // HTML and BODY from internal iframes should not overwrite top-level ones. if (!this.ownerDocument.documentElement && this.nodeName === "HTML") @@ -149,28 +151,6 @@ WebInspector.DOMNode.prototype = { return path.join(","); }, - setBreakpoint: function(type) - { - return WebInspector.domBreakpointManager.setBreakpoint(this.id, type, true, this.path()); - }, - - hasBreakpoint: function(type) - { - return !!WebInspector.domBreakpointManager.findBreakpoint(this.id, type); - }, - - removeBreakpoint: function(type) - { - var breakpoint = WebInspector.domBreakpointManager.findBreakpoint(this.id, type); - if (breakpoint) - breakpoint.remove(); - }, - - removeBreakpoints: function() - { - WebInspector.domBreakpointManager.removeBreakpointsForNode(this.id); - }, - _setAttributesPayload: function(attrs) { this.attributes = []; @@ -399,7 +379,7 @@ WebInspector.DOMAgent.prototype = { this.document = new WebInspector.DOMDocument(this, this._window, payload); this._idToDOMNode[payload.id] = this.document; this._bindNodes(this.document.children); - WebInspector.domBreakpointManager.restoreBreakpoints(); + WebInspector.breakpointManager.restoreDOMBreakpoints(); } else this.document = null; WebInspector.panels.elements.setDocument(this.document); @@ -461,12 +441,13 @@ WebInspector.DOMAgent.prototype = { _removeBreakpoints: function(node) { - node.removeBreakpoints(); + for (var type in node.breakpoints) + node.breakpoints[type].remove(); if (!node.children) return; for (var i = 0; i < node.children.length; ++i) this._removeBreakpoints(node.children[i]); - } + } } WebInspector.ApplicationCache = {} @@ -519,17 +500,12 @@ WebInspector.Cookies.buildCookiesFromString = function(rawCookieString) WebInspector.Cookies.cookieMatchesResourceURL = function(cookie, resourceURL) { - var match = resourceURL.match(WebInspector.GenericURLRegExp); - if (!match) + var url = resourceURL.asParsedURL(); + if (!url || !this.cookieDomainMatchesResourceDomain(cookie.domain, url.host)) return false; - // See WebInspector.URLRegExp for definitions of the group index constants. - if (!this.cookieDomainMatchesResourceDomain(cookie.domain, match[2])) - return false; - var resourcePort = match[3] ? match[3] : undefined; - var resourcePath = match[4] ? match[4] : '/'; - return (resourcePath.indexOf(cookie.path) === 0 - && (!cookie.port || resourcePort == cookie.port) - && (!cookie.secure || match[1].toLowerCase() === 'https')); + return (url.path.indexOf(cookie.path) === 0 + && (!cookie.port || url.port == cookie.port) + && (!cookie.secure || url.scheme === "https")); } WebInspector.Cookies.cookieDomainMatchesResourceDomain = function(cookieDomain, resourceDomain) @@ -728,166 +704,3 @@ WebInspector.childNodeRemoved = function() { this.domAgent._childNodeRemoved.apply(this.domAgent, arguments); } - -WebInspector.DOMBreakpointManager = function() -{ - this._breakpoints = {}; - this._pathCache = {}; -} - -WebInspector.DOMBreakpointManager.prototype = { - setBreakpoint: function(nodeId, type, enabled, path) - { - if (!(nodeId in this._breakpoints)) - this._breakpoints[nodeId] = {}; - else if (type in this._breakpoints[nodeId]) - return; - - var breakpoint = new WebInspector.DOMBreakpoint(nodeId, type, enabled); - this._breakpoints[nodeId][type] = breakpoint; - breakpoint.addEventListener("removed", this._breakpointRemoved, this); - - if (!(nodeId in this._pathCache)) - this._pathCache[nodeId] = path; - - this.dispatchEventToListeners("dom-breakpoint-added", breakpoint); - }, - - findBreakpoint: function(nodeId, type) - { - var nodeBreakpoints = this._breakpoints[nodeId]; - if (nodeBreakpoints && type in nodeBreakpoints) - return nodeBreakpoints[type]; - }, - - removeBreakpointsForNode: function(nodeId) - { - var nodeBreakpoints = this._breakpoints[nodeId]; - for (var type in nodeBreakpoints) - nodeBreakpoints[type].remove(); - }, - - _breakpointRemoved: function(event) - { - var breakpoint = event.target; - - var nodeBreakpoints = this._breakpoints[breakpoint.nodeId]; - delete nodeBreakpoints[breakpoint.type]; - for (var type in nodeBreakpoints) - return; - - delete this._breakpoints[breakpoint.nodeId]; - delete this._pathCache[breakpoint.nodeId]; - }, - - restoreBreakpoints: function() - { - var breakpoints = this._breakpoints; - this._breakpoints = {}; - var pathCache = this._pathCache; - this._pathCache = {}; - - for (var oldNodeId in breakpoints) { - var path = pathCache[oldNodeId]; - InspectorBackend.pushNodeByPathToFrontend(path, restoreBreakpointsForNode.bind(this, breakpoints[oldNodeId], path)); - } - - function restoreBreakpointsForNode(nodeBreakpoints, path, nodeId) - { - if (!nodeId) - return; - for (var type in nodeBreakpoints) { - var breakpoint = nodeBreakpoints[type]; - this.setBreakpoint(nodeId, breakpoint.type, breakpoint.enabled, path); - } - } - } -} - -WebInspector.DOMBreakpointManager.prototype.__proto__ = WebInspector.Object.prototype; - -WebInspector.DOMBreakpoint = function(nodeId, type, enabled) -{ - this._nodeId = nodeId; - this._type = type; - this._enabled = enabled; - - if (this.enabled) - InspectorBackend.setDOMBreakpoint(this.nodeId, this.type); -} - -WebInspector.DOMBreakpoint.Types = { - SubtreeModified: 0, - AttributeModified: 1, - NodeRemoved: 2 -}; - -WebInspector.DOMBreakpoint.labelForType = function(type) -{ - if (!WebInspector.DOMBreakpoint._labels) { - WebInspector.DOMBreakpoint._labels = {}; - WebInspector.DOMBreakpoint._labels[WebInspector.DOMBreakpoint.Types.SubtreeModified] = WebInspector.UIString("Subtree Modified"); - WebInspector.DOMBreakpoint._labels[WebInspector.DOMBreakpoint.Types.AttributeModified] = WebInspector.UIString("Attribute Modified"); - WebInspector.DOMBreakpoint._labels[WebInspector.DOMBreakpoint.Types.NodeRemoved] = WebInspector.UIString("Node Removed"); - } - return WebInspector.DOMBreakpoint._labels[type]; -} - -WebInspector.DOMBreakpoint.contextMenuLabelForType = function(type) -{ - if (!WebInspector.DOMBreakpoint._contextMenuLabels) { - WebInspector.DOMBreakpoint._contextMenuLabels = {}; - WebInspector.DOMBreakpoint._contextMenuLabels[WebInspector.DOMBreakpoint.Types.SubtreeModified] = WebInspector.UIString("Break on Subtree Modifications"); - WebInspector.DOMBreakpoint._contextMenuLabels[WebInspector.DOMBreakpoint.Types.AttributeModified] = WebInspector.UIString("Break on Attributes Modifications"); - WebInspector.DOMBreakpoint._contextMenuLabels[WebInspector.DOMBreakpoint.Types.NodeRemoved] = WebInspector.UIString("Break on Node Removal"); - } - return WebInspector.DOMBreakpoint._contextMenuLabels[type]; -} - -WebInspector.DOMBreakpoint.prototype = { - get nodeId() - { - return this._nodeId; - }, - - get type() - { - return this._type; - }, - - get enabled() - { - return this._enabled; - }, - - set enabled(enabled) - { - if (this._enabled === enabled) - return; - - this._enabled = enabled; - if (this.enabled) - InspectorBackend.setDOMBreakpoint(this.nodeId, this.type); - else - InspectorBackend.removeDOMBreakpoint(this.nodeId, this.type); - - this.dispatchEventToListeners("enable-changed"); - }, - - compareTo: function(other) - { - if (this.type != other.type) - return this.type < other.type ? -1 : 1; - return 0; - }, - - remove: function() - { - if (this.enabled) - InspectorBackend.removeDOMBreakpoint(this.nodeId, this.type); - this.dispatchEventToListeners("removed"); - } -} - -WebInspector.DOMBreakpoint.prototype.__proto__ = WebInspector.Object.prototype; - diff --git a/WebCore/inspector/front-end/DataGrid.js b/WebCore/inspector/front-end/DataGrid.js index f68fe48..3007497 100644 --- a/WebCore/inspector/front-end/DataGrid.js +++ b/WebCore/inspector/front-end/DataGrid.js @@ -32,6 +32,7 @@ WebInspector.DataGrid = function(columns, editCallback, deleteCallback) this._headerTable = document.createElement("table"); this._headerTable.className = "header"; + this._headerTableHeaders = {}; this._dataTable = document.createElement("table"); this._dataTable.className = "data"; @@ -77,9 +78,13 @@ WebInspector.DataGrid = function(columns, editCallback, deleteCallback) var cell = document.createElement("th"); cell.className = columnIdentifier + "-column"; cell.columnIdentifier = columnIdentifier; + this._headerTableHeaders[columnIdentifier] = cell; var div = document.createElement("div"); - div.textContent = column.title; + if (column.titleDOMFragment) + div.appendChild(column.titleDOMFragment); + else + div.textContent = column.title; cell.appendChild(div); if (column.sort) { @@ -113,16 +118,27 @@ WebInspector.DataGrid = function(columns, editCallback, deleteCallback) var fillerRow = document.createElement("tr"); fillerRow.className = "filler"; - for (var i = 0; i < this._columnCount; ++i) { + for (var columnIdentifier in columns) { + var column = columns[columnIdentifier]; var cell = document.createElement("td"); + cell.className = columnIdentifier + "-column"; fillerRow.appendChild(cell); } - + this._dataTableColumnGroup = columnGroup.cloneNode(true); this._dataTable.appendChild(this._dataTableColumnGroup); this.dataTableBody.appendChild(fillerRow); this.columns = columns || {}; + this._columnsArray = []; + for (var columnIdentifier in columns) { + columns[columnIdentifier].ordinal = this._columnsArray.length; + this._columnsArray.push(columns[columnIdentifier]); + } + + for (var i = 0; i < this._columnsArray.length; ++i) + this._columnsArray[i].bodyElement = this._dataTableColumnGroup.children[i]; + this.children = []; this.selectedNode = null; this.expandNodesWhenArrowing = false; @@ -134,7 +150,7 @@ WebInspector.DataGrid = function(columns, editCallback, deleteCallback) this.dataGrid = this; this.indentWidth = 15; this.resizers = []; - this.columnWidthsInitialized = false; + this._columnWidthsInitialized = false; } WebInspector.DataGrid.prototype = { @@ -351,7 +367,7 @@ WebInspector.DataGrid.prototype = { for (var columnIdentifier in columns) columns[columnIdentifier].element.style.width = widths[columnIdentifier] + "%"; - this.columnWidthsInitialized = false; + this._columnWidthsInitialized = false; this.updateWidths(); }, @@ -369,11 +385,10 @@ WebInspector.DataGrid.prototype = { { var headerTableColumns = this._headerTableColumnGroup.children; - var left = 0; var tableWidth = this._dataTable.offsetWidth; var numColumns = headerTableColumns.length; - if (!this.columnWidthsInitialized) { + if (!this._columnWidthsInitialized) { // Give all the columns initial widths now so that during a resize, // when the two columns that get resized get a percent value for // their widths, all the other columns already have percent values @@ -384,9 +399,86 @@ WebInspector.DataGrid.prototype = { this._headerTableColumnGroup.children[i].style.width = percentWidth; this._dataTableColumnGroup.children[i].style.width = percentWidth; } - this.columnWidthsInitialized = true; + this._columnWidthsInitialized = true; } - + this._positionResizers(); + this.dispatchEventToListeners("width changed"); + }, + + columnWidthsMap: function() + { + var result = {}; + for (var i = 0; i < this._columnsArray.length; ++i) { + var width = this._headerTableColumnGroup.children[i].style.width; + result[this._columnsArray[i].columnIdentifier] = parseFloat(width); + } + return result; + }, + + applyColumnWidthsMap: function(columnWidthsMap) + { + for (var columnIdentifier in this.columns) { + var column = this.columns[columnIdentifier]; + var width = (columnWidthsMap[columnIdentifier] || 0) + "%"; + this._headerTableColumnGroup.children[column.ordinal].style.width = width; + this._dataTableColumnGroup.children[column.ordinal].style.width = width; + } + + // Normalize widths + delete this._columnWidthsInitialized; + this.updateWidths(); + }, + + isColumnVisible: function(columnIdentifier) + { + var column = this.columns[columnIdentifier]; + var columnElement = column.element; + return !columnElement.hidden; + }, + + showColumn: function(columnIdentifier) + { + var column = this.columns[columnIdentifier]; + var columnElement = column.element; + if (!columnElement.hidden) + return; + + columnElement.hidden = false; + columnElement.removeStyleClass("hidden"); + + var columnBodyElement = column.bodyElement; + columnBodyElement.hidden = false; + columnBodyElement.removeStyleClass("hidden"); + }, + + hideColumn: function(columnIdentifier) + { + var column = this.columns[columnIdentifier]; + var columnElement = column.element; + if (columnElement.hidden) + return; + + var oldWidth = parseFloat(columnElement.style.width); + + columnElement.hidden = true; + columnElement.addStyleClass("hidden"); + columnElement.style.width = 0; + + var columnBodyElement = column.bodyElement; + columnBodyElement.hidden = true; + columnBodyElement.addStyleClass("hidden"); + columnBodyElement.style.width = 0; + + this._columnWidthsInitialized = false; + }, + + _positionResizers: function() + { + var headerTableColumns = this._headerTableColumnGroup.children; + var numColumns = headerTableColumns.length; + var left = 0; + var previousResizer = null; + // Make n - 1 resizers for n columns. for (var i = 0; i < numColumns - 1; i++) { var resizer = this.resizers[i]; @@ -397,7 +489,6 @@ WebInspector.DataGrid.prototype = { resizer = document.createElement("div"); resizer.addStyleClass("data-grid-resizer"); // This resizer is associated with the column to its right. - resizer.rightNeighboringColumnID = i + 1; resizer.addEventListener("mousedown", this._startResizerDragging.bind(this), false); this.element.appendChild(resizer); this.resizers[i] = resizer; @@ -407,10 +498,23 @@ WebInspector.DataGrid.prototype = { // header table in order to determine the width of the column, since // it is not possible to query a column for its width. left += this.headerTableBody.rows[0].cells[i].offsetWidth; - - resizer.style.left = left + "px"; + + var columnIsVisible = !this._headerTableColumnGroup.children[i].hidden; + if (columnIsVisible) { + resizer.style.removeProperty("display"); + resizer.style.left = left + "px"; + resizer.leftNeighboringColumnID = i; + if (previousResizer) + previousResizer.rightNeighboringColumnID = i; + previousResizer = resizer; + } else { + resizer.style.setProperty("display", "none"); + resizer.leftNeighboringColumnID = 0; + resizer.rightNeighboringColumnID = 0; + } } - this.dispatchEventToListeners("width changed"); + if (previousResizer) + previousResizer.rightNeighboringColumnID = numColumns - 1; }, addCreationNode: function(hasChildren) @@ -474,6 +578,7 @@ WebInspector.DataGrid.prototype = { throw("removeChild: Node is not a child of this node."); child.deselect(); + child._detach(); this.children.remove(child, true); @@ -534,17 +639,18 @@ WebInspector.DataGrid.prototype = { this.children = []; }, - sortNodes: function(comparator, descending) + sortNodes: function(comparator, reverseMode) { function comparatorWrapper(a, b) { + if (a._dataGridNode._data.summaryRow) + return 1; + if (b._dataGridNode._data.summaryRow) + return -1; + var aDataGirdNode = a._dataGridNode; var bDataGirdNode = b._dataGridNode; - if (!aDataGirdNode) - return 1; // Filler row. - if (!bDataGirdNode) - return -1; // Filler row. - return descending ? comparator(bDataGirdNode, aDataGirdNode) : comparator(aDataGirdNode, bDataGirdNode); + return reverseMode ? comparator(bDataGirdNode, aDataGirdNode) : comparator(aDataGirdNode, bDataGirdNode); } var tbody = this.dataTableBody; @@ -552,15 +658,27 @@ WebInspector.DataGrid.prototype = { tbodyParent.removeChild(tbody); var childNodes = tbody.childNodes; - var sortedNodes = Array.prototype.slice.call(childNodes); - sortedNodes.sort(comparatorWrapper.bind(this)); + var fillerRow = childNodes[childNodes.length - 1]; + + var sortedRows = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1); + sortedRows.sort(comparatorWrapper); + var sortedRowsLength = sortedRows.length; - var sortedNodesLength = sortedNodes.length; tbody.removeChildren(); - for (var i = 0; i < sortedNodesLength; ++i) { - var node = sortedNodes[i]; - tbody.appendChild(node); + var previousSiblingNode = null; + for (var i = 0; i < sortedRowsLength; ++i) { + var row = sortedRows[i]; + var node = row._dataGridNode; + node.previousSibling = previousSiblingNode; + if (previousSiblingNode) + previousSiblingNode.nextSibling = node; + tbody.appendChild(row); + previousSiblingNode = node; } + if (previousSiblingNode) + previousSiblingNode.nextSibling = null; + + tbody.appendChild(fillerRow); tbodyParent.appendChild(tbody); }, @@ -673,13 +791,11 @@ WebInspector.DataGrid.prototype = { var sortOrder = this.sortOrder; - if (this._sortColumnCell) { - this._sortColumnCell.removeStyleClass("sort-ascending"); - this._sortColumnCell.removeStyleClass("sort-descending"); - } + if (this._sortColumnCell) + this._sortColumnCell.removeMatchingStyleClasses("sort-\\w+"); if (cell == this._sortColumnCell) { - if (sortOrder == "ascending") + if (sortOrder === "ascending") sortOrder = "descending"; else sortOrder = "ascending"; @@ -692,6 +808,19 @@ WebInspector.DataGrid.prototype = { this.dispatchEventToListeners("sorting changed"); }, + markColumnAsSortedBy: function(columnIdentifier, sortOrder) + { + if (this._sortColumnCell) + this._sortColumnCell.removeMatchingStyleClasses("sort-\\w+"); + this._sortColumnCell = this._headerTableHeaders[columnIdentifier]; + this._sortColumnCell.addStyleClass("sort-" + sortOrder); + }, + + headerTableHeader: function(columnIdentifier) + { + return this._headerTableHeaders[columnIdentifier]; + }, + _mouseDownInDataTable: function(event) { var gridNode = this.dataGridNodeFromNode(event.target); @@ -778,27 +907,28 @@ WebInspector.DataGrid.prototype = { // column directly to the left and the column directly to the right. var leftEdgeOfPreviousColumn = 0; var firstRowCells = this.headerTableBody.rows[0].cells; - for (var i = 0; i < resizer.rightNeighboringColumnID - 1; i++) + for (var i = 0; i < resizer.leftNeighboringColumnID; i++) leftEdgeOfPreviousColumn += firstRowCells[i].offsetWidth; - var rightEdgeOfNextColumn = leftEdgeOfPreviousColumn + firstRowCells[resizer.rightNeighboringColumnID - 1].offsetWidth + firstRowCells[resizer.rightNeighboringColumnID].offsetWidth; - - // Give each column some padding so that they don't disappear. + var rightEdgeOfNextColumn = leftEdgeOfPreviousColumn + firstRowCells[resizer.leftNeighboringColumnID].offsetWidth + firstRowCells[resizer.rightNeighboringColumnID].offsetWidth; + + // Give each column some padding so that they don't disappear. var leftMinimum = leftEdgeOfPreviousColumn + this.ColumnResizePadding; var rightMaximum = rightEdgeOfNextColumn - this.ColumnResizePadding; - + dragPoint = Number.constrain(dragPoint, leftMinimum, rightMaximum); - + resizer.style.left = (dragPoint - this.CenterResizerOverBorderAdjustment) + "px"; - + var percentLeftColumn = (((dragPoint - leftEdgeOfPreviousColumn) / this._dataTable.offsetWidth) * 100) + "%"; - this._headerTableColumnGroup.children[resizer.rightNeighboringColumnID - 1].style.width = percentLeftColumn; - this._dataTableColumnGroup.children[resizer.rightNeighboringColumnID - 1].style.width = percentLeftColumn; - + this._headerTableColumnGroup.children[resizer.leftNeighboringColumnID].style.width = percentLeftColumn; + this._dataTableColumnGroup.children[resizer.leftNeighboringColumnID].style.width = percentLeftColumn; + var percentRightColumn = (((rightEdgeOfNextColumn - dragPoint) / this._dataTable.offsetWidth) * 100) + "%"; this._headerTableColumnGroup.children[resizer.rightNeighboringColumnID].style.width = percentRightColumn; this._dataTableColumnGroup.children[resizer.rightNeighboringColumnID].style.width = percentRightColumn; - + + this._positionResizers(); event.preventDefault(); this.dispatchEventToListeners("width changed"); }, diff --git a/WebCore/inspector/front-end/ElementsPanel.js b/WebCore/inspector/front-end/ElementsPanel.js index d6437fc..76c22c5 100644 --- a/WebCore/inspector/front-end/ElementsPanel.js +++ b/WebCore/inspector/front-end/ElementsPanel.js @@ -107,7 +107,6 @@ WebInspector.ElementsPanel = function() this.element.appendChild(this.sidebarResizeElement); this._registerShortcuts(); - this._changedStyles = {}; this.reset(); } @@ -346,115 +345,6 @@ WebInspector.ElementsPanel.prototype = { // TODO: Implement Shifting the oldSelector, and its contents to a newSelector }, - addStyleChange: function(identifier, style, property) - { - if (!style.parentRule) - return; - - var selector = style.parentRule.selectorText; - if (!this._changedStyles[identifier]) - this._changedStyles[identifier] = {}; - - if (!this._changedStyles[identifier][selector]) - this._changedStyles[identifier][selector] = {}; - - if (!this._changedStyles[identifier][selector][property]) - WebInspector.styleChanges += 1; - - this._changedStyles[identifier][selector][property] = style.getPropertyValue(property); - }, - - removeStyleChange: function(identifier, style, property) - { - if (!style.parentRule) - return; - - var selector = style.parentRule.selectorText; - if (!this._changedStyles[identifier] || !this._changedStyles[identifier][selector]) - return; - - if (this._changedStyles[identifier][selector][property]) { - delete this._changedStyles[identifier][selector][property]; - WebInspector.styleChanges -= 1; - } - }, - - generateStylesheet: function() - { - if (!WebInspector.styleChanges) - return; - - // Merge Down to Just Selectors - var mergedSelectors = {}; - for (var identifier in this._changedStyles) { - for (var selector in this._changedStyles[identifier]) { - if (!mergedSelectors[selector]) - mergedSelectors[selector] = this._changedStyles[identifier][selector]; - else { // merge on selector - var merge = {}; - for (var property in mergedSelectors[selector]) - merge[property] = mergedSelectors[selector][property]; - for (var property in this._changedStyles[identifier][selector]) { - if (!merge[property]) - merge[property] = this._changedStyles[identifier][selector][property]; - else { // merge on property within a selector, include comment to notify user - var value1 = merge[property]; - var value2 = this._changedStyles[identifier][selector][property]; - - if (value1 === value2) - merge[property] = [value1]; - else if (value1 instanceof Array) - merge[property].push(value2); - else - merge[property] = [value1, value2]; - } - } - mergedSelectors[selector] = merge; - } - } - } - - var builder = []; - builder.push("/**"); - builder.push(" * Inspector Generated Stylesheet"); // UIString? - builder.push(" */\n"); - - var indent = " "; - function displayProperty(property, value, comment) { - if (comment) - return indent + "/* " + property + ": " + value + "; */"; - else - return indent + property + ": " + value + ";"; - } - - for (var selector in mergedSelectors) { - var psuedoStyle = mergedSelectors[selector]; - var properties = Object.properties(psuedoStyle); - if (properties.length) { - builder.push(selector + " {"); - for (var i = 0; i < properties.length; ++i) { - var property = properties[i]; - var value = psuedoStyle[property]; - if (!(value instanceof Array)) - builder.push(displayProperty(property, value)); - else { - if (value.length === 1) - builder.push(displayProperty(property, value) + " /* merged from equivalent edits */"); // UIString? - else { - builder.push(indent + "/* There was a Conflict... There were Multiple Edits for '" + property + "' */"); // UIString? - for (var j = 0; j < value.length; ++j) - builder.push(displayProperty(property, value, true)); - } - } - } - builder.push("}\n"); - } - } - - WebInspector.showConsole(); - WebInspector.console.addMessage(WebInspector.ConsoleMessage.createTextMessage(builder.join("\n"))); - }, - get rootDOMNode() { return this.treeOutline.rootDOMNode; diff --git a/WebCore/inspector/front-end/ElementsTreeOutline.js b/WebCore/inspector/front-end/ElementsTreeOutline.js index 1479c9a..1d546c2 100644 --- a/WebCore/inspector/front-end/ElementsTreeOutline.js +++ b/WebCore/inspector/front-end/ElementsTreeOutline.js @@ -759,15 +759,15 @@ WebInspector.ElementsTreeElement.prototype = { contextMenu.appendSeparator(); var node = this.representedObject; - for (var key in WebInspector.DOMBreakpoint.Types) { - var type = WebInspector.DOMBreakpoint.Types[key]; - var label = WebInspector.DOMBreakpoint.contextMenuLabelForType(type); - var hasBreakpoint = node.hasBreakpoint(type); - if (!hasBreakpoint) - var handler = node.setBreakpoint.bind(node, type); + for (var key in WebInspector.DOMBreakpointTypes) { + var type = WebInspector.DOMBreakpointTypes[key]; + var label = WebInspector.domBreakpointTypeContextMenuLabel(type); + var breakpoint = node.breakpoints[type]; + if (!breakpoint) + var handler = WebInspector.breakpointManager.createDOMBreakpoint.bind(WebInspector.breakpointManager, node.id, type); else - var handler = node.removeBreakpoint.bind(node, type); - contextMenu.appendCheckboxItem(label, handler, hasBreakpoint); + var handler = breakpoint.remove.bind(breakpoint); + contextMenu.appendCheckboxItem(label, handler, !!breakpoint); } } }, diff --git a/WebCore/inspector/front-end/EventListenersSidebarPane.js b/WebCore/inspector/front-end/EventListenersSidebarPane.js index 34dea00..e2ad259 100644 --- a/WebCore/inspector/front-end/EventListenersSidebarPane.js +++ b/WebCore/inspector/front-end/EventListenersSidebarPane.js @@ -183,7 +183,7 @@ WebInspector.EventListenerBar = function(eventListener, nodeId) this._setFunctionSubtitle(); this.editable = false; this.element.className = "event-bar"; /* Changed from "section" */ - this.propertiesElement.className = "event-properties source-code"; /* Changed from "properties" */ + this.propertiesElement.className = "event-properties properties-tree source-code"; /* Changed from "properties" */ } WebInspector.EventListenerBar.prototype = { diff --git a/WebCore/inspector/front-end/ExtensionAPI.js b/WebCore/inspector/front-end/ExtensionAPI.js index 64f5482..5d090b0 100644 --- a/WebCore/inspector/front-end/ExtensionAPI.js +++ b/WebCore/inspector/front-end/ExtensionAPI.js @@ -123,6 +123,16 @@ Resources.prototype = { get: function(id, callback) { return extensionServer.sendRequest({ command: "getResources", id: id }, callback); + }, + + getPageTimings: function(callback) + { + return extensionServer.sendRequest({ command: "getPageTimings" }, callback); + }, + + getContent: function(ids, callback) + { + return extensionServer.sendRequest({ command: "getResourceContent", ids: ids }, callback); } } diff --git a/WebCore/inspector/front-end/ExtensionServer.js b/WebCore/inspector/front-end/ExtensionServer.js index bdf3a25..9ab4c0c 100644 --- a/WebCore/inspector/front-end/ExtensionServer.js +++ b/WebCore/inspector/front-end/ExtensionServer.js @@ -38,6 +38,8 @@ WebInspector.ExtensionServer = function() this._registerHandler("subscribe", this._onSubscribe.bind(this)); this._registerHandler("unsubscribe", this._onUnsubscribe.bind(this)); this._registerHandler("getResources", this._onGetResources.bind(this)); + this._registerHandler("getResourceContent", this._onGetResourceContent.bind(this)); + this._registerHandler("getPageTimings", this._onGetPageTimings.bind(this)); this._registerHandler("createPanel", this._onCreatePanel.bind(this)); this._registerHandler("createSidebarPane", this._onCreateSidebar.bind(this)); this._registerHandler("log", this._onLog.bind(this)); @@ -238,7 +240,7 @@ WebInspector.ExtensionServer.prototype = { var id = message.id; var resource = null; - resource = typeof id === "number" ? WebInspector.resources[id] : WebInspector.resourceForURL(id); + resource = WebInspector.resources[id] || WebInspector.resourceForURL(id); if (!resource) return this._status.E_NOTFOUND(typeof id + ": " + id); WebInspector.panels.resources.showResource(resource, message.line); @@ -261,10 +263,53 @@ WebInspector.ExtensionServer.prototype = { if (request.id) response = WebInspector.resources[request.id] ? resourceWrapper(request.id) : this._status.E_NOTFOUND(request.id); else - response = Object.properties(WebInspector.resources).map(resourceWrapper); + response = Object.keys(WebInspector.resources).map(resourceWrapper); return response; }, + _onGetResourceContent: function(message, port) + { + var ids; + var response = []; + + function onContentAvailable(id, encoded, content) + { + var resourceContent = { + id: id, + encoding: encoded ? "base64" : "", + content: content + }; + response.push(resourceContent); + if (response.length === ids.length) + this._dispatchCallback(message.requestId, port, response); + } + + if (typeof message.ids === "number") + ids = [ message.ids ]; + else if (message.ids instanceof Array) + ids = message.ids; + else + return this._status.E_BADARGTYPE("message.ids", "Array", typeof message.ids); + + for (var i = 0; i < ids.length; ++i) { + var id = ids[i]; + var resource = WebInspector.resources[id]; + if (!resource) + response.push(this._status.E_NOTFOUND(id)); + else { + var encode = !WebInspector.Resource.Type.isTextType(resource.type); + WebInspector.getEncodedResourceContent(id, encode, onContentAvailable.bind(this, id, encode)); + } + } + if (response.length === ids.length) + this._dispatchCallback(message.requestId, port, response); + }, + + _onGetPageTimings: function() + { + return (new WebInspector.HARLog()).buildMainResourceTimings(); + }, + _onAddAuditCategory: function(request) { var category = new WebInspector.ExtensionAuditCategory(request.id, request.displayName, request.ruleCount); @@ -329,12 +374,13 @@ WebInspector.ExtensionServer.prototype = { if (typeof propValue === "number") resourceTypes[propName] = WebInspector.Resource.Type.toString(propValue); } - + var platformAPI = WebInspector.buildPlatformExtensionAPI ? WebInspector.buildPlatformExtensionAPI() : ""; return "(function(){ " + "var private = {};" + "(" + WebInspector.commonExtensionSymbols.toString() + ")(private);" + "(" + WebInspector.injectedExtensionAPI.toString() + ").apply(this, arguments);" + "webInspector.resources.Types = " + JSON.stringify(resourceTypes) + ";" + + platformAPI + "})"; }, @@ -400,3 +446,8 @@ WebInspector.addExtensions = function(extensions) } WebInspector.extensionServer = new WebInspector.ExtensionServer(); + +WebInspector.getEncodedResourceContent = function(identifier, encode, callback) +{ + InspectorBackend.getResourceContent(identifier, encode, callback); +} diff --git a/WebCore/inspector/front-end/HAREntry.js b/WebCore/inspector/front-end/HAREntry.js index 85e4f59..2b8f41b 100644 --- a/WebCore/inspector/front-end/HAREntry.js +++ b/WebCore/inspector/front-end/HAREntry.js @@ -56,7 +56,6 @@ WebInspector.HAREntry.prototype = { method: this._resource.requestMethod, url: this._resource.url, // httpVersion: "HTTP/1.1" -- Not available. - // cookies: [] -- Not available. headers: this._buildHeaders(this._resource.requestHeaders), headersSize: -1, // Not available. bodySize: -1 // Not available. @@ -65,22 +64,26 @@ WebInspector.HAREntry.prototype = { res.queryString = this._buildParameters(this._resource.queryParameters); if (this._resource.requestFormData) res.postData = this._buildPostData(); + if (this._resource.requestCookies) + res.cookies = this._buildCookies(this._resource.requestCookies); return res; }, _buildResponse: function() { - return { + var res = { status: this._resource.statusCode, statusText: this._resource.statusText, // "httpVersion": "HTTP/1.1" -- Not available. - // "cookies": [], -- Not available. headers: this._buildHeaders(this._resource.responseHeaders), content: this._buildContent(), redirectURL: this._resource.responseHeaderValue("Location") || "", headersSize: -1, // Not available. bodySize: this._resource.resourceSize }; + if (this._resource.responseCookies) + res.cookies = this._buildCookies(this._resource.responseCookies); + return res; }, _buildContent: function() @@ -150,6 +153,25 @@ WebInspector.HAREntry.prototype = { return parameters.slice(); }, + _buildCookies: function(cookies) + { + return cookies.map(this._buildCookie.bind(this)); + }, + + _buildCookie: function(cookie) + { + + return { + name: cookie.name, + value: cookie.value, + path: cookie.path, + domain: cookie.domain, + expires: cookie.expires(new Date(this._resource.startTime * 1000)), + httpOnly: cookie.httpOnly, + secure: cookie.secure + }; + }, + _interval: function(start, end) { var timing = this._resource.timing; @@ -181,7 +203,7 @@ WebInspector.HARLog.prototype = { version: webKitVersion ? webKitVersion[1] : "n/a" }, pages: this._buildPages(), - entries: Object.properties(WebInspector.resources).map(this._convertResource) + entries: Object.keys(WebInspector.resources).map(this._convertResource) } }, @@ -192,23 +214,31 @@ WebInspector.HARLog.prototype = { startedDateTime: new Date(WebInspector.mainResource.startTime * 1000), id: WebInspector.mainResource.documentURL, title: "", - pageTimings: this._buildMainResourceTimings() + pageTimings: this.buildMainResourceTimings() } ]; }, - _buildMainResourceTimings: function() + buildMainResourceTimings: function() { var resourcesPanel = WebInspector.panels.resources; var startTime = WebInspector.mainResource.startTime; return { - onContentLoad: WebInspector.HAREntry._toMilliseconds(resourcesPanel.mainResourceDOMContentTime - startTime), - onLoad: WebInspector.HAREntry._toMilliseconds(resourcesPanel.mainResourceLoadTime - startTime), + onContentLoad: this._pageEventTime(resourcesPanel.mainResourceDOMContentTime), + onLoad: this._pageEventTime(resourcesPanel.mainResourceLoadTime), } }, _convertResource: function(id) { return (new WebInspector.HAREntry(WebInspector.resources[id])).build(); + }, + + _pageEventTime: function(time) + { + var startTime = WebInspector.mainResource.startTime; + if (time === -1 || startTime === -1) + return -1; + return WebInspector.HAREntry._toMilliseconds(time - startTime); } }; diff --git a/WebCore/inspector/front-end/InjectedScript.js b/WebCore/inspector/front-end/InjectedScript.js index 5544ed5..24b270b 100644 --- a/WebCore/inspector/front-end/InjectedScript.js +++ b/WebCore/inspector/front-end/InjectedScript.js @@ -220,7 +220,7 @@ InjectedScript.prototype = { if (!callFrame) return props; if (expression) - expressionResult = this._evaluateOn(callFrame.evaluate, callFrame, expression); + expressionResult = this._evaluateOn(callFrame.evaluate, callFrame, expression, true); else { // Evaluate into properties in scope of the selected call frame. var scopeChain = callFrame.scopeChain; @@ -230,7 +230,7 @@ InjectedScript.prototype = { } else { if (!expression) expression = "this"; - expressionResult = this._evaluateOn(inspectedWindow.eval, inspectedWindow, expression); + expressionResult = this._evaluateOn(inspectedWindow.eval, inspectedWindow, expression, false); } if (typeof expressionResult === "object") this._populatePropertyNames(expressionResult, props); @@ -246,26 +246,29 @@ InjectedScript.prototype = { evaluate: function(expression, objectGroup) { - return this._evaluateAndWrap(inspectedWindow.eval, inspectedWindow, expression, objectGroup); + return this._evaluateAndWrap(inspectedWindow.eval, inspectedWindow, expression, objectGroup, false); }, - _evaluateAndWrap: function(evalFunction, object, expression, objectGroup) + _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame) { try { - return this._wrapObject(this._evaluateOn(evalFunction, object, expression), objectGroup); + return this._wrapObject(this._evaluateOn(evalFunction, object, expression, isEvalOnCallFrame), objectGroup); } catch (e) { return InjectedScript.RemoteObject.fromException(e); } }, - _evaluateOn: function(evalFunction, object, expression) + _evaluateOn: function(evalFunction, object, expression, isEvalOnCallFrame) { // Only install command line api object for the time of evaluation. // Surround the expression in with statements to inject our command line API so that // the window object properties still take more precedent than our API functions. inspectedWindow.console._commandLineAPI = this._commandLineAPI; - expression = "with (window.console._commandLineAPI) { with (window) {\n" + expression + "\n} }"; + // We don't want local variables to be shadowed by global ones when evaluating on CallFrame. + if (!isEvalOnCallFrame) + expression = "with (window) {\n" + expression + "\n} "; + expression = "with (window.console._commandLineAPI) {\n" + expression + "\n}"; var value = evalFunction.call(object, expression); delete inspectedWindow.console._commandLineAPI; @@ -303,7 +306,7 @@ InjectedScript.prototype = { var callFrame = this._callFrameForId(callFrameId); if (!callFrame) return false; - return this._evaluateAndWrap(callFrame.evaluate, callFrame, code, objectGroup); + return this._evaluateAndWrap(callFrame.evaluate, callFrame, code, objectGroup, true); }, _callFrameForId: function(id) diff --git a/WebCore/inspector/front-end/NetworkPanel.js b/WebCore/inspector/front-end/NetworkPanel.js index f07b3b0..8eed425 100644 --- a/WebCore/inspector/front-end/NetworkPanel.js +++ b/WebCore/inspector/front-end/NetworkPanel.js @@ -32,25 +32,41 @@ WebInspector.NetworkPanel = function() { WebInspector.Panel.call(this, "network"); + this.createSidebar(); + this.sidebarElement.className = "network-sidebar"; + this._resources = []; this._staleResources = []; this._resourceGridNodes = {}; this._mainResourceLoadTime = -1; this._mainResourceDOMContentTime = -1; + this._hiddenCategories = {}; + + this._categories = WebInspector.resourceCategories; + + this.containerElement = document.createElement("div"); + this.containerElement.id = "network-container"; + this.sidebarElement.appendChild(this.containerElement); this._viewsContainerElement = document.createElement("div"); this._viewsContainerElement.id = "network-views"; + this._viewsContainerElement.className = "hidden"; + this.element.appendChild(this._viewsContainerElement); this._createSortingFunctions(); - this._createTimelineGrid(); this._createTable(); + this._createTimelineGrid(); this._createStatusbarButtons(); + this._createFilterStatusBarItems(); + this._createSummaryBar(); this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), true); this.calculator = new WebInspector.NetworkTransferTimeCalculator(); - this.filter(this.filterAllElement, false); + this._filter(this._filterAllElement, false); + + this._toggleGridMode(); } WebInspector.NetworkPanel.prototype = { @@ -61,7 +77,7 @@ WebInspector.NetworkPanel.prototype = { get statusBarItems() { - return [this._largerResourcesButton.element, this._clearButton.element]; + return [this._largerResourcesButton.element, this._clearButton.element, this._filterBarElement]; }, isCategoryVisible: function(categoryName) @@ -78,32 +94,87 @@ WebInspector.NetworkPanel.prototype = { { WebInspector.Panel.prototype.resize.call(this); this._dataGrid.updateWidths(); + this._positionSummaryBar(); + }, + + updateSidebarWidth: function() + { + if (!this._viewingResourceMode) + return; + WebInspector.Panel.prototype.updateSidebarWidth.apply(this, arguments); + }, + + updateMainViewWidth: function(width) + { + this._viewsContainerElement.style.left = width + "px"; + }, + + handleShortcut: function(event) + { + if (this._viewingResourceMode && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) { + this._toggleGridMode(); + event.handled = true; + } + }, + + _positionSummaryBar: function() + { + // Position the total bar. + const rowHeight = 22; + const summaryBarHeight = 22; + var offsetHeight = this.element.offsetHeight; + + var parentElement = this._summaryBarElement.parentElement; + + if (this._summaryBarElement.parentElement !== this.element && offsetHeight > (this._dataGrid.children.length - 1) * rowHeight + summaryBarHeight) { + // Glue status to bottom. + if (this._summaryBarRowNode) { + this._dataGrid.removeChild(this._summaryBarRowNode); + delete this._summaryBarRowNode; + } + this._summaryBarElement.addStyleClass("network-summary-bar-bottom"); + this.element.appendChild(this._summaryBarElement); + this._dataGrid.element.style.bottom = "20px"; + return; + } + + if (!this._summaryBarRowNode && offsetHeight - summaryBarHeight < this._dataGrid.children.length * rowHeight) { + // Glue status to table. + this._summaryBarRowNode = new WebInspector.NetworkTotalGridNode(this._summaryBarElement); + this._summaryBarElement.removeStyleClass("network-summary-bar-bottom"); + this._dataGrid.appendChild(this._summaryBarRowNode); + this._dataGrid.element.style.bottom = 0; + this._sortItems(); + } + }, + + _resetSummaryBar: function() + { + delete this._summaryBarRowNode; + this._summaryBarElement.parentElement.removeChild(this._summaryBarElement); + this._updateSummaryBar(); }, _createTimelineGrid: function() { this._timelineGrid = new WebInspector.TimelineGrid(); this._timelineGrid.element.addStyleClass("network-timeline-grid"); - this.element.appendChild(this._timelineGrid.element); + this._dataGrid.element.appendChild(this._timelineGrid.element); }, _createTable: function() { - this.containerElement = document.createElement("div"); - this.containerElement.id = "network-container"; - this.element.appendChild(this.containerElement); + var columns = {name: {}, method: {}, status: {}, type: {}, size: {}, time: {}, timeline: {}}; + columns.name.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Name"), WebInspector.UIString("Path")); + columns.name.sortable = true; + columns.name.width = "20%"; + columns.name.disclosure = true; - var columns = {url: {}, method: {}, status: {}, type: {}, size: {}, time: {}, timeline: {}}; - columns.url.title = WebInspector.UIString("URL"); - columns.url.sortable = true; - columns.url.width = "20%"; - columns.url.disclosure = true; - columns.method.title = WebInspector.UIString("Method"); columns.method.sortable = true; columns.method.width = "7%"; - columns.status.title = WebInspector.UIString("Status"); + columns.status.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Status"), WebInspector.UIString("Text")); columns.status.sortable = true; columns.status.width = "8%"; @@ -111,41 +182,233 @@ WebInspector.NetworkPanel.prototype = { columns.type.sortable = true; columns.type.width = "7%"; - columns.size.title = WebInspector.UIString("Size"); + columns.size.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Size"), WebInspector.UIString("Transfer")); columns.size.sortable = true; columns.size.width = "10%"; columns.size.aligned = "right"; - columns.time.title = WebInspector.UIString("Time"); + columns.time.titleDOMFragment = this._makeHeaderFragment(WebInspector.UIString("Time"), WebInspector.UIString("Duration")); columns.time.sortable = true; columns.time.width = "10%"; columns.time.aligned = "right"; columns.timeline.title = ""; - columns.timeline.sortable = true; + columns.timeline.sortable = false; columns.timeline.width = "40%"; - columns.timeline.sort = true; + columns.timeline.sort = "ascending"; this._dataGrid = new WebInspector.DataGrid(columns); - this.element.appendChild(this._dataGrid.element); + this.containerElement.appendChild(this._dataGrid.element); this._dataGrid.addEventListener("sorting changed", this._sortItems, this); this._dataGrid.addEventListener("width changed", this._updateDividersIfNeeded, this); + + this._patchTimelineHeader(); + }, + + _makeHeaderFragment: function(title, subtitle) + { + var fragment = document.createDocumentFragment(); + fragment.appendChild(document.createTextNode(title)); + var subtitleDiv = document.createElement("div"); + subtitleDiv.className = "network-header-subtitle"; + subtitleDiv.textContent = subtitle; + fragment.appendChild(subtitleDiv); + return fragment; + }, + + _patchTimelineHeader: function() + { + var timelineSorting = document.createElement("select"); + + var option = document.createElement("option"); + option.value = "startTime"; + option.label = WebInspector.UIString("Timeline"); + timelineSorting.appendChild(option); + + option = document.createElement("option"); + option.value = "startTime"; + option.label = WebInspector.UIString("Start Time"); + timelineSorting.appendChild(option); + + option = document.createElement("option"); + option.value = "responseTime"; + option.label = WebInspector.UIString("Response Time"); + timelineSorting.appendChild(option); + + option = document.createElement("option"); + option.value = "endTime"; + option.label = WebInspector.UIString("End Time"); + timelineSorting.appendChild(option); + + option = document.createElement("option"); + option.value = "duration"; + option.label = WebInspector.UIString("Duration"); + timelineSorting.appendChild(option); + + option = document.createElement("option"); + option.value = "latency"; + option.label = WebInspector.UIString("Latency"); + timelineSorting.appendChild(option); + + var header = this._dataGrid.headerTableHeader("timeline"); + header.firstChild.appendChild(timelineSorting); + + timelineSorting.addEventListener("click", function(event) { event.stopPropagation() }, false); + timelineSorting.addEventListener("change", this._sortByTimeline.bind(this), false); + this._timelineSortSelector = timelineSorting; }, _createSortingFunctions: function() { this._sortingFunctions = {}; - this._sortingFunctions.url = WebInspector.NetworkDataGridNode.URLComparator; - this._sortingFunctions.method = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "method"); - this._sortingFunctions.status = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "statusCode"); - this._sortingFunctions.type = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "mimeType"); - this._sortingFunctions.size = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "resourceSize"); - this._sortingFunctions.time = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration"); - this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime"); + this._sortingFunctions.name = WebInspector.NetworkDataGridNode.NameComparator; + this._sortingFunctions.method = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "method", false); + this._sortingFunctions.status = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "statusCode", false); + this._sortingFunctions.type = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "mimeType", false); + this._sortingFunctions.size = WebInspector.NetworkDataGridNode.SizeComparator; + this._sortingFunctions.time = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", false); + this._sortingFunctions.timeline = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false); + this._sortingFunctions.startTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "startTime", false); + this._sortingFunctions.endTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "endTime", false); + this._sortingFunctions.responseTime = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "responseReceivedTime", false); + this._sortingFunctions.duration = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "duration", true); + this._sortingFunctions.latency = WebInspector.NetworkDataGridNode.ResourcePropertyComparator.bind(null, "latency", true); + + var timeCalculator = new WebInspector.NetworkTransferTimeCalculator(); + var durationCalculator = new WebInspector.NetworkTransferDurationCalculator(); + + this._calculators = {}; + this._calculators.timeline = timeCalculator; + this._calculators.startTime = timeCalculator; + this._calculators.endTime = timeCalculator; + this._calculators.responseTime = timeCalculator; + this._calculators.duration = durationCalculator; + this._calculators.latency = durationCalculator; }, - filter: function(target, selectMultiple) + _sortItems: function() { + var columnIdentifier = this._dataGrid.sortColumnIdentifier; + if (columnIdentifier === "timeline") { + this._sortByTimeline(); + return; + } + var sortingFunction = this._sortingFunctions[columnIdentifier]; + if (!sortingFunction) + return; + + this._dataGrid.sortNodes(sortingFunction, this._dataGrid.sortOrder === "descending"); + this._timelineSortSelector.selectedIndex = 0; + }, + + _sortByTimeline: function() + { + var selectedIndex = this._timelineSortSelector.selectedIndex; + if (!selectedIndex) + selectedIndex = 1; // Sort by start time by default. + var selectedOption = this._timelineSortSelector[selectedIndex]; + var value = selectedOption.value; + + var sortingFunction = this._sortingFunctions[value]; + this._dataGrid.sortNodes(sortingFunction); + this.calculator = this._calculators[value]; + if (this.calculator.startAtZero) + this._timelineGrid.hideEventDividers(); + else + this._timelineGrid.showEventDividers(); + this._dataGrid.markColumnAsSortedBy("timeline", "ascending"); + }, + + _createFilterStatusBarItems: function() + { + var filterBarElement = document.createElement("div"); + filterBarElement.className = "scope-bar status-bar-item"; + filterBarElement.id = "network-filter"; + + function createFilterElement(category, label) + { + var categoryElement = document.createElement("li"); + categoryElement.category = category; + categoryElement.className = category; + categoryElement.appendChild(document.createTextNode(label)); + categoryElement.addEventListener("click", this._updateFilter.bind(this), false); + filterBarElement.appendChild(categoryElement); + + return categoryElement; + } + + this._filterAllElement = createFilterElement.call(this, "all", WebInspector.UIString("All")); + + // Add a divider + var dividerElement = document.createElement("div"); + dividerElement.addStyleClass("scope-bar-divider"); + filterBarElement.appendChild(dividerElement); + + for (var category in this._categories) + createFilterElement.call(this, category, this._categories[category].title); + this._filterBarElement = filterBarElement; + }, + + _createSummaryBar: function() + { + this._summaryBarElement = document.createElement("div"); + this._summaryBarElement.className = "network-summary-bar"; + this.containerElement.appendChild(this._summaryBarElement); + }, + + _updateSummaryBar: function() + { + this._positionSummaryBar(); // Grid is growing. + var numRequests = this._resources.length; + + if (!numRequests) { + if (this._summaryBarElement._isDisplayingWarning) + return; + this._summaryBarElement._isDisplayingWarning = true; + + var img = document.createElement("img"); + img.src = "Images/warningIcon.png"; + this._summaryBarElement.removeChildren(); + this._summaryBarElement.appendChild(img); + this._summaryBarElement.appendChild(document.createTextNode(" ")); + this._summaryBarElement.appendChild(document.createTextNode( + WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity."))); + return; + } + delete this._summaryBarElement._isDisplayingWarning; + + var transferSize = 0; + var baseTime = -1; + var maxTime = -1; + for (var i = 0; i < this._resources.length; ++i) { + var resource = this._resources[i]; + transferSize += resource.cached ? 0 : resource.transferSize; + if (resource.isMainResource) + baseTime = resource.startTime; + if (resource.endTime > maxTime) + maxTime = resource.endTime; + } + var text = String.sprintf(WebInspector.UIString("%d requests"), numRequests); + text += " \u2758 " + String.sprintf(WebInspector.UIString("%s transferred"), Number.bytesToString(transferSize)); + if (baseTime !== -1 && this._mainResourceLoadTime !== -1 && this._mainResourceDOMContentTime !== -1 && this._mainResourceDOMContentTime > baseTime) { + text += " \u2758 " + String.sprintf(WebInspector.UIString("%s (onload: %s, DOMContentLoaded: %s)"), + Number.secondsToString(maxTime - baseTime), + Number.secondsToString(this._mainResourceLoadTime - baseTime), + Number.secondsToString(this._mainResourceDOMContentTime - baseTime)); + } + this._summaryBarElement.textContent = text; + }, + + _showCategory: function(category) + { + this._dataGrid.element.addStyleClass("filter-" + category); + delete this._hiddenCategories[category]; + }, + + _hideCategory: function(category) + { + this._dataGrid.element.removeStyleClass("filter-" + category); + this._hiddenCategories[category] = true; }, _updateFilter: function(e) @@ -157,16 +420,65 @@ WebInspector.NetworkPanel.prototype = { if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey) selectMultiple = true; - this.filter(e.target, selectMultiple); - - // When we are updating our filtering, scroll to the top so we don't end up - // in blank graph under all the resources. - this.containerElement.scrollTop = 0; + this._filter(e.target, selectMultiple); var searchField = document.getElementById("search"); WebInspector.doPerformSearch(searchField.value, WebInspector.shortSearchWasForcedByKeyEvent, false, true); }, + _filter: function(target, selectMultiple) + { + function unselectAll() + { + for (var i = 0; i < this._filterBarElement.childNodes.length; ++i) { + var child = this._filterBarElement.childNodes[i]; + if (!child.category) + continue; + + child.removeStyleClass("selected"); + this._hideCategory(child.category); + } + } + + if (target.category === this._filterAllElement) { + if (target.hasStyleClass("selected")) { + // We can't unselect All, so we break early here + return; + } + + // If All wasn't selected, and now is, unselect everything else. + unselectAll.call(this); + } else { + // Something other than All is being selected, so we want to unselect All. + if (this._filterAllElement.hasStyleClass("selected")) { + this._filterAllElement.removeStyleClass("selected"); + this._hideCategory("all"); + } + } + + if (!selectMultiple) { + // If multiple selection is off, we want to unselect everything else + // and just select ourselves. + unselectAll.call(this); + + target.addStyleClass("selected"); + this._showCategory(target.category); + return; + } + + if (target.hasStyleClass("selected")) { + // If selectMultiple is turned on, and we were selected, we just + // want to unselect ourselves. + target.removeStyleClass("selected"); + this._hideCategory(target.category); + } else { + // If selectMultiple is turned on, and we weren't selected, we just + // want to select ourselves. + target.addStyleClass("selected"); + this._showCategory(target.category); + } + }, + _scheduleRefresh: function() { if (this._needsRefresh) @@ -178,18 +490,16 @@ WebInspector.NetworkPanel.prototype = { this._refreshTimeout = setTimeout(this.refresh.bind(this), 500); }, - _sortItems: function() - { - var columnIdentifier = this._dataGrid.sortColumnIdentifier; - var sortingFunction = this._sortingFunctions[columnIdentifier]; - if (!sortingFunction) - return; - this._dataGrid.sortNodes(sortingFunction, this._dataGrid.sortOrder === "descending"); - }, - _updateDividersIfNeeded: function(force) { - this._timelineGrid.element.style.left = this._dataGrid.resizers[this._dataGrid.resizers.length - 1].style.left; + var timelineColumn = this._dataGrid.columns.timeline; + for (var i = 0; i < this._dataGrid.resizers.length; ++i) { + if (timelineColumn.ordinal === this._dataGrid.resizers[i].rightNeighboringColumnID) { + // Position timline grid location. + this._timelineGrid.element.style.left = this._dataGrid.resizers[i].style.left; + this._timelineGrid.element.style.right = "18px"; + } + } var proceed = true; if (!this.visible) { @@ -296,7 +606,7 @@ WebInspector.NetworkPanel.prototype = { _createStatusbarButtons: function() { this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "clear-status-bar-item"); - this._clearButton.addEventListener("click", this.reset.bind(this), false); + this._clearButton.addEventListener("click", this._reset.bind(this), false); this._largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "network-larger-resources-status-bar-item"); WebInspector.applicationSettings.addEventListener("loaded", this._settingsLoaded, this); @@ -354,27 +664,18 @@ WebInspector.NetworkPanel.prototype = { view.visible = false; } this._dataGrid.updateWidths(); + this._positionSummaryBar(); + }, + + hide: function() + { + WebInspector.Panel.prototype.hide.call(this); + this._popoverHelper.hidePopup(); }, get searchableViews() { var views = []; - - const visibleView = this.visibleView; - if (visibleView && visibleView.performSearch) - views.push(visibleView); - - var resourcesLength = this._resources.length; - for (var i = 0; i < resourcesLength; ++i) { - var resource = this._resources[i]; - if (!this._resourceGridNode(resource) || !this._resourceGridNode(resource).selectable) - continue; - var resourceView = this.resourceViewForResource(resource); - if (!resourceView.performSearch || resourceView === visibleView) - continue; - views.push(resourceView); - } - return views; }, @@ -420,7 +721,7 @@ WebInspector.NetworkPanel.prototype = { var node = this._resourceGridNode(resource); if (!node) { // Create the timeline tree element and graph. - node = new WebInspector.NetworkDataGridNode(resource); + node = new WebInspector.NetworkDataGridNode(this, resource); this._resourceGridNodes[resource.identifier] = node; this._dataGrid.appendChild(node); } @@ -441,54 +742,35 @@ WebInspector.NetworkPanel.prototype = { this._staleResources = []; this._sortItems(); + this._updateSummaryBar(); this._dataGrid.updateWidths(); }, - reset: function() + _reset: function() { this._popoverHelper.hidePopup(); - this.closeVisibleResource(); + this._closeVisibleResource(); - delete this.currentQuery; - this.searchCanceled(); - - if (this._resources) { - var resourcesLength = this._resources.length; - for (var i = 0; i < resourcesLength; ++i) { - var resource = this._resources[i]; - - resource.warnings = 0; - resource.errors = 0; - - delete resource._resourcesView; - } - } + this._toggleGridMode(); // Begin reset timeline - this.containerElement.scrollTop = 0; - if (this._calculator) this._calculator.reset(); - if (this._resources) { - var itemsLength = this._resources.length; - for (var i = 0; i < itemsLength; ++i) { - var item = this._resources[i]; - } - } - this._resources = []; this._staleResources = []; this._resourceGridNodes = {}; this._dataGrid.removeChildren(); + delete this._summaryBarRowNode; this._updateDividersIfNeeded(true); // End reset timeline. - + this._mainResourceLoadTime = -1; this._mainResourceDOMContentTime = -1; this._viewsContainerElement.removeChildren(); + this._resetSummaryBar(); }, addResource: function(resource) @@ -501,20 +783,17 @@ WebInspector.NetworkPanel.prototype = { { this._staleResources.push(resource); this._scheduleRefresh(); - }, - recreateViewForResourceIfNeeded: function(resource) - { if (!resource || !resource._resourcesView) return; + if (this._resourceViewTypeMatchesResource(resource, resource._resourcesView)) + return; + var newView = this._createResourceView(resource); if (newView.__proto__ === resource._resourcesView.__proto__) return; - if (!this.currentQuery && this._resourceGridNode(resource)) - this._resourceGridNode(resource).updateErrorsAndWarnings(); - var oldView = resource._resourcesView; var oldViewParentNode = oldView.visible ? oldView.element.parentNode : null; @@ -533,27 +812,26 @@ WebInspector.NetworkPanel.prototype = { canShowSourceLine: function(url, line) { - return this._resourceTrackingEnabled && !!WebInspector.resourceForURL(url); + return false; }, showSourceLine: function(url, line) { - this.showResource(WebInspector.resourceForURL(url), line); }, - showResource: function(resource, line) + _showResource: function(resource, line) { if (!resource) return; this._popoverHelper.hidePopup(); - this.containerElement.addStyleClass("viewing-resource"); + this._toggleViewingResourceMode(); if (this.visibleResource && this.visibleResource._resourcesView) this.visibleResource._resourcesView.hide(); - var view = this.resourceViewForResource(resource); + var view = this._resourceViewForResource(resource); view.headersVisible = true; view.show(this._viewsContainerElement); @@ -565,23 +843,13 @@ WebInspector.NetworkPanel.prototype = { view.highlightLine(line); } - this.revealAndSelectItem(resource); - this.visibleResource = resource; - this.updateSidebarWidth(); }, - showView: function(view) - { - if (!view) - return; - this.showResource(view.resource); - }, - - closeVisibleResource: function() + _closeVisibleResource: function() { - this.containerElement.removeStyleClass("viewing-resource"); + this.element.removeStyleClass("viewing-resource"); if (this.visibleResource && this.visibleResource._resourcesView) this.visibleResource._resourcesView.hide(); @@ -593,7 +861,7 @@ WebInspector.NetworkPanel.prototype = { this.updateSidebarWidth(); }, - resourceViewForResource: function(resource) + _resourceViewForResource: function(resource) { if (!resource) return null; @@ -602,23 +870,6 @@ WebInspector.NetworkPanel.prototype = { return resource._resourcesView; }, - sourceFrameForResource: function(resource) - { - var view = this.resourceViewForResource(resource); - if (!view) - return null; - - if (!view.setupSourceFrameIfNeeded) - return null; - - // Setting up the source frame requires that we be attached. - if (!this.element.parentNode) - this.attach(); - - view.setupSourceFrameIfNeeded(); - return view.sourceFrame; - }, - _toggleLargerResources: function() { WebInspector.applicationSettings.resourcesLargeRows = !WebInspector.applicationSettings.resourcesLargeRows; @@ -631,9 +882,11 @@ WebInspector.NetworkPanel.prototype = { if (!enabled) { this._largerResourcesButton.title = WebInspector.UIString("Use large resource rows."); this._dataGrid.element.addStyleClass("small"); + this._timelineGrid.element.addStyleClass("small"); } else { this._largerResourcesButton.title = WebInspector.UIString("Use small resource rows."); this._dataGrid.element.removeStyleClass("small"); + this._timelineGrid.element.removeStyleClass("small"); } }, @@ -654,6 +907,24 @@ WebInspector.NetworkPanel.prototype = { } }, + _resourceViewTypeMatchesResource: function(resource, resourceView) + { + switch (resource.category) { + case WebInspector.resourceCategories.documents: + case WebInspector.resourceCategories.stylesheets: + case WebInspector.resourceCategories.scripts: + case WebInspector.resourceCategories.xhr: + return resourceView instanceof WebInspector.SourceView; + case WebInspector.resourceCategories.images: + return resourceView instanceof WebInspector.ImageView; + case WebInspector.resourceCategories.fonts: + return resourceView instanceof WebInspector.FontView; + default: + return resourceView instanceof WebInspector.ResourceView; + } + return false; + }, + _getPopoverAnchor: function(element) { var anchor = element.enclosingNodeOrSelfWithClass("network-graph-bar") || element.enclosingNodeOrSelfWithClass("network-graph-label"); @@ -751,20 +1022,85 @@ WebInspector.NetworkPanel.prototype = { return popover; }, - hide: function() + _toggleGridMode: function() { - WebInspector.Panel.prototype.hide.call(this); - this._popoverHelper.hidePopup(); + if (this._viewingResourceMode) { + this._viewingResourceMode = false; + this.element.removeStyleClass("viewing-resource"); + this._dataGrid.element.removeStyleClass("viewing-resource-mode"); + this._viewsContainerElement.addStyleClass("hidden"); + this.sidebarElement.style.right = 0; + this.sidebarElement.style.removeProperty("width"); + } + + if (this._briefGrid) { + this._dataGrid.element.removeStyleClass("full-grid-mode"); + this._dataGrid.element.addStyleClass("brief-grid-mode"); + + this._dataGrid.hideColumn("method"); + this._dataGrid.hideColumn("status"); + this._dataGrid.hideColumn("type"); + this._dataGrid.hideColumn("size"); + this._dataGrid.hideColumn("time"); + + var widths = {}; + widths.name = 20; + widths.timeline = 80; + } else { + this._dataGrid.element.addStyleClass("full-grid-mode"); + this._dataGrid.element.removeStyleClass("brief-grid-mode"); + + this._dataGrid.showColumn("method"); + this._dataGrid.showColumn("status"); + this._dataGrid.showColumn("type"); + this._dataGrid.showColumn("size"); + this._dataGrid.showColumn("time"); + + var widths = {}; + widths.name = 20; + widths.method = 7; + widths.status = 8; + widths.type = 7; + widths.size = 10; + widths.time = 10; + widths.timeline = 40; + } + + this._dataGrid.showColumn("timeline"); + this._dataGrid.applyColumnWidthsMap(widths); + + }, + + _toggleViewingResourceMode: function() + { + if (this._viewingResourceMode) + return; + this._viewingResourceMode = true; + this._preservedColumnWidths = this._dataGrid.columnWidthsMap(); + + this.element.addStyleClass("viewing-resource"); + this._dataGrid.element.addStyleClass("viewing-resource-mode"); + this._dataGrid.element.removeStyleClass("full-grid-mode"); + this._dataGrid.element.removeStyleClass("brief-grid-mode"); + + this._dataGrid.hideColumn("method"); + this._dataGrid.hideColumn("status"); + this._dataGrid.hideColumn("type"); + this._dataGrid.hideColumn("size"); + this._dataGrid.hideColumn("time"); + this._dataGrid.hideColumn("timeline"); + + this._viewsContainerElement.removeStyleClass("hidden"); + this.updateSidebarWidth(200); + + var widths = {}; + widths.name = 100; + this._dataGrid.applyColumnWidthsMap(widths); } } WebInspector.NetworkPanel.prototype.__proto__ = WebInspector.Panel.prototype; -WebInspector.getResourceContent = function(identifier, callback) -{ - InspectorBackend.getResourceContent(identifier, callback); -} - WebInspector.NetworkBaseCalculator = function() { } @@ -1047,86 +1383,50 @@ WebInspector.NetworkTransferDurationCalculator.prototype = { WebInspector.NetworkTransferDurationCalculator.prototype.__proto__ = WebInspector.NetworkTimeCalculator.prototype; -WebInspector.NetworkTransferSizeCalculator = function() +WebInspector.NetworkDataGridNode = function(panel, resource) { - WebInspector.NetworkBaseCalculator.call(this); + WebInspector.DataGridNode.call(this, {}); + this._panel = panel; + this._resource = resource; } -WebInspector.NetworkTransferSizeCalculator.prototype = { - computeBarGraphLabels: function(resource) - { - var networkBytes = this._networkBytes(resource); - var resourceBytes = this._value(resource); - if (networkBytes && networkBytes !== resourceBytes) { - // Transferred size is not the same as reported resource length. - var networkBytesString = this.formatValue(networkBytes); - var left = networkBytesString; - var right = this.formatValue(resourceBytes); - var tooltip = right ? WebInspector.UIString("%s (%s transferred)", right, networkBytesString) : right; - } else { - var left = this.formatValue(resourceBytes); - var right = left; - var tooltip = left; - } - if (resource.cached) - tooltip = WebInspector.UIString("%s (from cache)", tooltip); - return {left: left, right: right, tooltip: tooltip}; - }, - - computeBarGraphPercentages: function(item) +WebInspector.NetworkDataGridNode.prototype = { + createCells: function() { - const resourceBytesAsPercent = (this._value(item) / this.boundarySpan) * 100; - const networkBytesAsPercent = this._networkBytes(item) ? (this._networkBytes(item) / this.boundarySpan) * 100 : resourceBytesAsPercent; - return {start: 0, middle: networkBytesAsPercent, end: resourceBytesAsPercent}; + this._nameCell = this._createDivInTD("name"); + this._methodCell = this._createDivInTD("method"); + this._statusCell = this._createDivInTD("status"); + this._typeCell = this._createDivInTD("type"); + this._sizeCell = this._createDivInTD("size"); + this._timeCell = this._createDivInTD("time"); + this._createTimelineCell(); }, - _value: function(resource) + select: function() { - return resource.resourceSize; + WebInspector.DataGridNode.prototype.select.apply(this, arguments); + this._panel._showResource(this._resource); }, - _networkBytes: function(resource) + get selectable() { - return resource.transferSize; + if (!this._panel._hiddenCategories.all) + return true; + if (this._panel._hiddenCategories[this._resource.category.name]) + return false; + return true; }, - formatValue: function(value) - { - return Number.bytesToString(value, WebInspector.UIString); - } -} - -WebInspector.NetworkTransferSizeCalculator.prototype.__proto__ = WebInspector.NetworkBaseCalculator.prototype; - -WebInspector.NetworkDataGridNode = function(resource) -{ - WebInspector.DataGridNode.call(this, {}); - this._resource = resource; -} - -WebInspector.NetworkDataGridNode.prototype = { - createCells: function() + _createDivInTD: function(columnIdentifier) { - this._urlCell = this._createDivInTD("url-column"); - this._methodCell = this._createDivInTD("optional-column"); - this._statusCell = this._createDivInTD("optional-column"); - this._typeCell = this._createDivInTD("optional-column"); - this._sizeCell = this._createDivInTD("optional-column right"); - this._timeCell = this._createDivInTD("optional-column right"); - this._createTimelineCell(); - }, - - _createDivInTD: function(className) { var td = document.createElement("td"); - if (className) - td.className = className; + td.className = columnIdentifier + "-column"; var div = document.createElement("div"); td.appendChild(div); this._element.appendChild(td); return div; }, - _createTimelineCell: function() { this._graphElement = document.createElement("div"); @@ -1156,13 +1456,14 @@ WebInspector.NetworkDataGridNode.prototype = { this._barAreaElement.appendChild(this._labelRightElement); this._timelineCell = document.createElement("td"); + this._timelineCell.className = "timeline-column"; this._element.appendChild(this._timelineCell); this._timelineCell.appendChild(this._graphElement); }, refreshResource: function() { - this._refreshURLCell(); + this._refreshNameCell(); this._methodCell.textContent = this._resource.requestMethod; @@ -1188,9 +1489,9 @@ WebInspector.NetworkDataGridNode.prototype = { } }, - _refreshURLCell: function() + _refreshNameCell: function() { - this._urlCell.removeChildren(); + this._nameCell.removeChildren(); if (this._resource.category === WebInspector.resourceCategories.images) { var previewImage = document.createElement("img"); @@ -1204,8 +1505,8 @@ WebInspector.NetworkDataGridNode.prototype = { var iconElement = document.createElement("img"); iconElement.className = "icon"; } - this._urlCell.appendChild(iconElement); - this._urlCell.appendChild(document.createTextNode(this._fileName())); + this._nameCell.appendChild(iconElement); + this._nameCell.appendChild(document.createTextNode(this._fileName())); var subtitle = this._resource.displayDomain; @@ -1216,13 +1517,8 @@ WebInspector.NetworkDataGridNode.prototype = { subtitle += this._resource.path.substring(0, lastPathComponentIndex); } - var subtitleElement = document.createElement("div"); - subtitleElement.className = "network-grid-subtitle"; - subtitleElement.textContent = subtitle; - this._urlCell.appendChild(subtitleElement); - - - this._urlCell.title = this._resource.url; + this._appendSubtitle(this._nameCell, subtitle); + this._nameCell.title = this._resource.url; }, _fileName: function() @@ -1238,16 +1534,6 @@ WebInspector.NetworkDataGridNode.prototype = { this._statusCell.removeChildren(); if (this._resource.statusCode) { - var img = document.createElement("img"); - if (this._resource.statusCode < 300) - img.src = "Images/successGreenDot.png"; - else if (this._resource.statusCode < 400) - img.src = "Images/warningOrangeDot.png"; - else - img.src = "Images/errorRedDot.png"; - - img.className = "resource-status-image"; - this._statusCell.appendChild(img); this._statusCell.appendChild(document.createTextNode(this._resource.statusCode)); this._statusCell.removeStyleClass("network-dim-cell"); this._appendSubtitle(this._statusCell, this._resource.statusText); @@ -1287,7 +1573,7 @@ WebInspector.NetworkDataGridNode.prototype = { _appendSubtitle: function(cellElement, subtitleText) { var subtitleElement = document.createElement("div"); - subtitleElement.className = "network-grid-subtitle"; + subtitleElement.className = "network-cell-subtitle"; subtitleElement.textContent = subtitleText; cellElement.appendChild(subtitleElement); }, @@ -1324,6 +1610,8 @@ WebInspector.NetworkDataGridNode.prototype = { _refreshLabelPositions: function() { + if (!this._percentages) + return; this._labelLeftElement.style.removeProperty("left"); this._labelLeftElement.style.removeProperty("right"); this._labelLeftElement.removeStyleClass("before"); @@ -1389,7 +1677,7 @@ WebInspector.NetworkDataGridNode.prototype = { } } -WebInspector.NetworkDataGridNode.URLComparator = function(a, b) +WebInspector.NetworkDataGridNode.NameComparator = function(a, b) { var aFileName = a._resource.displayName + (a._resource.queryString ? a._resource.queryString : ""); var bFileName = b._resource.displayName + (b._resource.queryString ? b._resource.queryString : ""); @@ -1400,15 +1688,47 @@ WebInspector.NetworkDataGridNode.URLComparator = function(a, b) return 0; } -WebInspector.NetworkDataGridNode.ResourcePropertyComparator = function(propertyName, a, b) +WebInspector.NetworkDataGridNode.SizeComparator = function(a, b) +{ + if (b._resource.cached && !a._resource.cached) + return 1; + if (a._resource.cached && !b._resource.cached) + return -1; + + if (a._resource.resourceSize === b._resource.resourceSize) + return 0; + + return a._resource.resourceSize - b._resource.resourceSize; +} + +WebInspector.NetworkDataGridNode.ResourcePropertyComparator = function(propertyName, revert, a, b) { var aValue = a._resource[propertyName]; var bValue = b._resource[propertyName]; if (aValue > bValue) - return 1; + return revert ? -1 : 1; if (bValue > aValue) - return -1; + return revert ? 1 : -1; return 0; } WebInspector.NetworkDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype; + +WebInspector.NetworkTotalGridNode = function(element) +{ + this._summaryBarElement = element; + WebInspector.DataGridNode.call(this, {summaryRow: true}); +} + +WebInspector.NetworkTotalGridNode.prototype = { + createCells: function() + { + var td = document.createElement("td"); + td.setAttribute("colspan", 7); + td.className = "network-summary"; + td.appendChild(this._summaryBarElement); + this._element.appendChild(td); + } +} + +WebInspector.NetworkTotalGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype; diff --git a/WebCore/inspector/front-end/ProfilesPanel.js b/WebCore/inspector/front-end/ProfilesPanel.js index 2bd76f9..ca02f9e 100644 --- a/WebCore/inspector/front-end/ProfilesPanel.js +++ b/WebCore/inspector/front-end/ProfilesPanel.js @@ -116,7 +116,7 @@ WebInspector.ProfilesPanel = function() this.clearResultsButton.addEventListener("click", this._clearProfiles.bind(this), false); this.profileViewStatusBarItemsContainer = document.createElement("div"); - this.profileViewStatusBarItemsContainer.id = "profile-view-status-bar-items"; + this.profileViewStatusBarItemsContainer.className = "status-bar-items"; this.welcomeView = new WebInspector.WelcomeView("profiles", WebInspector.UIString("Welcome to the Profiles panel")); this.element.appendChild(this.welcomeView.element); @@ -550,7 +550,7 @@ WebInspector.ProfilesPanel.prototype = { { this.welcomeView.element.style.left = width + "px"; this.profileViews.style.left = width + "px"; - this.profileViewStatusBarItemsContainer.style.left = width + "px"; + this.profileViewStatusBarItemsContainer.style.left = Math.max(155, width) + "px"; this.resize(); } } diff --git a/WebCore/inspector/front-end/PropertiesSection.js b/WebCore/inspector/front-end/PropertiesSection.js index 5ced9ef..1ca52ce 100644 --- a/WebCore/inspector/front-end/PropertiesSection.js +++ b/WebCore/inspector/front-end/PropertiesSection.js @@ -32,7 +32,7 @@ WebInspector.PropertiesSection = function(title, subtitle) WebInspector.Section.call(this, title, subtitle); this.propertiesElement = document.createElement("ol"); - this.propertiesElement.className = "properties source-code"; + this.propertiesElement.className = "properties properties-tree source-code"; this.propertiesElement.tabIndex = 0; this.propertiesTreeOutline = new TreeOutline(this.propertiesElement); this.propertiesTreeOutline.section = this; diff --git a/WebCore/inspector/front-end/Resource.js b/WebCore/inspector/front-end/Resource.js index ea9052d..fe2f7d2 100644 --- a/WebCore/inspector/front-end/Resource.js +++ b/WebCore/inspector/front-end/Resource.js @@ -29,7 +29,7 @@ WebInspector.Resource = function(identifier, url) { this.identifier = identifier; - this._url = url; + this.url = url; this._startTime = -1; this._endTime = -1; this._requestMethod = ""; @@ -97,14 +97,19 @@ WebInspector.Resource.prototype = { if (this._url === x) return; - var oldURL = this._url; this._url = x; delete this._parsedQueryParameters; - // FIXME: We should make the WebInspector object listen for the "url changed" event. - // Then resourceURLChanged can be removed. - WebInspector.resourceURLChanged(this, oldURL); - this.dispatchEventToListeners("url changed"); + var parsedURL = x.asParsedURL(); + this.domain = parsedURL ? parsedURL.host : ""; + this.path = parsedURL ? parsedURL.path : ""; + this.lastPathComponent = ""; + if (parsedURL && parsedURL.path) { + var lastSlashIndex = parsedURL.path.lastIndexOf("/"); + if (lastSlashIndex !== -1) + this.lastPathComponent = parsedURL.path.substring(lastSlashIndex + 1); + } + this.lastPathComponentLowerCase = this.lastPathComponent.toLowerCase(); }, get documentURL() @@ -114,46 +119,21 @@ WebInspector.Resource.prototype = { set documentURL(x) { - if (this._documentURL === x) - return; this._documentURL = x; }, - get domain() - { - return this._domain; - }, - - set domain(x) - { - if (this._domain === x) - return; - this._domain = x; - }, - - get lastPathComponent() - { - return this._lastPathComponent; - }, - - set lastPathComponent(x) - { - if (this._lastPathComponent === x) - return; - this._lastPathComponent = x; - this._lastPathComponentLowerCase = x ? x.toLowerCase() : null; - }, - get displayName() { - var title = this.lastPathComponent; - if (!title) - title = this.displayDomain; - if (!title && this.url) - title = this.url.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : ""); - if (title === "/") - title = this.url; - return title; + if (this._displayName) + return this._displayName; + this._displayName = this.lastPathComponent; + if (!this._displayName) + this._displayName = this.displayDomain; + if (!this._displayName && this.url) + this._displayName = this.url.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : ""); + if (this._displayName === "/") + this._displayName = this.url; + return this._displayName; }, get displayDomain() @@ -171,29 +151,22 @@ WebInspector.Resource.prototype = { set startTime(x) { - if (this._startTime === x) - return; - this._startTime = x; - - if (WebInspector.panels.resources) - WebInspector.panels.resources.refreshResource(this); }, get responseReceivedTime() { + if (this.timing && this.timing.requestTime) { + // Calculate responseReceivedTime from timing data for better accuracy. + // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis. + return this.timing.requestTime + this.timing.receiveHeadersEnd / 1000.0; + } return this._responseReceivedTime || -1; }, set responseReceivedTime(x) { - if (this._responseReceivedTime === x) - return; - this._responseReceivedTime = x; - - if (WebInspector.panels.resources) - WebInspector.panels.resources.refreshResource(this); }, get endTime() @@ -203,13 +176,13 @@ WebInspector.Resource.prototype = { set endTime(x) { - if (this._endTime === x) - return; + // In case of fast load (or in case of cached resources), endTime on network stack + // can be less then m_responseReceivedTime measured in WebCore. Normalize it here, + // prefer actualEndTime to m_responseReceivedTime. + if (x < this.responseReceivedTime) + this.responseReceivedTime = x; this._endTime = x; - - if (WebInspector.panels.resources) - WebInspector.panels.resources.refreshResource(this); }, get duration() @@ -240,13 +213,7 @@ WebInspector.Resource.prototype = { set resourceSize(x) { - if (this._resourceSize === x) - return; - this._resourceSize = x; - - if (WebInspector.panels.resources) - WebInspector.panels.resources.refreshResource(this); }, get transferSize() @@ -262,8 +229,6 @@ WebInspector.Resource.prototype = { set expectedContentLength(x) { - if (this._expectedContentLength === x) - return; this._expectedContentLength = x; }, @@ -313,11 +278,6 @@ WebInspector.Resource.prototype = { if (this._category) this._category.addResource(this); - - if (WebInspector.panels.resources) { - WebInspector.panels.resources.refreshResource(this); - WebInspector.panels.resources.recreateViewForResourceIfNeeded(this); - } }, get cached() @@ -328,7 +288,6 @@ WebInspector.Resource.prototype = { set cached(x) { this._cached = x; - this.dispatchEventToListeners("cached changed"); }, get mimeType() @@ -338,9 +297,6 @@ WebInspector.Resource.prototype = { set mimeType(x) { - if (this._mimeType === x) - return; - this._mimeType = x; }, @@ -387,18 +343,14 @@ WebInspector.Resource.prototype = { get requestHeaders() { - if (this._requestHeaders === undefined) - this._requestHeaders = {}; - return this._requestHeaders; + return this._requestHeaders || {}; }, set requestHeaders(x) { - if (this._requestHeaders === x) - return; - this._requestHeaders = x; delete this._sortedRequestHeaders; + delete this._requestCookies; this.dispatchEventToListeners("requestHeaders changed"); }, @@ -421,6 +373,13 @@ WebInspector.Resource.prototype = { return this._headerValue(this.requestHeaders, headerName); }, + get requestCookies() + { + if (!this._requestCookies) + this._requestCookies = WebInspector.CookieParser.parseCookie(this.requestHeaderValue("Cookie")); + return this._requestCookies; + }, + get requestFormData() { return this._requestFormData; @@ -434,18 +393,14 @@ WebInspector.Resource.prototype = { get responseHeaders() { - if (this._responseHeaders === undefined) - this._responseHeaders = {}; - return this._responseHeaders; + return this._responseHeaders || {}; }, set responseHeaders(x) { - if (this._responseHeaders === x) - return; - this._responseHeaders = x; delete this._sortedResponseHeaders; + delete this._responseCookies; this.dispatchEventToListeners("responseHeaders changed"); }, @@ -468,6 +423,13 @@ WebInspector.Resource.prototype = { return this._headerValue(this.responseHeaders, headerName); }, + get responseCookies() + { + if (!this._responseCookies) + this._responseCookies = WebInspector.CookieParser.parseSetCookie(this.responseHeaderValue("Set-Cookie")); + return this._responseCookies; + }, + get queryParameters() { if (this._parsedQueryParameters) @@ -627,6 +589,15 @@ WebInspector.Resource.prototype = { if (msg) WebInspector.console.addMessage(msg); + }, + + getContents: function(callback) + { + // FIXME: eventually, cached resources will have no identifiers. + if (this.frameID) + InspectorBackend.resourceContent(this.frameID, this.url, callback); + else + InspectorBackend.getResourceContent(this.identifier, false, callback); } } diff --git a/WebCore/inspector/front-end/ResourceCategory.js b/WebCore/inspector/front-end/ResourceCategory.js index 84f2cf9..7d95a1f 100644 --- a/WebCore/inspector/front-end/ResourceCategory.js +++ b/WebCore/inspector/front-end/ResourceCategory.js @@ -47,8 +47,8 @@ WebInspector.ResourceCategory.prototype = { var resourcesLength = this.resources.length; for (var i = 0; i < resourcesLength; ++i) { var b = this.resources[i]; - if (a._lastPathComponentLowerCase && b._lastPathComponentLowerCase) - if (a._lastPathComponentLowerCase < b._lastPathComponentLowerCase) + if (a.lastPathComponentLowerCase && b.lastPathComponentLowerCase) + if (a.lastPathComponentLowerCase < b.lastPathComponentLowerCase) break; else if (a.name && b.name) if (a.name < b.name) diff --git a/WebCore/inspector/front-end/ResourceManager.js b/WebCore/inspector/front-end/ResourceManager.js new file mode 100644 index 0000000..7244cea --- /dev/null +++ b/WebCore/inspector/front-end/ResourceManager.js @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2009, 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.ResourceManager = function() +{ + this._registerNotifyHandlers( + "identifierForInitialRequest", + "willSendRequest", + "markResourceAsCached", + "didReceiveResponse", + "didReceiveContentLength", + "didFinishLoading", + "didFailLoading", + "didLoadResourceFromMemoryCache", + "setOverrideContent", + "didCommitLoad", + "frameDetachedFromParent", + "didCreateWebSocket", + "willSendWebSocketHandshakeRequest", + "didReceiveWebSocketHandshakeResponse", + "didCloseWebSocket"); + + this._resources = {}; + this._resourcesByFrame = {}; + this._lastCachedId = 0; +} + +WebInspector.ResourceManager.prototype = { + _registerNotifyHandlers: function() + { + for (var i = 0; i < arguments.length; ++i) + WebInspector[arguments[i]] = this[arguments[i]].bind(this); + }, + + identifierForInitialRequest: function(identifier, url, frameID, isMainResource) + { + var resource = new WebInspector.Resource(identifier, url); + if (isMainResource) + resource.isMainResource = true; + this._resources[identifier] = resource; + + if (frameID) { + resource.frameID = frameID; + var resourcesForFrame = this._resourcesByFrame[frameID]; + if (!resourcesForFrame) { + resourcesForFrame = []; + this._resourcesByFrame[frameID] = resourcesForFrame; + } + resourcesForFrame.push(resource); + } + + if (WebInspector.panels.network) + WebInspector.panels.network.addResource(resource); + }, + + willSendRequest: function(identifier, time, request, redirectResponse) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + // 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 (!redirectResponse.isNull && request.url.length) { + resource.endTime = time; + this.didReceiveResponse(identifier, time, "Other", redirectResponse); + resource = this._appendRedirect(resource.identifier, request.url); + } + + resource.requestMethod = request.httpMethod; + resource.requestHeaders = request.httpHeaderFields; + resource.requestFormData = request.requestFormData; + resource.startTime = time; + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + }, + + _appendRedirect: function(identifier, redirectURL) + { + // We always store last redirect by the original id key. Rest of the redirects are referenced from within the last one. + + var originalResource = this._resources[identifier]; + var redirectIdentifier = originalResource.identifier + ":" + (originalResource.redirects ? originalResource.redirects.length : 0); + originalResource.identifier = redirectIdentifier; + this._resources[redirectIdentifier] = originalResource; + + this.identifierForInitialRequest(identifier, redirectURL, originalResource.frameID); + + var newResource = this._resources[identifier]; + newResource.redirects = originalResource.redirects || []; + delete originalResource.redirects; + newResource.redirects.push(originalResource); + return newResource; + }, + + markResourceAsCached: function(identifier) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + resource.cached = true; + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + }, + + didReceiveResponse: function(identifier, time, resourceType, response) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + resource.type = WebInspector.Resource.Type[resourceType]; + resource.mimeType = response.mimeType; + resource.expectedContentLength = response.expectedContentLength; + resource.textEncodingName = response.textEncodingName; + resource.suggestedFilename = response.suggestedFilename; + resource.statusCode = response.httpStatusCode; + resource.statusText = response.httpStatusText; + + resource.responseHeaders = response.httpHeaderFields; + resource.connectionReused = response.connectionReused; + resource.connectionID = response.connectionID; + resource.responseReceivedTime = time; + + if (response.wasCached) + resource.cached = true; + else + resource.timing = response.timing; + + if (response.rawHeaders) { + resource.requestHeaders = response.rawHeaders.requestHeaders; + resource.responseHeaders = response.rawHeaders.responseHeaders; + } + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + }, + + didReceiveContentLength: function(identifier, time, lengthReceived) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + resource.resourceSize += lengthReceived; + resource.endTime = time; + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + }, + + didFinishLoading: function(identifier, finishTime) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + resource.finished = true; + resource.endTime = finishTime; + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + }, + + didFailLoading: function(identifier, time, localizedDescription) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + resource.failed = true; + resource.endTime = time; + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + }, + + didLoadResourceFromMemoryCache: function(time, frameID, cachedResource) + { + var identifier = "cached:" + this._lastCachedId++; + this.identifierForInitialRequest(identifier, cachedResource.url, frameID); + + var resource = this._resources[identifier]; + resource.cached = true; + resource.startTime = resource.responseReceivedTime = time; + resource.resourceSize = cachedResource.encodedSize(); + + this.didReceiveResponse(identifier, time, cachedResource.response); + }, + + setOverrideContent: function(identifier, sourceString, type) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + resource.type = WebInspector.Resource.Type[type]; + resource.overridenContent = sourceString; + + if (WebInspector.panels.network) + WebInspector.panels.network.addResource(resource); + }, + + didCommitLoad: function(frameID) + { + }, + + frameDetachedFromParent: function(frameID) + { + var resourcesForFrame = this._resourcesByFrame[frameID]; + for (var i = 0; resourcesForFrame && i < resourcesForFrame.length; ++i) + delete this._resources[resourcesForFrame[i].identifier]; + delete this._resourcesByFrame[frameID]; + }, + + didCreateWebSocket: function(identifier, requestURL) + { + this.identifierForInitialRequest(identifier, requestURL); + var resource = this._resources[identifier]; + resource.type = WebInspector.Resource.Type.WebSocket; + + if (WebInspector.panels.network) + WebInspector.panels.network.addResource(resource); + }, + + willSendWebSocketHandshakeRequest: function(identifier, time, request) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + resource.requestMethod = "GET"; + resource.requestHeaders = request.webSocketHeaderFields; + resource.webSocketRequestKey3 = request.webSocketRequestKey3; + resource.startTime = time; + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + }, + + didReceiveWebSocketHandshakeResponse: function(identifier, time, response) + { + var resource = this._resources[identifier]; + if (!resource) + return; + + resource.statusCode = response.statusCode; + resource.statusText = response.statusText; + resource.responseHeaders = response.webSocketHeaderFields; + resource.webSocketChallengeResponse = response.webSocketChallengeResponse; + resource.responseReceivedTime = time; + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + }, + + didCloseWebSocket: function(identifier, time) + { + var resource = this._resources[identifier]; + if (!resource) + return; + resource.endTime = time; + + if (WebInspector.panels.network) + WebInspector.panels.network.refreshResource(resource); + } +} diff --git a/WebCore/inspector/front-end/ResourceView.js b/WebCore/inspector/front-end/ResourceView.js index 1c2574f..862569f 100644 --- a/WebCore/inspector/front-end/ResourceView.js +++ b/WebCore/inspector/front-end/ResourceView.js @@ -99,7 +99,6 @@ WebInspector.ResourceView = function(resource) this.headersVisible = true; - resource.addEventListener("url changed", this._refreshURL, this); resource.addEventListener("requestHeaders changed", this._refreshRequestHeaders, this); resource.addEventListener("responseHeaders changed", this._refreshResponseHeaders, this); resource.addEventListener("finished", this._refreshHTTPInformation, this); diff --git a/WebCore/inspector/front-end/ResourcesPanel.js b/WebCore/inspector/front-end/ResourcesPanel.js index aa021ff..ae67bef 100644 --- a/WebCore/inspector/front-end/ResourcesPanel.js +++ b/WebCore/inspector/front-end/ResourcesPanel.js @@ -142,7 +142,7 @@ WebInspector.ResourcesPanel.prototype = { // Add a divider var dividerElement = document.createElement("div"); - dividerElement.addStyleClass("divider"); + dividerElement.addStyleClass("scope-bar-divider"); this.filterBarElement.appendChild(dividerElement); for (var category in this.categories) @@ -757,7 +757,6 @@ WebInspector.ResourcesPanel.prototype = { addResource: function(resource) { this._resources.push(resource); - this.refreshResource(resource); }, removeResource: function(resource) @@ -815,18 +814,19 @@ WebInspector.ResourcesPanel.prototype = { refreshResource: function(resource) { + this._recreateViewForResourceIfNeeded(resource); this.refreshItem(resource); }, - recreateViewForResourceIfNeeded: function(resource) + _recreateViewForResourceIfNeeded: function(resource) { if (!resource || !resource._resourcesView) return; - var newView = this._createResourceView(resource); - if (newView.__proto__ === resource._resourcesView.__proto__) + if (this._resourceViewIsConsistentWithCategory(resource, resource._resourcesView)) return; + var newView = this._createResourceView(resource); if (!this.currentQuery && resource._itemsTreeElement) resource._itemsTreeElement.updateErrorsAndWarnings(); @@ -1068,6 +1068,23 @@ WebInspector.ResourcesPanel.prototype = { this.calculator = this.summaryBar.calculator = selectedOption.calculator; }, + _resourceViewIsConsistentWithCategory: function(resource, resourceView) + { + switch (resource.category) { + case WebInspector.resourceCategories.documents: + case WebInspector.resourceCategories.stylesheets: + case WebInspector.resourceCategories.scripts: + case WebInspector.resourceCategories.xhr: + return resourceView.__proto__ === WebInspector.SourceView.prototype; + case WebInspector.resourceCategories.images: + return resourceView.__proto__ === WebInspector.ImageView.prototype; + case WebInspector.resourceCategories.fonts: + return resourceView.__proto__ === WebInspector.FontView.prototype; + default: + return resourceView.__proto__ === WebInspector.ResourceView.prototype; + } + }, + _createResourceView: function(resource) { switch (resource.category) { @@ -1250,17 +1267,27 @@ WebInspector.ResourcesPanel.prototype = { _contextMenu: function(event) { - // createBlobURL is enabled conditionally, do not expose resource export if it's not available. - if (typeof window.createBlobURL !== "function" || !Preferences.resourceExportEnabled) - return; - var contextMenu = new WebInspector.ContextMenu(); var resourceTreeItem = event.target.enclosingNodeOrSelfWithClass("resource-sidebar-tree-item"); - if (resourceTreeItem && resourceTreeItem.treeElement) { - var resource = resourceTreeItem.treeElement.representedObject; - contextMenu.appendItem(WebInspector.UIString("Export to HAR"), this._exportResource.bind(this, resource)); + var resource; + if (resourceTreeItem && resourceTreeItem.treeElement) + resource = resourceTreeItem.treeElement.representedObject; + + var needSeparator = false; + // createObjectURL is enabled conditionally, do not expose resource export if it's not available. + if (typeof window.createObjectURL === "function" && Preferences.resourceExportEnabled) { + if (resource) + contextMenu.appendItem(WebInspector.UIString("Export to HAR"), this._exportResource.bind(this, resource)); + contextMenu.appendItem(WebInspector.UIString("Export all to HAR"), this._exportAll.bind(this)); + needSeparator = true; } - contextMenu.appendItem(WebInspector.UIString("Export all to HAR"), this._exportAll.bind(this)); + + if (resource && resource.category === WebInspector.resourceCategories.xhr) { + if (needSeparator) + contextMenu.appendSeparator(); + contextMenu.appendItem(WebInspector.UIString("Set XHR Breakpoint"), WebInspector.breakpointManager.createXHRBreakpoint.bind(WebInspector.breakpointManager, resource.url)); + } + contextMenu.show(event); }, @@ -1281,11 +1308,6 @@ WebInspector.ResourcesPanel.prototype = { WebInspector.ResourcesPanel.prototype.__proto__ = WebInspector.Panel.prototype; -WebInspector.getResourceContent = function(identifier, callback) -{ - InspectorBackend.getResourceContent(identifier, callback); -} - WebInspector.ResourceBaseCalculator = function() { } @@ -1814,7 +1836,8 @@ WebInspector.ResourceGraph = function(resource) this._graphElement.className = "resources-graph-side"; this._graphElement.addEventListener("mouseover", this.refreshLabelPositions.bind(this), false); - this._cachedChanged(); + if (this.resource.cached) + this._graphElement.addStyleClass("resource-cached"); this._barAreaElement = document.createElement("div"); this._barAreaElement.className = "resources-graph-bar-area hidden"; @@ -1838,8 +1861,6 @@ WebInspector.ResourceGraph = function(resource) this._barAreaElement.appendChild(this._labelRightElement); this._graphElement.addStyleClass("resources-category-" + resource.category.name); - - resource.addEventListener("cached changed", this._cachedChanged, this); } WebInspector.ResourceGraph.prototype = { @@ -1963,11 +1984,8 @@ WebInspector.ResourceGraph.prototype = { this._labelLeftElement.title = tooltip; this._labelRightElement.title = tooltip; this._barRightElement.title = tooltip; - }, - _cachedChanged: function() - { - if (this.resource.cached) + if (this.resource.cached && !this._graphElement.hasStyleClass("resource-cached")) this._graphElement.addStyleClass("resource-cached"); } } diff --git a/WebCore/inspector/front-end/ScriptsPanel.js b/WebCore/inspector/front-end/ScriptsPanel.js index 8283528..0a653c9 100644 --- a/WebCore/inspector/front-end/ScriptsPanel.js +++ b/WebCore/inspector/front-end/ScriptsPanel.js @@ -134,10 +134,9 @@ WebInspector.ScriptsPanel = function() this.sidebarPanes.jsBreakpoints = WebInspector.createJSBreakpointsSidebarPane(); if (Preferences.nativeInstrumentationEnabled) { this.sidebarPanes.domBreakpoints = WebInspector.createDOMBreakpointsSidebarPane(); - this.sidebarPanes.domBreakpoints.expanded = true; this.sidebarPanes.xhrBreakpoints = WebInspector.createXHRBreakpointsSidebarPane(); - this.sidebarPanes.xhrBreakpoints.expanded = true; } + this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane(); this.sidebarPanes.workers = new WebInspector.WorkersSidebarPane(); @@ -389,9 +388,6 @@ WebInspector.ScriptsPanel.prototype = { this.sidebarPanes.callstack.update(details.callFrames, this._sourceIDMap); this.sidebarPanes.callstack.selectedCallFrame = details.callFrames[0]; - if ("eventType" in details) - this.sidebarPanes.callstack.updateStatus(details.eventType, details.eventData); - WebInspector.currentPanel = this; window.focus(); }, @@ -439,13 +435,7 @@ WebInspector.ScriptsPanel.prototype = { delete this.currentQuery; this.searchCanceled(); - if (!this._debuggerEnabled) { - this._paused = false; - this._waitingToPause = false; - this._stepping = false; - } - - this._clearInterface(); + this.debuggerResumed(); this._backForwardList = []; this._currentBackForwardIndex = -1; @@ -473,6 +463,7 @@ WebInspector.ScriptsPanel.prototype = { this.sidebarPanes.domBreakpoints.reset(); this.sidebarPanes.xhrBreakpoints.reset(); } + this.sidebarPanes.eventListenerBreakpoints.reset(); this.sidebarPanes.workers.reset(); } }, diff --git a/WebCore/inspector/front-end/SourceFrame.js b/WebCore/inspector/front-end/SourceFrame.js index aecd57b..4b391ac 100644 --- a/WebCore/inspector/front-end/SourceFrame.js +++ b/WebCore/inspector/front-end/SourceFrame.js @@ -169,6 +169,14 @@ WebInspector.SourceFrame.prototype = { this._lineToHighlight = line; }, + clearLineHighlight: function() + { + if (this._textViewer) + this._textViewer.clearLineHighlight(); + else + delete this._lineToHighlight; + }, + _createViewerIfNeeded: function() { if (!this._visible || !this._loaded || this._textViewer) diff --git a/WebCore/inspector/front-end/SourceView.js b/WebCore/inspector/front-end/SourceView.js index 3f762cc..8092505 100644 --- a/WebCore/inspector/front-end/SourceView.js +++ b/WebCore/inspector/front-end/SourceView.js @@ -59,6 +59,8 @@ WebInspector.SourceView.prototype = { hide: function() { this.sourceFrame.visible = false; + if (!this._frameNeedsSetup) + this.sourceFrame.clearLineHighlight(); WebInspector.View.prototype.hide.call(this); if (this.localSourceFrame) this.localSourceFrame.visible = false; @@ -81,7 +83,7 @@ WebInspector.SourceView.prototype = { this.attach(); delete this._frameNeedsSetup; - WebInspector.getResourceContent(this.resource.identifier, this._contentLoaded.bind(this)); + this.resource.getContents(this._contentLoaded.bind(this)); }, hasContentTab: function() diff --git a/WebCore/inspector/front-end/StoragePanel.js b/WebCore/inspector/front-end/StoragePanel.js index 2d17989..e033b57 100644 --- a/WebCore/inspector/front-end/StoragePanel.js +++ b/WebCore/inspector/front-end/StoragePanel.js @@ -59,7 +59,7 @@ WebInspector.StoragePanel = function(database) this.element.appendChild(this.storageViews); this.storageViewStatusBarItemsContainer = document.createElement("div"); - this.storageViewStatusBarItemsContainer.id = "storage-view-status-bar-items"; + this.storageViewStatusBarItemsContainer.className = "status-bar-items"; this.reset(); } diff --git a/WebCore/inspector/front-end/StylesSidebarPane.js b/WebCore/inspector/front-end/StylesSidebarPane.js index 6aff37d..36d854c 100644 --- a/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/WebCore/inspector/front-end/StylesSidebarPane.js @@ -1695,17 +1695,13 @@ WebInspector.StylePropertyTreeElement.prototype = { self.updateTitle(); } - function successCallback(newStyle, changedProperties) + function successCallback(newStyle) { - elementsPanel.removeStyleChange(section.identifier, self.style, self.name); - if (!styleTextLength) { // Do remove ourselves from UI when the property removal is confirmed. self.parent.removeChild(self); } else { self.style = newStyle; - for (var i = 0; i < changedProperties.length; ++i) - elementsPanel.addStyleChange(section.identifier, self.style, changedProperties[i]); self._styleRule.style = self.style; } diff --git a/WebCore/inspector/front-end/TabbedPane.js b/WebCore/inspector/front-end/TabbedPane.js index 6acd163..dec3a0b 100644 --- a/WebCore/inspector/front-end/TabbedPane.js +++ b/WebCore/inspector/front-end/TabbedPane.js @@ -33,7 +33,7 @@ WebInspector.TabbedPane = function(element) this.element = element || document.createElement("div"); this.tabsElement = document.createElement("div"); - this.tabsElement.className = "scope-bar"; + this.tabsElement.className = "tabbed-pane-header"; this.element.appendChild(this.tabsElement); this._tabObjects = {}; diff --git a/WebCore/inspector/front-end/TextViewer.js b/WebCore/inspector/front-end/TextViewer.js index 13b2836..9ad5e49 100644 --- a/WebCore/inspector/front-end/TextViewer.js +++ b/WebCore/inspector/front-end/TextViewer.js @@ -117,16 +117,22 @@ WebInspector.TextViewer.prototype = { highlightLine: function(lineNumber) { - if (typeof this._highlightedLine === "number") { - var chunk = this._makeLineAChunk(this._highlightedLine); - chunk.removeDecoration("webkit-highlighted-line"); - } + this.clearLineHighlight(); this._highlightedLine = lineNumber; this.revealLine(lineNumber); var chunk = this._makeLineAChunk(lineNumber); chunk.addDecoration("webkit-highlighted-line"); }, + clearLineHighlight: function() + { + if (typeof this._highlightedLine === "number") { + var chunk = this._makeLineAChunk(this._highlightedLine); + chunk.removeDecoration("webkit-highlighted-line"); + delete this._highlightedLine; + } + }, + freeCachedElements: function() { this._cachedSpans = []; diff --git a/WebCore/inspector/front-end/TimelineGrid.js b/WebCore/inspector/front-end/TimelineGrid.js index ad7e769..adc8e47 100644 --- a/WebCore/inspector/front-end/TimelineGrid.js +++ b/WebCore/inspector/front-end/TimelineGrid.js @@ -55,6 +55,7 @@ WebInspector.TimelineGrid.prototype = { return this._itemsGraphsElement; }, + updateDividers: function(force, calculator, paddingLeft) { var dividerCount = Math.round(this._dividersElement.offsetWidth / 64); @@ -88,10 +89,21 @@ WebInspector.TimelineGrid.prototype = { dividersLabelBarElementClientWidth = this._dividersLabelBarElement.clientWidth; } - if (i === dividerCount) + if (i === (paddingLeft ? 0 : 1)) { + divider.addStyleClass("first"); + dividerLabelBar.addStyleClass("first"); + } else { + divider.removeStyleClass("first"); + dividerLabelBar.removeStyleClass("first"); + } + + if (i === dividerCount) { divider.addStyleClass("last"); - else + dividerLabelBar.addStyleClass("last"); + } else { divider.removeStyleClass("last"); + dividerLabelBar.removeStyleClass("last"); + } var left = paddingLeft + clientWidth * (i / dividerCount); var percentLeft = 100 * left / dividersLabelBarElementClientWidth; diff --git a/WebCore/inspector/front-end/TimelineOverviewPane.js b/WebCore/inspector/front-end/TimelineOverviewPane.js index 3e83f62..55e24c5 100644 --- a/WebCore/inspector/front-end/TimelineOverviewPane.js +++ b/WebCore/inspector/front-end/TimelineOverviewPane.js @@ -33,8 +33,7 @@ WebInspector.TimelineOverviewPane = function(categories) this._categories = categories; this.statusBarFilters = document.createElement("div"); - this.statusBarFilters.id = "timeline-view-status-bar-items"; - this.statusBarFilters.addStyleClass("status-bar-item"); + this.statusBarFilters.className = "status-bar-items"; for (var categoryName in this._categories) { var category = this._categories[categoryName]; this.statusBarFilters.appendChild(this._createTimelineCategoryStatusBarCheckbox(category, this._onCheckboxClicked.bind(this, category))); @@ -197,6 +196,7 @@ WebInspector.TimelineOverviewPane.prototype = { updateMainViewWidth: function(width, records) { this._overviewGrid.element.style.left = width + "px"; + this.statusBarFilters.style.left = Math.max(155, width) + "px"; }, reset: function() diff --git a/WebCore/inspector/front-end/WebKit.qrc b/WebCore/inspector/front-end/WebKit.qrc index 11382cd..1e5a508 100644 --- a/WebCore/inspector/front-end/WebKit.qrc +++ b/WebCore/inspector/front-end/WebKit.qrc @@ -20,6 +20,7 @@ <file>ConsoleView.js</file> <file>ContextMenu.js</file> <file>CookieItemsView.js</file> + <file>CookieParser.js</file> <file>CSSCompletions.js</file> <file>CSSStyleModel.js</file> <file>Database.js</file> @@ -68,6 +69,7 @@ <file>RemoteObject.js</file> <file>Resource.js</file> <file>ResourceCategory.js</file> + <file>ResourceManager.js</file> <file>ResourcesPanel.js</file> <file>ResourceView.js</file> <file>ScopeChainSidebarPane.js</file> diff --git a/WebCore/inspector/front-end/inspector.css b/WebCore/inspector/front-end/inspector.css index 848afdc..0662954 100644 --- a/WebCore/inspector/front-end/inspector.css +++ b/WebCore/inspector/front-end/inspector.css @@ -810,6 +810,7 @@ body.platform-linux .monospace, body.platform-linux .source-code { .resource-view { display: none; position: absolute; + background: white; top: 0; left: 0; right: 0; @@ -820,7 +821,7 @@ body.platform-linux .monospace, body.platform-linux .source-code { display: block; } -.resource-view .scope-bar { +.resource-view .tabbed-pane-header { display: none; position: absolute; height: 20px; @@ -831,7 +832,7 @@ body.platform-linux .monospace, body.platform-linux .source-code { border-bottom: 1px solid rgb(163, 163, 163); } -.resource-view.headers-visible .scope-bar { +.resource-view.headers-visible .tabbed-pane-header { display: block; } @@ -1373,10 +1374,10 @@ body.inactive .placard.selected { .section .properties, .event-bar .event-properties { display: none; - margin: 0; - padding: 2px 6px 3px; - list-style: none; - min-height: 18px; +} + +.section.expanded .properties, .event-bar.expanded .event-properties { + display: block; } .section.no-affect .properties li { @@ -1387,11 +1388,14 @@ body.inactive .placard.selected { opacity: 1.0; } -.section.expanded .properties, .event-bar.expanded .event-properties { - display: block; +.properties-tree { + margin: 0; + padding: 2px 6px 3px; + list-style: none; + min-height: 18px; } -.section .properties li, .event-properties li { +.properties-tree li { margin-left: 12px; white-space: nowrap; text-overflow: ellipsis; @@ -1400,26 +1404,11 @@ body.inactive .placard.selected { cursor: auto; } -.section .properties li.parent, .event-properties li.parent { +.properties-tree li.parent { margin-left: 1px; } -.section .properties ol, .event-properties ol, .stack-trace ol, ol.stack-trace { - display: none; - margin: 0; - -webkit-padding-start: 12px; - list-style: none; -} - -ol.stack-trace { - -webkit-padding-start: 0px; -} - -.section .properties ol.expanded, .event-properties ol.expanded, .stack-trace ol, ol.stack-trace { - display: block; -} - -.section .properties li.parent::before, .event-properties li.parent::before { +.properties-tree li.parent::before { content: url(Images/treeRightTriangleBlack.png); opacity: 0.75; float: left; @@ -1431,16 +1420,47 @@ ol.stack-trace { cursor: default; } -.section .properties li.parent.expanded::before, .event-properties li.parent.expanded::before { +.properties-tree li.parent.expanded::before { content: url(Images/treeDownTriangleBlack.png); margin-top: 1px; } -.section .properties li .info, .event-properties li .info { +.properties-tree li .info { padding-top: 4px; padding-bottom: 3px; } +.properties-tree ol, .stack-trace ol, ol.stack-trace { + display: none; + margin: 0; + -webkit-padding-start: 12px; + list-style: none; +} + +.properties-tree ol.expanded, .stack-trace ol, ol.stack-trace { + display: block; +} + +ol.stack-trace { + -webkit-padding-start: 0px; +} + +.event-listener-breakpoints .event-category { + font-size: 12px; + font-weight: bold; + color: rgb(110, 110, 110); +} + +.event-listener-breakpoints.properties-tree .children li { + margin-left: 17px; +} + +.event-listener-breakpoints .checkbox-elem { + float: left; + margin-top: 1px; + margin-left: 0px; +} + .section .event-bars { display: none; } @@ -2086,22 +2106,27 @@ body.inactive .sidebar { background-image: url(Images/glossyHeaderSelectedPressed.png); } -.data-grid th.sort-ascending div::after { +.data-grid th.sort-ascending > div::after { position: absolute; top: 0; + bottom: 0; right: 0; + height: 12px; + margin-bottom: auto; + margin-top: auto; width: 8px; - height: 8px; content: url(Images/treeUpTriangleBlack.png); } -.data-grid th.sort-descending div::after { +.data-grid th.sort-descending > div::after { position: absolute; top: 0; + bottom: 0; right: 0; - margin-top: 1px; - width: 8px; height: 8px; + margin-bottom: auto; + margin-top: auto; + width: 8px; content: url(Images/treeDownTriangleBlack.png); } @@ -2560,6 +2585,33 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { margin-top: 1px; } +.tabbed-pane-header { + height: 23px; + padding: 0 10px; + border-bottom: 1px solid rgb(163, 163, 163); +} + +.tabbed-pane-header li { + display: inline-block; + margin-top: 2px; + font-size: 11px; + font-weight: bold; + color: rgb(46, 46, 46); + background: transparent; + text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0; + vertical-align: middle; + padding: 1px 7px 2px; + height: 18px; + border: 1px solid transparent; + border-bottom: none; +} + +.tabbed-pane-header li.selected { + background-color: white; + border: 1px solid rgb(163, 163, 163); + border-bottom: none; +} + .scope-bar { height: 23px; padding: 2px 10px 0; @@ -2580,7 +2632,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { vertical-align: middle; } -.scope-bar .divider { +.scope-bar-divider { margin: 1px 9px 0 8px; background-color: rgba(0, 0, 0, 0.4); height: 16px; @@ -2718,7 +2770,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { .resources-event-dividers { position: absolute; left: 0; - right: 0; + right: 5px; height: 100%; top: 0; z-index: 300; @@ -3837,8 +3889,7 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches { bottom: 0; } -#timeline-view-status-bar-items, -#profile-view-status-bar-items { +.status-bar-items { position: absolute; top: 0; bottom: 0; @@ -3962,30 +4013,10 @@ button.enable-toggle-status-bar-item .glyph { -webkit-mask-image: url(Images/excludeButtonGlyph.png); } -#storage-view-status-bar-items { - position: absolute; - top: 0; - bottom: 0; - left: 200px; - overflow: hidden; - border-left: 1px solid rgb(184, 184, 184); - margin-left: -1px; -} - .refresh-storage-status-bar-item .glyph { -webkit-mask-image: url(Images/reloadButtonGlyph.png); } -#storage-view-status-bar-items { - position: absolute; - top: 0; - bottom: 0; - left: 200px; - overflow: hidden; - border-left: 1px solid rgb(184, 184, 184); - margin-left: -1px; -} - ol.breakpoint-list { -webkit-padding-start: 2px; list-style: none; @@ -4020,6 +4051,10 @@ ol.breakpoint-list { margin: 2px 0 0px 20px; } +.breakpoint-list .breakpoint-hit { + background-color: yellow; +} + .webkit-html-js-node, .webkit-html-css-node { white-space: pre; } diff --git a/WebCore/inspector/front-end/inspector.html b/WebCore/inspector/front-end/inspector.html index 949b18f..96d0cfe 100644 --- a/WebCore/inspector/front-end/inspector.html +++ b/WebCore/inspector/front-end/inspector.html @@ -61,6 +61,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="Panel.js"></script> <script type="text/javascript" src="TimelineGrid.js"></script> <script type="text/javascript" src="Resource.js"></script> + <script type="text/javascript" src="ResourceManager.js"></script> <script type="text/javascript" src="ResourceCategory.js"></script> <script type="text/javascript" src="Database.js"></script> <script type="text/javascript" src="DOMStorage.js"></script> @@ -143,6 +144,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="GoToLineDialog.js"></script> <script type="text/javascript" src="ShortcutsHelp.js"></script> <script type="text/javascript" src="HAREntry.js"></script> + <script type="text/javascript" src="CookieParser.js"></script> </head> <body class="detached"> <div id="toolbar"> diff --git a/WebCore/inspector/front-end/inspector.js b/WebCore/inspector/front-end/inspector.js index 3edae8f..9bd88d6 100644 --- a/WebCore/inspector/front-end/inspector.js +++ b/WebCore/inspector/front-end/inspector.js @@ -56,15 +56,6 @@ var WebInspector = { missingLocalizedStrings: {}, pendingDispatches: 0, - // RegExp groups: - // 1 - scheme - // 2 - hostname - // 3 - ?port - // 4 - ?path - // 5 - ?fragment - URLRegExp: /^(http[s]?|file):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i, - GenericURLRegExp: /^([^:]+):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i, - get platform() { if (!("_platform" in this)) @@ -213,9 +204,9 @@ var WebInspector = { var pane = new WebInspector.BreakpointsSidebarPane(WebInspector.UIString("DOM Breakpoints")); function breakpointAdded(event) { - pane.addBreakpoint(new WebInspector.DOMBreakpointItem(event.data)); + pane.addBreakpoint(new WebInspector.BreakpointItem(event.data)); } - WebInspector.domBreakpointManager.addEventListener("dom-breakpoint-added", breakpointAdded); + WebInspector.breakpointManager.addEventListener("dom-breakpoint-added", breakpointAdded); return pane; }, @@ -224,7 +215,7 @@ var WebInspector = { var pane = new WebInspector.XHRBreakpointsSidebarPane(); function breakpointAdded(event) { - pane.addBreakpoint(new WebInspector.XHRBreakpointItem(event.data)); + pane.addBreakpoint(new WebInspector.BreakpointItem(event.data)); } WebInspector.breakpointManager.addEventListener("xhr-breakpoint-added", breakpointAdded); return pane; @@ -516,6 +507,8 @@ WebInspector.doLoadedDone = function() // this.changes = new WebInspector.ChangesView(this.drawer); // TODO: Remove class="hidden" from inspector.html on button#changes-status-bar-item this.drawer.visibleView = this.console; + // FIXME: uncomment when ready. + // this.resourceManager = new WebInspector.ResourceManager(); this.domAgent = new WebInspector.DOMAgent(); this.resourceCategories = { @@ -530,7 +523,6 @@ WebInspector.doLoadedDone = function() }; this.breakpointManager = new WebInspector.BreakpointManager(); - this.domBreakpointManager = new WebInspector.DOMBreakpointManager(); this.cssModel = new WebInspector.CSSStyleModel(); this.panels = {}; @@ -604,6 +596,7 @@ WebInspector.doLoadedDone = function() InspectorBackend.getInspectorState(populateInspectorState); InspectorBackend.populateScriptObjects(); + InspectorBackend.setConsoleMessagesEnabled(true); // As a DOMAgent method, this needs to happen after the frontend has loaded and the agent is available. InspectorBackend.getSupportedCSSProperties(WebInspector.CSSCompletions._load); @@ -770,10 +763,10 @@ WebInspector.documentClick = function(event) return; } - const urlMatch = WebInspector.GenericURLRegExp.exec(anchor.href); - if (urlMatch && urlMatch[1] === "webkit-link-action") { - if (urlMatch[2] === "show-panel") { - const panel = urlMatch[4].substring(1); + var parsedURL = anchor.href.asParsedURL(); + if (parsedURL && parsedURL.scheme === "webkit-link-action") { + if (parsedURL.host === "show-panel") { + var panel = parsedURL.path.substring(1); if (WebInspector.panels[panel]) WebInspector.currentPanel = WebInspector.panels[panel]; } @@ -1229,14 +1222,9 @@ WebInspector.updateResource = function(payload) this.resourceURLMap[resource.url] = resource; this.panels.resources.addResource(resource); this.panels.audits.resourceStarted(resource); - if (this.panels.network) - this.panels.network.addResource(resource); } if (payload.didRequestChange) { - resource.domain = payload.host; - resource.path = payload.path; - resource.lastPathComponent = payload.lastPathComponent; resource.requestHeaders = payload.requestHeaders; resource.mainResource = payload.mainResource; resource.requestMethod = payload.requestMethod; @@ -1248,11 +1236,10 @@ WebInspector.updateResource = function(payload) if (resource.mainResource) this.mainResource = resource; - var match = payload.documentURL.match(WebInspector.GenericURLRegExp); - if (match) { - var protocol = match[1].toLowerCase(); - this._addCookieDomain(match[2]); - this._addAppCacheDomain(match[2]); + var parsedURL = payload.documentURL.asParsedURL(); + if (parsedURL) { + this._addCookieDomain(parsedURL.host); + this._addAppCacheDomain(parsedURL.host); } } @@ -1293,29 +1280,24 @@ WebInspector.updateResource = function(payload) resource.responseReceivedTime = payload.responseReceivedTime; if (payload.endTime) resource.endTime = payload.endTime; - - if (payload.loadEventTime) { - // This loadEventTime is for the main resource, and we want to show it - // for all resources on this page. This means we want to set it as a member - // of the resources panel instead of the individual resource. - this.panels.resources.mainResourceLoadTime = payload.loadEventTime; - this.panels.audits.mainResourceLoadTime = payload.loadEventTime; - if (this.panels.network) - this.panels.network.mainResourceLoadTime = payload.loadEventTime; - } - - if (payload.domContentEventTime) { - // This domContentEventTime is for the main resource, so it should go in - // the resources panel for the same reasons as above. - this.panels.resources.mainResourceDOMContentTime = payload.domContentEventTime; - this.panels.audits.mainResourceDOMContentTime = payload.domContentEventTime; - if (this.panels.network) - this.panels.network.mainResourceDOMContentTime = payload.domContentEventTime; - } } + this.panels.resources.refreshResource(resource); +} +WebInspector.domContentEventFired = function(time) +{ + this.panels.resources.mainResourceDOMContentTime = time; + this.panels.audits.mainResourceDOMContentTime = time; if (this.panels.network) - this.panels.network.refreshResource(resource); + this.panels.network.mainResourceDOMContentTime = time; +} + +WebInspector.loadEventFired = function(time) +{ + this.panels.resources.mainResourceLoadTime = time; + this.panels.audits.mainResourceLoadTime = time; + if (this.panels.network) + this.panels.network.mainResourceLoadTime = time; } WebInspector.removeResource = function(identifier) @@ -1447,16 +1429,20 @@ WebInspector.failedToParseScriptSource = function(sourceURL, source, startingLin WebInspector.pausedScript = function(details) { this.panels.scripts.debuggerPaused(details); + this.breakpointManager.debuggerPaused(details); InspectorFrontendHost.bringToFront(); } WebInspector.resumedScript = function() { + this.breakpointManager.debuggerResumed(); this.panels.scripts.debuggerResumed(); } WebInspector.reset = function() { + this.breakpointManager.reset(); + for (var panelName in this.panels) { var panel = this.panels[panelName]; if ("reset" in panel) @@ -1464,7 +1450,6 @@ WebInspector.reset = function() } this.sessionSettings.reset(); - this.breakpointManager.reset(); for (var category in this.resourceCategories) this.resourceCategories[category].removeAllResources(); @@ -1498,12 +1483,6 @@ WebInspector.inspectedURLChanged = function(url) this.extensionServer.notifyInspectedURLChanged(); } -WebInspector.resourceURLChanged = function(resource, oldURL) -{ - delete this.resourceURLMap[oldURL]; - this.resourceURLMap[resource.url] = resource; -} - WebInspector.didCommitLoad = function() { // Cleanup elements panel early on inspected page refresh. @@ -1837,8 +1816,8 @@ WebInspector.resourceURLForRelatedNode = function(node, url) // documentURL not found or has bad value for (var resourceURL in WebInspector.resourceURLMap) { - var match = resourceURL.match(WebInspector.URLRegExp); - if (match && match[4] === url) + var parsedURL = resourceURL.asParsedURL(); + if (parsedURL && parsedURL.path === url) return resourceURL; } return url; @@ -1846,17 +1825,17 @@ WebInspector.resourceURLForRelatedNode = function(node, url) WebInspector.completeURL = function(baseURL, href) { - var match = baseURL.match(WebInspector.URLRegExp); - if (match) { + var parsedURL = baseURL.asParsedURL(); + if (parsedURL) { var path = href; if (path.charAt(0) !== "/") { - var basePath = match[4] || "/"; + var basePath = parsedURL.path; path = basePath.substring(0, basePath.lastIndexOf("/")) + "/" + path; } else if (path.length > 1 && path.charAt(1) === "/") { // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol). - return match[1] + ":" + path; + return parsedURL.scheme + ":" + path; } - return match[1] + "://" + match[2] + (match[3] ? (":" + match[3]) : "") + path; + return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + path; } return null; } diff --git a/WebCore/inspector/front-end/networkPanel.css b/WebCore/inspector/front-end/networkPanel.css index cdea7fe..773fe99 100644 --- a/WebCore/inspector/front-end/networkPanel.css +++ b/WebCore/inspector/front-end/networkPanel.css @@ -1,383 +1,601 @@ -.network-larger-resources-status-bar-item .glyph {
- -webkit-mask-image: url(Images/largerResourcesButtonGlyph.png);
-}
-
-.network.panel .data-grid {
- border: none;
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- font-size: 11px;
-}
-
-.network.panel .data-grid table.data {
- -webkit-background-size: 1px 82px;
- background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), color-stop(0.5, rgba(0, 0, 0, 0)), color-stop(0.5, rgba(0, 0, 0, 0.05)), to(rgba(0, 0, 0, 0.05)));
- font-size: 11px;
-}
-
-.network.panel .data-grid.small table.data {
- -webkit-background-size: 1px 42px;
-}
-
-.network.panel .data-grid td {
- line-height: 17px;
- border-right: 1px solid rgb(210, 210, 210);
- -webkit-user-select: none;
-}
-
-.network.panel .data-grid th {
- border-bottom: 1px solid rgb(64%, 64%, 64%);
- height: 22px;
-}
-
-.network.panel .data-grid th, .network.panel .data-grid th.sort-descending, .network.panel .data-grid th.sort-ascending {
- background: -webkit-gradient(linear, left top, left bottom, from(rgb(236, 236, 236)), to(rgb(217, 217, 217)));
-}
-
-.network.panel .data-grid .data-container {
- top: 23px;
-}
-
-.network.panel .data-grid td.url-column {
- font-weight: bold;
-}
-
-.network.panel .data-grid td.optional-column {
- background-color: rgba(0, 0, 0, 0.07);
-}
-
-.network.panel .small .network-graph-side {
- height: 14px;
-}
-
-.network.panel .data-grid th.sortable:active {
- background-image: none;
-}
-
-.network-grid-subtitle {
- font-weight: normal;
- color: grey;
-}
-
-.network.panel .data-grid.small .network-grid-subtitle {
- display: none;
-}
-
-/* Resource preview icons */
-
-.network.panel .data-grid .icon {
- content: url(Images/resourcePlainIcon.png);
-}
-
-.network.panel .data-grid.small .icon {
- content: url(Images/resourcePlainIconSmall.png);
-}
-
-.network.panel .network-category-scripts .icon {
- content: url(Images/resourceJSIcon.png);
-}
-
-.network.panel .data-grid.small .network-category-scripts .icon {
- content: url(Images/resourceDocumentIconSmall.png);
-}
-
-.network.panel .network-category-documents .icon {
- content: url(Images/resourceDocumentIcon.png);
-}
-
-.network.panel .data-grid.small .network-category-documents .icon {
- content: url(Images/resourceDocumentIconSmall.png);
-}
-
-.network.panel .network-category-stylesheets .icon {
- content: url(Images/resourceCSSIcon.png);
-}
-
-.network.panel .data-grid.small .network-category-stylesheets .icon {
- content: url(Images/resourceDocumentIconSmall.png);
-}
-
-.network.panel .network-category-images .icon {
- position: relative;
- background-image: url(Images/resourcePlainIcon.png);
- background-repeat: no-repeat;
- content: "";
-}
-
-.network.panel .network-category-images .icon {
- position: relative;
- background-image: url(Images/resourcePlainIcon.png);
- background-repeat: no-repeat;
- content: "";
-}
-
-.network.panel .data-grid.small .network-category-images .icon {
- background-image: url(Images/resourcePlainIconSmall.png);
- content: "";
-}
-
-.network.panel .data-grid .icon {
- float: left;
- width: 32px;
- height: 32px;
- margin-top: 1px;
- margin-right: 3px;
-}
-
-.network.panel .data-grid.small .icon {
- width: 16px;
- height: 16px;
-}
-
-.network.panel .image-network-icon-preview {
- position: absolute;
- margin: auto;
- top: 3px;
- bottom: 4px;
- left: 5px;
- right: 5px;
- max-width: 18px;
- max-height: 21px;
- min-width: 1px;
- min-height: 1px;
-}
-
-.network.panel .data-grid.small .image-network-icon-preview {
- top: 2px;
- bottom: 1px;
- left: 3px;
- right: 3px;
- max-width: 8px;
- max-height: 11px;
-}
-
-/* Graph styles */
-
-.network-graph-side {
- position: relative;
- height: 36px;
- padding: 0 5px;
- white-space: nowrap;
- margin-top: 1px;
- border-top: 1px solid transparent;
- overflow: hidden;
-}
-
-.network-graph-bar-area {
- position: absolute;
- top: 0;
- bottom: 0;
- right: 8px;
- left: 9px;
-}
-
-.network-graph-label {
- position: absolute;
- top: 0;
- bottom: 0;
- margin: auto -7px;
- height: 13px;
- line-height: 13px;
- font-size: 9px;
- color: rgba(0, 0, 0, 0.75);
- text-shadow: rgba(255, 255, 255, 0.25) 1px 0 0, rgba(255, 255, 255, 0.25) -1px 0 0, rgba(255, 255, 255, 0.333) 0 1px 0, rgba(255, 255, 255, 0.25) 0 -1px 0;
- z-index: 150;
- overflow: hidden;
- text-align: center;
- font-weight: bold;
- opacity: 0;
- -webkit-transition: opacity 250ms ease-in-out;
-}
-
-.network-graph-side:hover .network-graph-label {
- opacity: 1;
-}
-
-.network-graph-label:empty {
- display: none;
-}
-
-.network-graph-label.waiting {
- margin-right: 5px;
-}
-
-.network-graph-label.waiting-right {
- margin-left: 5px;
-}
-
-.network-graph-label.before {
- color: rgba(0, 0, 0, 0.7);
- text-shadow: none;
- text-align: right;
- margin-right: 2px;
-}
-
-.network-graph-label.before::after {
- padding-left: 2px;
- height: 6px;
- content: url(Images/graphLabelCalloutLeft.png);
-}
-
-.network-graph-label.after {
- color: rgba(0, 0, 0, 0.7);
- text-shadow: none;
- text-align: left;
- margin-left: 2px;
-}
-
-.network-graph-label.after::before {
- padding-right: 2px;
- height: 6px;
- content: url(Images/graphLabelCalloutRight.png);
-}
-
-.network-graph-bar {
- position: absolute;
- top: 0;
- bottom: 0;
- margin: auto -7px;
- border-width: 6px 7px;
- height: 13px;
- min-width: 14px;
- opacity: 0.65;
- -webkit-border-image: url(Images/timelinePillGray.png) 6 7 6 7;
-}
-
-.network-graph-bar.waiting, .network-graph-bar.waiting-right {
- opacity: 0.35;
-}
-
-/* Resource categories */
-
-
-.resource-cached .network-graph-bar {
- -webkit-border-image: url(Images/timelineHollowPillGray.png) 6 7 6 7;
-}
-
-.network-category-documents .network-graph-bar {
- -webkit-border-image: url(Images/timelinePillBlue.png) 6 7 6 7;
-}
-
-.network-category-documents.resource-cached .network-graph-bar {
- -webkit-border-image: url(Images/timelineHollowPillBlue.png) 6 7 6 7;
-}
-
-.network-category-stylesheets .network-graph-bar {
- -webkit-border-image: url(Images/timelinePillGreen.png) 6 7 6 7;
-}
-
-.network-category-stylesheets.resource-cached .network-graph-bar {
- -webkit-border-image: url(Images/timelineHollowPillGreen.png) 6 7 6 7;
-}
-
-.network-category-images .network-graph-bar {
- -webkit-border-image: url(Images/timelinePillPurple.png) 6 7 6 7;
-}
-
-.network-category-images.resource-cached .network-graph-bar {
- -webkit-border-image: url(Images/timelineHollowPillPurple.png) 6 7 6 7;
-}
-
-.network-category-fonts .network-graph-bar {
- -webkit-border-image: url(Images/timelinePillRed.png) 6 7 6 7;
-}
-
-.network-category-fonts.resource-cached .network-graph-bar {
- -webkit-border-image: url(Images/timelineHollowPillRed.png) 6 7 6 7;
-}
-
-.network-category-scripts .network-graph-bar {
- -webkit-border-image: url(Images/timelinePillOrange.png) 6 7 6 7;
-}
-
-.network-category-scripts.resource-cached .network-graph-bar {
- -webkit-border-image: url(Images/timelineHollowPillOrange.png) 6 7 6 7;
-}
-
-.network-category-xhr .network-graph-bar {
- -webkit-border-image: url(Images/timelinePillYellow.png) 6 7 6 7;
-}
-
-.network-category-xhr.resource-cached .network-graph-bar {
- -webkit-border-image: url(Images/timelineHollowPillYellow.png) 6 7 6 7;
-}
-
-.network-category-websockets .network-graph-bar {
- -webkit-border-image: url(Images/timelinePillGray.png) 6 7 6 7;
-}
-
-.network-category-websockets.resource-cached .network-graph-bar {
- -webkit-border-image: url(Images/timelineHollowPillGray.png) 6 7 6 7;
-}
-
-
-/* Popover */
-
-.network-timing-row {
- position: relative;
- height: 12px;
-}
-
-.network-timing-bar {
- position: absolute;
- background-color: red;
- border-left: 1px solid red;
- opacity: 0.4;
-}
-
-.network-timing-bar-title {
- position: absolute;
-}
-
-.network-dim-cell {
- color: grey;
-}
-
-/* Dividers */
-
-.network-timeline-grid {
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
-}
-
-.network-event-divider-padding {
- position: absolute;
- width: 8px;
- top: 0;
- bottom: 0;
- pointer-events: auto;
-}
-
-.network-event-divider {
- position: absolute;
- width: 2px;
- top: 0;
- bottom: 0;
- z-index: 300;
-}
-
-.network-red-divider {
- background-color: rgba(255, 0, 0, 0.5);
-}
-
-.network-blue-divider {
- background-color: rgba(0, 0, 255, 0.5);
-}
-
-.network.panel .resources-dividers-label-bar {
- background-color: transparent;
- border: none;
- height: 23px;
- pointer-events: none;
-}
-
-.network.panel .resources-divider-label {
- top: 6px;
- color: black;
-}
+.network-larger-resources-status-bar-item .glyph { + -webkit-mask-image: url(Images/largerResourcesButtonGlyph.png); +} + +.network.panel .data-grid { + border: none; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + font-size: 11px; +} + +.network.panel .data-grid table.data { + -webkit-background-size: 1px 82px; + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), color-stop(0.5, rgba(0, 0, 0, 0)), color-stop(0.5, rgba(0, 0, 0, 0.05)), to(rgba(0, 0, 0, 0.05))); + font-size: 11px; +} + +.network.panel .data-grid.small table.data { + -webkit-background-size: 1px 42px; +} + +.network.panel .data-grid td { + line-height: 17px; + border-right: 1px solid rgb(210, 210, 210); + -webkit-user-select: none; + vertical-align: middle; +} + +.network.panel .data-grid th { + border-bottom: 1px solid rgb(64%, 64%, 64%); + height: 30px; + font-size: 11px; + font-weight: bold; +} + +.network.panel .data-grid.small th { + height: 22px; +} + +.network.panel .data-grid th, .network.panel .data-grid th.sort-descending, .network.panel .data-grid th.sort-ascending { + background: -webkit-gradient(linear, left top, left bottom, from(rgb(236, 236, 236)), to(rgb(217, 217, 217))); +} + +.network.panel .data-grid .data-container { + top: 31px; +} + +.network.panel .data-grid.small .data-container { + top: 23px; +} + +.network.panel .data-grid select { + -webkit-appearance: none; + background-color: transparent; + border: none; + width: 100%; + font-size: 11px; + font-weight: bold; +} + +.network.panel .data-grid tr.filler { + background-color: white; +} + +.network.panel .data-grid td.name-column { + font-weight: bold; +} + +.network.panel .data-grid td.method-column, +.network.panel .data-grid td.status-column, +.network.panel .data-grid td.type-column, +.network.panel .data-grid td.size-column, +.network.panel .data-grid td.time-column { + background-color: rgba(0, 0, 0, 0.07); +} + +.network.panel .data-grid td.size-column, +.network.panel .data-grid td.time-column { + text-align: right; +} + +.network.panel .small .network-graph-side { + height: 14px; +} + +.network.panel .data-grid th.sortable:active { + background-image: none; +} + +.network-cell-subtitle { + font-weight: normal; + color: grey; +} + +.network-header-subtitle { + color: grey; +} + +.network.panel .data-grid.small .network-cell-subtitle, +.network.panel .data-grid.small .network-header-subtitle +{ + display: none; +} + +/* Resource preview icons */ + +.network.panel .data-grid .icon { + content: url(Images/resourcePlainIcon.png); +} + +.network.panel .data-grid.small .icon { + content: url(Images/resourcePlainIconSmall.png); +} + +.network.panel .network-category-scripts .icon { + content: url(Images/resourceJSIcon.png); +} + +.network.panel .data-grid.small .network-category-scripts .icon { + content: url(Images/resourceDocumentIconSmall.png); +} + +.network.panel .network-category-documents .icon { + content: url(Images/resourceDocumentIcon.png); +} + +.network.panel .data-grid.small .network-category-documents .icon { + content: url(Images/resourceDocumentIconSmall.png); +} + +.network.panel .network-category-stylesheets .icon { + content: url(Images/resourceCSSIcon.png); +} + +.network.panel .data-grid.small .network-category-stylesheets .icon { + content: url(Images/resourceDocumentIconSmall.png); +} + +.network.panel .network-category-images .icon { + position: relative; + background-image: url(Images/resourcePlainIcon.png); + background-repeat: no-repeat; + content: ""; +} + +.network.panel .network-category-images .icon { + position: relative; + background-image: url(Images/resourcePlainIcon.png); + background-repeat: no-repeat; + content: ""; +} + +.network.panel .data-grid.small .network-category-images .icon { + background-image: url(Images/resourcePlainIconSmall.png); + content: ""; +} + +.network.panel .data-grid .icon { + float: left; + width: 32px; + height: 32px; + margin-top: 1px; + margin-right: 3px; +} + +.network.panel .data-grid.small .icon { + width: 16px; + height: 16px; +} + +.network.panel .image-network-icon-preview { + position: absolute; + margin: auto; + top: 3px; + bottom: 4px; + left: 5px; + right: 5px; + max-width: 18px; + max-height: 21px; + min-width: 1px; + min-height: 1px; +} + +.network.panel .data-grid.small .image-network-icon-preview { + top: 2px; + bottom: 1px; + left: 3px; + right: 3px; + max-width: 8px; + max-height: 11px; +} + +/* Graph styles */ + +.network-graph-side { + position: relative; + height: 36px; + padding: 0 5px; + white-space: nowrap; + margin-top: 1px; + border-top: 1px solid transparent; + overflow: hidden; +} + +.network-graph-bar-area { + position: absolute; + top: 0; + bottom: 0; + right: 8px; + left: 9px; +} + +.network-graph-label { + position: absolute; + top: 0; + bottom: 0; + margin: auto -7px; + height: 13px; + line-height: 13px; + font-size: 9px; + color: rgba(0, 0, 0, 0.75); + text-shadow: rgba(255, 255, 255, 0.25) 1px 0 0, rgba(255, 255, 255, 0.25) -1px 0 0, rgba(255, 255, 255, 0.333) 0 1px 0, rgba(255, 255, 255, 0.25) 0 -1px 0; + z-index: 150; + overflow: hidden; + text-align: center; + font-weight: bold; + opacity: 0; + -webkit-transition: opacity 250ms ease-in-out; +} + +.network-graph-side:hover .network-graph-label { + opacity: 1; +} + +.network-graph-label:empty { + display: none; +} + +.network-graph-label.waiting { + margin-right: 5px; +} + +.network-graph-label.waiting-right { + margin-left: 5px; +} + +.network-graph-label.before { + color: rgba(0, 0, 0, 0.7); + text-shadow: none; + text-align: right; + margin-right: 2px; +} + +.network-graph-label.before::after { + padding-left: 2px; + height: 6px; + content: url(Images/graphLabelCalloutLeft.png); +} + +.network-graph-label.after { + color: rgba(0, 0, 0, 0.7); + text-shadow: none; + text-align: left; + margin-left: 2px; +} + +.network-graph-label.after::before { + padding-right: 2px; + height: 6px; + content: url(Images/graphLabelCalloutRight.png); +} + +.network-graph-bar { + position: absolute; + top: 0; + bottom: 0; + margin: auto -7px; + border-width: 6px 7px; + height: 13px; + min-width: 14px; + opacity: 0.65; + -webkit-border-image: url(Images/timelinePillGray.png) 6 7 6 7; +} + +.network-graph-bar.waiting, .network-graph-bar.waiting-right { + opacity: 0.35; +} + +/* Resource categories */ + + +.resource-cached .network-graph-bar { + -webkit-border-image: url(Images/timelineHollowPillGray.png) 6 7 6 7; +} + +.network-category-documents .network-graph-bar { + -webkit-border-image: url(Images/timelinePillBlue.png) 6 7 6 7; +} + +.network-category-documents.resource-cached .network-graph-bar { + -webkit-border-image: url(Images/timelineHollowPillBlue.png) 6 7 6 7; +} + +.network-category-stylesheets .network-graph-bar { + -webkit-border-image: url(Images/timelinePillGreen.png) 6 7 6 7; +} + +.network-category-stylesheets.resource-cached .network-graph-bar { + -webkit-border-image: url(Images/timelineHollowPillGreen.png) 6 7 6 7; +} + +.network-category-images .network-graph-bar { + -webkit-border-image: url(Images/timelinePillPurple.png) 6 7 6 7; +} + +.network-category-images.resource-cached .network-graph-bar { + -webkit-border-image: url(Images/timelineHollowPillPurple.png) 6 7 6 7; +} + +.network-category-fonts .network-graph-bar { + -webkit-border-image: url(Images/timelinePillRed.png) 6 7 6 7; +} + +.network-category-fonts.resource-cached .network-graph-bar { + -webkit-border-image: url(Images/timelineHollowPillRed.png) 6 7 6 7; +} + +.network-category-scripts .network-graph-bar { + -webkit-border-image: url(Images/timelinePillOrange.png) 6 7 6 7; +} + +.network-category-scripts.resource-cached .network-graph-bar { + -webkit-border-image: url(Images/timelineHollowPillOrange.png) 6 7 6 7; +} + +.network-category-xhr .network-graph-bar { + -webkit-border-image: url(Images/timelinePillYellow.png) 6 7 6 7; +} + +.network-category-xhr.resource-cached .network-graph-bar { + -webkit-border-image: url(Images/timelineHollowPillYellow.png) 6 7 6 7; +} + +.network-category-websockets .network-graph-bar { + -webkit-border-image: url(Images/timelinePillGray.png) 6 7 6 7; +} + +.network-category-websockets.resource-cached .network-graph-bar { + -webkit-border-image: url(Images/timelineHollowPillGray.png) 6 7 6 7; +} + + +/* Popover */ + +.network-timing-row { + position: relative; + height: 12px; +} + +.network-timing-bar { + position: absolute; + background-color: red; + border-left: 1px solid red; + opacity: 0.4; +} + +.network-timing-bar-title { + position: absolute; +} + +.network-dim-cell { + color: grey; +} + +/* Dividers */ + +.network-timeline-grid { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + pointer-events: none; +} + +.network-event-divider-padding { + position: absolute; + width: 8px; + top: 0; + bottom: 0; + pointer-events: auto; +} + +.network-event-divider { + position: absolute; + width: 2px; + top: 31px; + bottom: 0; + z-index: 300; +} + +.network.panel .network-timeline-grid.small .network-event-divider { + top: 23px; +} + +.network-red-divider { + background-color: rgba(255, 0, 0, 0.5); +} + +.network-blue-divider { + background-color: rgba(0, 0, 255, 0.5); +} + +.network.panel .resources-dividers-label-bar { + background-color: transparent; + border: none; + height: 30px; + pointer-events: none; +} + +.network.panel .network-timeline-grid.small .resources-dividers-label-bar { + height: 23px; +} + +.network.panel .resources-divider-label { + top: 0px; + margin-top: -4px; + color: black; +} + +.network.panel .resources-dividers-label-bar .resources-divider { + top: 23px; +} + +.network.panel .network-timeline-grid.small .resources-dividers-label-bar .resources-divider { + top: 15px; +} + +.network.panel .resources-divider.first .resources-divider-label { + display: none; +} + +.network.panel .resources-dividers-label-bar .resources-divider.first { + background-color: transparent; +} + +/* Filters */ +#network-filter { + margin-top: 1px; +} + +.data-grid table.data tr.revealed.network-category-documents, .data-grid table.data tr.revealed.network-category-stylesheets, +.data-grid table.data tr.revealed.network-category-images, .data-grid table.data tr.revealed.network-category-scripts, +.data-grid table.data tr.revealed.network-category-xhr, .data-grid table.data tr.revealed.network-category-fonts, +.data-grid table.data tr.revealed.network-category-websockets, .data-grid table.data tr.revealed.network-category-other { + display: none; +} + +.data-grid.filter-all table.data tr.revealed.network-category-documents, .data-grid.filter-documents table.data tr.revealed.network-category-documents, +.data-grid.filter-all table.data tr.revealed.network-category-stylesheets, .data-grid.filter-stylesheets table.data tr.revealed.network-category-stylesheets, +.data-grid.filter-all table.data tr.revealed.network-category-images, .data-grid.filter-images table.data tr.revealed.network-category-images, +.data-grid.filter-all table.data tr.revealed.network-category-scripts, .data-grid.filter-scripts table.data tr.revealed.network-category-scripts, +.data-grid.filter-all table.data tr.revealed.network-category-xhr, .data-grid.filter-xhr table.data tr.revealed.network-category-xhr, +.data-grid.filter-all table.data tr.revealed.network-category-fonts, .data-grid.filter-fonts table.data tr.revealed.network-category-fonts, +.data-grid.filter-all table.data tr.revealed.network-category-websockets, .data-grid.filter-websockets table.data tr.revealed.network-category-websockets, +.data-grid.filter-all table.data tr.revealed.network-category-other, .data-grid.filter-other table.data tr.revealed.network-category-other { + display: table-row; +} + +/* Summary */ + +.network-summary-bar { + background-color: rgb(101, 111, 130); + color: white; + height: 20px; + font-size: 11px; + font-weight: bold; + padding-top: 1px; + padding-left: 10px; + z-index: 2000; + white-space: pre; +} + +.network-summary-bar-bottom { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding-top: 3px; +} + +.data-grid td .network-summary-bar { + white-space: pre; +} + +.network.panel .data-grid td.network-summary { + padding: 0; +} + +/* Viewer */ + +.network.panel.viewing-resource .data-grid td, +.network.panel.viewing-resource .data-grid th { + border-right: none; +} + +.network.panel.viewing-resource .data-grid th.corner { + display: none; +} + +#network-container { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + border-right: 0 none transparent; + overflow-y: auto; + overflow-x: hidden; +} + +.network.panel.viewing-resource #network-container { + border-right: 1px solid rgb(163, 163, 163); +} + +#network-views { + position: absolute; + background: rgb(203, 203, 203); + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.network.panel .data-grid.full-grid-mode .viewer-column { + display: none; +} + +.network.panel .data-grid.brief-grid-mode .viewer-column, +.network.panel .data-grid.brief-grid-mode .method-column, +.network.panel .data-grid.brief-grid-mode .status-column, +.network.panel .data-grid.brief-grid-mode .type-column, +.network.panel .data-grid.brief-grid-mode .size-column, +.network.panel .data-grid.brief-grid-mode .time-column { + display: none; +} + +.network.panel.viewing-resource .network-timeline-grid { + display: none; +} + +.network.panel .data-grid.viewing-resource-mode .method-column, +.network.panel .data-grid.viewing-resource-mode .status-column, +.network.panel .data-grid.viewing-resource-mode .type-column, +.network.panel .data-grid.viewing-resource-mode .size-column, +.network.panel .data-grid.viewing-resource-mode .time-column, +.network.panel .data-grid.viewing-resource-mode .timeline-column { + display: none; +} + +.network.panel .network-sidebar { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +.network.panel:not(.viewing-resource) .sidebar-resizer-vertical { + display: none; +} + +.network.panel.viewing-resource .data-grid-resizer { + display: none; +} + +.network.panel .scope-bar { + height: 23px; + padding-top: 5px; +} + +.network.panel .resource-view.headers-visible .resource-view-content { + top: 23px; +} + +.network.panel:not(.viewing-resource) .data-grid tr.selected { + background-color: transparent; + color: black; +} + +.network.panel .resource-view .tabbed-pane-header { + height: 23px; + padding-top: 3px; +} + +.network.panel.viewing-resource .data-grid .data-container { + padding-right: 0; +} diff --git a/WebCore/inspector/front-end/utilities.js b/WebCore/inspector/front-end/utilities.js index 5e41da6..3aff6bb 100644 --- a/WebCore/inspector/front-end/utilities.js +++ b/WebCore/inspector/front-end/utilities.js @@ -26,19 +26,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -Object.properties = function(obj) -{ - var properties = []; - for (var prop in obj) - properties.push(prop); - return properties; -} - -Object.sortedProperties = function(obj, sortFunc) -{ - return Object.properties(obj).sort(sortFunc); -} - Function.prototype.bind = function(thisObject) { var func = this; @@ -393,6 +380,26 @@ String.prototype.hasSubstring = function(string, caseInsensitive) return this.match(new RegExp(string.escapeForRegExp(), "i")); } +String.prototype.asParsedURL = function() +{ + // RegExp groups: + // 1 - scheme + // 2 - hostname + // 3 - ?port + // 4 - ?path + // 5 - ?fragment + var match = this.match(/^([^:]+):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i); + if (!match) + return null; + var result = {}; + result.scheme = match[1].toLowerCase(); + result.host = match[2]; + result.port = match[3]; + result.path = match[4] || "/"; + result.fragment = match[5]; + return result; +} + String.prototype.escapeCharacters = function(chars) { var foundChar = false; @@ -990,6 +997,6 @@ function offerFileForDownload(contents) var builder = new BlobBuilder(); builder.append(contents); var blob = builder.getBlob("application/octet-stream"); - var url = window.createBlobURL(blob); + var url = window.createObjectURL(blob); window.open(url); } |