diff options
Diffstat (limited to 'WebCore/inspector')
109 files changed, 6905 insertions, 4675 deletions
diff --git a/WebCore/inspector/ConsoleMessage.cpp b/WebCore/inspector/ConsoleMessage.cpp index 5539e9d..934e2e9 100644 --- a/WebCore/inspector/ConsoleMessage.cpp +++ b/WebCore/inspector/ConsoleMessage.cpp @@ -31,9 +31,12 @@ #include "config.h" #include "ConsoleMessage.h" +#include "InjectedScript.h" +#include "InjectedScriptHost.h" #include "InspectorFrontend.h" #include "ScriptCallStack.h" #include "ScriptObject.h" +#include "SerializedScriptValue.h" namespace WebCore { @@ -80,7 +83,7 @@ ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, S } #if ENABLE(INSPECTOR) -void ConsoleMessage::addToConsole(InspectorFrontend* frontend) +void ConsoleMessage::addToFrontend(InspectorFrontend* frontend, InjectedScriptHost* injectedScriptHost) { ScriptObject jsonObj = frontend->newScriptObject(); jsonObj.set("source", static_cast<int>(m_source)); @@ -90,7 +93,15 @@ void ConsoleMessage::addToConsole(InspectorFrontend* frontend) jsonObj.set("url", m_url); jsonObj.set("groupLevel", static_cast<int>(m_groupLevel)); jsonObj.set("repeatCount", static_cast<int>(m_repeatCount)); - frontend->addConsoleMessage(jsonObj, m_frames, m_scriptState, m_arguments, m_message); + Vector<RefPtr<SerializedScriptValue> > arguments; + if (!m_arguments.isEmpty()) { + InjectedScript injectedScript = injectedScriptHost->injectedScriptFor(m_scriptState.get()); + for (unsigned i = 0; i < m_arguments.size(); ++i) { + RefPtr<SerializedScriptValue> serializedValue = injectedScript.wrapForConsole(m_arguments[i]); + arguments.append(serializedValue); + } + } + frontend->addConsoleMessage(jsonObj, m_frames, arguments, m_message); } void ConsoleMessage::updateRepeatCountInConsole(InspectorFrontend* frontend) diff --git a/WebCore/inspector/ConsoleMessage.h b/WebCore/inspector/ConsoleMessage.h index e9ae130..77a010c 100644 --- a/WebCore/inspector/ConsoleMessage.h +++ b/WebCore/inspector/ConsoleMessage.h @@ -38,40 +38,41 @@ #include <wtf/Vector.h> namespace WebCore { - class InspectorFrontend; - class ScriptCallStack; - class ScriptString; +class InjectedScriptHost; +class InspectorFrontend; +class ScriptCallStack; +class ScriptString; - class ConsoleMessage : public Noncopyable { - public: - ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& m, unsigned li, const String& u, unsigned g); - ConsoleMessage(MessageSource, MessageType, MessageLevel, ScriptCallStack*, unsigned g, bool storeTrace = false); +class ConsoleMessage : public Noncopyable { +public: + ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& m, unsigned li, const String& u, unsigned g); + ConsoleMessage(MessageSource, MessageType, MessageLevel, ScriptCallStack*, unsigned g, bool storeTrace = false); #if ENABLE(INSPECTOR) - void addToConsole(InspectorFrontend* frontend); - void updateRepeatCountInConsole(InspectorFrontend* frontend); + void addToFrontend(InspectorFrontend*, InjectedScriptHost*); + void updateRepeatCountInConsole(InspectorFrontend* frontend); #endif - void incrementCount() { ++m_repeatCount; }; - bool isEqual(ScriptState*, ConsoleMessage* msg) const; + void incrementCount() { ++m_repeatCount; } + bool isEqual(ScriptState*, ConsoleMessage* msg) const; - MessageSource source() const { return m_source; } - const String& message() const { return m_message; } + MessageSource source() const { return m_source; } + const String& message() const { return m_message; } - private: - MessageSource m_source; - MessageType m_type; - MessageLevel m_level; - String m_message; +private: + MessageSource m_source; + MessageType m_type; + MessageLevel m_level; + String m_message; #if ENABLE(INSPECTOR) - Vector<ScriptValue> m_arguments; - ScriptState* m_scriptState; + Vector<ScriptValue> m_arguments; + ScriptStateProtectedPtr m_scriptState; #endif - Vector<ScriptString> m_frames; - unsigned m_line; - String m_url; - unsigned m_groupLevel; - unsigned m_repeatCount; - }; + Vector<ScriptString> m_frames; + unsigned m_line; + String m_url; + unsigned m_groupLevel; + unsigned m_repeatCount; +}; } // namespace WebCore diff --git a/WebCore/inspector/InjectedScript.cpp b/WebCore/inspector/InjectedScript.cpp index 42a2856..2e35e4b 100644 --- a/WebCore/inspector/InjectedScript.cpp +++ b/WebCore/inspector/InjectedScript.cpp @@ -47,6 +47,11 @@ InjectedScript::InjectedScript(ScriptObject injectedScriptObject) void InjectedScript::dispatch(long callId, const String& methodName, const String& arguments, bool async, RefPtr<SerializedScriptValue>* result, bool* hadException) { ASSERT(!hasNoValue()); + if (!canAccessInspectedWindow()) { + *hadException = true; + return; + } + ScriptFunctionCall function(m_injectedScriptObject, "dispatch"); function.appendArgument(methodName); function.appendArgument(arguments); @@ -71,10 +76,13 @@ PassRefPtr<SerializedScriptValue> InjectedScript::callFrames() PassRefPtr<SerializedScriptValue> InjectedScript::wrapForConsole(ScriptValue value) { ASSERT(!hasNoValue()); - ScriptFunctionCall wrapFunction(m_injectedScriptObject, "wrapObject"); + ScriptFunctionCall wrapFunction(m_injectedScriptObject, "wrapObjectForConsole"); wrapFunction.appendArgument(value); - wrapFunction.appendArgument("console"); - ScriptValue r = wrapFunction.call(); + wrapFunction.appendArgument(canAccessInspectedWindow()); + bool hadException = false; + ScriptValue r = wrapFunction.call(hadException); + if (hadException) + return SerializedScriptValue::create("<exception>"); return r.serialize(m_injectedScriptObject.scriptState()); } @@ -85,6 +93,10 @@ void InjectedScript::releaseWrapperObjectGroup(const String& objectGroup) releaseFunction.appendArgument(objectGroup); releaseFunction.call(); } +bool InjectedScript::canAccessInspectedWindow() +{ + return InjectedScriptHost::canAccessInspectedWindow(m_injectedScriptObject.scriptState()); +} } // namespace WebCore diff --git a/WebCore/inspector/InjectedScript.h b/WebCore/inspector/InjectedScript.h index db40f80..1e9b787 100644 --- a/WebCore/inspector/InjectedScript.h +++ b/WebCore/inspector/InjectedScript.h @@ -58,6 +58,7 @@ public: private: friend InjectedScript InjectedScriptHost::injectedScriptFor(ScriptState*); explicit InjectedScript(ScriptObject); + bool canAccessInspectedWindow(); ScriptObject m_injectedScriptObject; }; diff --git a/WebCore/inspector/InjectedScriptHost.cpp b/WebCore/inspector/InjectedScriptHost.cpp index 62db4b4..c58073d 100644 --- a/WebCore/inspector/InjectedScriptHost.cpp +++ b/WebCore/inspector/InjectedScriptHost.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> - * Copyright (C) 2009 Google Inc. All rights reserved. + * 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 @@ -46,10 +46,8 @@ #include "InspectorResource.h" #include "Pasteboard.h" -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) -#include "JavaScriptCallFrame.h" -#include "JavaScriptDebugServer.h" -using namespace JSC; +#if ENABLE(JAVASCRIPT_DEBUGGER) +#include "ScriptDebugServer.h" #endif #if ENABLE(DATABASE) @@ -72,6 +70,7 @@ namespace WebCore { InjectedScriptHost::InjectedScriptHost(InspectorController* inspectorController) : m_inspectorController(inspectorController) , m_nextInjectedScriptId(1) + , m_lastWorkerId(1 << 31) // Distinguish ids of fake workers from real ones, to minimize the chances they overlap. { } @@ -130,13 +129,6 @@ long InjectedScriptHost::pushNodeByPathToFrontend(const String& path) return domAgent->pushNodePathToFrontend(node); } -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) -JavaScriptCallFrame* InjectedScriptHost::currentCallFrame() const -{ - return JavaScriptDebugServer::shared().currentCallFrame(); -} -#endif - #if ENABLE(DATABASE) Database* InjectedScriptHost::databaseForId(long databaseId) { @@ -203,6 +195,31 @@ InspectorFrontend* InjectedScriptHost::inspectorFrontend() return m_inspectorController->m_frontend.get(); } +pair<long, ScriptObject> InjectedScriptHost::injectScript(const String& source, ScriptState* scriptState) +{ + long id = m_nextInjectedScriptId++; + return std::make_pair(id, createInjectedScript(source, scriptState, id)); +} + +#if ENABLE(WORKERS) +long InjectedScriptHost::nextWorkerId() +{ + return ++m_lastWorkerId; +} + +void InjectedScriptHost::didCreateWorker(long id, const String& url, bool isSharedWorker) +{ + if (m_inspectorController) + m_inspectorController->didCreateWorker(id, url, isSharedWorker); +} + +void InjectedScriptHost::didDestroyWorker(long id) +{ + if (m_inspectorController) + m_inspectorController->didDestroyWorker(id); +} +#endif // ENABLE(WORKERS) + } // namespace WebCore #endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InjectedScriptHost.h b/WebCore/inspector/InjectedScriptHost.h index fb56115..beffc9d 100644 --- a/WebCore/inspector/InjectedScriptHost.h +++ b/WebCore/inspector/InjectedScriptHost.h @@ -44,7 +44,6 @@ class Database; class InjectedScript; class InspectorDOMAgent; class InspectorFrontend; -class JavaScriptCallFrame; class Node; class SerializedScriptValue; class Storage; @@ -73,9 +72,6 @@ public: void addNodesToSearchResult(const String& nodeIds); long pushNodeByPathToFrontend(const String& path); -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - JavaScriptCallFrame* currentCallFrame() const; -#endif #if ENABLE(DATABASE) Database* databaseForId(long databaseId); void selectDatabase(Database* database); @@ -83,21 +79,31 @@ public: #if ENABLE(DOM_STORAGE) void selectDOMStorage(Storage* storage); #endif +#if ENABLE(WORKERS) + long nextWorkerId(); + void didCreateWorker(long id, const String& url, bool isSharedWorker); + void didDestroyWorker(long id); +#endif void reportDidDispatchOnInjectedScript(long callId, SerializedScriptValue* result, bool isException); + pair<long, ScriptObject> injectScript(const String& source, ScriptState*); InjectedScript injectedScriptFor(ScriptState*); InjectedScript injectedScriptForId(long); void discardInjectedScripts(); void releaseWrapperObjectGroup(long injectedScriptId, const String& objectGroup); + static bool canAccessInspectedWindow(ScriptState*); + private: InjectedScriptHost(InspectorController* inspectorController); InspectorDOMAgent* inspectorDOMAgent(); InspectorFrontend* inspectorFrontend(); + ScriptObject createInjectedScript(const String& source, ScriptState* scriptState, long id); InspectorController* m_inspectorController; String m_injectedScriptSource; long m_nextInjectedScriptId; + long m_lastWorkerId; typedef HashMap<long, InjectedScript> IdToInjectedScriptMap; IdToInjectedScriptMap m_idToInjectedScript; }; diff --git a/WebCore/inspector/InjectedScriptHost.idl b/WebCore/inspector/InjectedScriptHost.idl index bb57c3a..e3cd976 100644 --- a/WebCore/inspector/InjectedScriptHost.idl +++ b/WebCore/inspector/InjectedScriptHost.idl @@ -1,7 +1,7 @@ /* * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> - * Copyright (C) 2009 Google Inc. All rights reserved. + * 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 @@ -41,7 +41,7 @@ module core { void addNodesToSearchResult(in DOMString nodeIds); long pushNodeByPathToFrontend(in DOMString path); -#if defined(ENABLE_JAVASCRIPT_DEBUGGER) && ENABLE_JAVASCRIPT_DEBUGGER && !(defined(V8_BINDING) && V8_BINDING) +#if defined(ENABLE_JAVASCRIPT_DEBUGGER) && ENABLE_JAVASCRIPT_DEBUGGER [Custom] DOMObject currentCallFrame(); [Custom] boolean isActivation(in DOMObject object); #endif @@ -55,6 +55,11 @@ module core { [Custom] void selectDOMStorage(in DOMObject storage); #endif +#if defined(ENABLE_WORKERS) && ENABLE_WORKERS + void didCreateWorker(in long id, in DOMString url, in boolean isFakeWorker); + void didDestroyWorker(in long id); + long nextWorkerId(); +#endif [Custom] void reportDidDispatchOnInjectedScript(in long callId, in DOMObject result, in boolean isException); }; } diff --git a/WebCore/inspector/InspectorBackend.cpp b/WebCore/inspector/InspectorBackend.cpp index c43be63..aed8f2b 100644 --- a/WebCore/inspector/InspectorBackend.cpp +++ b/WebCore/inspector/InspectorBackend.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> * * Redistribution and use in source and binary forms, with or without @@ -47,18 +47,18 @@ #include "InspectorDOMAgent.h" #include "InspectorFrontend.h" #include "InspectorResource.h" +#include "Page.h" #include "Pasteboard.h" #include "ScriptArray.h" +#include "ScriptBreakpoint.h" #include "SerializedScriptValue.h" #if ENABLE(DOM_STORAGE) #include "Storage.h" #endif -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) -#include "JavaScriptCallFrame.h" -#include "JavaScriptDebugServer.h" -using namespace JSC; +#if ENABLE(JAVASCRIPT_DEBUGGER) +#include "ScriptDebugServer.h" #endif #include "markup.h" @@ -82,7 +82,7 @@ InspectorBackend::~InspectorBackend() void InspectorBackend::saveFrontendSettings(const String& settings) { if (m_inspectorController) - m_inspectorController->setSetting(InspectorController::FrontendSettingsSettingName, settings); + m_inspectorController->setSetting(InspectorController::frontendSettingsSettingName(), settings); } void InspectorBackend::storeLastActivePanel(const String& panelName) @@ -91,24 +91,16 @@ void InspectorBackend::storeLastActivePanel(const String& panelName) m_inspectorController->storeLastActivePanel(panelName); } -void InspectorBackend::toggleNodeSearch() -{ - if (m_inspectorController) - m_inspectorController->toggleSearchForNodeInPage(); -} - -bool InspectorBackend::searchingForNode() +void InspectorBackend::enableSearchingForNode() { if (m_inspectorController) - return m_inspectorController->searchingForNodeInPage(); - return false; + m_inspectorController->setSearchingForNode(true); } -bool InspectorBackend::resourceTrackingEnabled() const +void InspectorBackend::disableSearchingForNode() { if (m_inspectorController) - return m_inspectorController->resourceTrackingEnabled(); - return false; + m_inspectorController->setSearchingForNode(false); } void InspectorBackend::enableResourceTracking(bool always) @@ -136,6 +128,12 @@ void InspectorBackend::getResourceContent(long callId, unsigned long identifier) frontend->didGetResourceContent(callId, ""); } +void InspectorBackend::reloadPage() +{ + if (m_inspectorController) + m_inspectorController->m_inspectedPage->mainFrame()->redirectScheduler()->scheduleRefresh(true); +} + void InspectorBackend::startTimelineProfiler() { if (m_inspectorController) @@ -148,13 +146,7 @@ void InspectorBackend::stopTimelineProfiler() m_inspectorController->stopTimelineProfiler(); } -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) -bool InspectorBackend::debuggerEnabled() const -{ - if (m_inspectorController) - return m_inspectorController->debuggerEnabled(); - return false; -} +#if ENABLE(JAVASCRIPT_DEBUGGER) void InspectorBackend::enableDebugger(bool always) { @@ -168,27 +160,31 @@ void InspectorBackend::disableDebugger(bool always) m_inspectorController->disableDebugger(always); } -void InspectorBackend::addBreakpoint(const String& sourceID, unsigned lineNumber, const String& condition) +void InspectorBackend::setBreakpoint(const String& sourceID, unsigned lineNumber, bool enabled, const String& condition) { - intptr_t sourceIDValue = sourceID.toIntPtr(); - JavaScriptDebugServer::shared().addBreakpoint(sourceIDValue, lineNumber, condition); + if (m_inspectorController) + m_inspectorController->setBreakpoint(sourceID, lineNumber, enabled, condition); } -void InspectorBackend::updateBreakpoint(const String& sourceID, unsigned lineNumber, const String& condition) +void InspectorBackend::removeBreakpoint(const String& sourceID, unsigned lineNumber) { - intptr_t sourceIDValue = sourceID.toIntPtr(); - JavaScriptDebugServer::shared().updateBreakpoint(sourceIDValue, lineNumber, condition); + if (m_inspectorController) + m_inspectorController->removeBreakpoint(sourceID, lineNumber); } -void InspectorBackend::removeBreakpoint(const String& sourceID, unsigned lineNumber) +void InspectorBackend::activateBreakpoints() { - intptr_t sourceIDValue = sourceID.toIntPtr(); - JavaScriptDebugServer::shared().removeBreakpoint(sourceIDValue, lineNumber); + ScriptDebugServer::shared().setBreakpointsActivated(true); +} + +void InspectorBackend::deactivateBreakpoints() +{ + ScriptDebugServer::shared().setBreakpointsActivated(false); } void InspectorBackend::pauseInDebugger() { - JavaScriptDebugServer::shared().pauseProgram(); + ScriptDebugServer::shared().pauseProgram(); } void InspectorBackend::resumeDebugger() @@ -199,42 +195,29 @@ void InspectorBackend::resumeDebugger() void InspectorBackend::stepOverStatementInDebugger() { - JavaScriptDebugServer::shared().stepOverStatement(); + ScriptDebugServer::shared().stepOverStatement(); } void InspectorBackend::stepIntoStatementInDebugger() { - JavaScriptDebugServer::shared().stepIntoStatement(); + ScriptDebugServer::shared().stepIntoStatement(); } void InspectorBackend::stepOutOfFunctionInDebugger() { - JavaScriptDebugServer::shared().stepOutOfFunction(); -} - -long InspectorBackend::pauseOnExceptionsState() -{ - return JavaScriptDebugServer::shared().pauseOnExceptionsState(); + ScriptDebugServer::shared().stepOutOfFunction(); } void InspectorBackend::setPauseOnExceptionsState(long pauseState) { - JavaScriptDebugServer::shared().setPauseOnExceptionsState(static_cast<JavaScriptDebugServer::PauseOnExceptionsState>(pauseState)); + ScriptDebugServer::shared().setPauseOnExceptionsState(static_cast<ScriptDebugServer::PauseOnExceptionsState>(pauseState)); + if (InspectorFrontend* frontend = inspectorFrontend()) + frontend->updatePauseOnExceptionsState(ScriptDebugServer::shared().pauseOnExceptionsState()); } -JavaScriptCallFrame* InspectorBackend::currentCallFrame() const -{ - return JavaScriptDebugServer::shared().currentCallFrame(); -} #endif #if ENABLE(JAVASCRIPT_DEBUGGER) -bool InspectorBackend::profilerEnabled() -{ - if (m_inspectorController) - return m_inspectorController->profilerEnabled(); - return false; -} void InspectorBackend::enableProfiler(bool always) { @@ -343,34 +326,77 @@ void InspectorBackend::copyNode(long nodeId) String markup = createMarkup(node); Pasteboard::generalPasteboard()->writePlainText(markup); } - + void InspectorBackend::removeNode(long callId, long nodeId) { - InspectorFrontend* frontend = inspectorFrontend(); - if (!frontend) - return; + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->removeNode(callId, nodeId); +} - Node* node = nodeForId(nodeId); - if (!node) { - // Use -1 to denote an error condition. - frontend->didRemoveNode(callId, -1); - return; - } +void InspectorBackend::changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->changeTagName(callId, nodeId, tagName, expanded); +} - Node* parentNode = node->parentNode(); - if (!parentNode) { - frontend->didRemoveNode(callId, -1); - return; - } +void InspectorBackend::getStyles(long callId, long nodeId, bool authorOnly) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->getStyles(callId, nodeId, authorOnly); +} - ExceptionCode code; - parentNode->removeChild(node, code); - if (code) { - frontend->didRemoveNode(callId, -1); - return; - } +void InspectorBackend::getAllStyles(long callId) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->getAllStyles(callId); +} + +void InspectorBackend::getInlineStyle(long callId, long nodeId) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->getInlineStyle(callId, nodeId); +} + +void InspectorBackend::getComputedStyle(long callId, long nodeId) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->getComputedStyle(callId, nodeId); +} + +void InspectorBackend::applyStyleText(long callId, long styleId, const String& styleText, const String& propertyName) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->applyStyleText(callId, styleId, styleText, propertyName); +} + +void InspectorBackend::setStyleText(long callId, long styleId, const String& cssText) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->setStyleText(callId, styleId, cssText); +} - frontend->didRemoveNode(callId, nodeId); +void InspectorBackend::setStyleProperty(long callId, long styleId, const String& name, const String& value) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->setStyleProperty(callId, styleId, name, value); +} + +void InspectorBackend::toggleStyleEnabled(long callId, long styleId, const String& propertyName, bool disabled) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->toggleStyleEnabled(callId, styleId, propertyName, disabled); +} + +void InspectorBackend::setRuleSelector(long callId, long ruleId, const String& selector, long selectedNodeId) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->setRuleSelector(callId, ruleId, selector, selectedNodeId); +} + +void InspectorBackend::addRule(long callId, const String& selector, long selectedNodeId) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->addRule(callId, selector, selectedNodeId); } void InspectorBackend::highlightDOMNode(long nodeId) @@ -470,6 +496,18 @@ Node* InspectorBackend::nodeForId(long nodeId) return 0; } +void InspectorBackend::addScriptToEvaluateOnLoad(const String& source) +{ + if (m_inspectorController) + m_inspectorController->addScriptToEvaluateOnLoad(source); +} + +void InspectorBackend::removeAllScriptsToEvaluateOnLoad() +{ + if (m_inspectorController) + m_inspectorController->removeAllScriptsToEvaluateOnLoad(); +} + } // namespace WebCore #endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorBackend.h b/WebCore/inspector/InspectorBackend.h index 0df13f5..7331843 100644 --- a/WebCore/inspector/InspectorBackend.h +++ b/WebCore/inspector/InspectorBackend.h @@ -6,13 +6,13 @@ * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 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. + * 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. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -41,7 +41,6 @@ class CachedResource; class Database; class InspectorDOMAgent; class InspectorFrontend; -class JavaScriptCallFrame; class Node; class Storage; @@ -62,40 +61,35 @@ public: void storeLastActivePanel(const String& panelName); - void toggleNodeSearch(); - bool searchingForNode(); + void enableSearchingForNode(); + void disableSearchingForNode(); void enableResourceTracking(bool always); void disableResourceTracking(bool always); - bool resourceTrackingEnabled() const; void getResourceContent(long callId, unsigned long identifier); + void reloadPage(); void startTimelineProfiler(); void stopTimelineProfiler(); -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - bool debuggerEnabled() const; +#if ENABLE(JAVASCRIPT_DEBUGGER) void enableDebugger(bool always); void disableDebugger(bool always); - void addBreakpoint(const String& sourceID, unsigned lineNumber, const String& condition); - void updateBreakpoint(const String& sourceID, unsigned lineNumber, const String& condition); + void setBreakpoint(const String& sourceID, unsigned lineNumber, bool enabled, const String& condition); void removeBreakpoint(const String& sourceID, unsigned lineNumber); + void activateBreakpoints(); + void deactivateBreakpoints(); void pauseInDebugger(); void resumeDebugger(); - long pauseOnExceptionsState(); void setPauseOnExceptionsState(long pauseState); void stepOverStatementInDebugger(); void stepIntoStatementInDebugger(); void stepOutOfFunctionInDebugger(); - JavaScriptCallFrame* currentCallFrame() const; -#endif -#if ENABLE(JAVASCRIPT_DEBUGGER) - bool profilerEnabled(); void enableProfiler(bool always); void disableProfiler(bool always); @@ -108,6 +102,9 @@ public: void setInjectedScriptSource(const String& source); void dispatchOnInjectedScript(long callId, long injectedScriptId, const String& methodName, const String& arguments, bool async); + void addScriptToEvaluateOnLoad(const String& source); + void removeAllScriptsToEvaluateOnLoad(); + void getChildNodes(long callId, long nodeId); void setAttribute(long callId, long elementId, const String& name, const String& value); void removeAttribute(long callId, long elementId, const String& name); @@ -115,6 +112,19 @@ public: void getEventListenersForNode(long callId, long nodeId); void copyNode(long nodeId); void removeNode(long callId, long nodeId); + void changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded); + + void getStyles(long callId, long nodeId, bool authOnly); + void getAllStyles(long callId); + void getInlineStyle(long callId, long nodeId); + void getComputedStyle(long callId, long nodeId); + void applyStyleText(long callId, long styleId, const String& styleText, const String& propertyName); + void setStyleText(long callId, long styleId, const String& cssText); + void setStyleProperty(long callId, long styleId, const String& name, const String& value); + void toggleStyleEnabled(long callId, long styleId, const String& propertyName, bool disabled); + void setRuleSelector(long callId, long ruleId, const String& selector, long selectedNodeId); + void addRule(long callId, const String& selector, long selectedNodeId); + void highlightDOMNode(long nodeId); void hideDOMNodeHighlight(); diff --git a/WebCore/inspector/InspectorBackend.idl b/WebCore/inspector/InspectorBackend.idl index 9faae76..cf0fe01 100644 --- a/WebCore/inspector/InspectorBackend.idl +++ b/WebCore/inspector/InspectorBackend.idl @@ -36,25 +36,25 @@ module core { void saveFrontendSettings(in DOMString settings); - void toggleNodeSearch(); - boolean searchingForNode(); + void enableSearchingForNode(); + void disableSearchingForNode(); - boolean resourceTrackingEnabled(); void enableResourceTracking(in boolean always); void disableResourceTracking(in boolean always); void getResourceContent(in long callId, in unsigned long identifier); + void reloadPage(); void startTimelineProfiler(); void stopTimelineProfiler(); -#if defined(ENABLE_JAVASCRIPT_DEBUGGER) && ENABLE_JAVASCRIPT_DEBUGGER && !(defined(V8_BINDING) && V8_BINDING) - boolean debuggerEnabled(); +#if defined(ENABLE_JAVASCRIPT_DEBUGGER) && ENABLE_JAVASCRIPT_DEBUGGER void enableDebugger(in boolean always); void disableDebugger(in boolean always); - void addBreakpoint(in DOMString sourceID, in unsigned long lineNumber, in DOMString condition); - void updateBreakpoint(in DOMString sourceID, in unsigned long lineNumber, in DOMString condition); + void setBreakpoint(in DOMString sourceID, in unsigned long lineNumber, in boolean enabled, in DOMString condition); void removeBreakpoint(in DOMString sourceID, in unsigned long lineNumber); + void activateBreakpoints(); + void deactivateBreakpoints(); void pauseInDebugger(); void resumeDebugger(); @@ -63,11 +63,8 @@ module core { void stepIntoStatementInDebugger(); void stepOutOfFunctionInDebugger(); - long pauseOnExceptionsState(); void setPauseOnExceptionsState(in long pauseOnExceptionsState); -#endif -#if defined(ENABLE_JAVASCRIPT_DEBUGGER) && ENABLE_JAVASCRIPT_DEBUGGER - boolean profilerEnabled(); + void enableProfiler(in boolean always); void disableProfiler(in boolean always); @@ -80,6 +77,9 @@ module core { void setInjectedScriptSource(in DOMString scriptSource); void dispatchOnInjectedScript(in long callId, in long injectedScriptId, in DOMString methodName, in DOMString arguments, in boolean async); + void addScriptToEvaluateOnLoad(in DOMString scriptSource); + void removeAllScriptsToEvaluateOnLoad(); + void getChildNodes(in long callId, in long nodeId); void setAttribute(in long callId, in long elementId, in DOMString name, in DOMString value); void removeAttribute(in long callId, in long elementId, in DOMString name); @@ -87,9 +87,21 @@ module core { void getEventListenersForNode(in long callId, in long nodeId); void copyNode(in long nodeId); void removeNode(in long callId, in long nodeId); + void changeTagName(in long callId, in long nodeId, in DOMString newTagName, in boolean expanded); void highlightDOMNode(in long nodeId); void hideDOMNodeHighlight(); + void getStyles(in long callId, in long nodeId, in boolean authOnly); + void getAllStyles(in long callId); + void getInlineStyle(in long callId, in long nodeId); + void getComputedStyle(in long callId, in long nodeId); + void applyStyleText(in long callId, in long styleId, in DOMString styleText, in DOMString propertyName); + void setStyleText(in long callId, in long styleId, in DOMString styleText); + void setStyleProperty(in long callId, in long styleId, in DOMString name, in DOMString value); + void toggleStyleEnabled(in long callId, in long styleId, in DOMString propertyName, in boolean disabled); + void setRuleSelector(in long callId, in long ruleId, in DOMString selector, in long selectedNodeId); + void addRule(in long callId, in DOMString selector, in long selectedNodeId); + void getCookies(in long callId); void deleteCookie(in DOMString cookieName, in DOMString domain); diff --git a/WebCore/inspector/InspectorClient.h b/WebCore/inspector/InspectorClient.h index e448cd2..841c15e 100644 --- a/WebCore/inspector/InspectorClient.h +++ b/WebCore/inspector/InspectorClient.h @@ -30,6 +30,7 @@ namespace WebCore { +class InspectorController; class Node; class Page; class String; @@ -40,29 +41,13 @@ public: virtual void inspectorDestroyed() = 0; - virtual Page* createPage() = 0; - - virtual String localizedStringsURL() = 0; - - virtual String hiddenPanels() = 0; - - virtual void showWindow() = 0; - virtual void closeWindow() = 0; - - virtual void attachWindow() = 0; - virtual void detachWindow() = 0; - - virtual void setAttachedWindowHeight(unsigned height) = 0; + virtual void openInspectorFrontend(InspectorController*) = 0; virtual void highlight(Node*) = 0; virtual void hideHighlight() = 0; - virtual void inspectedURLChanged(const String& newURL) = 0; - virtual void populateSetting(const String& key, String* value) = 0; virtual void storeSetting(const String& key, const String& value) = 0; - - virtual void inspectorWindowObjectCleared() = 0; }; } // namespace WebCore diff --git a/WebCore/inspector/InspectorController.cpp b/WebCore/inspector/InspectorController.cpp index b487ad8..c54ee5b 100644 --- a/WebCore/inspector/InspectorController.cpp +++ b/WebCore/inspector/InspectorController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,6 @@ #if ENABLE(INSPECTOR) -#include "CString.h" #include "CachedResource.h" #include "Chrome.h" #include "Console.h" @@ -57,12 +56,12 @@ #include "InjectedScriptHost.h" #include "InspectorBackend.h" #include "InspectorClient.h" -#include "InspectorDOMAgent.h" +#include "InspectorFrontendClient.h" #include "InspectorDOMStorageResource.h" #include "InspectorDatabaseResource.h" #include "InspectorFrontend.h" -#include "InspectorFrontendHost.h" #include "InspectorResource.h" +#include "InspectorWorkerResource.h" #include "InspectorTimelineAgent.h" #include "Page.h" #include "ProgressTracker.h" @@ -70,18 +69,20 @@ #include "RenderInline.h" #include "ResourceRequest.h" #include "ResourceResponse.h" +#include "ScriptBreakpoint.h" #include "ScriptCallStack.h" -#include "ScriptDebugServer.h" #include "ScriptFunctionCall.h" #include "ScriptObject.h" #include "ScriptProfile.h" #include "ScriptProfiler.h" +#include "ScriptSourceCode.h" #include "ScriptString.h" #include "SecurityOrigin.h" #include "Settings.h" #include "SharedBuffer.h" #include "TextEncoding.h" #include "TextIterator.h" +#include <wtf/text/CString.h> #include <wtf/CurrentTime.h> #include <wtf/ListHashSet.h> #include <wtf/RefCounted.h> @@ -96,17 +97,17 @@ #include "StorageArea.h" #endif -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) -#include "JSJavaScriptCallFrame.h" -#include "JavaScriptCallFrame.h" -#include "JavaScriptDebugServer.h" -#include "JavaScriptProfile.h" - +#if ENABLE(JAVASCRIPT_DEBUGGER) +#include "ScriptDebugServer.h" +#if USE(JSC) #include <runtime/JSLock.h> #include <runtime/UString.h> - -using namespace JSC; +#include "JSScriptProfile.h" +#else +#include "V8ScriptProfile.h" +#endif #endif + using namespace std; namespace WebCore { @@ -118,7 +119,18 @@ static const char* const debuggerEnabledSettingName = "debuggerEnabled"; static const char* const profilerEnabledSettingName = "profilerEnabled"; static const char* const inspectorAttachedHeightName = "inspectorAttachedHeight"; static const char* const lastActivePanelSettingName = "lastActivePanel"; -const char* const InspectorController::FrontendSettingsSettingName = "frontendSettings"; + +const String& InspectorController::frontendSettingsSettingName() +{ + DEFINE_STATIC_LOCAL(String, settingName, ("frontendSettings")); + return settingName; +} + +const String& InspectorController::inspectorStartsAttachedSettingName() +{ + DEFINE_STATIC_LOCAL(String, settingName, ("inspectorStartsAttached")); + return settingName; +} static const unsigned defaultAttachedHeight = 300; static const float minimumAttachedHeight = 250.0f; @@ -131,10 +143,8 @@ static unsigned s_inspectorControllerCount; InspectorController::InspectorController(Page* page, InspectorClient* client) : m_inspectedPage(page) , m_client(client) - , m_page(0) + , m_openingFrontend(false) , m_expiredConsoleMessageCount(0) - , m_frontendScriptState(0) - , m_windowVisible(false) , m_showAfterVisible(CurrentPanel) , m_groupLevel(0) , m_searchingForNode(false) @@ -142,9 +152,8 @@ InspectorController::InspectorController(Page* page, InspectorClient* client) , m_resourceTrackingEnabled(false) , m_resourceTrackingSettingsLoaded(false) , m_inspectorBackend(InspectorBackend::create(this)) - , m_inspectorFrontendHost(InspectorFrontendHost::create(this, client)) , m_injectedScriptHost(InjectedScriptHost::create(this)) -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) +#if ENABLE(JAVASCRIPT_DEBUGGER) , m_debuggerEnabled(false) , m_attachDebuggerWhenShown(false) #endif @@ -165,9 +174,7 @@ InspectorController::~InspectorController() { // These should have been cleared in inspectedPageDestroyed(). ASSERT(!m_client); - ASSERT(!m_frontendScriptState); ASSERT(!m_inspectedPage); - ASSERT(!m_page || (m_page && !m_page->parentInspectorController())); deleteAllValues(m_frameResources); deleteAllValues(m_consoleMessages); @@ -178,18 +185,14 @@ InspectorController::~InspectorController() releaseDOMAgent(); m_inspectorBackend->disconnectController(); - m_inspectorFrontendHost->disconnectController(); m_injectedScriptHost->disconnectController(); } void InspectorController::inspectedPageDestroyed() { - close(); + if (m_frontend) + m_frontend->inspectedPageDestroyed(); - if (m_frontendScriptState) { - ScriptGlobalObject::remove(m_frontendScriptState, "InspectorBackend"); - ScriptGlobalObject::remove(m_frontendScriptState, "InspectorFrontendHost"); - } ASSERT(m_inspectedPage); m_inspectedPage = 0; @@ -222,20 +225,9 @@ void InspectorController::setSetting(const String& key, const String& value) m_client->storeSetting(key, value); } -// Trying to inspect something in a frame with JavaScript disabled would later lead to -// crashes trying to create JavaScript wrappers. Some day we could fix this issue, but -// for now prevent crashes here by never targeting a node in such a frame. -static bool canPassNodeToJavaScript(Node* node) -{ - if (!node) - return false; - Frame* frame = node->document()->frame(); - return frame && frame->script()->canExecuteScripts(); -} - void InspectorController::inspect(Node* node) { - if (!canPassNodeToJavaScript(node) || !enabled()) + if (!enabled()) return; show(); @@ -284,48 +276,7 @@ void InspectorController::hideHighlight() bool InspectorController::windowVisible() { - return m_windowVisible; -} - -void InspectorController::setWindowVisible(bool visible, bool attached) -{ - if (visible == m_windowVisible || !m_frontend) - return; - - m_windowVisible = visible; - - if (m_windowVisible) { - setAttachedWindow(attached); - populateScriptObjects(); - - if (m_showAfterVisible == CurrentPanel) { - String lastActivePanelSetting = setting(lastActivePanelSettingName); - m_showAfterVisible = specialPanelForJSName(lastActivePanelSetting); - } - - if (m_nodeToFocus) - focusNode(); -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - if (m_attachDebuggerWhenShown) - enableDebugger(); -#endif - showPanel(m_showAfterVisible); - } else { -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - // If the window is being closed with the debugger enabled, - // remember this state to re-enable debugger on the next window - // opening. - bool debuggerWasEnabled = m_debuggerEnabled; - disableDebugger(); - if (debuggerWasEnabled) - m_attachDebuggerWhenShown = true; -#endif - if (m_searchingForNode) - toggleSearchForNodeInPage(); - resetScriptObjects(); - stopTimelineProfiler(); - } - m_showAfterVisible = CurrentPanel; + return m_frontend; } void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, ScriptCallStack* callStack) @@ -352,16 +303,16 @@ void InspectorController::addConsoleMessage(ScriptState* scriptState, ConsoleMes if (m_previousMessage && m_previousMessage->isEqual(scriptState, consoleMessage)) { m_previousMessage->incrementCount(); delete consoleMessage; - if (windowVisible()) + if (m_frontend) m_previousMessage->updateRepeatCountInConsole(m_frontend.get()); } else { m_previousMessage = consoleMessage; m_consoleMessages.append(consoleMessage); - if (windowVisible()) - m_previousMessage->addToConsole(m_frontend.get()); + if (m_frontend) + m_previousMessage->addToFrontend(m_frontend.get(), m_injectedScriptHost.get()); } - if (!windowVisible() && m_consoleMessages.size() >= maximumConsoleMessages) { + if (!m_frontend && m_consoleMessages.size() >= maximumConsoleMessages) { m_expiredConsoleMessageCount += expireConsoleMessagesStep; for (size_t i = 0; i < expireConsoleMessagesStep; ++i) delete m_consoleMessages[i]; @@ -406,127 +357,79 @@ void InspectorController::markTimeline(const String& message) timelineAgent()->didMarkTimeline(message); } -static unsigned constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight) -{ - return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio))); -} - -void InspectorController::attachWindow() -{ - if (!enabled()) - return; - - unsigned inspectedPageHeight = m_inspectedPage->mainFrame()->view()->visibleHeight(); - - m_client->attachWindow(); - - String attachedHeight = setting(inspectorAttachedHeightName); - bool success = true; - int height = attachedHeight.toInt(&success); - unsigned preferredHeight = success ? height : defaultAttachedHeight; - - // We need to constrain the window height here in case the user has resized the inspected page's window so that - // the user's preferred height would be too big to display. - m_client->setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight)); -} - -void InspectorController::detachWindow() -{ - if (!enabled()) - return; - m_client->detachWindow(); -} - -void InspectorController::setAttachedWindow(bool attached) -{ - if (!enabled() || !m_frontend) - return; - - m_frontend->setAttachedWindow(attached); -} - -void InspectorController::setAttachedWindowHeight(unsigned height) -{ - if (!enabled()) - return; - - unsigned totalHeight = m_page->mainFrame()->view()->visibleHeight() + m_inspectedPage->mainFrame()->view()->visibleHeight(); - unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight); - - setSetting(inspectorAttachedHeightName, String::number(attachedHeight)); - - m_client->setAttachedWindowHeight(attachedHeight); -} - void InspectorController::storeLastActivePanel(const String& panelName) { setSetting(lastActivePanelSettingName, panelName); } -void InspectorController::toggleSearchForNodeInPage() -{ - if (!enabled()) - return; - - m_searchingForNode = !m_searchingForNode; - if (!m_searchingForNode) - hideHighlight(); -} - void InspectorController::mouseDidMoveOverElement(const HitTestResult& result, unsigned) { if (!enabled() || !m_searchingForNode) return; Node* node = result.innerNode(); + while (node && node->nodeType() == Node::TEXT_NODE) + node = node->parentNode(); if (node) highlight(node); } -void InspectorController::handleMousePressOnNode(Node* node) +void InspectorController::handleMousePress() { if (!enabled()) return; ASSERT(m_searchingForNode); - ASSERT(node); - if (!node) + if (!m_highlightedNode) return; - // inspect() will implicitly call ElementsPanel's focusedNodeChanged() and the hover feedback will be stopped there. - inspect(node); + RefPtr<Node> node = m_highlightedNode; + setSearchingForNode(false); + inspect(node.get()); +} + +void InspectorController::setInspectorFrontendClient(PassOwnPtr<InspectorFrontendClient> client) +{ + ASSERT(!m_inspectorFrontendClient); + m_inspectorFrontendClient = client; } void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame) { + // If the page is supposed to serve as InspectorFrontend notify inspetor frontend + // client that it's cleared so that the client can expose inspector bindings. + if (m_inspectorFrontendClient && frame == m_inspectedPage->mainFrame()) + m_inspectorFrontendClient->windowObjectCleared(); + if (!enabled() || !m_frontend || frame != m_inspectedPage->mainFrame()) return; m_injectedScriptHost->discardInjectedScripts(); } -void InspectorController::windowScriptObjectAvailable() +void InspectorController::setSearchingForNode(bool enabled) { - if (!m_page || !enabled()) + if (m_searchingForNode == enabled) return; - - // Grant the inspector the ability to script the inspected page. - m_page->mainFrame()->document()->securityOrigin()->grantUniversalAccess(); - m_frontendScriptState = scriptStateFromPage(debuggerWorld(), m_page); - ScriptGlobalObject::set(m_frontendScriptState, "InspectorBackend", m_inspectorBackend.get()); - ScriptGlobalObject::set(m_frontendScriptState, "InspectorFrontendHost", m_inspectorFrontendHost.get()); + m_searchingForNode = enabled; + if (!m_searchingForNode) + hideHighlight(); + if (m_frontend) { + if (enabled) + m_frontend->searchingForNodeWasEnabled(); + else + m_frontend->searchingForNodeWasDisabled(); + } } -void InspectorController::scriptObjectReady() +void InspectorController::setFrontend(PassOwnPtr<InspectorFrontend> frontend) { - ASSERT(m_frontendScriptState); - if (!m_frontendScriptState) - return; - - ScriptObject webInspectorObj; - if (!ScriptGlobalObject::get(m_frontendScriptState, "WebInspector", webInspectorObj)) - return; - setFrontendProxyObject(m_frontendScriptState, webInspectorObj); - + ASSERT(frontend); + m_openingFrontend = false; + m_frontend = frontend; + releaseDOMAgent(); + m_domAgent = InspectorDOMAgent::create(m_frontend.get()); + if (m_timelineAgent) + m_timelineAgent->resetFrontendProxyObject(m_frontend.get()); #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) String debuggerEnabled = setting(debuggerEnabledSettingName); if (debuggerEnabled == "true") @@ -536,41 +439,39 @@ void InspectorController::scriptObjectReady() enableProfiler(); #endif - // Make sure our window is visible now that the page loaded - showWindow(); - - m_client->inspectorWindowObjectCleared(); -} + // Initialize Web Inspector title. + m_frontend->inspectedURLChanged(m_inspectedPage->mainFrame()->loader()->url().string()); -void InspectorController::setFrontendProxyObject(ScriptState* scriptState, ScriptObject webInspectorObj, ScriptObject) -{ - m_frontendScriptState = scriptState; - m_frontend.set(new InspectorFrontend(this, webInspectorObj)); - releaseDOMAgent(); - m_domAgent = InspectorDOMAgent::create(m_frontend.get()); - if (m_timelineAgent) - m_timelineAgent->resetFrontendProxyObject(m_frontend.get()); + populateScriptObjects(); + + if (m_showAfterVisible == CurrentPanel) { + String lastActivePanelSetting = setting(lastActivePanelSettingName); + m_showAfterVisible = specialPanelForJSName(lastActivePanelSetting); + } + + if (m_nodeToFocus) + focusNode(); +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (m_attachDebuggerWhenShown) + enableDebugger(); +#endif + showPanel(m_showAfterVisible); } void InspectorController::show() { if (!enabled()) return; - - if (!m_page) { - if (m_frontend) - return; // We are using custom frontend - no need to create page. - m_page = m_client->createPage(); - if (!m_page) - return; - m_page->setParentInspectorController(this); - - // showWindow() will be called after the page loads in scriptObjectReady() + if (m_openingFrontend) return; + + if (m_frontend) + m_frontend->bringToFront(); + else { + m_openingFrontend = true; + m_client->openInspectorFrontend(this); } - - showWindow(); } void InspectorController::showPanel(SpecialPanels panel) @@ -593,49 +494,40 @@ void InspectorController::showPanel(SpecialPanels panel) void InspectorController::close() { - if (!enabled()) + if (!m_frontend) return; - -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - stopUserInitiatedProfiling(); - disableDebugger(); -#endif - closeWindow(); - - releaseDOMAgent(); - m_frontend.set(0); - m_timelineAgent = 0; - m_frontendScriptState = 0; - if (m_page) { - if (!m_page->mainFrame() || !m_page->mainFrame()->loader() || !m_page->mainFrame()->loader()->isLoading()) { - m_page->setParentInspectorController(0); - m_page = 0; - } - } + m_frontend->close(); } -void InspectorController::showWindow() +void InspectorController::disconnectFrontend() { - ASSERT(enabled()); + if (!m_frontend) + return; + m_frontend.clear(); - unsigned inspectedPageHeight = m_inspectedPage->mainFrame()->view()->visibleHeight(); +#if ENABLE(JAVASCRIPT_DEBUGGER) + // If the window is being closed with the debugger enabled, + // remember this state to re-enable debugger on the next window + // opening. + bool debuggerWasEnabled = m_debuggerEnabled; + disableDebugger(); + if (debuggerWasEnabled) + m_attachDebuggerWhenShown = true; +#endif + setSearchingForNode(false); + unbindAllResources(); + stopTimelineProfiler(); - m_client->showWindow(); + m_showAfterVisible = CurrentPanel; - String attachedHeight = setting(inspectorAttachedHeightName); - bool success = true; - int height = attachedHeight.toInt(&success); - unsigned preferredHeight = success ? height : defaultAttachedHeight; + hideHighlight(); - // 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. - // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow - m_client->setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight)); -} +#if ENABLE(JAVASCRIPT_DEBUGGER) + stopUserInitiatedProfiling(); +#endif -void InspectorController::closeWindow() -{ - m_client->closeWindow(); + releaseDOMAgent(); + m_timelineAgent.clear(); } void InspectorController::releaseDOMAgent() @@ -644,7 +536,7 @@ void InspectorController::releaseDOMAgent() // no references to the DOM agent from the DOM tree. if (m_domAgent) m_domAgent->reset(); - m_domAgent = 0; + m_domAgent.clear(); } void InspectorController::populateScriptObjects() @@ -653,7 +545,20 @@ void InspectorController::populateScriptObjects() if (!m_frontend) return; - m_frontend->populateFrontendSettings(setting(FrontendSettingsSettingName)); + m_frontend->populateFrontendSettings(setting(frontendSettingsSettingName())); + + if (m_resourceTrackingEnabled) + m_frontend->resourceTrackingWasEnabled(); + + if (m_searchingForNode) + m_frontend->searchingForNodeWasEnabled(); + else + m_frontend->searchingForNodeWasDisabled(); + +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (m_profilerEnabled) + m_frontend->profilerWasEnabled(); +#endif ResourcesMap::iterator resourcesEnd = m_resources.end(); for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) @@ -665,8 +570,12 @@ void InspectorController::populateScriptObjects() m_frontend->updateConsoleMessageExpiredCount(m_expiredConsoleMessageCount); unsigned messageCount = m_consoleMessages.size(); for (unsigned i = 0; i < messageCount; ++i) - m_consoleMessages[i]->addToConsole(m_frontend.get()); + m_consoleMessages[i]->addToFrontend(m_frontend.get(), m_injectedScriptHost.get()); +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (m_debuggerEnabled) + m_frontend->updatePauseOnExceptionsState(ScriptDebugServer::shared().pauseOnExceptionsState()); +#endif #if ENABLE(DATABASE) DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end(); for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it) @@ -677,6 +586,11 @@ void InspectorController::populateScriptObjects() for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it) it->second->bind(m_frontend.get()); #endif +#if ENABLE(WORKERS) + WorkersMap::iterator workersEnd = m_workers.end(); + for (WorkersMap::iterator it = m_workers.begin(); it != workersEnd; ++it) + m_frontend->didCreateWorker(*it->second); +#endif m_frontend->populateInterface(); @@ -686,15 +600,12 @@ void InspectorController::populateScriptObjects() m_pendingEvaluateTestCommands.clear(); } -void InspectorController::resetScriptObjects() +void InspectorController::unbindAllResources() { - if (!m_frontend) - return; - - ResourcesMap::iterator resourcesEnd = m_resources.end(); - for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) - it->second->releaseScriptObject(m_frontend.get(), false); - + ResourcesMap::iterator resourcesEnd = m_resources.end(); + for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) + it->second->releaseScriptObject(0); + #if ENABLE(DATABASE) DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end(); for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it) @@ -705,12 +616,8 @@ void InspectorController::resetScriptObjects() for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it) it->second->unbind(); #endif - if (m_timelineAgent) m_timelineAgent->reset(); - - m_frontend->reset(); - m_domAgent->reset(); } void InspectorController::pruneResources(ResourcesMap* resourceMap, DocumentLoader* loaderToKeep) @@ -726,8 +633,8 @@ void InspectorController::pruneResources(ResourcesMap* resourceMap, DocumentLoad if (!loaderToKeep || !resource->isSameLoader(loaderToKeep)) { removeResource(resource); - if (windowVisible()) - resource->releaseScriptObject(m_frontend.get(), true); + if (m_frontend) + resource->releaseScriptObject(m_frontend.get()); } } } @@ -740,13 +647,17 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) ASSERT(m_inspectedPage); if (loader->frame() == m_inspectedPage->mainFrame()) { - m_client->inspectedURLChanged(loader->url().string()); + if (m_frontend) + m_frontend->inspectedURLChanged(loader->url().string()); m_injectedScriptHost->discardInjectedScripts(); clearConsoleMessages(); m_times.clear(); m_counts.clear(); +#if ENABLE(JAVASCRIPT_DEBUGGER) + m_sourceIDToURL.clear(); +#endif #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) m_profiles.clear(); m_currentUserInitiatedProfileNumber = 1; @@ -754,8 +665,13 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) #endif // resetScriptObjects should be called before database and DOM storage // resources are cleared so that it has a chance to unbind them. - resetScriptObjects(); - + if (m_frontend) { + m_frontend->reset(); + m_domAgent->reset(); + } +#if ENABLE(WORKERS) + m_workers.clear(); +#endif #if ENABLE(DATABASE) m_databaseResources.clear(); #endif @@ -769,8 +685,7 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) // We don't add the main resource until its load is committed. This is // needed to keep the load for a user-entered URL from showing up in the // list of resources for the page they are navigating away from. - if (windowVisible()) - m_mainResource->updateScriptObject(m_frontend.get()); + m_mainResource->updateScriptObject(m_frontend.get()); } else { // Pages loaded from the page cache are committed before // m_mainResource is the right resource for this load, so we @@ -778,16 +693,22 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) // identifierForInitialRequest. m_mainResource = 0; } - if (windowVisible()) { - m_frontend->didCommitLoad(); - m_domAgent->setDocument(m_inspectedPage->mainFrame()->document()); - } + m_frontend->didCommitLoad(); + m_domAgent->setDocument(m_inspectedPage->mainFrame()->document()); } } for (Frame* frame = loader->frame(); frame; frame = frame->tree()->traverseNext(loader->frame())) if (ResourcesMap* resourceMap = m_frameResources.get(frame)) pruneResources(resourceMap, loader); + + if (m_scriptsToEvaluateOnLoad.size()) { + ScriptState* scriptState = mainWorldScriptState(loader->frame()); + for (Vector<String>::iterator it = m_scriptsToEvaluateOnLoad.begin(); + it != m_scriptsToEvaluateOnLoad.end(); ++it) { + m_injectedScriptHost->injectScript(*it, scriptState); + } + } } void InspectorController::frameDetachedFromParent(Frame* frame) @@ -874,7 +795,7 @@ void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, addResource(resource.get()); - if (windowVisible()) + if (m_frontend) resource->updateScriptObject(m_frontend.get()); } @@ -898,7 +819,7 @@ void InspectorController::identifierForInitialRequest(unsigned long identifier, addResource(resource.get()); - if (windowVisible() && loader->frameLoader()->isLoadingFromCachedPage() && resource == m_mainResource) + if (m_frontend && loader->frameLoader()->isLoadingFromCachedPage() && resource == m_mainResource) resource->updateScriptObject(m_frontend.get()); } @@ -909,7 +830,9 @@ void InspectorController::mainResourceFiredDOMContentEvent(DocumentLoader* loade if (m_mainResource) { m_mainResource->markDOMContentEventTime(); - if (windowVisible()) + if (m_timelineAgent) + m_timelineAgent->didMarkDOMContentEvent(); + if (m_frontend) m_mainResource->updateScriptObject(m_frontend.get()); } } @@ -921,7 +844,9 @@ void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, con if (m_mainResource) { m_mainResource->markLoadEventTime(); - if (windowVisible()) + if (m_timelineAgent) + m_timelineAgent->didMarkLoadEvent(); + if (m_frontend) m_mainResource->updateScriptObject(m_frontend.get()); } } @@ -961,15 +886,12 @@ void InspectorController::willSendRequest(unsigned long identifier, const Resour resource->startTiming(); resource->updateRequest(request); - if (resource != m_mainResource && windowVisible()) + if (resource != m_mainResource && m_frontend) resource->updateScriptObject(m_frontend.get()); } void InspectorController::didReceiveResponse(unsigned long identifier, const ResourceResponse& response) { - if (m_timelineAgent) - m_timelineAgent->didReceiveResourceResponse(identifier, response); - RefPtr<InspectorResource> resource = getTrackedResource(identifier); if (!resource) return; @@ -977,7 +899,7 @@ void InspectorController::didReceiveResponse(unsigned long identifier, const Res resource->updateResponse(response); resource->markResponseReceivedTime(); - if (resource != m_mainResource && windowVisible()) + if (resource != m_mainResource && m_frontend) resource->updateScriptObject(m_frontend.get()); } @@ -989,7 +911,7 @@ void InspectorController::didReceiveContentLength(unsigned long identifier, int resource->addLength(lengthReceived); - if (resource != m_mainResource && windowVisible()) + if (resource != m_mainResource && m_frontend) resource->updateScriptObject(m_frontend.get()); } @@ -1004,7 +926,8 @@ void InspectorController::didFinishLoading(unsigned long identifier) resource->endTiming(); - if (resource != m_mainResource && windowVisible()) + // No need to mute this event for main resource since it happens after did commit load. + if (m_frontend) resource->updateScriptObject(m_frontend.get()); } @@ -1020,7 +943,8 @@ void InspectorController::didFailLoading(unsigned long identifier, const Resourc resource->markFailed(); resource->endTiming(); - if (resource != m_mainResource && windowVisible()) + // No need to mute this event for main resource since it happens after did commit load. + if (m_frontend) resource->updateScriptObject(m_frontend.get()); } @@ -1033,9 +957,9 @@ void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identi if (!resource) return; - resource->setXMLHttpResponseText(sourceString); + resource->setOverrideContent(sourceString, InspectorResource::XHR); - if (windowVisible()) + if (m_frontend) resource->updateScriptObject(m_frontend.get()); } @@ -1048,11 +972,9 @@ void InspectorController::scriptImported(unsigned long identifier, const String& if (!resource) return; - // FIXME: imported script and XHR response are currently viewed as the same - // thing by the Inspector. They should be made into distinct types. - resource->setXMLHttpResponseText(ScriptString(sourceString)); + resource->setOverrideContent(ScriptString(sourceString), InspectorResource::Script); - if (windowVisible()) + if (m_frontend) resource->updateScriptObject(m_frontend.get()); } @@ -1073,7 +995,7 @@ void InspectorController::enableResourceTracking(bool always, bool reload) m_frontend->resourceTrackingWasEnabled(); if (reload) - m_inspectedPage->mainFrame()->loader()->reload(); + m_inspectedPage->mainFrame()->redirectScheduler()->scheduleRefresh(true); } void InspectorController::disableResourceTracking(bool always) @@ -1127,6 +1049,71 @@ void InspectorController::stopTimelineProfiler() m_frontend->timelineProfilerWasStopped(); } +#if ENABLE(WORKERS) +class PostWorkerNotificationToFrontendTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorController::WorkerAction action) + { + return new PostWorkerNotificationToFrontendTask(worker, action); + } + +private: + PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker, InspectorController::WorkerAction action) + : m_worker(worker) + , m_action(action) + { + } + + virtual void performTask(ScriptExecutionContext* scriptContext) + { + if (InspectorController* inspector = scriptContext->inspectorController()) + inspector->postWorkerNotificationToFrontend(*m_worker, m_action); + } + +private: + RefPtr<InspectorWorkerResource> m_worker; + InspectorController::WorkerAction m_action; +}; + +void InspectorController::postWorkerNotificationToFrontend(const InspectorWorkerResource& worker, InspectorController::WorkerAction action) +{ + if (!m_frontend) + return; + switch (action) { + case InspectorController::WorkerCreated: + m_frontend->didCreateWorker(worker); + break; + case InspectorController::WorkerDestroyed: + m_frontend->didDestroyWorker(worker); + break; + } +} + +void InspectorController::didCreateWorker(intptr_t id, const String& url, bool isSharedWorker) +{ + if (!enabled()) + return; + + RefPtr<InspectorWorkerResource> workerResource(InspectorWorkerResource::create(id, url, isSharedWorker)); + m_workers.set(id, workerResource); + if (m_inspectedPage && m_frontend) + m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource, InspectorController::WorkerCreated)); +} + +void InspectorController::didDestroyWorker(intptr_t id) +{ + if (!enabled()) + return; + + WorkersMap::iterator workerResource = m_workers.find(id); + if (workerResource == m_workers.end()) + return; + if (m_inspectedPage && m_frontend) + m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource->second, InspectorController::WorkerDestroyed)); + m_workers.remove(workerResource); +} +#endif // ENABLE(WORKERS) + #if ENABLE(DATABASE) void InspectorController::selectDatabase(Database* database) { @@ -1141,7 +1128,7 @@ void InspectorController::selectDatabase(Database* database) } } -Database* InspectorController::databaseForId(int databaseId) +Database* InspectorController::databaseForId(long databaseId) { DatabaseResourcesMap::iterator it = m_databaseResources.find(databaseId); if (it == m_databaseResources.end()) @@ -1159,7 +1146,7 @@ void InspectorController::didOpenDatabase(Database* database, const String& doma m_databaseResources.set(resource->id(), resource); // Resources are only bound while visible. - if (windowVisible()) + if (m_frontend) resource->bind(m_frontend.get()); } #endif @@ -1252,7 +1239,7 @@ void InspectorController::didUseDOMStorage(StorageArea* storageArea, bool isLoca m_domStorageResources.set(resource->id(), resource); // Resources are only bound while visible. - if (windowVisible()) + if (m_frontend) resource->bind(m_frontend.get()); } @@ -1263,8 +1250,9 @@ void InspectorController::selectDOMStorage(Storage* storage) return; Frame* frame = storage->frame(); - bool isLocalStorage = (frame->domWindow()->localStorage() == storage); - int storageResourceId = 0; + ExceptionCode ec = 0; + bool isLocalStorage = (frame->domWindow()->localStorage(ec) == storage && !ec); + long storageResourceId = 0; DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end(); for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it) { if (it->second->isSameHostAndType(frame, isLocalStorage)) { @@ -1276,7 +1264,7 @@ void InspectorController::selectDOMStorage(Storage* storage) m_frontend->selectDOMStorage(storageResourceId); } -void InspectorController::getDOMStorageEntries(int callId, int storageId) +void InspectorController::getDOMStorageEntries(long callId, long storageId) { if (!m_frontend) return; @@ -1327,7 +1315,7 @@ void InspectorController::removeDOMStorageItem(long callId, long storageId, cons m_frontend->didRemoveDOMStorageItem(callId, success); } -InspectorDOMStorageResource* InspectorController::getDOMStorageResourceForId(int storageId) +InspectorDOMStorageResource* InspectorController::getDOMStorageResourceForId(long storageId) { DOMStorageResourcesMap::iterator it = m_domStorageResources.find(storageId); if (it == m_domStorageResources.end()) @@ -1336,16 +1324,6 @@ InspectorDOMStorageResource* InspectorController::getDOMStorageResourceForId(int } #endif -void InspectorController::moveWindowBy(float x, float y) const -{ - if (!m_page || !enabled()) - return; - - FloatRect frameRect = m_page->chrome()->windowRect(); - frameRect.move(x, y); - m_page->chrome()->setWindowRect(frameRect); -} - #if ENABLE(JAVASCRIPT_DEBUGGER) void InspectorController::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL) { @@ -1357,7 +1335,7 @@ void InspectorController::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsig if (m_frontend) { #if USE(JSC) - JSLock lock(SilenceAssertionsOnly); + JSC::JSLock lock(JSC::SilenceAssertionsOnly); #endif m_frontend->addProfileHeader(createProfileHeader(*profile)); } @@ -1369,7 +1347,12 @@ void InspectorController::addProfileFinishedMessageToConsole(PassRefPtr<ScriptPr { RefPtr<ScriptProfile> profile = prpProfile; - String message = String::format("Profile \"webkit-profile://%s/%s#%d\" finished.", CPUProfileType, encodeWithURLEscapeSequences(profile->title()).utf8().data(), profile->uid()); +#if USE(JSC) + String title = ustringToString(profile->title()); +#else + String title = profile->title(); +#endif + String message = String::format("Profile \"webkit-profile://%s/%s#%d\" finished.", CPUProfileType, encodeWithURLEscapeSequences(title).utf8().data(), profile->uid()); addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL); } @@ -1396,16 +1379,24 @@ void InspectorController::getProfile(long callId, unsigned uid) if (!m_frontend) return; ProfilesMap::iterator it = m_profiles.find(uid); -#if USE(JSC) if (it != m_profiles.end()) - m_frontend->didGetProfile(callId, toJS(m_frontendScriptState, it->second.get())); +#if USE(JSC) + m_frontend->didGetProfile(callId, toJS(m_frontend->scriptState(), it->second.get())); +#else + m_frontend->didGetProfile(callId, toV8(it->second.get())); #endif } ScriptObject InspectorController::createProfileHeader(const ScriptProfile& profile) { +#if USE(JSC) + String title = ustringToString(profile.title()); +#else + String title = profile.title(); +#endif + ScriptObject header = m_frontend->newScriptObject(); - header.set("title", profile.title()); + header.set("title", title); header.set("uid", profile.uid()); header.set("typeId", String(CPUProfileType)); return header; @@ -1431,7 +1422,7 @@ void InspectorController::startUserInitiatedProfiling(Timer<InspectorController> if (!profilerEnabled()) { enableProfiler(false, true); - ScriptDebugServer::recompileAllJSFunctions(); + ScriptDebugServer::shared().recompileAllJSFunctions(); } m_recordingUserInitiatedProfile = true; @@ -1439,7 +1430,7 @@ void InspectorController::startUserInitiatedProfiling(Timer<InspectorController> String title = getCurrentUserInitiatedProfileName(true); #if USE(JSC) - ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec(); + JSC::ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec(); #else ScriptState* scriptState = 0; #endif @@ -1460,7 +1451,7 @@ void InspectorController::stopUserInitiatedProfiling() String title = getCurrentUserInitiatedProfileName(); #if USE(JSC) - ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec(); + JSC::ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec(); #else ScriptState* scriptState = 0; #endif @@ -1489,7 +1480,7 @@ void InspectorController::enableProfiler(bool always, bool skipRecompile) m_profilerEnabled = true; if (!skipRecompile) - ScriptDebugServer::recompileAllJSFunctionsSoon(); + ScriptDebugServer::shared().recompileAllJSFunctionsSoon(); if (m_frontend) m_frontend->profilerWasEnabled(); @@ -1505,14 +1496,14 @@ void InspectorController::disableProfiler(bool always) m_profilerEnabled = false; - ScriptDebugServer::recompileAllJSFunctionsSoon(); + ScriptDebugServer::shared().recompileAllJSFunctionsSoon(); if (m_frontend) m_frontend->profilerWasDisabled(); } #endif -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) +#if ENABLE(JAVASCRIPT_DEBUGGER) void InspectorController::enableDebuggerFromFrontend(bool always) { if (always) @@ -1520,8 +1511,8 @@ void InspectorController::enableDebuggerFromFrontend(bool always) ASSERT(m_inspectedPage); - JavaScriptDebugServer::shared().addListener(this, m_inspectedPage); - JavaScriptDebugServer::shared().clearBreakpoints(); + ScriptDebugServer::shared().addListener(this, m_inspectedPage); + ScriptDebugServer::shared().clearBreakpoints(); m_debuggerEnabled = true; m_frontend->debuggerWasEnabled(); @@ -1535,7 +1526,7 @@ void InspectorController::enableDebugger() if (m_debuggerEnabled) return; - if (!m_frontendScriptState || !m_frontend) + if (!m_frontend) m_attachDebuggerWhenShown = true; else { m_frontend->attachDebuggerWhenShown(); @@ -1553,7 +1544,7 @@ void InspectorController::disableDebugger(bool always) ASSERT(m_inspectedPage); - JavaScriptDebugServer::shared().removeListener(this, m_inspectedPage); + ScriptDebugServer::shared().removeListener(this, m_inspectedPage); m_debuggerEnabled = false; m_attachDebuggerWhenShown = false; @@ -1566,25 +1557,66 @@ void InspectorController::resumeDebugger() { if (!m_debuggerEnabled) return; - JavaScriptDebugServer::shared().continueProgram(); + ScriptDebugServer::shared().continueProgram(); +} + +void InspectorController::setBreakpoint(const String& sourceID, unsigned lineNumber, bool enabled, const String& condition) +{ + ScriptBreakpoint breakpoint(enabled, condition); + ScriptDebugServer::shared().setBreakpoint(sourceID, lineNumber, breakpoint); + String url = m_sourceIDToURL.get(sourceID); + if (url.isEmpty()) + return; + + HashMap<String, SourceBreakpoints>::iterator it = m_stickyBreakpoints.find(url); + if (it == m_stickyBreakpoints.end()) + it = m_stickyBreakpoints.set(url, SourceBreakpoints()).first; + it->second.set(lineNumber, breakpoint); +} + +void InspectorController::removeBreakpoint(const String& sourceID, unsigned lineNumber) +{ + ScriptDebugServer::shared().removeBreakpoint(sourceID, lineNumber); + + String url = m_sourceIDToURL.get(sourceID); + if (url.isEmpty()) + return; + + HashMap<String, SourceBreakpoints>::iterator it = m_stickyBreakpoints.find(url); + if (it != m_stickyBreakpoints.end()) + it->second.remove(lineNumber); } // JavaScriptDebugListener functions -void InspectorController::didParseSource(ExecState*, const SourceCode& source) +void InspectorController::didParseSource(const String& sourceID, const String& url, const String& data, int firstLine) { - m_frontend->parsedScriptSource(source); + m_frontend->parsedScriptSource(sourceID, url, data, firstLine); + + if (url.isEmpty()) + return; + + HashMap<String, SourceBreakpoints>::iterator it = m_stickyBreakpoints.find(url); + if (it != m_stickyBreakpoints.end()) { + for (SourceBreakpoints::iterator breakpointIt = it->second.begin(); breakpointIt != it->second.end(); ++breakpointIt) { + if (firstLine <= breakpointIt->first) { + ScriptDebugServer::shared().setBreakpoint(sourceID, breakpointIt->first, breakpointIt->second); + m_frontend->restoredBreakpoint(sourceID, url, breakpointIt->first, breakpointIt->second.enabled, breakpointIt->second.condition); + } + } + } + + m_sourceIDToURL.set(sourceID, url); } -void InspectorController::failedToParseSource(ExecState*, const SourceCode& source, int errorLine, const UString& errorMessage) +void InspectorController::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) { - m_frontend->failedToParseScriptSource(source, errorLine, errorMessage); + m_frontend->failedToParseScriptSource(url, data, firstLine, errorLine, errorMessage); } void InspectorController::didPause() { - JavaScriptCallFrame* callFrame = m_injectedScriptHost->currentCallFrame(); - ScriptState* scriptState = callFrame->scopeChain()->globalObject->globalExec(); + ScriptState* scriptState = ScriptDebugServer::shared().currentCallFrameState(); ASSERT(scriptState); InjectedScript injectedScript = m_injectedScriptHost->injectedScriptFor(scriptState); RefPtr<SerializedScriptValue> callFrames = injectedScript.callFrames(); @@ -1809,6 +1841,8 @@ InspectorController::SpecialPanels InspectorController::specialPanelForJSName(co return ProfilesPanel; if (panelName == "storage" || panelName == "databases") return StoragePanel; + if (panelName == "audits") + return AuditsPanel; if (panelName == "console") return ConsolePanel; return ElementsPanel; @@ -1845,6 +1879,16 @@ InjectedScript InspectorController::injectedScriptForNodeId(long id) return InjectedScript(); } +void InspectorController::addScriptToEvaluateOnLoad(const String& source) +{ + m_scriptsToEvaluateOnLoad.append(source); +} + +void InspectorController::removeAllScriptsToEvaluateOnLoad() +{ + m_scriptsToEvaluateOnLoad.clear(); +} + } // namespace WebCore #endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorController.h b/WebCore/inspector/InspectorController.h index 2f25eec..3c67975 100644 --- a/WebCore/inspector/InspectorController.h +++ b/WebCore/inspector/InspectorController.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,6 +34,7 @@ #include "InspectorDOMAgent.h" #include "PlatformString.h" #include "ScriptArray.h" +#include "ScriptBreakpoint.h" #include "ScriptObject.h" #include "ScriptProfile.h" #include "ScriptState.h" @@ -47,12 +48,8 @@ #include <wtf/RefCounted.h> #include <wtf/Vector.h> -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) -#include "JavaScriptDebugListener.h" - -namespace JSC { -class UString; -} +#if ENABLE(JAVASCRIPT_DEBUGGER) +#include "ScriptDebugListener.h" #endif namespace WebCore { @@ -69,9 +66,8 @@ class InjectedScriptHost; class InspectorBackend; class InspectorClient; class InspectorFrontend; -class InspectorFrontendHost; +class InspectorFrontendClient; class InspectorTimelineAgent; -class JavaScriptCallFrame; class KURL; class Node; class Page; @@ -88,10 +84,11 @@ class ConsoleMessage; class InspectorDatabaseResource; class InspectorDOMStorageResource; class InspectorResource; +class InspectorWorkerResource; class InspectorController -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - : JavaScriptDebugListener, public Noncopyable +#if ENABLE(JAVASCRIPT_DEBUGGER) + : ScriptDebugListener, public Noncopyable #else : public Noncopyable #endif @@ -103,6 +100,7 @@ public: typedef HashMap<int, RefPtr<InspectorDOMStorageResource> > DOMStorageResourcesMap; typedef enum { + AuditsPanel, CurrentPanel, ConsolePanel, ElementsPanel, @@ -117,11 +115,9 @@ public: ~InspectorController(); InspectorBackend* inspectorBackend() { return m_inspectorBackend.get(); } - InspectorFrontendHost* inspectorFrontendHost() { return m_inspectorFrontendHost.get(); } InjectedScriptHost* injectedScriptHost() { return m_injectedScriptHost.get(); } void inspectedPageDestroyed(); - void pageDestroyed() { m_page = 0; } bool enabled() const; @@ -137,31 +133,24 @@ public: void show(); void showPanel(SpecialPanels); void close(); - - bool windowVisible(); - void setWindowVisible(bool visible = true, bool attached = false); + void disconnectFrontend(); void addMessageToConsole(MessageSource, MessageType, MessageLevel, ScriptCallStack*); void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, unsigned lineNumber, const String& sourceID); void clearConsoleMessages(); const Vector<ConsoleMessage*>& consoleMessages() const { return m_consoleMessages; } - void attachWindow(); - void detachWindow(); - - void toggleSearchForNodeInPage(); bool searchingForNodeInPage() const { return m_searchingForNode; } void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags); - void handleMousePressOnNode(Node*); + void handleMousePress(); + void setInspectorFrontendClient(PassOwnPtr<InspectorFrontendClient> client); + bool hasInspectorFrontendClient() const { return m_inspectorFrontendClient; } + void inspectedWindowScriptObjectCleared(Frame*); - void windowScriptObjectAvailable(); - void setFrontendProxyObject(ScriptState* state, ScriptObject webInspectorObj, ScriptObject injectedScriptObj = ScriptObject()); - ScriptState* frontendScriptState() const { return m_frontendScriptState; } - - void populateScriptObjects(); - void resetScriptObjects(); + bool windowVisible(); + void setFrontend(PassOwnPtr<InspectorFrontend>); void didCommitLoad(DocumentLoader*); void frameDetachedFromParent(Frame*); @@ -188,11 +177,17 @@ public: void mainResourceFiredLoadEvent(DocumentLoader*, const KURL&); void mainResourceFiredDOMContentEvent(DocumentLoader*, const KURL&); - + void didInsertDOMNode(Node*); void didRemoveDOMNode(Node*); void didModifyDOMAttr(Element*); - +#if ENABLE(WORKERS) + enum WorkerAction { WorkerCreated, WorkerDestroyed }; + + void postWorkerNotificationToFrontend(const InspectorWorkerResource&, WorkerAction); + void didCreateWorker(intptr_t, const String& url, bool isSharedWorker); + void didDestroyWorker(intptr_t); +#endif void getCookies(long callId); #if ENABLE(DATABASE) @@ -201,7 +196,7 @@ public: #if ENABLE(DOM_STORAGE) void didUseDOMStorage(StorageArea* storageArea, bool isLocalStorage, Frame* frame); void selectDOMStorage(Storage* storage); - void getDOMStorageEntries(int callId, int storageId); + void getDOMStorageEntries(long callId, long storageId); void setDOMStorageItem(long callId, long storageId, const String& key, const String& value); void removeDOMStorageItem(long callId, long storageId, const String& key); #endif @@ -218,7 +213,7 @@ public: void startGroup(MessageSource source, ScriptCallStack* callFrame); void endGroup(MessageSource source, unsigned lineNumber, const String& sourceURL); - void markTimeline(const String& message); + void markTimeline(const String& message); #if ENABLE(JAVASCRIPT_DEBUGGER) void addProfile(PassRefPtr<ScriptProfile>, unsigned lineNumber, const String& sourceURL); @@ -236,15 +231,15 @@ public: bool profilerEnabled() const { return enabled() && m_profilerEnabled; } #endif -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) +#if ENABLE(JAVASCRIPT_DEBUGGER) void enableDebugger(); void disableDebugger(bool always = false); bool debuggerEnabled() const { return m_debuggerEnabled; } void resumeDebugger(); - virtual void didParseSource(JSC::ExecState*, const JSC::SourceCode&); - virtual void failedToParseSource(JSC::ExecState*, const JSC::SourceCode&, int errorLine, const JSC::UString& errorMessage); + virtual void didParseSource(const String& sourceID, const String& url, const String& data, int firstLine); + virtual void failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage); virtual void didPause(); virtual void didContinue(); #endif @@ -252,25 +247,34 @@ public: void evaluateForTestInFrontend(long callId, const String& script); InjectedScript injectedScriptForNodeId(long id); + void addScriptToEvaluateOnLoad(const String& source); + void removeAllScriptsToEvaluateOnLoad(); + + static const String& inspectorStartsAttachedSettingName(); private: - static const char* const FrontendSettingsSettingName; + static const String& frontendSettingsSettingName(); + friend class InspectorBackend; - friend class InspectorFrontendHost; friend class InjectedScriptHost; + + void populateScriptObjects(); + void unbindAllResources(); + + // Following are used from InspectorBackend and internally. + void setSearchingForNode(bool enabled); + // Following are used from InspectorBackend and internally. - void scriptObjectReady(); - void moveWindowBy(float x, float y) const; - void setAttachedWindow(bool); - void setAttachedWindowHeight(unsigned height); void storeLastActivePanel(const String& panelName); - void closeWindow(); InspectorDOMAgent* domAgent() { return m_domAgent.get(); } void releaseDOMAgent(); void deleteCookie(const String& cookieName, const String& domain); #if ENABLE(JAVASCRIPT_DEBUGGER) + void setBreakpoint(const String& sourceID, unsigned lineNumber, bool enabled, const String& condition); + void removeBreakpoint(const String& sourceID, unsigned lineNumber); + typedef HashMap<unsigned int, RefPtr<ScriptProfile> > ProfilesMap; void startUserInitiatedProfilingSoon(); @@ -282,10 +286,10 @@ private: #endif #if ENABLE(DATABASE) void selectDatabase(Database* database); - Database* databaseForId(int databaseId); + Database* databaseForId(long databaseId); #endif #if ENABLE(DOM_STORAGE) - InspectorDOMStorageResource* getDOMStorageResourceForId(int storageId); + InspectorDOMStorageResource* getDOMStorageResourceForId(long storageId); #endif ScriptObject buildObjectForCookie(const Cookie&); @@ -302,8 +306,6 @@ private: void pruneResources(ResourcesMap*, DocumentLoader* loaderToKeep = 0); void removeAllResources(ResourcesMap* map) { pruneResources(map); } - void showWindow(); - bool isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl); SpecialPanels specialPanelForJSName(const String& panelName); @@ -312,10 +314,11 @@ private: Page* m_inspectedPage; InspectorClient* m_client; + OwnPtr<InspectorFrontendClient> m_inspectorFrontendClient; + bool m_openingFrontend; OwnPtr<InspectorFrontend> m_frontend; RefPtr<InspectorDOMAgent> m_domAgent; OwnPtr<InspectorTimelineAgent> m_timelineAgent; - Page* m_page; RefPtr<Node> m_nodeToFocus; RefPtr<InspectorResource> m_mainResource; ResourcesMap m_resources; @@ -331,8 +334,6 @@ private: #if ENABLE(DOM_STORAGE) DOMStorageResourcesMap m_domStorageResources; #endif - ScriptState* m_frontendScriptState; - bool m_windowVisible; SpecialPanels m_showAfterVisible; RefPtr<Node> m_highlightedNode; unsigned m_groupLevel; @@ -341,18 +342,19 @@ private: bool m_resourceTrackingEnabled; bool m_resourceTrackingSettingsLoaded; RefPtr<InspectorBackend> m_inspectorBackend; - RefPtr<InspectorFrontendHost> m_inspectorFrontendHost; RefPtr<InjectedScriptHost> m_injectedScriptHost; typedef HashMap<String, String> Settings; mutable Settings m_settings; Vector<pair<long, String> > m_pendingEvaluateTestCommands; -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) + Vector<String> m_scriptsToEvaluateOnLoad; +#if ENABLE(JAVASCRIPT_DEBUGGER) bool m_debuggerEnabled; bool m_attachDebuggerWhenShown; -#endif -#if ENABLE(JAVASCRIPT_DEBUGGER) + HashMap<String, String> m_sourceIDToURL; + HashMap<String, SourceBreakpoints> m_stickyBreakpoints; + bool m_profilerEnabled; bool m_recordingUserInitiatedProfile; int m_currentUserInitiatedProfileNumber; @@ -360,6 +362,11 @@ private: Timer<InspectorController> m_startProfiling; ProfilesMap m_profiles; #endif +#if ENABLE(WORKERS) + typedef HashMap<intptr_t, RefPtr<InspectorWorkerResource> > WorkersMap; + + WorkersMap m_workers; +#endif }; inline void InspectorController::didInsertDOMNode(Node* node) diff --git a/WebCore/inspector/InspectorDOMAgent.cpp b/WebCore/inspector/InspectorDOMAgent.cpp index 0387f30..31fad80 100644 --- a/WebCore/inspector/InspectorDOMAgent.cpp +++ b/WebCore/inspector/InspectorDOMAgent.cpp @@ -34,6 +34,13 @@ #if ENABLE(INSPECTOR) #include "AtomicString.h" +#include "CSSComputedStyleDeclaration.h" +#include "CSSMutableStyleDeclaration.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSStyleRule.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" #include "ContainerNode.h" #include "Cookie.h" #include "CookieJar.h" @@ -45,16 +52,22 @@ #include "EventNames.h" #include "EventTarget.h" #include "HTMLFrameOwnerElement.h" +#include "HTMLHeadElement.h" #include "InspectorFrontend.h" #include "markup.h" #include "MutationEvent.h" #include "Node.h" #include "NodeList.h" #include "PlatformString.h" +#include "RenderStyle.h" +#include "RenderStyleConstants.h" #include "ScriptEventListener.h" #include "ScriptObject.h" +#include "StyleSheetList.h" #include "Text.h" +#include <wtf/text/CString.h> +#include <wtf/HashSet.h> #include <wtf/OwnPtr.h> #include <wtf/Vector.h> @@ -64,6 +77,8 @@ InspectorDOMAgent::InspectorDOMAgent(InspectorFrontend* frontend) : EventListener(InspectorDOMAgentType) , m_frontend(frontend) , m_lastNodeId(1) + , m_lastStyleId(1) + , m_lastRuleId(1) { } @@ -174,7 +189,7 @@ void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap) stopListening(frameOwner->contentDocument()); } - int id = nodesMap->get(node); + long id = nodesMap->get(node); if (!id) return; m_idToNode.remove(id); @@ -221,6 +236,12 @@ void InspectorDOMAgent::discardBindings() m_idToNode.clear(); releaseDanglingNodes(); m_childrenRequested.clear(); + m_styleToId.clear(); + m_idToStyle.clear(); + m_ruleToId.clear(); + m_idToRule.clear(); + m_idToDisabledStyle.clear(); + m_inspectorStyleSheet = 0; } Node* InspectorDOMAgent::nodeForId(long id) @@ -337,6 +358,74 @@ void InspectorDOMAgent::removeAttribute(long callId, long elementId, const Strin } } +void InspectorDOMAgent::removeNode(long callId, long nodeId) +{ + Node* node = nodeForId(nodeId); + if (!node) { + // Use -1 to denote an error condition. + m_frontend->didRemoveNode(callId, -1); + return; + } + + Node* parentNode = node->parentNode(); + if (!parentNode) { + m_frontend->didRemoveNode(callId, -1); + return; + } + + ExceptionCode code; + parentNode->removeChild(node, code); + if (code) { + m_frontend->didRemoveNode(callId, -1); + return; + } + + m_frontend->didRemoveNode(callId, nodeId); +} + +void InspectorDOMAgent::changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded) +{ + Node* oldNode = nodeForId(nodeId); + if (!oldNode || !oldNode->isElementNode()) { + // Use -1 to denote an error condition. + m_frontend->didChangeTagName(callId, -1); + return; + } + + ExceptionCode code = 0; + RefPtr<Element> newElem = oldNode->document()->createElement(tagName, code); + if (code) { + m_frontend->didChangeTagName(callId, -1); + return; + } + + // Copy over the original node's attributes. + Element* oldElem = static_cast<Element*>(oldNode); + newElem->copyNonAttributeProperties(oldElem); + if (oldElem->attributes()) + newElem->attributes()->setAttributes(*(oldElem->attributes(true))); + + // Copy over the original node's children. + Node* child; + while ((child = oldNode->firstChild())) + newElem->appendChild(child, code); + + // Replace the old node with the new node + Node* parent = oldNode->parentNode(); + parent->insertBefore(newElem, oldNode->nextSibling(), code); + parent->removeChild(oldNode, code); + + if (code) { + m_frontend->didChangeTagName(callId, -1); + return; + } + + long newId = pushNodePathToFrontend(newElem.get()); + if (expanded) + pushChildNodesToFrontend(newId); + m_frontend->didChangeTagName(callId, newId); +} + void InspectorDOMAgent::setTextNodeValue(long callId, long nodeId, const String& value) { Node* node = nodeForId(nodeId); @@ -671,6 +760,558 @@ void InspectorDOMAgent::didModifyDOMAttr(Element* element) m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element)); } +void InspectorDOMAgent::getStyles(long callId, long nodeId, bool authorOnly) +{ + Node* node = nodeForId(nodeId); + if (!node || node->nodeType() != Node::ELEMENT_NODE) { + m_frontend->didGetStyles(callId, ScriptValue::undefined()); + return; + } + + DOMWindow* defaultView = node->ownerDocument()->defaultView(); + if (!defaultView) { + m_frontend->didGetStyles(callId, ScriptValue::undefined()); + return; + } + + Element* element = static_cast<Element*>(node); + RefPtr<CSSComputedStyleDeclaration> computedStyleInfo = computedStyle(node, true); // Support the viewing of :visited information in computed style. + + ScriptObject result = m_frontend->newScriptObject(); + if (element->style()) + result.set("inlineStyle", buildObjectForStyle(element->style(), true)); + result.set("computedStyle", buildObjectForStyle(computedStyleInfo.get(), false)); + + CSSStyleSelector* selector = element->ownerDocument()->styleSelector(); + RefPtr<CSSRuleList> matchedRules = selector->styleRulesForElement(element, authorOnly); + result.set("matchedCSSRules", buildArrayForCSSRules(matchedRules.get())); + + result.set("styleAttributes", buildObjectForAttributeStyles(element)); + result.set("pseudoElements", buildArrayForPseudoElements(element, authorOnly)); + + ScriptObject currentStyle = result; + Element* parentElement = element->parentElement(); + while (parentElement) { + ScriptObject parentStyle = m_frontend->newScriptObject(); + currentStyle.set("parent", parentStyle); + if (parentElement->style() && parentElement->style()->length()) + parentStyle.set("inlineStyle", buildObjectForStyle(parentElement->style(), true)); + + CSSStyleSelector* parentSelector = parentElement->ownerDocument()->styleSelector(); + RefPtr<CSSRuleList> parentMatchedRules = parentSelector->styleRulesForElement(parentElement, authorOnly); + parentStyle.set("matchedCSSRules", buildArrayForCSSRules(parentMatchedRules.get())); + + parentElement = parentElement->parentElement(); + currentStyle = parentStyle; + } + m_frontend->didGetStyles(callId, result); +} + +void InspectorDOMAgent::getAllStyles(long callId) +{ + ScriptArray result = m_frontend->newScriptArray(); + unsigned counter = 0; + for (ListHashSet<RefPtr<Document> >::iterator it = m_documents.begin(); it != m_documents.end(); ++it) { + StyleSheetList* list = (*it)->styleSheets(); + for (unsigned i = 0; i < list->length(); ++i) { + StyleSheet* styleSheet = list->item(i); + if (styleSheet->isCSSStyleSheet()) + result.set(counter++, buildObjectForStyleSheet(static_cast<CSSStyleSheet*>(styleSheet))); + } + } + m_frontend->didGetAllStyles(callId, result); +} + +void InspectorDOMAgent::getInlineStyle(long callId, long nodeId) +{ + Node* node = nodeForId(nodeId); + if (!node || node->nodeType() != Node::ELEMENT_NODE) { + m_frontend->didGetInlineStyle(callId, ScriptValue::undefined()); + return; + } + Element* element = static_cast<Element*>(node); + m_frontend->didGetInlineStyle(callId, buildObjectForStyle(element->style(), true)); +} + +void InspectorDOMAgent::getComputedStyle(long callId, long nodeId) +{ + Node* node = nodeForId(nodeId); + if (!node || node->nodeType() != Node::ELEMENT_NODE) { + m_frontend->didGetComputedStyle(callId, ScriptValue::undefined()); + return; + } + + DOMWindow* defaultView = node->ownerDocument()->defaultView(); + if (!defaultView) { + m_frontend->didGetComputedStyle(callId, ScriptValue::undefined()); + return; + } + + Element* element = static_cast<Element*>(node); + RefPtr<CSSStyleDeclaration> computedStyle = defaultView->getComputedStyle(element, ""); + m_frontend->didGetComputedStyle(callId, buildObjectForStyle(computedStyle.get(), false)); +} + +ScriptObject InspectorDOMAgent::buildObjectForAttributeStyles(Element* element) +{ + ScriptObject styleAttributes = m_frontend->newScriptObject(); + 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.set(attributeName.utf8().data(), buildObjectForStyle(attribute->style(), true)); + } + } + return styleAttributes; +} + +ScriptArray InspectorDOMAgent::buildArrayForCSSRules(CSSRuleList* matchedRules) +{ + ScriptArray matchedCSSRules = m_frontend->newScriptArray(); + unsigned counter = 0; + for (unsigned i = 0; matchedRules && i < matchedRules->length(); ++i) { + CSSRule* rule = matchedRules->item(i); + if (rule->type() == CSSRule::STYLE_RULE) + matchedCSSRules.set(counter++, buildObjectForRule(static_cast<CSSStyleRule*>(rule))); + } + return matchedCSSRules; +} + +ScriptArray InspectorDOMAgent::buildArrayForPseudoElements(Element* element, bool authorOnly) +{ + ScriptArray result = m_frontend->newScriptArray(); + CSSStyleSelector* selector = element->ownerDocument()->styleSelector(); + RefPtr<RenderStyle> renderStyle = element->styleForRenderer(); + unsigned counter = 0; + + for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) { + RefPtr<CSSRuleList> matchedRules = selector->pseudoStyleRulesForElement(element, pseudoId, authorOnly); + if (matchedRules && matchedRules->length()) { + ScriptObject pseudoStyles = m_frontend->newScriptObject(); + pseudoStyles.set("pseudoId", static_cast<int>(pseudoId)); + pseudoStyles.set("rules", buildArrayForCSSRules(matchedRules.get())); + result.set(counter++, pseudoStyles); + } + } + return result; +} + +void InspectorDOMAgent::applyStyleText(long callId, long styleId, const String& styleText, const String& propertyName) +{ + IdToStyleMap::iterator it = m_idToStyle.find(styleId); + if (it == m_idToStyle.end()) { + m_frontend->didApplyStyleText(callId, false, ScriptValue::undefined(), m_frontend->newScriptArray()); + return; + } + + CSSStyleDeclaration* style = it->second.get(); + int styleTextLength = styleText.length(); + + RefPtr<CSSMutableStyleDeclaration> tempMutableStyle = CSSMutableStyleDeclaration::create(); + tempMutableStyle->parseDeclaration(styleText); + CSSStyleDeclaration* tempStyle = static_cast<CSSStyleDeclaration*>(tempMutableStyle.get()); + + if (tempStyle->length() || !styleTextLength) { + ExceptionCode ec = 0; + // The input was parsable or the user deleted everything, so remove the + // original property from the real style declaration. If this represents + // a shorthand remove all the longhand properties. + if (!style->getPropertyShorthand(propertyName).isEmpty()) { + Vector<String> longhandProps = longhandProperties(style, propertyName); + for (unsigned i = 0; !ec && i < longhandProps.size(); ++i) + style->removeProperty(longhandProps[i], ec); + } else + style->removeProperty(propertyName, ec); + if (ec) { + m_frontend->didApplyStyleText(callId, false, ScriptValue::undefined(), m_frontend->newScriptArray()); + return; + } + } + + // Notify caller that the property was successfully deleted. + if (!styleTextLength) { + ScriptArray changedProperties = m_frontend->newScriptArray(); + changedProperties.set(0, propertyName); + m_frontend->didApplyStyleText(callId, true, ScriptValue::undefined(), changedProperties); + return; + } + + if (!tempStyle->length()) { + m_frontend->didApplyStyleText(callId, false, ScriptValue::undefined(), m_frontend->newScriptArray()); + return; + } + + // 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); + String shorthand = tempStyle->getPropertyShorthand(name); + + if (!shorthand.isEmpty() && foundShorthands.contains(shorthand)) + continue; + + String value; + String priority; + if (!shorthand.isEmpty()) { + value = shorthandValue(tempStyle, shorthand); + priority = shorthandPriority(tempStyle, shorthand); + foundShorthands.add(shorthand); + name = shorthand; + } else { + value = tempStyle->getPropertyValue(name); + priority = tempStyle->getPropertyPriority(name); + } + + // Set the property on the real style declaration. + ExceptionCode ec = 0; + style->setProperty(name, value, priority, ec); + changedProperties.append(name); + } + m_frontend->didApplyStyleText(callId, true, buildObjectForStyle(style, true), toArray(changedProperties)); +} + +void InspectorDOMAgent::setStyleText(long callId, long styleId, const String& cssText) +{ + IdToStyleMap::iterator it = m_idToStyle.find(styleId); + if (it == m_idToStyle.end()) { + m_frontend->didSetStyleText(callId, false); + return; + } + CSSStyleDeclaration* style = it->second.get(); + ExceptionCode ec = 0; + style->setCssText(cssText, ec); + m_frontend->didSetStyleText(callId, !ec); +} + +void InspectorDOMAgent::setStyleProperty(long callId, long styleId, const String& name, const String& value) +{ + IdToStyleMap::iterator it = m_idToStyle.find(styleId); + if (it == m_idToStyle.end()) { + m_frontend->didSetStyleProperty(callId, false); + return; + } + + CSSStyleDeclaration* style = it->second.get(); + ExceptionCode ec = 0; + style->setProperty(name, value, ec); + m_frontend->didSetStyleProperty(callId, !ec); +} + +void InspectorDOMAgent::toggleStyleEnabled(long callId, long styleId, const String& propertyName, bool disabled) +{ + IdToStyleMap::iterator it = m_idToStyle.find(styleId); + if (it == m_idToStyle.end()) { + m_frontend->didToggleStyleEnabled(callId, ScriptValue::undefined()); + return; + } + CSSStyleDeclaration* style = it->second.get(); + + IdToDisabledStyleMap::iterator disabledIt = m_idToDisabledStyle.find(styleId); + if (disabledIt == m_idToDisabledStyle.end()) + disabledIt = m_idToDisabledStyle.set(styleId, DisabledStyleDeclaration()).first; + + // TODO: make sure this works with shorthands right. + ExceptionCode ec = 0; + if (disabled) { + disabledIt->second.set(propertyName, std::make_pair(style->getPropertyValue(propertyName), style->getPropertyPriority(propertyName))); + if (!ec) + style->removeProperty(propertyName, ec); + } else if (disabledIt->second.contains(propertyName)) { + PropertyValueAndPriority valueAndPriority = disabledIt->second.get(propertyName); + style->setProperty(propertyName, valueAndPriority.first, valueAndPriority.second, ec); + if (!ec) + disabledIt->second.remove(propertyName); + } + if (ec) { + m_frontend->didToggleStyleEnabled(callId, ScriptValue::undefined()); + return; + } + m_frontend->didToggleStyleEnabled(callId, buildObjectForStyle(style, true)); +} + +void InspectorDOMAgent::setRuleSelector(long callId, long ruleId, const String& selector, long selectedNodeId) +{ + IdToRuleMap::iterator it = m_idToRule.find(ruleId); + if (it == m_idToRule.end()) { + m_frontend->didSetRuleSelector(callId, ScriptValue::undefined(), false); + return; + } + + CSSStyleRule* rule = it->second.get(); + Node* node = nodeForId(selectedNodeId); + + CSSStyleSheet* styleSheet = rule->parentStyleSheet(); + ExceptionCode ec = 0; + styleSheet->addRule(selector, rule->style()->cssText(), ec); + if (ec) { + m_frontend->didSetRuleSelector(callId, ScriptValue::undefined(), false); + return; + } + + CSSStyleRule* newRule = static_cast<CSSStyleRule*>(styleSheet->item(styleSheet->length() - 1)); + for (unsigned i = 0; i < styleSheet->length(); ++i) { + if (styleSheet->item(i) == rule) { + styleSheet->deleteRule(i, ec); + break; + } + } + + if (ec) { + m_frontend->didSetRuleSelector(callId, ScriptValue::undefined(), false); + return; + } + + m_frontend->didSetRuleSelector(callId, buildObjectForRule(newRule), ruleAffectsNode(newRule, node)); +} + +void InspectorDOMAgent::addRule(long callId, const String& selector, long selectedNodeId) +{ + Node* node = nodeForId(selectedNodeId); + if (!node) { + m_frontend->didAddRule(callId, ScriptValue::undefined(), false); + return; + } + + if (!m_inspectorStyleSheet.get()) { + Document* ownerDocument = node->ownerDocument(); + ExceptionCode ec = 0; + RefPtr<Element> styleElement = ownerDocument->createElement("style", ec); + if (!ec) + styleElement->setAttribute("type", "text/css", ec); + if (!ec) + ownerDocument->head()->appendChild(styleElement, ec); + if (ec) { + m_frontend->didAddRule(callId, ScriptValue::undefined(), false); + return; + } + StyleSheetList* styleSheets = ownerDocument->styleSheets(); + StyleSheet* styleSheet = styleSheets->item(styleSheets->length() - 1); + if (!styleSheet->isCSSStyleSheet()) { + m_frontend->didAddRule(callId, ScriptValue::undefined(), false); + return; + } + m_inspectorStyleSheet = static_cast<CSSStyleSheet*>(styleSheet); + } + + ExceptionCode ec = 0; + m_inspectorStyleSheet->addRule(selector, "", ec); + if (ec) { + m_frontend->didAddRule(callId, ScriptValue::undefined(), false); + return; + } + + CSSStyleRule* newRule = static_cast<CSSStyleRule*>(m_inspectorStyleSheet->item(m_inspectorStyleSheet->length() - 1)); + m_frontend->didAddRule(callId, buildObjectForRule(newRule), ruleAffectsNode(newRule, node)); +} + +long InspectorDOMAgent::bindStyle(CSSStyleDeclaration* style) +{ + long id = m_styleToId.get(style); + if (!id) { + id = m_lastStyleId++; + m_idToStyle.set(id, style); + m_styleToId.set(style, id); + } + return id; +} + +long InspectorDOMAgent::bindRule(CSSStyleRule* rule) +{ + long id = m_ruleToId.get(rule); + if (!id) { + id = m_lastRuleId++; + m_idToRule.set(id, rule); + m_ruleToId.set(rule, id); + } + return id; +} + +ScriptObject InspectorDOMAgent::buildObjectForStyle(CSSStyleDeclaration* style, bool bind) +{ + ScriptObject result = m_frontend->newScriptObject(); + if (bind) { + long styleId = bindStyle(style); + result.set("id", styleId); + + IdToDisabledStyleMap::iterator disabledIt = m_idToDisabledStyle.find(styleId); + if (disabledIt != m_idToDisabledStyle.end()) + result.set("disabled", buildArrayForDisabledStyleProperties(disabledIt->second)); + } + result.set("width", style->getPropertyValue("width")); + result.set("height", style->getPropertyValue("height")); + populateObjectWithStyleProperties(style, result); + return result; +} + +void InspectorDOMAgent::populateObjectWithStyleProperties(CSSStyleDeclaration* style, ScriptObject& result) +{ + ScriptArray properties = m_frontend->newScriptArray(); + ScriptObject shorthandValues = m_frontend->newScriptObject(); + result.set("properties", properties); + result.set("shorthandValues", shorthandValues); + + HashSet<String> foundShorthands; + for (unsigned i = 0; i < style->length(); ++i) { + ScriptObject property = m_frontend->newScriptObject(); + String name = style->item(i); + property.set("name", name); + property.set("priority", style->getPropertyPriority(name)); + property.set("implicit", style->isPropertyImplicit(name)); + String shorthand = style->getPropertyShorthand(name); + property.set("shorthand", shorthand); + if (!shorthand.isEmpty() && !foundShorthands.contains(shorthand)) { + foundShorthands.add(shorthand); + shorthandValues.set(shorthand, shorthandValue(style, shorthand)); + } + property.set("value", style->getPropertyValue(name)); + properties.set(i, property); + } +} + +ScriptArray InspectorDOMAgent::buildArrayForDisabledStyleProperties(DisabledStyleDeclaration& declaration) +{ + int counter = 0; + ScriptArray properties = m_frontend->newScriptArray(); + for (DisabledStyleDeclaration::iterator it = declaration.begin(); it != declaration.end(); ++it) { + ScriptObject property = m_frontend->newScriptObject(); + property.set("name", it->first); + property.set("value", it->second.first); + property.set("priority", it->second.second); + properties.set(counter++, property); + } + return properties; +} + +ScriptObject InspectorDOMAgent::buildObjectForStyleSheet(CSSStyleSheet* styleSheet) +{ + ScriptObject result = m_frontend->newScriptObject(); + result.set("disabled", styleSheet->disabled()); + result.set("href", styleSheet->href()); + result.set("title", styleSheet->title()); + result.set("documentElementId", m_documentNodeToIdMap.get(styleSheet->doc())); + ScriptArray cssRules = m_frontend->newScriptArray(); + result.set("cssRules", cssRules); + PassRefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet, true); + if (!cssRuleList) + return result; + unsigned counter = 0; + for (unsigned i = 0; i < cssRuleList->length(); ++i) { + CSSRule* rule = cssRuleList->item(i); + if (rule->isStyleRule()) + cssRules.set(counter++, buildObjectForRule(static_cast<CSSStyleRule*>(rule))); + } + return result; +} + +ScriptObject InspectorDOMAgent::buildObjectForRule(CSSStyleRule* rule) +{ + CSSStyleSheet* parentStyleSheet = rule->parentStyleSheet(); + + ScriptObject result = m_frontend->newScriptObject(); + result.set("selectorText", rule->selectorText()); + result.set("cssText", rule->cssText()); + result.set("sourceLine", rule->sourceLine()); + if (parentStyleSheet) { + ScriptObject parentStyleSheetValue = m_frontend->newScriptObject(); + result.set("parentStyleSheet", parentStyleSheetValue); + parentStyleSheetValue.set("href", parentStyleSheet->href()); + } + bool isUserAgent = parentStyleSheet && !parentStyleSheet->ownerNode() && parentStyleSheet->href().isEmpty(); + bool isUser = parentStyleSheet && parentStyleSheet->ownerNode() && parentStyleSheet->ownerNode()->nodeName() == "#document"; + result.set("isUserAgent", isUserAgent); + result.set("isUser", isUser); + result.set("isViaInspector", rule->parentStyleSheet() == m_inspectorStyleSheet.get()); + + // Bind editable scripts only. + bool bind = !isUserAgent && !isUser; + result.set("style", buildObjectForStyle(rule->style(), bind)); + + if (bind) + result.set("id", bindRule(rule)); + return result; +} + +Vector<String> InspectorDOMAgent::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; +} + +String InspectorDOMAgent::shorthandValue(CSSStyleDeclaration* style, const String& shorthandProperty) +{ + String value = style->getPropertyValue(shorthandProperty); + if (value.isEmpty()) { + // Some shorthands (like border) return a null value, so compute a shorthand value. + // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15823 is fixed. + 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; +} + +String InspectorDOMAgent::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; +} + +bool InspectorDOMAgent::ruleAffectsNode(CSSStyleRule* rule, Node* node) +{ + if (!node) + return false; + ExceptionCode ec = 0; + RefPtr<NodeList> nodes = node->ownerDocument()->querySelectorAll(rule->selectorText(), ec); + if (ec) + return false; + for (unsigned i = 0; i < nodes->length(); ++i) { + if (nodes->item(i) == node) + return true; + } + return false; +} + +ScriptArray InspectorDOMAgent::toArray(const Vector<String>& data) +{ + ScriptArray result = m_frontend->newScriptArray(); + for (unsigned i = 0; i < data.size(); ++i) + result.set(i, data[i]); + return result; +} + } // namespace WebCore #endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorDOMAgent.h b/WebCore/inspector/InspectorDOMAgent.h index d7334b7..7211ed2 100644 --- a/WebCore/inspector/InspectorDOMAgent.h +++ b/WebCore/inspector/InspectorDOMAgent.h @@ -45,6 +45,11 @@ namespace WebCore { class ContainerNode; + class CSSRule; + class CSSRuleList; + class CSSStyleDeclaration; + class CSSStyleRule; + class CSSStyleSheet; class Element; class Event; class Document; @@ -87,13 +92,27 @@ namespace WebCore { virtual bool operator==(const EventListener& other); - // Methods called from the frontend. + // Methods called from the frontend for DOM nodes inspection. void getChildNodes(long callId, long nodeId); void setAttribute(long callId, long elementId, const String& name, const String& value); void removeAttribute(long callId, long elementId, const String& name); + void removeNode(long callId, long nodeId); + void changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded); void setTextNodeValue(long callId, long nodeId, const String& value); void getEventListenersForNode(long callId, long nodeId); + // Methods called from the frontend for CSS styles inspection. + void getStyles(long callId, long nodeId, bool authorOnly); + void getAllStyles(long callId); + void getInlineStyle(long callId, long nodeId); + void getComputedStyle(long callId, long nodeId); + void applyStyleText(long callId, long styleId, const String& styleText, const String& propertyName); + void setStyleText(long callId, long styleId, const String& cssText); + void setStyleProperty(long callId, long styleId, const String& name, const String& value); + void toggleStyleEnabled(long callId, long styleId, const String& propertyName, bool disabled); + void setRuleSelector(long callId, long ruleId, const String& selector, long selectedNodeId); + void addRule(long callId, const String& selector, long selectedNodeId); + // Methods called from the InspectorController. void setDocument(Document* document); void releaseDanglingNodes(); @@ -107,22 +126,29 @@ namespace WebCore { long pushNodePathToFrontend(Node* node); void pushChildNodesToFrontend(long nodeId); - private: + private: + typedef std::pair<String, String> PropertyValueAndPriority; + typedef HashMap<String, PropertyValueAndPriority> DisabledStyleDeclaration; + void startListening(Document* document); void stopListening(Document* document); virtual void handleEvent(ScriptExecutionContext*, Event* event); + // Node-related methods. typedef HashMap<RefPtr<Node>, long> NodeToIdMap; long bind(Node* node, NodeToIdMap* nodesMap); void unbind(Node* node, NodeToIdMap* nodesMap); bool pushDocumentToFrontend(); + ScriptObject buildObjectForAttributeStyles(Element* element); + ScriptArray buildArrayForCSSRules(CSSRuleList*); + ScriptArray buildArrayForPseudoElements(Element* element, bool authorOnly); + ScriptObject buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap); ScriptArray buildArrayForElementAttributes(Element* element); ScriptArray buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap); - ScriptObject buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node); // We represent embedded doms as a part of the same hierarchy. Hence we treat children of frame owners differently. @@ -136,6 +162,20 @@ namespace WebCore { Document* mainFrameDocument() const; String documentURLString(Document* document) const; + + long bindStyle(CSSStyleDeclaration*); + long bindRule(CSSStyleRule*); + ScriptObject buildObjectForStyle(CSSStyleDeclaration*, bool bind); + void populateObjectWithStyleProperties(CSSStyleDeclaration*, ScriptObject& result); + ScriptArray buildArrayForDisabledStyleProperties(DisabledStyleDeclaration&); + ScriptObject buildObjectForRule(CSSStyleRule*); + ScriptObject buildObjectForStyleSheet(CSSStyleSheet*); + Vector<String> longhandProperties(CSSStyleDeclaration*, const String& shorthandProperty); + String shorthandValue(CSSStyleDeclaration*, const String& shorthandProperty); + String shorthandPriority(CSSStyleDeclaration*, const String& shorthandProperty); + bool ruleAffectsNode(CSSStyleRule*, Node*); + ScriptArray toArray(const Vector<String>& data); + void discardBindings(); InspectorFrontend* m_frontend; @@ -146,6 +186,21 @@ namespace WebCore { HashMap<long, NodeToIdMap*> m_idToNodesMap; HashSet<long> m_childrenRequested; long m_lastNodeId; + + typedef HashMap<CSSStyleDeclaration*, long> StyleToIdMap; + typedef HashMap<long, RefPtr<CSSStyleDeclaration> > IdToStyleMap; + StyleToIdMap m_styleToId; + IdToStyleMap m_idToStyle; + typedef HashMap<CSSStyleRule*, long> RuleToIdMap; + typedef HashMap<long, RefPtr<CSSStyleRule> > IdToRuleMap; + RuleToIdMap m_ruleToId; + IdToRuleMap m_idToRule; + typedef HashMap<long, DisabledStyleDeclaration> IdToDisabledStyleMap; + IdToDisabledStyleMap m_idToDisabledStyle; + RefPtr<CSSStyleSheet> m_inspectorStyleSheet; + + long m_lastStyleId; + long m_lastRuleId; ListHashSet<RefPtr<Document> > m_documents; }; diff --git a/WebCore/inspector/InspectorDOMStorageResource.cpp b/WebCore/inspector/InspectorDOMStorageResource.cpp index c93e987..af0530a 100644 --- a/WebCore/inspector/InspectorDOMStorageResource.cpp +++ b/WebCore/inspector/InspectorDOMStorageResource.cpp @@ -104,7 +104,8 @@ void InspectorDOMStorageResource::handleEvent(ScriptExecutionContext*, Event* ev ASSERT(eventNames().storageEvent == event->type()); StorageEvent* storageEvent = static_cast<StorageEvent*>(event); Storage* storage = storageEvent->storageArea(); - bool isLocalStorage = storage->frame()->domWindow()->localStorage() == storage; + ExceptionCode ec = 0; + bool isLocalStorage = (storage->frame()->domWindow()->localStorage(ec) == storage && !ec); if (isSameHostAndType(storage->frame(), isLocalStorage)) m_frontend->updateDOMStorage(m_id); } diff --git a/WebCore/inspector/InspectorFrontend.cpp b/WebCore/inspector/InspectorFrontend.cpp index 90b60f4..d89ae71 100755 --- a/WebCore/inspector/InspectorFrontend.cpp +++ b/WebCore/inspector/InspectorFrontend.cpp @@ -37,6 +37,7 @@ #include "InjectedScript.h" #include "InjectedScriptHost.h" #include "InspectorController.h" +#include "InspectorWorkerResource.h" #include "Node.h" #include "ScriptFunctionCall.h" #include "ScriptObject.h" @@ -46,17 +47,10 @@ #include "SerializedScriptValue.h" #include <wtf/OwnPtr.h> -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) -#include <parser/SourceCode.h> -#include <runtime/JSValue.h> -#include <runtime/UString.h> -#endif - namespace WebCore { -InspectorFrontend::InspectorFrontend(InspectorController* inspectorController, ScriptObject webInspector) - : m_inspectorController(inspectorController) - , m_webInspector(webInspector) +InspectorFrontend::InspectorFrontend(ScriptObject webInspector) + : m_webInspector(webInspector) { } @@ -65,6 +59,18 @@ InspectorFrontend::~InspectorFrontend() m_webInspector = ScriptObject(); } +void InspectorFrontend::close() +{ + ScriptFunctionCall function(m_webInspector, "close"); + function.call(); +} + +void InspectorFrontend::inspectedPageDestroyed() +{ + ScriptFunctionCall function(m_webInspector, "inspectedPageDestroyed"); + function.call(); +} + ScriptArray InspectorFrontend::newScriptArray() { return ScriptArray::createNew(scriptState()); @@ -96,7 +102,7 @@ void InspectorFrontend::updateConsoleMessageExpiredCount(unsigned count) function.call(); } -void InspectorFrontend::addConsoleMessage(const ScriptObject& messageObj, const Vector<ScriptString>& frames, ScriptState* scriptState, const Vector<ScriptValue> arguments, const String& message) +void InspectorFrontend::addConsoleMessage(const ScriptObject& messageObj, const Vector<ScriptString>& frames, const Vector<RefPtr<SerializedScriptValue> >& arguments, const String& message) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("addConsoleMessage"); @@ -105,10 +111,8 @@ void InspectorFrontend::addConsoleMessage(const ScriptObject& messageObj, const for (unsigned i = 0; i < frames.size(); ++i) function.appendArgument(frames[i]); } else if (!arguments.isEmpty()) { - InjectedScript injectedScript = m_inspectorController->injectedScriptHost()->injectedScriptFor(scriptState); for (unsigned i = 0; i < arguments.size(); ++i) { - RefPtr<SerializedScriptValue> serializedValue = injectedScript.wrapForConsole(arguments[i]); - ScriptValue scriptValue = ScriptValue::deserialize(this->scriptState(), serializedValue.get()); + ScriptValue scriptValue = ScriptValue::deserialize(scriptState(), arguments[i].get()); if (scriptValue.hasNoValue()) { ASSERT_NOT_REACHED(); return; @@ -153,7 +157,7 @@ void InspectorFrontend::removeResource(unsigned long identifier) function.call(); } -void InspectorFrontend::didGetResourceContent(int callId, const String& content) +void InspectorFrontend::didGetResourceContent(long callId, const String& content) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didGetResourceContent"); @@ -182,6 +186,9 @@ void InspectorFrontend::showPanel(int panel) { const char* showFunctionName; switch (panel) { + case InspectorController::AuditsPanel: + showFunctionName = "showAuditsPanel"; + break; case InspectorController::ConsolePanel: showFunctionName = "showConsolePanel"; break; @@ -191,15 +198,15 @@ void InspectorFrontend::showPanel(int panel) case InspectorController::ResourcesPanel: showFunctionName = "showResourcesPanel"; break; - case InspectorController::ScriptsPanel: - showFunctionName = "showScriptsPanel"; - break; case InspectorController::TimelinePanel: showFunctionName = "showTimelinePanel"; break; case InspectorController::ProfilesPanel: showFunctionName = "showProfilesPanel"; break; + case InspectorController::ScriptsPanel: + showFunctionName = "showScriptsPanel"; + break; case InspectorController::StoragePanel: showFunctionName = "showStoragePanel"; break; @@ -222,6 +229,19 @@ void InspectorFrontend::reset() callSimpleFunction("reset"); } +void InspectorFrontend::bringToFront() +{ + callSimpleFunction("bringToFront"); +} + +void InspectorFrontend::inspectedURLChanged(const String& url) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("inspectedURLChanged"); + function.appendArgument(url); + function.call(); +} + void InspectorFrontend::resourceTrackingWasEnabled() { callSimpleFunction("resourceTrackingWasEnabled"); @@ -232,6 +252,25 @@ void InspectorFrontend::resourceTrackingWasDisabled() callSimpleFunction("resourceTrackingWasDisabled"); } + +void InspectorFrontend::searchingForNodeWasEnabled() +{ + callSimpleFunction("searchingForNodeWasEnabled"); +} + +void InspectorFrontend::searchingForNodeWasDisabled() +{ + callSimpleFunction("searchingForNodeWasDisabled"); +} + +void InspectorFrontend::updatePauseOnExceptionsState(long state) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("updatePauseOnExceptionsState"); + function.appendArgument(state); + function.call(); +} + void InspectorFrontend::timelineProfilerWasStarted() { callSimpleFunction("timelineProfilerWasStarted"); @@ -250,7 +289,7 @@ void InspectorFrontend::addRecordToTimeline(const ScriptObject& record) function.call(); } -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) +#if ENABLE(JAVASCRIPT_DEBUGGER) void InspectorFrontend::attachDebuggerWhenShown() { callSimpleFunction("attachDebuggerWhenShown"); @@ -266,24 +305,36 @@ void InspectorFrontend::debuggerWasDisabled() callSimpleFunction("debuggerWasDisabled"); } -void InspectorFrontend::parsedScriptSource(const JSC::SourceCode& source) +void InspectorFrontend::parsedScriptSource(const String& sourceID, const String& url, const String& data, int firstLine) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("parsedScriptSource"); - function.appendArgument(JSC::UString(JSC::UString::from(source.provider()->asID()))); - function.appendArgument(source.provider()->url()); - function.appendArgument(JSC::UString(source.data(), source.length())); - function.appendArgument(source.firstLine()); + function.appendArgument(sourceID); + function.appendArgument(url); + function.appendArgument(data); + function.appendArgument(firstLine); + function.call(); +} + +void InspectorFrontend::restoredBreakpoint(const String& sourceID, const String& url, int line, bool enabled, const String& condition) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("restoredBreakpoint"); + function.appendArgument(sourceID); + function.appendArgument(url); + function.appendArgument(line); + function.appendArgument(enabled); + function.appendArgument(condition); function.call(); } -void InspectorFrontend::failedToParseScriptSource(const JSC::SourceCode& source, int errorLine, const JSC::UString& errorMessage) +void InspectorFrontend::failedToParseScriptSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("failedToParseScriptSource"); - function.appendArgument(source.provider()->url()); - function.appendArgument(JSC::UString(source.data(), source.length())); - function.appendArgument(source.firstLine()); + function.appendArgument(url); + function.appendArgument(data); + function.appendArgument(firstLine); function.appendArgument(errorLine); function.appendArgument(errorMessage); function.call(); @@ -302,9 +353,7 @@ void InspectorFrontend::resumedScript() { callSimpleFunction("resumedScript"); } -#endif -#if ENABLE(JAVASCRIPT_DEBUGGER) void InspectorFrontend::profilerWasEnabled() { callSimpleFunction("profilerWasEnabled"); @@ -331,7 +380,7 @@ void InspectorFrontend::setRecordingProfile(bool isProfiling) function.call(); } -void InspectorFrontend::didGetProfileHeaders(int callId, const ScriptArray& headers) +void InspectorFrontend::didGetProfileHeaders(long callId, const ScriptArray& headers) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didGetProfileHeaders"); @@ -340,7 +389,7 @@ void InspectorFrontend::didGetProfileHeaders(int callId, const ScriptArray& head function.call(); } -void InspectorFrontend::didGetProfile(int callId, const ScriptValue& profile) +void InspectorFrontend::didGetProfile(long callId, const ScriptValue& profile) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didGetProfile"); @@ -366,7 +415,7 @@ void InspectorFrontend::setDetachedRoot(const ScriptObject& root) function.call(); } -void InspectorFrontend::setChildNodes(int parentId, const ScriptArray& nodes) +void InspectorFrontend::setChildNodes(long parentId, const ScriptArray& nodes) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("setChildNodes"); @@ -375,7 +424,7 @@ void InspectorFrontend::setChildNodes(int parentId, const ScriptArray& nodes) function.call(); } -void InspectorFrontend::childNodeCountUpdated(int id, int newValue) +void InspectorFrontend::childNodeCountUpdated(long id, int newValue) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("childNodeCountUpdated"); @@ -384,7 +433,7 @@ void InspectorFrontend::childNodeCountUpdated(int id, int newValue) function.call(); } -void InspectorFrontend::childNodeInserted(int parentId, int prevId, const ScriptObject& node) +void InspectorFrontend::childNodeInserted(long parentId, long prevId, const ScriptObject& node) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("childNodeInserted"); @@ -394,7 +443,7 @@ void InspectorFrontend::childNodeInserted(int parentId, int prevId, const Script function.call(); } -void InspectorFrontend::childNodeRemoved(int parentId, int id) +void InspectorFrontend::childNodeRemoved(long parentId, long id) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("childNodeRemoved"); @@ -403,7 +452,7 @@ void InspectorFrontend::childNodeRemoved(int parentId, int id) function.call(); } -void InspectorFrontend::attributesUpdated(int id, const ScriptArray& attributes) +void InspectorFrontend::attributesUpdated(long id, const ScriptArray& attributes) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("attributesUpdated"); @@ -412,7 +461,7 @@ void InspectorFrontend::attributesUpdated(int id, const ScriptArray& attributes) function.call(); } -void InspectorFrontend::didRemoveNode(int callId, int nodeId) +void InspectorFrontend::didRemoveNode(long callId, long nodeId) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didRemoveNode"); @@ -421,7 +470,16 @@ void InspectorFrontend::didRemoveNode(int callId, int nodeId) function.call(); } -void InspectorFrontend::didGetChildNodes(int callId) +void InspectorFrontend::didChangeTagName(long callId, long nodeId) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didChangeTagName"); + function.appendArgument(callId); + function.appendArgument(nodeId); + function.call(); +} + +void InspectorFrontend::didGetChildNodes(long callId) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didGetChildNodes"); @@ -429,7 +487,7 @@ void InspectorFrontend::didGetChildNodes(int callId) function.call(); } -void InspectorFrontend::didApplyDomChange(int callId, bool success) +void InspectorFrontend::didApplyDomChange(long callId, bool success) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didApplyDomChange"); @@ -438,7 +496,7 @@ void InspectorFrontend::didApplyDomChange(int callId, bool success) function.call(); } -void InspectorFrontend::didGetEventListenersForNode(int callId, int nodeId, ScriptArray& listenersArray) +void InspectorFrontend::didGetEventListenersForNode(long callId, long nodeId, const ScriptArray& listenersArray) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didGetEventListenersForNode"); @@ -448,7 +506,121 @@ void InspectorFrontend::didGetEventListenersForNode(int callId, int nodeId, Scri function.call(); } -void InspectorFrontend::didGetCookies(int callId, const ScriptArray& cookies, const String& cookiesString) +void InspectorFrontend::didGetStyles(long callId, const ScriptValue& styles) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didGetStyles"); + function.appendArgument(callId); + function.appendArgument(styles); + function.call(); +} + +void InspectorFrontend::didGetAllStyles(long callId, const ScriptArray& styles) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didGetAllStyles"); + function.appendArgument(callId); + function.appendArgument(styles); + function.call(); +} + +void InspectorFrontend::didGetComputedStyle(long callId, const ScriptValue& style) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didGetComputedStyle"); + function.appendArgument(callId); + function.appendArgument(style); + function.call(); +} + +void InspectorFrontend::didGetInlineStyle(long callId, const ScriptValue& style) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didGetInlineStyle"); + function.appendArgument(callId); + function.appendArgument(style); + function.call(); +} + +void InspectorFrontend::didApplyStyleText(long callId, bool success, const ScriptValue& style, const ScriptArray& changedProperties) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didApplyStyleText"); + function.appendArgument(callId); + function.appendArgument(success); + function.appendArgument(style); + function.appendArgument(changedProperties); + function.call(); +} + +void InspectorFrontend::didSetStyleText(long callId, bool success) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didSetStyleText"); + function.appendArgument(callId); + function.appendArgument(success); + function.call(); +} + +void InspectorFrontend::didSetStyleProperty(long callId, bool success) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didSetStyleProperty"); + function.appendArgument(callId); + function.appendArgument(success); + function.call(); +} + +void InspectorFrontend::didToggleStyleEnabled(long callId, const ScriptValue& style) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didToggleStyleEnabled"); + function.appendArgument(callId); + function.appendArgument(style); + function.call(); +} + +void InspectorFrontend::didSetRuleSelector(long callId, const ScriptValue& rule, bool selectorAffectsNode) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didSetRuleSelector"); + function.appendArgument(callId); + function.appendArgument(rule); + function.appendArgument(selectorAffectsNode); + function.call(); +} + +void InspectorFrontend::didAddRule(long callId, const ScriptValue& rule, bool selectorAffectsNode) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didAddRule"); + function.appendArgument(callId); + function.appendArgument(rule); + function.appendArgument(selectorAffectsNode); + function.call(); +} + +#if ENABLE(WORKERS) +void InspectorFrontend::didCreateWorker(const InspectorWorkerResource& worker) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didCreateWorker"); + function.appendArgument(worker.id()); + function.appendArgument(worker.url()); + function.appendArgument(worker.isSharedWorker()); + function.call(); +} + +void InspectorFrontend::didDestroyWorker(const InspectorWorkerResource& worker) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didDestroyWorker"); + function.appendArgument(worker.id()); + function.call(); +} +#endif // ENABLE(WORKERS) + +void InspectorFrontend::didGetCookies(long callId, const ScriptArray& cookies, const String& cookiesString) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didGetCookies"); @@ -458,7 +630,7 @@ void InspectorFrontend::didGetCookies(int callId, const ScriptArray& cookies, co function.call(); } -void InspectorFrontend::didDispatchOnInjectedScript(int callId, SerializedScriptValue* result, bool isException) +void InspectorFrontend::didDispatchOnInjectedScript(long callId, SerializedScriptValue* result, bool isException) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didDispatchOnInjectedScript"); @@ -491,7 +663,8 @@ void InspectorFrontend::selectDatabase(int databaseId) function.appendArgument(databaseId); function.call(); } -void InspectorFrontend::didGetDatabaseTableNames(int callId, const ScriptArray& tableNames) + +void InspectorFrontend::didGetDatabaseTableNames(long callId, const ScriptArray& tableNames) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didGetDatabaseTableNames"); @@ -512,7 +685,7 @@ bool InspectorFrontend::addDOMStorage(const ScriptObject& domStorageObj) return !hadException; } -void InspectorFrontend::selectDOMStorage(int storageId) +void InspectorFrontend::selectDOMStorage(long storageId) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("selectDOMStorage"); @@ -520,7 +693,7 @@ void InspectorFrontend::selectDOMStorage(int storageId) function.call(); } -void InspectorFrontend::didGetDOMStorageEntries(int callId, const ScriptArray& entries) +void InspectorFrontend::didGetDOMStorageEntries(long callId, const ScriptArray& entries) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didGetDOMStorageEntries"); @@ -529,7 +702,7 @@ void InspectorFrontend::didGetDOMStorageEntries(int callId, const ScriptArray& e function.call(); } -void InspectorFrontend::didSetDOMStorageItem(int callId, bool success) +void InspectorFrontend::didSetDOMStorageItem(long callId, bool success) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didSetDOMStorageItem"); @@ -538,7 +711,7 @@ void InspectorFrontend::didSetDOMStorageItem(int callId, bool success) function.call(); } -void InspectorFrontend::didRemoveDOMStorageItem(int callId, bool success) +void InspectorFrontend::didRemoveDOMStorageItem(long callId, bool success) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("didRemoveDOMStorageItem"); @@ -547,7 +720,7 @@ void InspectorFrontend::didRemoveDOMStorageItem(int callId, bool success) function.call(); } -void InspectorFrontend::updateDOMStorage(int storageId) +void InspectorFrontend::updateDOMStorage(long storageId) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("updateDOMStorage"); @@ -577,7 +750,7 @@ void InspectorFrontend::contextMenuCleared() callSimpleFunction("contextMenuCleared"); } -void InspectorFrontend::evaluateForTestInFrontend(int callId, const String& script) +void InspectorFrontend::evaluateForTestInFrontend(long callId, const String& script) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("evaluateForTestInFrontend"); diff --git a/WebCore/inspector/InspectorFrontend.h b/WebCore/inspector/InspectorFrontend.h index 1a37256..0e7d7b3 100644 --- a/WebCore/inspector/InspectorFrontend.h +++ b/WebCore/inspector/InspectorFrontend.h @@ -35,29 +35,24 @@ #include "ScriptState.h" #include <wtf/PassOwnPtr.h> -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) -namespace JSC { - class JSValue; - class SourceCode; - class UString; -} -#endif - namespace WebCore { class ConsoleMessage; class Database; class Frame; - class InspectorController; class InspectorResource; class Node; class ScriptString; class SerializedScriptValue; class Storage; + class InspectorWorkerResource; class InspectorFrontend : public Noncopyable { public: - InspectorFrontend(InspectorController* inspectorController, ScriptObject webInspector); + InspectorFrontend(ScriptObject webInspector); ~InspectorFrontend(); + + void close(); + void inspectedPageDestroyed(); ScriptArray newScriptArray(); ScriptObject newScriptObject(); @@ -67,74 +62,100 @@ namespace WebCore { void populateFrontendSettings(const String& settings); void updateConsoleMessageExpiredCount(unsigned count); - void addConsoleMessage(const ScriptObject& messageObj, const Vector<ScriptString>& frames, ScriptState*, const Vector<ScriptValue> arguments, const String& message); + void addConsoleMessage(const ScriptObject& messageObj, const Vector<ScriptString>& frames, const Vector<RefPtr<SerializedScriptValue> >& arguments, const String& message); void updateConsoleMessageRepeatCount(unsigned count); void clearConsoleMessages(); bool updateResource(unsigned long identifier, const ScriptObject& resourceObj); void removeResource(unsigned long identifier); - void didGetResourceContent(int callId, const String& content); + void didGetResourceContent(long callId, const String& content); void updateFocusedNode(long nodeId); void setAttachedWindow(bool attached); void showPanel(int panel); void populateInterface(); void reset(); + + void bringToFront(); + void inspectedURLChanged(const String&); void resourceTrackingWasEnabled(); void resourceTrackingWasDisabled(); -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) + void searchingForNodeWasEnabled(); + void searchingForNodeWasDisabled(); + + void updatePauseOnExceptionsState(long state); + +#if ENABLE(JAVASCRIPT_DEBUGGER) void attachDebuggerWhenShown(); void debuggerWasEnabled(); void debuggerWasDisabled(); - void parsedScriptSource(const JSC::SourceCode&); - void failedToParseScriptSource(const JSC::SourceCode&, int errorLine, const JSC::UString& errorMessage); + + void parsedScriptSource(const String& sourceID, const String& url, const String& data, int firstLine); + void restoredBreakpoint(const String& sourceID, const String& url, int line, bool enabled, const String& condition); + void failedToParseScriptSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage); void pausedScript(SerializedScriptValue* callFrames); void resumedScript(); -#endif -#if ENABLE(JAVASCRIPT_DEBUGGER) + void profilerWasEnabled(); void profilerWasDisabled(); void addProfileHeader(const ScriptValue& profile); void setRecordingProfile(bool isProfiling); - void didGetProfileHeaders(int callId, const ScriptArray& headers); - void didGetProfile(int callId, const ScriptValue& profile); + void didGetProfileHeaders(long callId, const ScriptArray& headers); + void didGetProfile(long callId, const ScriptValue& profile); #endif #if ENABLE(DATABASE) bool addDatabase(const ScriptObject& dbObj); void selectDatabase(int databaseId); - void didGetDatabaseTableNames(int callId, const ScriptArray& tableNames); + void didGetDatabaseTableNames(long callId, const ScriptArray& tableNames); #endif #if ENABLE(DOM_STORAGE) bool addDOMStorage(const ScriptObject& domStorageObj); - void selectDOMStorage(int storageId); - void didGetDOMStorageEntries(int callId, const ScriptArray& entries); - void didSetDOMStorageItem(int callId, bool success); - void didRemoveDOMStorageItem(int callId, bool success); - void updateDOMStorage(int storageId); + void selectDOMStorage(long storageId); + void didGetDOMStorageEntries(long callId, const ScriptArray& entries); + void didSetDOMStorageItem(long callId, bool success); + void didRemoveDOMStorageItem(long callId, bool success); + void updateDOMStorage(long storageId); #endif void setDocument(const ScriptObject& root); void setDetachedRoot(const ScriptObject& root); - void setChildNodes(int parentId, const ScriptArray& nodes); - void childNodeCountUpdated(int id, int newValue); - void childNodeInserted(int parentId, int prevId, const ScriptObject& node); - void childNodeRemoved(int parentId, int id); - void attributesUpdated(int id, const ScriptArray& attributes); - void didGetChildNodes(int callId); - void didApplyDomChange(int callId, bool success); - void didGetEventListenersForNode(int callId, int nodeId, ScriptArray& listenersArray); - void didRemoveNode(int callId, int nodeId); + void setChildNodes(long parentId, const ScriptArray& nodes); + void childNodeCountUpdated(long id, int newValue); + void childNodeInserted(long parentId, long prevId, const ScriptObject& node); + void childNodeRemoved(long parentId, long id); + void attributesUpdated(long id, const ScriptArray& attributes); + void didGetChildNodes(long callId); + void didApplyDomChange(long callId, bool success); + void didGetEventListenersForNode(long callId, long nodeId, const ScriptArray& listenersArray); + void didRemoveNode(long callId, long nodeId); + void didChangeTagName(long callId, long nodeId); + + void didGetStyles(long callId, const ScriptValue& styles); + void didGetAllStyles(long callId, const ScriptArray& styles); + void didGetInlineStyle(long callId, const ScriptValue& style); + void didGetComputedStyle(long callId, const ScriptValue& style); + void didApplyStyleText(long callId, bool success, const ScriptValue& style, const ScriptArray& changedProperties); + void didSetStyleText(long callId, bool success); + void didSetStyleProperty(long callId, bool success); + void didToggleStyleEnabled(long callId, const ScriptValue& style); + void didSetRuleSelector(long callId, const ScriptValue& rule, bool selectorAffectsNode); + void didAddRule(long callId, const ScriptValue& rule, bool selectorAffectsNode); void timelineProfilerWasStarted(); void timelineProfilerWasStopped(); void addRecordToTimeline(const ScriptObject&); - void didGetCookies(int callId, const ScriptArray& cookies, const String& cookiesString); - void didDispatchOnInjectedScript(int callId, SerializedScriptValue* result, bool isException); +#if ENABLE(WORKERS) + void didCreateWorker(const InspectorWorkerResource&); + void didDestroyWorker(const InspectorWorkerResource&); +#endif // ENABLE(WORKER) + + void didGetCookies(long callId, const ScriptArray& cookies, const String& cookiesString); + void didDispatchOnInjectedScript(long callId, SerializedScriptValue* result, bool isException); void addNodesToSearchResult(const String& nodeIds); @@ -143,10 +164,9 @@ namespace WebCore { ScriptState* scriptState() const { return m_webInspector.scriptState(); } - void evaluateForTestInFrontend(int callId, const String& script); + void evaluateForTestInFrontend(long callId, const String& script); private: void callSimpleFunction(const String& functionName); - InspectorController* m_inspectorController; ScriptObject m_webInspector; }; diff --git a/WebCore/inspector/InspectorFrontendClient.h b/WebCore/inspector/InspectorFrontendClient.h new file mode 100644 index 0000000..515388c --- /dev/null +++ b/WebCore/inspector/InspectorFrontendClient.h @@ -0,0 +1,66 @@ +/* + * 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 InspectorFrontendClient_h +#define InspectorFrontendClient_h + +#include <wtf/Vector.h> + +namespace WebCore { + +class ContextMenuItem; +class Event; +class String; + +class InspectorFrontendClient { +public: + virtual ~InspectorFrontendClient() { } + + virtual void windowObjectCleared() = 0; + virtual void frontendLoaded() = 0; + + virtual void moveWindowBy(float x, float y) = 0; + + virtual String localizedStringsURL() = 0; + virtual String hiddenPanels() = 0; + + virtual void bringToFront() = 0; + virtual void closeWindow() = 0; + + virtual void requestAttachWindow() = 0; + virtual void requestDetachWindow() = 0; + virtual void changeAttachedWindowHeight(unsigned) = 0; + + virtual void inspectedURLChanged(const String&) = 0; +}; + +} // namespace WebCore + +#endif diff --git a/WebCore/inspector/InspectorFrontendClientLocal.cpp b/WebCore/inspector/InspectorFrontendClientLocal.cpp new file mode 100644 index 0000000..18c52da --- /dev/null +++ b/WebCore/inspector/InspectorFrontendClientLocal.cpp @@ -0,0 +1,167 @@ +/* + * 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 "InspectorFrontendClientLocal.h" + +#if ENABLE(INSPECTOR) + +#include "Chrome.h" +#include "FloatRect.h" +#include "Frame.h" +#include "FrameView.h" +#include "InspectorController.h" +#include "InspectorFrontend.h" +#include "InspectorFrontendHost.h" +#include "Page.h" +#include "PlatformString.h" +#include "ScriptFunctionCall.h" +#include "ScriptObject.h" + +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; + +InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectorController, Page* frontendPage) + : m_inspectorController(inspectorController) + , m_frontendPage(frontendPage) + , m_frontendScriptState(0) +{ +} + +InspectorFrontendClientLocal::~InspectorFrontendClientLocal() +{ + if (m_frontendHost) + m_frontendHost->disconnectClient(); + m_frontendScriptState = 0; + m_frontendPage = 0; + m_inspectorController = 0; +} + +void InspectorFrontendClientLocal::windowObjectCleared() +{ + // Grant the inspector the ability to script the inspected page. + m_frontendPage->mainFrame()->document()->securityOrigin()->grantUniversalAccess(); + // FIXME: don't keep reference to the script state + m_frontendScriptState = scriptStateFromPage(debuggerWorld(), m_frontendPage); + ScriptGlobalObject::set(m_frontendScriptState, "InspectorBackend", m_inspectorController->inspectorBackend()); + m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage); + ScriptGlobalObject::set(m_frontendScriptState, "InspectorFrontendHost", m_frontendHost.get()); +} + +void InspectorFrontendClientLocal::frontendLoaded() +{ + bringToFront(); + // Create InspectorFrontend and set it to InspectorController. + ASSERT(m_frontendScriptState); + ScriptObject webInspectorObj; + if (!ScriptGlobalObject::get(m_frontendScriptState, "WebInspector", webInspectorObj)) { + ASSERT_NOT_REACHED(); + return; + } + m_inspectorController->setFrontend(new InspectorFrontend(webInspectorObj)); +} + +void InspectorFrontendClientLocal::requestAttachWindow() +{ + if (!canAttachWindow()) + return; + attachWindow(); + setAttachedWindow(true); +} + +void InspectorFrontendClientLocal::requestDetachWindow() +{ + detachWindow(); + setAttachedWindow(false); +} + +bool InspectorFrontendClientLocal::canAttachWindow() +{ + unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight(); + + // Don't allow the attach if the window would be too small to accommodate the minimum inspector height. + return minimumAttachedHeight <= inspectedPageHeight * maximumAttachedHeightRatio; +} + +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)); + setAttachedWindowHeight(attachedHeight); +} + +void InspectorFrontendClientLocal::moveWindowBy(float x, float y) +{ + FloatRect frameRect = m_frontendPage->chrome()->windowRect(); + frameRect.move(x, y); + m_frontendPage->chrome()->setWindowRect(frameRect); +} + +void InspectorFrontendClientLocal::setAttachedWindow(bool attached) +{ + ScriptObject webInspectorObj; + if (!ScriptGlobalObject::get(m_frontendScriptState, "WebInspector", webInspectorObj)) { + ASSERT_NOT_REACHED(); + return; + } + ScriptFunctionCall function(webInspectorObj, "dispatch"); + function.appendArgument("setAttachedWindow"); + function.appendArgument(attached); + function.call(); +} + +void InspectorFrontendClientLocal::restoreAttachedWindowHeight() +{ + unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight(); + String attachedHeight = m_inspectorController->setting(inspectorAttachedHeightName); + bool success = true; + int height = attachedHeight.toInt(&success); + unsigned preferredHeight = success ? height : 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. + // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow + setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight)); +} + +unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight) +{ + using namespace std; + return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio))); +} + +} // namespace WebCore + +#endif diff --git a/WebCore/inspector/InspectorFrontendClientLocal.h b/WebCore/inspector/InspectorFrontendClientLocal.h new file mode 100644 index 0000000..ce661fe --- /dev/null +++ b/WebCore/inspector/InspectorFrontendClientLocal.h @@ -0,0 +1,80 @@ +/* + * 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 InspectorFrontendClientLocal_h +#define InspectorFrontendClientLocal_h + +#include "InspectorFrontendClient.h" +#include "ScriptState.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class InspectorController; +class InspectorFrontendHost; +class Page; + +class InspectorFrontendClientLocal : public InspectorFrontendClient, public Noncopyable { +public: + InspectorFrontendClientLocal(InspectorController*, Page*); + virtual ~InspectorFrontendClientLocal(); + + virtual void windowObjectCleared(); + virtual void frontendLoaded(); + + virtual void moveWindowBy(float x, float y); + + virtual void requestAttachWindow(); + virtual void requestDetachWindow(); + virtual void changeAttachedWindowHeight(unsigned); + + virtual void attachWindow() = 0; + virtual void detachWindow() = 0; + bool canAttachWindow(); + +protected: + virtual void setAttachedWindowHeight(unsigned) = 0; + void setAttachedWindow(bool); + void restoreAttachedWindowHeight(); + +private: + static unsigned constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight); + + friend class FrontendMenuProvider; + InspectorController* m_inspectorController; + Page* m_frontendPage; + ScriptState* m_frontendScriptState; + // TODO(yurys): this ref shouldn't be needed. + RefPtr<InspectorFrontendHost> m_frontendHost; +}; + +} // namespace WebCore + +#endif diff --git a/WebCore/inspector/InspectorFrontendHost.cpp b/WebCore/inspector/InspectorFrontendHost.cpp index 1aeb1d7..70fc3ad 100644 --- a/WebCore/inspector/InspectorFrontendHost.cpp +++ b/WebCore/inspector/InspectorFrontendHost.cpp @@ -41,11 +41,11 @@ #include "FrameLoader.h" #include "HitTestResult.h" #include "HTMLFrameOwnerElement.h" -#include "InspectorClient.h" -#include "InspectorFrontend.h" +#include "InspectorFrontendClient.h" #include "InspectorResource.h" #include "Page.h" #include "Pasteboard.h" +#include "ScriptFunctionCall.h" #include <wtf/RefPtr.h> #include <wtf/StdLibExtras.h> @@ -54,58 +54,142 @@ using namespace std; namespace WebCore { -InspectorFrontendHost::InspectorFrontendHost(InspectorController* inspectorController, InspectorClient* client) - : m_inspectorController(inspectorController) - , m_client(client) +#if ENABLE(CONTEXT_MENUS) +class FrontendMenuProvider : public ContextMenuProvider { +public: + static PassRefPtr<FrontendMenuProvider> create(InspectorFrontendHost* frontendHost, ScriptObject webInspector, const Vector<ContextMenuItem*>& items) + { + return adoptRef(new FrontendMenuProvider(frontendHost, webInspector, items)); + } + + void disconnect() + { + m_webInspector = ScriptObject(); + m_frontendHost = 0; + } + +private: + FrontendMenuProvider(InspectorFrontendHost* frontendHost, ScriptObject webInspector, const Vector<ContextMenuItem*>& items) + : m_frontendHost(frontendHost) + , m_webInspector(webInspector) + , m_items(items) + { + } + + virtual ~FrontendMenuProvider() + { + contextMenuCleared(); + } + + virtual void populateContextMenu(ContextMenu* menu) + { + for (size_t i = 0; i < m_items.size(); ++i) + menu->appendItem(*m_items[i]); + } + + virtual void contextMenuItemSelected(ContextMenuItem* item) + { + if (m_frontendHost) { + int itemNumber = item->action() - ContextMenuItemBaseCustomTag; + + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("contextMenuItemSelected"); + function.appendArgument(itemNumber); + function.call(); + } + } + + virtual void contextMenuCleared() + { + if (m_frontendHost) { + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("contextMenuCleared"); + function.call(); + + m_frontendHost->m_menuProvider = 0; + } + deleteAllValues(m_items); + m_items.clear(); + } + + InspectorFrontendHost* m_frontendHost; + ScriptObject m_webInspector; + Vector<ContextMenuItem*> m_items; +}; +#endif + +InspectorFrontendHost::InspectorFrontendHost(InspectorFrontendClient* client, Page* frontendPage) + : m_client(client) + , m_frontendPage(frontendPage) +#if ENABLE(CONTEXT_MENUS) + , m_menuProvider(0) +#endif { } InspectorFrontendHost::~InspectorFrontendHost() { + ASSERT(!m_client); +} + +void InspectorFrontendHost::disconnectClient() +{ + m_client = 0; +#if ENABLE(CONTEXT_MENUS) if (m_menuProvider) m_menuProvider->disconnect(); +#endif + m_frontendPage = 0; } void InspectorFrontendHost::loaded() { - if (m_inspectorController) - m_inspectorController->scriptObjectReady(); + if (m_client) + m_client->frontendLoaded(); } -void InspectorFrontendHost::attach() +void InspectorFrontendHost::requestAttachWindow() { - if (m_inspectorController) - m_inspectorController->attachWindow(); + if (m_client) + m_client->requestAttachWindow(); } -void InspectorFrontendHost::detach() +void InspectorFrontendHost::requestDetachWindow() { - if (m_inspectorController) - m_inspectorController->detachWindow(); + if (m_client) + m_client->requestDetachWindow(); } void InspectorFrontendHost::closeWindow() { - if (m_inspectorController) - m_inspectorController->closeWindow(); + if (m_client) { + m_client->closeWindow(); + disconnectClient(); // Disconnect from client. + } +} + +void InspectorFrontendHost::bringToFront() +{ + if (m_client) + m_client->bringToFront(); } -void InspectorFrontendHost::windowUnloading() +void InspectorFrontendHost::inspectedURLChanged(const String& newURL) { - if (m_inspectorController) - m_inspectorController->close(); + if (m_client) + m_client->inspectedURLChanged(newURL); } void InspectorFrontendHost::setAttachedWindowHeight(unsigned height) { - if (m_inspectorController) - m_inspectorController->setAttachedWindowHeight(height); + if (m_client) + m_client->changeAttachedWindowHeight(height); } void InspectorFrontendHost::moveWindowBy(float x, float y) const { - if (m_inspectorController) - m_inspectorController->moveWindowBy(x, y); + if (m_client) + m_client->moveWindowBy(x, y); } String InspectorFrontendHost::localizedStringsURL() @@ -118,67 +202,27 @@ String InspectorFrontendHost::hiddenPanels() return m_client->hiddenPanels(); } -const String& InspectorFrontendHost::platform() const -{ -#if PLATFORM(MAC) - DEFINE_STATIC_LOCAL(const String, platform, ("mac")); -#elif OS(WINDOWS) - DEFINE_STATIC_LOCAL(const String, platform, ("windows")); -#elif OS(LINUX) - DEFINE_STATIC_LOCAL(const String, platform, ("linux")); -#else - DEFINE_STATIC_LOCAL(const String, platform, ("unknown")); -#endif - return platform; -} - -const String& InspectorFrontendHost::port() const -{ -#if PLATFORM(QT) - DEFINE_STATIC_LOCAL(const String, port, ("qt")); -#elif PLATFORM(GTK) - DEFINE_STATIC_LOCAL(const String, port, ("gtk")); -#elif PLATFORM(WX) - DEFINE_STATIC_LOCAL(const String, port, ("wx")); -#else - DEFINE_STATIC_LOCAL(const String, port, ("unknown")); -#endif - - return port; -} - void InspectorFrontendHost::copyText(const String& text) { Pasteboard::generalPasteboard()->writePlainText(text); } +#if ENABLE(CONTEXT_MENUS) void InspectorFrontendHost::showContextMenu(Event* event, const Vector<ContextMenuItem*>& items) { - if (!m_inspectorController) - return; - if (!m_inspectorController->windowVisible()) + ASSERT(m_frontendPage); + ScriptState* frontendScriptState = scriptStateFromPage(debuggerWorld(), m_frontendPage); + ScriptObject webInspectorObj; + if (!ScriptGlobalObject::get(frontendScriptState, "WebInspector", webInspectorObj)) { + ASSERT_NOT_REACHED(); return; - - - m_menuProvider = MenuProvider::create(this, items); - ContextMenuController* menuController = m_inspectorController->m_page->contextMenuController(); - menuController->showContextMenu(event, m_menuProvider); -} - -void InspectorFrontendHost::contextMenuItemSelected(ContextMenuItem* item) -{ - if (m_inspectorController && m_inspectorController->windowVisible()) { - int itemNumber = item->action() - ContextMenuItemBaseCustomTag; - m_inspectorController->m_frontend->contextMenuItemSelected(itemNumber); } + RefPtr<FrontendMenuProvider> menuProvider = FrontendMenuProvider::create(this, webInspectorObj, items); + ContextMenuController* menuController = m_frontendPage->contextMenuController(); + menuController->showContextMenu(event, menuProvider); + m_menuProvider = menuProvider.get(); } - -void InspectorFrontendHost::contextMenuCleared() -{ - m_menuProvider = 0; - if (m_inspectorController && m_inspectorController->windowVisible()) - m_inspectorController->m_frontend->contextMenuCleared(); -} +#endif } // namespace WebCore diff --git a/WebCore/inspector/InspectorFrontendHost.h b/WebCore/inspector/InspectorFrontendHost.h index 390b018..4d3e0d1 100644 --- a/WebCore/inspector/InspectorFrontendHost.h +++ b/WebCore/inspector/InspectorFrontendHost.h @@ -42,36 +42,34 @@ namespace WebCore { class ContextMenuItem; class Event; +class FrontendMenuProvider; class InspectorClient; +class InspectorFrontendClient; class Node; class InspectorFrontendHost : public RefCounted<InspectorFrontendHost> { public: - static PassRefPtr<InspectorFrontendHost> create(InspectorController* inspectorController, InspectorClient* client) + static PassRefPtr<InspectorFrontendHost> create(InspectorFrontendClient* client, Page* frontendPage) { - return adoptRef(new InspectorFrontendHost(inspectorController, client)); + return adoptRef(new InspectorFrontendHost(client, frontendPage)); } ~InspectorFrontendHost(); - - InspectorController* inspectorController() { return m_inspectorController; } - - void disconnectController() { m_inspectorController = 0; } + void disconnectClient(); void loaded(); - void attach(); - void detach(); + void requestAttachWindow(); + void requestDetachWindow(); void closeWindow(); - void windowUnloading(); + void bringToFront(); + void inspectedURLChanged(const String&); void setAttachedWindowHeight(unsigned height); void moveWindowBy(float x, float y) const; String localizedStringsURL(); String hiddenPanels(); - const String& platform() const; - const String& port() const; void copyText(const String& text); @@ -79,59 +77,16 @@ public: void showContextMenu(Event*, const Vector<ContextMenuItem*>& items); private: - class MenuProvider : public ContextMenuProvider { - public: - static PassRefPtr<MenuProvider> create(InspectorFrontendHost* frontendHost, const Vector<ContextMenuItem*>& items) - { - return adoptRef(new MenuProvider(frontendHost, items)); - } - - virtual ~MenuProvider() - { - contextMenuCleared(); - } - - void disconnect() - { - m_frontendHost = 0; - } - - virtual void populateContextMenu(ContextMenu* menu) - { - for (size_t i = 0; i < m_items.size(); ++i) - menu->appendItem(*m_items[i]); - } - - virtual void contextMenuItemSelected(ContextMenuItem* item) - { - if (m_frontendHost) - m_frontendHost->contextMenuItemSelected(item); - } - - virtual void contextMenuCleared() - { - if (m_frontendHost) - m_frontendHost->contextMenuCleared(); - deleteAllValues(m_items); - m_items.clear(); - } - - private: - MenuProvider(InspectorFrontendHost* frontendHost, const Vector<ContextMenuItem*>& items) - : m_frontendHost(frontendHost) - , m_items(items) { } - InspectorFrontendHost* m_frontendHost; - Vector<ContextMenuItem*> m_items; - }; - - InspectorFrontendHost(InspectorController* inspectorController, InspectorClient* client); - - void contextMenuItemSelected(ContextMenuItem*); - void contextMenuCleared(); - - InspectorController* m_inspectorController; - InspectorClient* m_client; - RefPtr<MenuProvider> m_menuProvider; +#if ENABLE(CONTEXT_MENUS) + friend class FrontendMenuProvider; +#endif + InspectorFrontendHost(InspectorFrontendClient* client, Page* frontendPage); + + InspectorFrontendClient* m_client; + Page* m_frontendPage; +#if ENABLE(CONTEXT_MENUS) + FrontendMenuProvider* m_menuProvider; +#endif }; } // namespace WebCore diff --git a/WebCore/inspector/InspectorFrontendHost.idl b/WebCore/inspector/InspectorFrontendHost.idl index 9de49c1..1139a1c 100644 --- a/WebCore/inspector/InspectorFrontendHost.idl +++ b/WebCore/inspector/InspectorFrontendHost.idl @@ -33,21 +33,22 @@ module core { interface [Conditional=INSPECTOR] InspectorFrontendHost { void loaded(); - void attach(); - void detach(); void closeWindow(); - void windowUnloading(); + void bringToFront(); + void inspectedURLChanged(in DOMString newURL); + void requestAttachWindow(); + void requestDetachWindow(); void setAttachedWindowHeight(in unsigned long height); void moveWindowBy(in float x, in float y); DOMString localizedStringsURL(); DOMString hiddenPanels(); - DOMString platform(); - DOMString port(); void copyText(in DOMString text); + [Custom] DOMString platform(); + [Custom] DOMString port(); [Custom] void showContextMenu(in MouseEvent event, in DOMObject items); }; } diff --git a/WebCore/inspector/InspectorResource.cpp b/WebCore/inspector/InspectorResource.cpp index 6f08d58..51ed290 100644 --- a/WebCore/inspector/InspectorResource.cpp +++ b/WebCore/inspector/InspectorResource.cpp @@ -180,7 +180,7 @@ void InspectorResource::updateScriptObject(InspectorFrontend* frontend) } if (m_changes.hasChange(LengthChange)) { - jsonObject.set("contentLength", m_length); + jsonObject.set("resourceSize", m_length); jsonObject.set("didLengthChange", true); } @@ -213,17 +213,15 @@ void InspectorResource::updateScriptObject(InspectorFrontend* frontend) m_changes.clearAll(); } -void InspectorResource::releaseScriptObject(InspectorFrontend* frontend, bool callRemoveResource) +void InspectorResource::releaseScriptObject(InspectorFrontend* frontend) { m_changes.setAll(); for (size_t i = 0; i < m_redirects.size(); ++i) - m_redirects[i]->releaseScriptObject(frontend, callRemoveResource); + m_redirects[i]->releaseScriptObject(frontend); - if (!callRemoveResource) - return; - - frontend->removeResource(m_identifier); + if (frontend) + frontend->removeResource(m_identifier); } CachedResource* InspectorResource::cachedResource() const @@ -264,8 +262,8 @@ InspectorResource::Type InspectorResource::cachedResourceType() const InspectorResource::Type InspectorResource::type() const { - if (!m_xmlHttpResponseText.isNull()) - return XHR; + if (!m_overrideContent.isNull()) + return m_overrideContentType; if (m_requestURL == m_loader->requestURL()) { InspectorResource::Type resourceType = cachedResourceType(); @@ -281,16 +279,17 @@ InspectorResource::Type InspectorResource::type() const return cachedResourceType(); } -void InspectorResource::setXMLHttpResponseText(const ScriptString& data) +void InspectorResource::setOverrideContent(const ScriptString& data, Type type) { - m_xmlHttpResponseText = data; + m_overrideContent = data; + m_overrideContentType = type; m_changes.set(TypeChange); } String InspectorResource::sourceString() const { - if (!m_xmlHttpResponseText.isNull()) - return String(m_xmlHttpResponseText); + if (!m_overrideContent.isNull()) + return String(m_overrideContent); String textEncodingName; RefPtr<SharedBuffer> buffer = resourceData(&textEncodingName); diff --git a/WebCore/inspector/InspectorResource.h b/WebCore/inspector/InspectorResource.h index d347e5c..30b1280 100644 --- a/WebCore/inspector/InspectorResource.h +++ b/WebCore/inspector/InspectorResource.h @@ -79,12 +79,12 @@ namespace WebCore { PassRefPtr<InspectorResource> appendRedirect(unsigned long identifier, const KURL& redirectURL); void updateScriptObject(InspectorFrontend* frontend); - void releaseScriptObject(InspectorFrontend* frontend, bool callRemoveResource); + void releaseScriptObject(InspectorFrontend* frontend); void updateRequest(const ResourceRequest&); void updateResponse(const ResourceResponse&); - void setXMLHttpResponseText(const ScriptString& data); + void setOverrideContent(const ScriptString& data, Type); String sourceString() const; PassRefPtr<SharedBuffer> resourceData(String* textEncodingName) const; @@ -171,7 +171,8 @@ namespace WebCore { double m_endTime; double m_loadEventTime; double m_domContentEventTime; - ScriptString m_xmlHttpResponseText; + ScriptString m_overrideContent; + Type m_overrideContentType; Changes m_changes; bool m_isMainResource; String m_requestMethod; diff --git a/WebCore/inspector/InspectorTimelineAgent.cpp b/WebCore/inspector/InspectorTimelineAgent.cpp index cbf6ee1..c0278c2 100644 --- a/WebCore/inspector/InspectorTimelineAgent.cpp +++ b/WebCore/inspector/InspectorTimelineAgent.cpp @@ -44,14 +44,47 @@ namespace WebCore { +int InspectorTimelineAgent::s_instanceCount = 0; + InspectorTimelineAgent::InspectorTimelineAgent(InspectorFrontend* frontend) : m_frontend(frontend) { + ++s_instanceCount; + ScriptGCEvent::addEventListener(this); ASSERT(m_frontend); } +void InspectorTimelineAgent::pushGCEventRecords() +{ + for (GCEvents::iterator i = m_gcEvents.begin(); i != m_gcEvents.end(); ++i) { + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, i->startTime); + record.set("data", TimelineRecordFactory::createGCEventData(m_frontend, i->collectedBytes)); + record.set("endTime", i->endTime); + addRecordToTimeline(record, GCEventTimelineRecordType); + } + m_gcEvents.clear(); +} + +void InspectorTimelineAgent::didGC(double startTime, double endTime, size_t collectedBytesCount) +{ + m_gcEvents.append(GCEvent(startTime, endTime, collectedBytesCount)); +} + InspectorTimelineAgent::~InspectorTimelineAgent() { + ASSERT(s_instanceCount); + --s_instanceCount; + ScriptGCEvent::removeEventListener(this); +} + +void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine) +{ + pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(m_frontend, scriptName, scriptLine), FunctionCallTimelineRecordType); +} + +void InspectorTimelineAgent::didCallFunction() +{ + didCompleteCurrentRecord(FunctionCallTimelineRecordType); } void InspectorTimelineAgent::willDispatchEvent(const Event& event) @@ -108,17 +141,19 @@ void InspectorTimelineAgent::didWriteHTML(unsigned int endLine) didCompleteCurrentRecord(ParseHTMLTimelineRecordType); } } - + void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot) { - ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()); + pushGCEventRecords(); + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, WTF::currentTimeMS()); record.set("data", TimelineRecordFactory::createTimerInstallData(m_frontend, timerId, timeout, singleShot)); addRecordToTimeline(record, TimerInstallTimelineRecordType); } void InspectorTimelineAgent::didRemoveTimer(int timerId) { - ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()); + pushGCEventRecords(); + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, WTF::currentTimeMS()); record.set("data", TimelineRecordFactory::createGenericTimerData(m_frontend, timerId)); addRecordToTimeline(record, TimerRemoveTimelineRecordType); } @@ -166,35 +201,66 @@ void InspectorTimelineAgent::didEvaluateScript() void InspectorTimelineAgent::willSendResourceRequest(unsigned long identifier, bool isMainResource, const ResourceRequest& request) { - ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()); + pushGCEventRecords(); + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, WTF::currentTimeMS()); record.set("data", TimelineRecordFactory::createResourceSendRequestData(m_frontend, identifier, isMainResource, request)); record.set("type", ResourceSendRequestTimelineRecordType); + setHeapSizeStatistic(record); m_frontend->addRecordToTimeline(record); } -void InspectorTimelineAgent::didReceiveResourceResponse(unsigned long identifier, const ResourceResponse& response) +void InspectorTimelineAgent::willReceiveResourceData(unsigned long identifier) { - ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()); - record.set("data", TimelineRecordFactory::createResourceReceiveResponseData(m_frontend, identifier, response)); - record.set("type", ResourceReceiveResponseTimelineRecordType); - m_frontend->addRecordToTimeline(record); + pushCurrentRecord(TimelineRecordFactory::createReceiveResourceData(m_frontend, identifier), ReceiveResourceDataTimelineRecordType); +} + +void InspectorTimelineAgent::didReceiveResourceData() +{ + didCompleteCurrentRecord(ReceiveResourceDataTimelineRecordType); +} + +void InspectorTimelineAgent::willReceiveResourceResponse(unsigned long identifier, const ResourceResponse& response) +{ + pushCurrentRecord(TimelineRecordFactory::createResourceReceiveResponseData(m_frontend, identifier, response), ResourceReceiveResponseTimelineRecordType); +} + +void InspectorTimelineAgent::didReceiveResourceResponse() +{ + didCompleteCurrentRecord(ResourceReceiveResponseTimelineRecordType); } void InspectorTimelineAgent::didFinishLoadingResource(unsigned long identifier, bool didFail) { - ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()); + pushGCEventRecords(); + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, WTF::currentTimeMS()); record.set("data", TimelineRecordFactory::createResourceFinishData(m_frontend, identifier, didFail)); record.set("type", ResourceFinishTimelineRecordType); + setHeapSizeStatistic(record); m_frontend->addRecordToTimeline(record); } void InspectorTimelineAgent::didMarkTimeline(const String& message) { - ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()); + pushGCEventRecords(); + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, WTF::currentTimeMS()); record.set("data", TimelineRecordFactory::createMarkTimelineData(m_frontend, message)); addRecordToTimeline(record, MarkTimelineRecordType); } +void InspectorTimelineAgent::didMarkDOMContentEvent() +{ + pushGCEventRecords(); + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, WTF::currentTimeMS()); + addRecordToTimeline(record, MarkDOMContentEventType); +} + +void InspectorTimelineAgent::didMarkLoadEvent() +{ + pushGCEventRecords(); + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, WTF::currentTimeMS()); + addRecordToTimeline(record, MarkLoadEventType); +} + void InspectorTimelineAgent::reset() { m_recordStack.clear(); @@ -210,6 +276,7 @@ void InspectorTimelineAgent::resetFrontendProxyObject(InspectorFrontend* fronten void InspectorTimelineAgent::addRecordToTimeline(ScriptObject record, TimelineRecordType type) { record.set("type", type); + setHeapSizeStatistic(record); if (m_recordStack.isEmpty()) m_frontend->addRecordToTimeline(record); else { @@ -218,31 +285,37 @@ void InspectorTimelineAgent::addRecordToTimeline(ScriptObject record, TimelineRe } } +void InspectorTimelineAgent::setHeapSizeStatistic(ScriptObject record) +{ + size_t usedHeapSize = 0; + size_t totalHeapSize = 0; + ScriptGCEvent::getHeapSize(usedHeapSize, totalHeapSize); + record.set("usedHeapSize", usedHeapSize); + record.set("totalHeapSize", totalHeapSize); +} + void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type) { // An empty stack could merely mean that the timeline agent was turned on in the middle of // an event. Don't treat as an error. if (!m_recordStack.isEmpty()) { + pushGCEventRecords(); TimelineRecordEntry entry = m_recordStack.last(); m_recordStack.removeLast(); ASSERT(entry.type == type); entry.record.set("data", entry.data); entry.record.set("children", entry.children); - entry.record.set("endTime", currentTimeInMilliseconds()); + entry.record.set("endTime", WTF::currentTimeMS()); addRecordToTimeline(entry.record, type); } } -double InspectorTimelineAgent::currentTimeInMilliseconds() -{ - return currentTime() * 1000.0; -} - void InspectorTimelineAgent::pushCurrentRecord(ScriptObject data, TimelineRecordType type) { - m_recordStack.append(TimelineRecordEntry(TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()), data, m_frontend->newScriptArray(), type)); + pushGCEventRecords(); + ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, WTF::currentTimeMS()); + m_recordStack.append(TimelineRecordEntry(record, data, m_frontend->newScriptArray(), type)); } - } // namespace WebCore #endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorTimelineAgent.h b/WebCore/inspector/InspectorTimelineAgent.h index fd86ba7..18ac737 100644 --- a/WebCore/inspector/InspectorTimelineAgent.h +++ b/WebCore/inspector/InspectorTimelineAgent.h @@ -35,101 +35,136 @@ #include "Document.h" #include "ScriptExecutionContext.h" +#include "ScriptGCEvent.h" +#include "ScriptGCEventListener.h" #include "ScriptObject.h" #include "ScriptArray.h" #include <wtf/Vector.h> namespace WebCore { - class Event; - class InspectorFrontend; - class IntRect; - class ResourceRequest; - class ResourceResponse; - - // Must be kept in sync with TimelineAgent.js - enum TimelineRecordType { - EventDispatchTimelineRecordType = 0, - LayoutTimelineRecordType = 1, - RecalculateStylesTimelineRecordType = 2, - PaintTimelineRecordType = 3, - ParseHTMLTimelineRecordType = 4, - TimerInstallTimelineRecordType = 5, - TimerRemoveTimelineRecordType = 6, - TimerFireTimelineRecordType = 7, - XHRReadyStateChangeRecordType = 8, - XHRLoadRecordType = 9, - EvaluateScriptTimelineRecordType = 10, - MarkTimelineRecordType = 11, - ResourceSendRequestTimelineRecordType = 12, - ResourceReceiveResponseTimelineRecordType = 13, - ResourceFinishTimelineRecordType = 14, - }; - - class InspectorTimelineAgent : public Noncopyable { - public: - InspectorTimelineAgent(InspectorFrontend* frontend); - ~InspectorTimelineAgent(); - - void reset(); - void resetFrontendProxyObject(InspectorFrontend*); - - // Methods called from WebCore. - void willDispatchEvent(const Event&); - void didDispatchEvent(); - - void willLayout(); - void didLayout(); - - void willRecalculateStyle(); - void didRecalculateStyle(); - - void willPaint(const IntRect&); - void didPaint(); - - void willWriteHTML(unsigned int length, unsigned int startLine); - void didWriteHTML(unsigned int endLine); +class Event; +class InspectorFrontend; +class IntRect; +class ResourceRequest; +class ResourceResponse; + +// Must be kept in sync with TimelineAgent.js +enum TimelineRecordType { + EventDispatchTimelineRecordType = 0, + LayoutTimelineRecordType = 1, + RecalculateStylesTimelineRecordType = 2, + PaintTimelineRecordType = 3, + ParseHTMLTimelineRecordType = 4, + TimerInstallTimelineRecordType = 5, + TimerRemoveTimelineRecordType = 6, + TimerFireTimelineRecordType = 7, + XHRReadyStateChangeRecordType = 8, + XHRLoadRecordType = 9, + EvaluateScriptTimelineRecordType = 10, + MarkTimelineRecordType = 11, + ResourceSendRequestTimelineRecordType = 12, + ResourceReceiveResponseTimelineRecordType = 13, + ResourceFinishTimelineRecordType = 14, + FunctionCallTimelineRecordType = 15, + ReceiveResourceDataTimelineRecordType = 16, + GCEventTimelineRecordType = 17, + MarkDOMContentEventType = 18, + MarkLoadEventType = 19 +}; + +class InspectorTimelineAgent : ScriptGCEventListener, public Noncopyable { +public: + InspectorTimelineAgent(InspectorFrontend* frontend); + ~InspectorTimelineAgent(); + + void reset(); + void resetFrontendProxyObject(InspectorFrontend*); + + // Methods called from WebCore. + void willCallFunction(const String& scriptName, int scriptLine); + void didCallFunction(); + + void willDispatchEvent(const Event&); + void didDispatchEvent(); + + void willLayout(); + void didLayout(); + + void willRecalculateStyle(); + void didRecalculateStyle(); + + void willPaint(const IntRect&); + void didPaint(); + + void willWriteHTML(unsigned int length, unsigned int startLine); + void didWriteHTML(unsigned int endLine); + + void didInstallTimer(int timerId, int timeout, bool singleShot); + void didRemoveTimer(int timerId); + void willFireTimer(int timerId); + void didFireTimer(); + + void willChangeXHRReadyState(const String&, int); + void didChangeXHRReadyState(); + void willLoadXHR(const String&); + void didLoadXHR(); + + void willEvaluateScript(const String&, int); + void didEvaluateScript(); + + void didMarkTimeline(const String&); + void didMarkDOMContentEvent(); + void didMarkLoadEvent(); + + void willSendResourceRequest(unsigned long, bool isMainResource, const ResourceRequest&); + void willReceiveResourceResponse(unsigned long, const ResourceResponse&); + void didReceiveResourceResponse(); + void didFinishLoadingResource(unsigned long, bool didFail); + void willReceiveResourceData(unsigned long identifier); + void didReceiveResourceData(); - void didInstallTimer(int timerId, int timeout, bool singleShot); - void didRemoveTimer(int timerId); - void willFireTimer(int timerId); - void didFireTimer(); - - void willChangeXHRReadyState(const String&, int); - void didChangeXHRReadyState(); - void willLoadXHR(const String&); - void didLoadXHR(); - - void willEvaluateScript(const String&, int); - void didEvaluateScript(); - - void didMarkTimeline(const String&); - - void willSendResourceRequest(unsigned long, bool isMainResource, const ResourceRequest&); - void didReceiveResourceResponse(unsigned long, const ResourceResponse&); - void didFinishLoadingResource(unsigned long, bool didFail); - - static InspectorTimelineAgent* retrieve(ScriptExecutionContext*); - private: - struct TimelineRecordEntry { - TimelineRecordEntry(ScriptObject record, ScriptObject data, ScriptArray children, TimelineRecordType type) : record(record), data(data), children(children), type(type) { } - ScriptObject record; - ScriptObject data; - ScriptArray children; - TimelineRecordType type; - }; + virtual void didGC(double, double, size_t); + + static int instanceCount() { return s_instanceCount; } + static InspectorTimelineAgent* retrieve(ScriptExecutionContext*); + +private: + struct TimelineRecordEntry { + TimelineRecordEntry(ScriptObject record, ScriptObject data, ScriptArray children, TimelineRecordType type) + : record(record), data(data), children(children), type(type) + { + } + ScriptObject record; + ScriptObject data; + ScriptArray children; + TimelineRecordType type; + }; - void pushCurrentRecord(ScriptObject, TimelineRecordType); + void pushCurrentRecord(ScriptObject, TimelineRecordType); + void setHeapSizeStatistic(ScriptObject record); - static double currentTimeInMilliseconds(); + void didCompleteCurrentRecord(TimelineRecordType); - void didCompleteCurrentRecord(TimelineRecordType); + void addRecordToTimeline(ScriptObject, TimelineRecordType); - void addRecordToTimeline(ScriptObject, TimelineRecordType); + void pushGCEventRecords(); - InspectorFrontend* m_frontend; - - Vector< TimelineRecordEntry > m_recordStack; + InspectorFrontend* m_frontend; + + Vector<TimelineRecordEntry> m_recordStack; + static int s_instanceCount; + struct GCEvent { + GCEvent(double startTime, double endTime, size_t collectedBytes) + : startTime(startTime), endTime(endTime), collectedBytes(collectedBytes) + { + } + double startTime; + double endTime; + size_t collectedBytes; }; + typedef Vector<GCEvent> GCEvents; + GCEvents m_gcEvents; +}; inline InspectorTimelineAgent* InspectorTimelineAgent::retrieve(ScriptExecutionContext* context) { diff --git a/WebCore/inspector/InspectorWorkerResource.h b/WebCore/inspector/InspectorWorkerResource.h new file mode 100644 index 0000000..ef1b8c9 --- /dev/null +++ b/WebCore/inspector/InspectorWorkerResource.h @@ -0,0 +1,70 @@ +/* + * 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 InspectorWorkerResource_h +#define InspectorWorkerResource_h + +#if ENABLE(WORKERS) && ENABLE(INSPECTOR) + +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class InspectorWorkerResource : public RefCounted<InspectorWorkerResource> { +public: + static PassRefPtr<InspectorWorkerResource> create(intptr_t id, const String& url, bool isSharedWorker) + { + return adoptRef(new InspectorWorkerResource(id, url, isSharedWorker)); + } + + intptr_t id() const { return m_id; } + const String& url() const { return m_url; } + bool isSharedWorker() const { return m_isSharedWorker; } +private: + InspectorWorkerResource(intptr_t id, const String& url, bool isSharedWorker) + : m_id(id) + , m_url(url) + , m_isSharedWorker(isSharedWorker) + { + } + + intptr_t m_id; + String m_url; + bool m_isSharedWorker; +}; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) && ENABLE(INSPECTOR) + +#endif // InspectorWorkerResource_h diff --git a/WebCore/inspector/JavaScriptCallFrame.cpp b/WebCore/inspector/JavaScriptCallFrame.cpp deleted file mode 100644 index e6f75b8..0000000 --- a/WebCore/inspector/JavaScriptCallFrame.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2008 Apple 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. ``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 - * 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 "JavaScriptCallFrame.h" -#include "JSDOMBinding.h" - -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - -#include "PlatformString.h" -#include <debugger/DebuggerCallFrame.h> -#include <runtime/JSGlobalObject.h> -#include <runtime/Completion.h> -#include <runtime/JSLock.h> -#include <runtime/JSObject.h> -#include <runtime/JSValue.h> - -using namespace JSC; - -namespace WebCore { - -JavaScriptCallFrame::JavaScriptCallFrame(const DebuggerCallFrame& debuggerCallFrame, PassRefPtr<JavaScriptCallFrame> caller, intptr_t sourceID, int line) - : m_debuggerCallFrame(debuggerCallFrame) - , m_caller(caller) - , m_sourceID(sourceID) - , m_line(line) - , m_isValid(true) -{ -} - -JavaScriptCallFrame* JavaScriptCallFrame::caller() -{ - return m_caller.get(); -} - -const JSC::ScopeChainNode* JavaScriptCallFrame::scopeChain() const -{ - ASSERT(m_isValid); - if (!m_isValid) - return 0; - return m_debuggerCallFrame.scopeChain(); -} - -JSC::JSGlobalObject* JavaScriptCallFrame::dynamicGlobalObject() const -{ - ASSERT(m_isValid); - if (!m_isValid) - return 0; - return m_debuggerCallFrame.dynamicGlobalObject(); -} - -String JavaScriptCallFrame::functionName() const -{ - ASSERT(m_isValid); - if (!m_isValid) - return String(); - UString functionName = m_debuggerCallFrame.calculatedFunctionName(); - if (functionName.isEmpty()) - return String(); - return functionName; -} - -DebuggerCallFrame::Type JavaScriptCallFrame::type() const -{ - ASSERT(m_isValid); - if (!m_isValid) - return DebuggerCallFrame::ProgramType; - return m_debuggerCallFrame.type(); -} - -JSObject* JavaScriptCallFrame::thisObject() const -{ - ASSERT(m_isValid); - if (!m_isValid) - return 0; - return m_debuggerCallFrame.thisObject(); -} - -// Evaluate some JavaScript code in the scope of this frame. -JSValue JavaScriptCallFrame::evaluate(const UString& script, JSValue& exception) const -{ - ASSERT(m_isValid); - if (!m_isValid) - return jsNull(); - - JSLock lock(SilenceAssertionsOnly); - return m_debuggerCallFrame.evaluate(script, exception); -} - -} // namespace WebCore - -#endif // ENABLE(JAVASCRIPT_DEBUGGER) diff --git a/WebCore/inspector/JavaScriptCallFrame.h b/WebCore/inspector/JavaScriptCallFrame.h deleted file mode 100644 index bf61c4b..0000000 --- a/WebCore/inspector/JavaScriptCallFrame.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2008 Apple 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. ``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 - * 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 JavaScriptCallFrame_h -#define JavaScriptCallFrame_h - -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - -#include <interpreter/CallFrame.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <debugger/DebuggerCallFrame.h> - -namespace WebCore { - - class String; - - class JavaScriptCallFrame : public RefCounted<JavaScriptCallFrame> { - public: - static PassRefPtr<JavaScriptCallFrame> create(const JSC::DebuggerCallFrame& debuggerCallFrame, PassRefPtr<JavaScriptCallFrame> caller, intptr_t sourceID, int line) - { - return adoptRef(new JavaScriptCallFrame(debuggerCallFrame, caller, sourceID, line)); - } - - void invalidate() - { - m_isValid = false; - m_debuggerCallFrame = 0; - } - - bool isValid() const { return m_isValid; } - - JavaScriptCallFrame* caller(); - - intptr_t sourceID() const { return m_sourceID; } - int line() const { return m_line; } - void update(const JSC::DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int line) - { - m_debuggerCallFrame = debuggerCallFrame; - m_line = line; - m_sourceID = sourceID; - m_isValid = true; - } - - String functionName() const; - JSC::DebuggerCallFrame::Type type() const; - const JSC::ScopeChainNode* scopeChain() const; - JSC::JSGlobalObject* dynamicGlobalObject() const; - - JSC::JSObject* thisObject() const; - JSC::JSValue evaluate(const JSC::UString& script, JSC::JSValue& exception) const; - - private: - JavaScriptCallFrame(const JSC::DebuggerCallFrame&, PassRefPtr<JavaScriptCallFrame> caller, intptr_t sourceID, int line); - - JSC::DebuggerCallFrame m_debuggerCallFrame; - RefPtr<JavaScriptCallFrame> m_caller; - intptr_t m_sourceID; - int m_line; - bool m_isValid; - }; - -} // namespace WebCore - -#endif // ENABLE(JAVASCRIPT_DEBUGGER) - -#endif // JavaScriptCallFrame_h diff --git a/WebCore/inspector/JavaScriptCallFrame.idl b/WebCore/inspector/JavaScriptCallFrame.idl index 639ecc9..28f4f34 100644 --- a/WebCore/inspector/JavaScriptCallFrame.idl +++ b/WebCore/inspector/JavaScriptCallFrame.idl @@ -26,12 +26,21 @@ module inspector { interface [Conditional=JAVASCRIPT_DEBUGGER, OmitConstructor] JavaScriptCallFrame { + + // Scope type + const unsigned short GLOBAL_SCOPE = 0; + const unsigned short LOCAL_SCOPE = 1; + const unsigned short WITH_SCOPE = 2; + const unsigned short CLOSURE_SCOPE = 3; + const unsigned short CATCH_SCOPE = 4; + [Custom] void evaluate(in DOMString script); readonly attribute JavaScriptCallFrame caller; readonly attribute long sourceID; readonly attribute long line; readonly attribute [CustomGetter] Array scopeChain; + [Custom] unsigned short scopeType(in int scopeIndex); readonly attribute [CustomGetter] Object thisObject; readonly attribute DOMString functionName; readonly attribute [CustomGetter] DOMString type; diff --git a/WebCore/inspector/JavaScriptDebugServer.cpp b/WebCore/inspector/JavaScriptDebugServer.cpp deleted file mode 100644 index 03c3577..0000000 --- a/WebCore/inspector/JavaScriptDebugServer.cpp +++ /dev/null @@ -1,641 +0,0 @@ -/* - * Copyright (C) 2008, 2009 Apple 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 APPLE 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 "JavaScriptDebugServer.h" - -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - -#include "DOMWindow.h" -#include "EventLoop.h" -#include "Frame.h" -#include "FrameTree.h" -#include "FrameView.h" -#include "JSDOMWindowCustom.h" -#include "JavaScriptCallFrame.h" -#include "JavaScriptDebugListener.h" -#include "Page.h" -#include "PageGroup.h" -#include "PluginView.h" -#include "ScrollView.h" -#include "Widget.h" -#include "ScriptController.h" -#include <debugger/DebuggerCallFrame.h> -#include <runtime/JSLock.h> -#include <wtf/MainThread.h> -#include <wtf/StdLibExtras.h> -#include <wtf/UnusedParam.h> - -using namespace JSC; - -namespace WebCore { - -typedef JavaScriptDebugServer::ListenerSet ListenerSet; - -inline const UString& JavaScriptDebugServer::BreakpointInfo::condition() const -{ - return m_condition; -} - -void JavaScriptDebugServer::BreakpointInfo::setCondition(const UString& condition) -{ - m_condition = condition; -} - -JavaScriptDebugServer& JavaScriptDebugServer::shared() -{ - DEFINE_STATIC_LOCAL(JavaScriptDebugServer, server, ()); - return server; -} - -JavaScriptDebugServer::JavaScriptDebugServer() - : m_callingListeners(false) - , m_pauseOnExceptionsState(DontPauseOnExceptions) - , m_pauseOnNextStatement(false) - , m_paused(false) - , m_doneProcessingDebuggerEvents(true) - , m_pauseOnCallFrame(0) - , m_recompileTimer(this, &JavaScriptDebugServer::recompileAllJSFunctions) -{ -} - -JavaScriptDebugServer::~JavaScriptDebugServer() -{ - deleteAllValues(m_pageListenersMap); - deleteAllValues(m_breakpoints); -} - -void JavaScriptDebugServer::addListener(JavaScriptDebugListener* listener) -{ - ASSERT_ARG(listener, listener); - - m_listeners.add(listener); - - didAddListener(0); -} - -void JavaScriptDebugServer::removeListener(JavaScriptDebugListener* listener) -{ - ASSERT_ARG(listener, listener); - - m_listeners.remove(listener); - - didRemoveListener(0); - if (!hasListeners()) - didRemoveLastListener(); -} - -void JavaScriptDebugServer::addListener(JavaScriptDebugListener* listener, Page* page) -{ - ASSERT_ARG(listener, listener); - ASSERT_ARG(page, page); - - pair<PageListenersMap::iterator, bool> result = m_pageListenersMap.add(page, 0); - if (result.second) - result.first->second = new ListenerSet; - - ListenerSet* listeners = result.first->second; - listeners->add(listener); - - didAddListener(page); -} - -void JavaScriptDebugServer::removeListener(JavaScriptDebugListener* listener, Page* page) -{ - ASSERT_ARG(listener, listener); - ASSERT_ARG(page, page); - - PageListenersMap::iterator it = m_pageListenersMap.find(page); - if (it == m_pageListenersMap.end()) - return; - - ListenerSet* listeners = it->second; - listeners->remove(listener); - if (listeners->isEmpty()) { - m_pageListenersMap.remove(it); - delete listeners; - } - - didRemoveListener(page); - if (!hasListeners()) - didRemoveLastListener(); -} - -void JavaScriptDebugServer::pageCreated(Page* page) -{ - ASSERT_ARG(page, page); - - if (!hasListenersInterestedInPage(page)) - return; - page->setDebugger(this); -} - -bool JavaScriptDebugServer::hasListenersInterestedInPage(Page* page) -{ - ASSERT_ARG(page, page); - - if (hasGlobalListeners()) - return true; - - return m_pageListenersMap.contains(page); -} - -void JavaScriptDebugServer::addBreakpoint(intptr_t sourceID, unsigned lineNumber, const UString& condition) -{ - LineToBreakpointInfoMap* sourceBreakpoints = m_breakpoints.get(sourceID); - if (!sourceBreakpoints) { - sourceBreakpoints = new LineToBreakpointInfoMap; - m_breakpoints.set(sourceID, sourceBreakpoints); - } - BreakpointInfo* info = sourceBreakpoints->get(lineNumber); - if (!info) - sourceBreakpoints->set(lineNumber, new BreakpointInfo(condition)); - else - updateBreakpointInfo(info, condition); -} - -JavaScriptDebugServer::BreakpointInfo* JavaScriptDebugServer::breakpointInfo(intptr_t sourceID, unsigned lineNumber) const -{ - LineToBreakpointInfoMap* sourceBreakpoints = m_breakpoints.get(sourceID); - if (!sourceBreakpoints) - return 0; - return sourceBreakpoints->get(lineNumber); -} - -void JavaScriptDebugServer::updateBreakpoint(intptr_t sourceID, unsigned lineNumber, const UString& condition) -{ - BreakpointInfo* info = breakpointInfo(sourceID, lineNumber); - if (!info) - return; - updateBreakpointInfo(info, condition); -} - -void JavaScriptDebugServer::updateBreakpointInfo(BreakpointInfo* info, const UString& condition) -{ - info->setCondition(condition); -} - -void JavaScriptDebugServer::removeBreakpoint(intptr_t sourceID, unsigned lineNumber) -{ - LineToBreakpointInfoMap* sourceBreakpoints = m_breakpoints.get(sourceID); - if (!sourceBreakpoints) - return; - - BreakpointInfo* info = sourceBreakpoints->get(lineNumber); - if (!info) - return; - - sourceBreakpoints->remove(lineNumber); - delete info; - - if (sourceBreakpoints->isEmpty()) { - m_breakpoints.remove(sourceID); - delete sourceBreakpoints; - } -} - -bool JavaScriptDebugServer::hasBreakpoint(intptr_t sourceID, unsigned lineNumber) const -{ - BreakpointInfo* info = breakpointInfo(sourceID, lineNumber); - if (!info) - return false; - - // An empty condition counts as no condition which is equivalent to "true". - if (info->condition().isEmpty()) - return true; - - JSValue exception; - JSValue result = m_currentCallFrame->evaluate(info->condition(), exception); - if (exception) { - // An erroneous condition counts as "false". - return false; - } - return result.toBoolean(m_currentCallFrame->scopeChain()->globalObject->globalExec()); -} - -void JavaScriptDebugServer::clearBreakpoints() -{ - BreakpointsMap::iterator end = m_breakpoints.end(); - for (BreakpointsMap::iterator it = m_breakpoints.begin(); it != end; ++it) { - deleteAllValues(*(it->second)); - it->second->clear(); - } - deleteAllValues(m_breakpoints); - m_breakpoints.clear(); -} - -void JavaScriptDebugServer::setPauseOnExceptionsState(PauseOnExceptionsState pause) -{ - m_pauseOnExceptionsState = pause; -} - -void JavaScriptDebugServer::pauseProgram() -{ - m_pauseOnNextStatement = true; -} - -void JavaScriptDebugServer::continueProgram() -{ - if (!m_paused) - return; - - m_pauseOnNextStatement = false; - m_doneProcessingDebuggerEvents = true; -} - -void JavaScriptDebugServer::stepIntoStatement() -{ - if (!m_paused) - return; - - m_pauseOnNextStatement = true; - m_doneProcessingDebuggerEvents = true; -} - -void JavaScriptDebugServer::stepOverStatement() -{ - if (!m_paused) - return; - - m_pauseOnCallFrame = m_currentCallFrame.get(); - m_doneProcessingDebuggerEvents = true; -} - -void JavaScriptDebugServer::stepOutOfFunction() -{ - if (!m_paused) - return; - - m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->caller() : 0; - m_doneProcessingDebuggerEvents = true; -} - -JavaScriptCallFrame* JavaScriptDebugServer::currentCallFrame() -{ - if (!m_paused) - return 0; - return m_currentCallFrame.get(); -} - -static void dispatchDidParseSource(const ListenerSet& listeners, ExecState* exec, const JSC::SourceCode& source) -{ - Vector<JavaScriptDebugListener*> copy; - copyToVector(listeners, copy); - for (size_t i = 0; i < copy.size(); ++i) - copy[i]->didParseSource(exec, source); -} - -static void dispatchFailedToParseSource(const ListenerSet& listeners, ExecState* exec, const SourceCode& source, int errorLine, const String& errorMessage) -{ - Vector<JavaScriptDebugListener*> copy; - copyToVector(listeners, copy); - for (size_t i = 0; i < copy.size(); ++i) - copy[i]->failedToParseSource(exec, source, errorLine, errorMessage); -} - -static Page* toPage(JSGlobalObject* globalObject) -{ - ASSERT_ARG(globalObject, globalObject); - - JSDOMWindow* window = asJSDOMWindow(globalObject); - Frame* frame = window->impl()->frame(); - return frame ? frame->page() : 0; -} - -void JavaScriptDebugServer::detach(JSGlobalObject* globalObject) -{ - // If we're detaching from the currently executing global object, manually tear down our - // stack, since we won't get further debugger callbacks to do so. Also, resume execution, - // since there's no point in staying paused once a window closes. - if (m_currentCallFrame && m_currentCallFrame->dynamicGlobalObject() == globalObject) { - m_currentCallFrame = 0; - m_pauseOnCallFrame = 0; - continueProgram(); - } - Debugger::detach(globalObject); -} - -void JavaScriptDebugServer::sourceParsed(ExecState* exec, const SourceCode& source, int errorLine, const UString& errorMessage) -{ - if (m_callingListeners) - return; - - Page* page = toPage(exec->dynamicGlobalObject()); - if (!page) - return; - - m_callingListeners = true; - - bool isError = errorLine != -1; - - if (hasGlobalListeners()) { - if (isError) - dispatchFailedToParseSource(m_listeners, exec, source, errorLine, errorMessage); - else - dispatchDidParseSource(m_listeners, exec, source); - } - - if (ListenerSet* pageListeners = m_pageListenersMap.get(page)) { - ASSERT(!pageListeners->isEmpty()); - if (isError) - dispatchFailedToParseSource(*pageListeners, exec, source, errorLine, errorMessage); - else - dispatchDidParseSource(*pageListeners, exec, source); - } - - m_callingListeners = false; -} - -static void dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptDebugServer::JavaScriptExecutionCallback callback) -{ - Vector<JavaScriptDebugListener*> copy; - copyToVector(listeners, copy); - for (size_t i = 0; i < copy.size(); ++i) - (copy[i]->*callback)(); -} - -void JavaScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback, Page* page) -{ - if (m_callingListeners) - return; - - m_callingListeners = true; - - ASSERT(hasListeners()); - - WebCore::dispatchFunctionToListeners(m_listeners, callback); - - if (ListenerSet* pageListeners = m_pageListenersMap.get(page)) { - ASSERT(!pageListeners->isEmpty()); - WebCore::dispatchFunctionToListeners(*pageListeners, callback); - } - - m_callingListeners = false; -} - -void JavaScriptDebugServer::setJavaScriptPaused(const PageGroup& pageGroup, bool paused) -{ - setMainThreadCallbacksPaused(paused); - - const HashSet<Page*>& pages = pageGroup.pages(); - - HashSet<Page*>::const_iterator end = pages.end(); - for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) - setJavaScriptPaused(*it, paused); -} - -void JavaScriptDebugServer::setJavaScriptPaused(Page* page, bool paused) -{ - ASSERT_ARG(page, page); - - page->setDefersLoading(paused); - - for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) - setJavaScriptPaused(frame, paused); -} - -void JavaScriptDebugServer::setJavaScriptPaused(Frame* frame, bool paused) -{ - ASSERT_ARG(frame, frame); - - if (!frame->script()->canExecuteScripts()) - return; - - frame->script()->setPaused(paused); - - Document* document = frame->document(); - if (paused) - document->suspendActiveDOMObjects(); - else - document->resumeActiveDOMObjects(); - - setJavaScriptPaused(frame->view(), paused); -} - -#if PLATFORM(MAC) - -void JavaScriptDebugServer::setJavaScriptPaused(FrameView*, bool) -{ -} - -#else - -void JavaScriptDebugServer::setJavaScriptPaused(FrameView* view, bool paused) -{ - if (!view) - return; - - const HashSet<RefPtr<Widget> >* children = view->children(); - ASSERT(children); - - HashSet<RefPtr<Widget> >::const_iterator end = children->end(); - for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(); it != end; ++it) { - Widget* widget = (*it).get(); - if (!widget->isPluginView()) - continue; - static_cast<PluginView*>(widget)->setJavaScriptPaused(paused); - } -} - -#endif - -void JavaScriptDebugServer::pauseIfNeeded(Page* page) -{ - if (m_paused) - return; - - if (!page || !hasListenersInterestedInPage(page)) - return; - - bool pauseNow = m_pauseOnNextStatement; - pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame); - pauseNow |= (m_currentCallFrame->line() > 0 && hasBreakpoint(m_currentCallFrame->sourceID(), m_currentCallFrame->line())); - if (!pauseNow) - return; - - m_pauseOnCallFrame = 0; - m_pauseOnNextStatement = false; - m_paused = true; - - dispatchFunctionToListeners(&JavaScriptDebugListener::didPause, page); - - setJavaScriptPaused(page->group(), true); - - TimerBase::fireTimersInNestedEventLoop(); - - EventLoop loop; - m_doneProcessingDebuggerEvents = false; - while (!m_doneProcessingDebuggerEvents && !loop.ended()) - loop.cycle(); - - setJavaScriptPaused(page->group(), false); - - m_paused = false; - - dispatchFunctionToListeners(&JavaScriptDebugListener::didContinue, page); -} - -void JavaScriptDebugServer::callEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) -{ - if (m_paused) - return; - - m_currentCallFrame = JavaScriptCallFrame::create(debuggerCallFrame, m_currentCallFrame, sourceID, lineNumber); - pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); -} - -void JavaScriptDebugServer::atStatement(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) -{ - if (m_paused) - return; - - ASSERT(m_currentCallFrame); - if (!m_currentCallFrame) - return; - - m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); - pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); -} - -void JavaScriptDebugServer::returnEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) -{ - if (m_paused) - return; - - ASSERT(m_currentCallFrame); - if (!m_currentCallFrame) - return; - - m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); - pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); - - // Treat stepping over a return statement like stepping out. - if (m_currentCallFrame == m_pauseOnCallFrame) - m_pauseOnCallFrame = m_currentCallFrame->caller(); - m_currentCallFrame = m_currentCallFrame->caller(); -} - -void JavaScriptDebugServer::exception(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber, bool hasHandler) -{ - if (m_paused) - return; - - ASSERT(m_currentCallFrame); - if (!m_currentCallFrame) - return; - - if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasHandler)) - m_pauseOnNextStatement = true; - - m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); - pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); -} - -void JavaScriptDebugServer::willExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) -{ - if (m_paused) - return; - - m_currentCallFrame = JavaScriptCallFrame::create(debuggerCallFrame, m_currentCallFrame, sourceID, lineNumber); - pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); -} - -void JavaScriptDebugServer::didExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) -{ - if (m_paused) - return; - - ASSERT(m_currentCallFrame); - if (!m_currentCallFrame) - return; - - m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); - pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); - - // Treat stepping over the end of a program like stepping out. - if (m_currentCallFrame == m_pauseOnCallFrame) - m_pauseOnCallFrame = m_currentCallFrame->caller(); - m_currentCallFrame = m_currentCallFrame->caller(); -} - -void JavaScriptDebugServer::didReachBreakpoint(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) -{ - if (m_paused) - return; - - ASSERT(m_currentCallFrame); - if (!m_currentCallFrame) - return; - - m_pauseOnNextStatement = true; - m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); - pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); -} - -void JavaScriptDebugServer::recompileAllJSFunctionsSoon() -{ - m_recompileTimer.startOneShot(0); -} - -void JavaScriptDebugServer::recompileAllJSFunctions(Timer<JavaScriptDebugServer>*) -{ - JSLock lock(SilenceAssertionsOnly); - Debugger::recompileAllJSFunctions(JSDOMWindow::commonJSGlobalData()); -} - -void JavaScriptDebugServer::didAddListener(Page* page) -{ - recompileAllJSFunctionsSoon(); - - if (page) - page->setDebugger(this); - else - Page::setDebuggerForAllPages(this); -} - -void JavaScriptDebugServer::didRemoveListener(Page* page) -{ - if (hasGlobalListeners() || (page && hasListenersInterestedInPage(page))) - return; - - recompileAllJSFunctionsSoon(); - - if (page) - page->setDebugger(0); - else - Page::setDebuggerForAllPages(0); -} - -void JavaScriptDebugServer::didRemoveLastListener() -{ - m_doneProcessingDebuggerEvents = true; -} - -} // namespace WebCore - -#endif // ENABLE(JAVASCRIPT_DEBUGGER) diff --git a/WebCore/inspector/JavaScriptDebugServer.h b/WebCore/inspector/JavaScriptDebugServer.h deleted file mode 100644 index d7c23bc..0000000 --- a/WebCore/inspector/JavaScriptDebugServer.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2008 Apple 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 APPLE 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 JavaScriptDebugServer_h -#define JavaScriptDebugServer_h - -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - -#include "Timer.h" -#include <debugger/Debugger.h> -#include <runtime/UString.h> -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/RefPtr.h> - -namespace JSC { - class DebuggerCallFrame; - class JSGlobalObject; -} - -namespace WebCore { - - class Frame; - class FrameView; - class Page; - class PageGroup; - class JavaScriptCallFrame; - class JavaScriptDebugListener; - - class JavaScriptDebugServer : JSC::Debugger, public Noncopyable { - public: - static JavaScriptDebugServer& shared(); - - void addListener(JavaScriptDebugListener*); - void removeListener(JavaScriptDebugListener*); - - void addListener(JavaScriptDebugListener*, Page*); - void removeListener(JavaScriptDebugListener*, Page*); - - void addBreakpoint(intptr_t sourceID, unsigned lineNumber, const JSC::UString& condition); - void updateBreakpoint(intptr_t sourceID, unsigned lineNumber, const JSC::UString& condition); - void removeBreakpoint(intptr_t sourceID, unsigned lineNumber); - bool hasBreakpoint(intptr_t sourceID, unsigned lineNumber) const; - void clearBreakpoints(); - - enum PauseOnExceptionsState { - DontPauseOnExceptions, - PauseOnAllExceptions, - PauseOnUncaughtExceptions - }; - PauseOnExceptionsState pauseOnExceptionsState() const { return m_pauseOnExceptionsState; } - void setPauseOnExceptionsState(PauseOnExceptionsState); - - void pauseProgram(); - void continueProgram(); - void stepIntoStatement(); - void stepOverStatement(); - void stepOutOfFunction(); - - void recompileAllJSFunctionsSoon(); - void recompileAllJSFunctions(Timer<JavaScriptDebugServer>* = 0); - - JavaScriptCallFrame* currentCallFrame(); - - void pageCreated(Page*); - - typedef HashSet<JavaScriptDebugListener*> ListenerSet; - typedef void (JavaScriptDebugListener::*JavaScriptExecutionCallback)(); - - private: - class BreakpointInfo { - public: - BreakpointInfo(const JSC::UString& condition) : m_condition(condition) {} - const JSC::UString& condition() const; - void setCondition(const JSC::UString& condition); - private: - JSC::UString m_condition; - }; - - JavaScriptDebugServer(); - ~JavaScriptDebugServer(); - - bool hasListeners() const { return !m_listeners.isEmpty() || !m_pageListenersMap.isEmpty(); } - bool hasGlobalListeners() const { return !m_listeners.isEmpty(); } - bool hasListenersInterestedInPage(Page*); - - void setJavaScriptPaused(const PageGroup&, bool paused); - void setJavaScriptPaused(Page*, bool paused); - void setJavaScriptPaused(Frame*, bool paused); - void setJavaScriptPaused(FrameView*, bool paused); - - void dispatchFunctionToListeners(JavaScriptExecutionCallback, Page*); - void pauseIfNeeded(Page*); - BreakpointInfo* breakpointInfo(intptr_t sourceID, unsigned lineNumber) const; - void updateBreakpointInfo(BreakpointInfo* info, const JSC::UString& condition); - - virtual void detach(JSC::JSGlobalObject*); - - virtual void sourceParsed(JSC::ExecState*, const JSC::SourceCode&, int errorLine, const JSC::UString& errorMsg); - virtual void callEvent(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineNumber); - virtual void atStatement(const JSC::DebuggerCallFrame&, intptr_t sourceID, int firstLine); - virtual void returnEvent(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineNumber); - virtual void exception(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineNumber, bool hasHandler); - virtual void willExecuteProgram(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineno); - virtual void didExecuteProgram(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineno); - virtual void didReachBreakpoint(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineno); - - void didAddListener(Page*); - void didRemoveListener(Page*); - void didRemoveLastListener(); - - typedef HashMap<Page*, ListenerSet*> PageListenersMap; - typedef HashMap<unsigned, BreakpointInfo*> LineToBreakpointInfoMap; - typedef HashMap<intptr_t, LineToBreakpointInfoMap*> BreakpointsMap; - - PageListenersMap m_pageListenersMap; - ListenerSet m_listeners; - bool m_callingListeners; - PauseOnExceptionsState m_pauseOnExceptionsState; - bool m_pauseOnNextStatement; - bool m_paused; - bool m_doneProcessingDebuggerEvents; - JavaScriptCallFrame* m_pauseOnCallFrame; - RefPtr<JavaScriptCallFrame> m_currentCallFrame; - BreakpointsMap m_breakpoints; - Timer<JavaScriptDebugServer> m_recompileTimer; - }; - -} // namespace WebCore - -#endif // ENABLE(JAVASCRIPT_DEBUGGER) - -#endif // JavaScriptDebugServer_h diff --git a/WebCore/inspector/ScriptBreakpoint.h b/WebCore/inspector/ScriptBreakpoint.h new file mode 100644 index 0000000..8775901 --- /dev/null +++ b/WebCore/inspector/ScriptBreakpoint.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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 APPLE 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 ScriptBreakpoint_h +#define ScriptBreakpoint_h + +#include "PlatformString.h" +#include <wtf/HashMap.h> + +namespace WebCore { + +struct ScriptBreakpoint { + ScriptBreakpoint(bool enabled, const String& condition) + : enabled(enabled) + , condition(condition) + { + } + + ScriptBreakpoint() + { + } + + bool enabled; + String condition; +}; + +typedef HashMap<int, ScriptBreakpoint> SourceBreakpoints; + +} // namespace WebCore + +#endif // !defined(ScriptBreakpoint_h) diff --git a/WebCore/inspector/JavaScriptDebugListener.h b/WebCore/inspector/ScriptDebugListener.h index 6570065..5a85da1 100644 --- a/WebCore/inspector/JavaScriptDebugListener.h +++ b/WebCore/inspector/ScriptDebugListener.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 Apple Inc. All rights reserved. + * 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 @@ -26,34 +27,27 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef JavaScriptDebugListener_h -#define JavaScriptDebugListener_h +#ifndef ScriptDebugListener_h +#define ScriptDebugListener_h -#if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) - -namespace JSC { - class ExecState; - class SourceCode; - class UString; -} +#if ENABLE(JAVASCRIPT_DEBUGGER) namespace WebCore { - class Frame; - class Page; +class String; - class JavaScriptDebugListener { - public: - virtual ~JavaScriptDebugListener() { } +class ScriptDebugListener { +public: + virtual ~ScriptDebugListener() { } - virtual void didParseSource(JSC::ExecState*, const JSC::SourceCode& source) = 0; - virtual void failedToParseSource(JSC::ExecState*, const JSC::SourceCode& source, int errorLine, const JSC::UString& errorMessage) = 0; - virtual void didPause() = 0; - virtual void didContinue() = 0; - }; + virtual void didParseSource(const String& sourceID, const String& url, const String& data, int firstLine) = 0; + virtual void failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) = 0; + virtual void didPause() = 0; + virtual void didContinue() = 0; +}; } // namespace WebCore #endif // ENABLE(JAVASCRIPT_DEBUGGER) -#endif // JavaScriptDebugListener_h +#endif // ScriptDebugListener_h diff --git a/WebCore/inspector/ScriptGCEventListener.h b/WebCore/inspector/ScriptGCEventListener.h new file mode 100644 index 0000000..0850f0c --- /dev/null +++ b/WebCore/inspector/ScriptGCEventListener.h @@ -0,0 +1,48 @@ +/* + * 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 ScriptGCEventListener_h +#define ScriptGCEventListener_h + +#if ENABLE(INSPECTOR) + +namespace WebCore { + +class ScriptGCEventListener +{ +public: + virtual void didGC(double startTime, double endTime, size_t collectedBytes) = 0; + virtual ~ScriptGCEventListener(){} +}; + +} // namespace WebCore + +#endif // !ENABLE(INSPECTOR) +#endif // !defined(ScriptGCEventListener_h) diff --git a/WebCore/inspector/ScriptProfile.idl b/WebCore/inspector/ScriptProfile.idl new file mode 100644 index 0000000..8d4b04a --- /dev/null +++ b/WebCore/inspector/ScriptProfile.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * 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 COMPUTER, INC. ``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 COMPUTER, INC. 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. + */ + +module core { + + interface [Conditional=JAVASCRIPT_DEBUGGER, OmitConstructor] ScriptProfile { + readonly attribute DOMString title; + readonly attribute unsigned long uid; + readonly attribute ScriptProfileNode head; + }; + +} diff --git a/WebCore/inspector/ScriptProfileNode.idl b/WebCore/inspector/ScriptProfileNode.idl new file mode 100644 index 0000000..eff17bd --- /dev/null +++ b/WebCore/inspector/ScriptProfileNode.idl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * 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 COMPUTER, INC. ``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 COMPUTER, INC. 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. + */ + +module core { + + interface [Conditional=JAVASCRIPT_DEBUGGER, OmitConstructor] ScriptProfileNode { + readonly attribute DOMString functionName; + readonly attribute DOMString url; + readonly attribute unsigned long lineNumber; + readonly attribute double totalTime; + readonly attribute double selfTime; + readonly attribute unsigned long numberOfCalls; + readonly attribute [CustomGetter] Array children; + readonly attribute boolean visible; + readonly attribute [CustomGetter] unsigned long callUID; + }; + +} diff --git a/WebCore/inspector/TimelineRecordFactory.cpp b/WebCore/inspector/TimelineRecordFactory.cpp index 525e61d..c08fc0f 100644 --- a/WebCore/inspector/TimelineRecordFactory.cpp +++ b/WebCore/inspector/TimelineRecordFactory.cpp @@ -39,6 +39,7 @@ #include "ResourceRequest.h" #include "ResourceResponse.h" #include "ScriptArray.h" +#include "ScriptCallStack.h" #include "ScriptObject.h" namespace WebCore { @@ -47,9 +48,33 @@ ScriptObject TimelineRecordFactory::createGenericRecord(InspectorFrontend* front { ScriptObject record = frontend->newScriptObject(); record.set("startTime", startTime); + + String sourceName; + int sourceLineNumber; + String functionName; + if (ScriptCallStack::callLocation(&sourceName, &sourceLineNumber, &functionName) && sourceName != "undefined") { + record.set("callerScriptName", sourceName); + record.set("callerScriptLine", sourceLineNumber); + record.set("callerFunctionName", functionName); + } return record; } +ScriptObject TimelineRecordFactory::createGCEventData(InspectorFrontend* frontend, const size_t usedHeapSizeDelta) +{ + ScriptObject data = frontend->newScriptObject(); + data.set("usedHeapSizeDelta", usedHeapSizeDelta); + return data; +} + +ScriptObject TimelineRecordFactory::createFunctionCallData(InspectorFrontend* frontend, const String& scriptName, int scriptLine) +{ + ScriptObject data = frontend->newScriptObject(); + data.set("scriptName", scriptName); + data.set("scriptLine", scriptLine); + return data; +} + ScriptObject TimelineRecordFactory::createEventDispatchData(InspectorFrontend* frontend, const Event& event) { ScriptObject data = frontend->newScriptObject(); @@ -132,6 +157,13 @@ ScriptObject TimelineRecordFactory::createResourceFinishData(InspectorFrontend* return data; } +ScriptObject TimelineRecordFactory::createReceiveResourceData(InspectorFrontend* frontend, unsigned long identifier) +{ + ScriptObject data = frontend->newScriptObject(); + data.set("identifier", identifier); + return data; +} + ScriptObject TimelineRecordFactory::createPaintData(InspectorFrontend* frontend, const IntRect& rect) { ScriptObject data = frontend->newScriptObject(); diff --git a/WebCore/inspector/TimelineRecordFactory.h b/WebCore/inspector/TimelineRecordFactory.h index b7437f5..2f485d3 100644 --- a/WebCore/inspector/TimelineRecordFactory.h +++ b/WebCore/inspector/TimelineRecordFactory.h @@ -46,6 +46,10 @@ namespace WebCore { public: static ScriptObject createGenericRecord(InspectorFrontend*, double startTime); + static ScriptObject createGCEventData(InspectorFrontend* frontend, const size_t usedHeapSizeDelta); + + static ScriptObject createFunctionCallData(InspectorFrontend*, const String& scriptName, int scriptLine); + static ScriptObject createEventDispatchData(InspectorFrontend*, const Event&); static ScriptObject createGenericTimerData(InspectorFrontend*, int timerId); @@ -65,6 +69,8 @@ namespace WebCore { static ScriptObject createResourceReceiveResponseData(InspectorFrontend*, unsigned long identifier, const ResourceResponse&); + static ScriptObject createReceiveResourceData(InspectorFrontend*, unsigned long identifier); + static ScriptObject createResourceFinishData(InspectorFrontend*, unsigned long identifier, bool didFail); static ScriptObject createPaintData(InspectorFrontend*, const IntRect&); diff --git a/WebCore/inspector/front-end/AbstractTimelinePanel.js b/WebCore/inspector/front-end/AbstractTimelinePanel.js index 6c5d715..cfec9da 100644 --- a/WebCore/inspector/front-end/AbstractTimelinePanel.js +++ b/WebCore/inspector/front-end/AbstractTimelinePanel.js @@ -308,8 +308,10 @@ WebInspector.AbstractTimelinePanel.prototype = { staleItemsLength = this._staleItems.length; } + + const isBarOpaqueAtLeft = this.sidebarTree.selectedTreeElement && this.sidebarTree.selectedTreeElement.isBarOpaqueAtLeft; for (var i = 0; i < staleItemsLength; ++i) - this._staleItems[i]._itemsTreeElement._itemGraph.refresh(this.calculator); + this._staleItems[i]._itemsTreeElement._itemGraph.refresh(this.calculator, isBarOpaqueAtLeft); this._staleItems = []; diff --git a/WebCore/inspector/front-end/AuditCategories.js b/WebCore/inspector/front-end/AuditCategories.js index 6931b5f..01b5ff9 100644 --- a/WebCore/inspector/front-end/AuditCategories.js +++ b/WebCore/inspector/front-end/AuditCategories.js @@ -37,9 +37,9 @@ WebInspector.AuditCategories.PagePerformance.AuditCategoryName = "Web Page Perfo WebInspector.AuditCategories.PagePerformance.prototype = { initialize: function() { - this.addRule(new WebInspector.AuditRules.UnusedCssRule()); - this.addRule(new WebInspector.AuditRules.CssInHeadRule({InlineURLScore: 6, InlineStylesheetScore: 21})); - this.addRule(new WebInspector.AuditRules.StylesScriptsOrderRule({CSSAfterJSURLScore: 11, InlineBetweenResourcesScore: 21})); + this.addRule(new WebInspector.AuditRules.UnusedCssRule(), WebInspector.AuditRule.Severity.Warning); + this.addRule(new WebInspector.AuditRules.CssInHeadRule(), WebInspector.AuditRule.Severity.Severe); + this.addRule(new WebInspector.AuditRules.StylesScriptsOrderRule(), WebInspector.AuditRule.Severity.Severe); } } @@ -54,16 +54,16 @@ WebInspector.AuditCategories.NetworkUtilization.AuditCategoryName = "Network Uti WebInspector.AuditCategories.NetworkUtilization.prototype = { initialize: function() { - this.addRule(new WebInspector.AuditRules.GzipRule()); - this.addRule(new WebInspector.AuditRules.ImageDimensionsRule({ScorePerImageUse: 5})); - this.addRule(new WebInspector.AuditRules.CookieSizeRule({MinBytesThreshold: 400, MaxBytesThreshold: 1000})); - this.addRule(new WebInspector.AuditRules.StaticCookielessRule({MinResources: 5})); - this.addRule(new WebInspector.AuditRules.CombineJsResourcesRule({AllowedPerDomain: 2, ScorePerResource: 11})); - this.addRule(new WebInspector.AuditRules.CombineCssResourcesRule({AllowedPerDomain: 2, ScorePerResource: 11})); - this.addRule(new WebInspector.AuditRules.MinimizeDnsLookupsRule({HostCountThreshold: 4, ViolationDomainScore: 6})); - this.addRule(new WebInspector.AuditRules.ParallelizeDownloadRule({OptimalHostnameCount: 4, MinRequestThreshold: 10, MinBalanceThreshold: 0.5})); - this.addRule(new WebInspector.AuditRules.BrowserCacheControlRule()); - this.addRule(new WebInspector.AuditRules.ProxyCacheControlRule()); + this.addRule(new WebInspector.AuditRules.GzipRule(), WebInspector.AuditRule.Severity.Severe); + this.addRule(new WebInspector.AuditRules.ImageDimensionsRule(), WebInspector.AuditRule.Severity.Warning); + this.addRule(new WebInspector.AuditRules.CookieSizeRule(400), WebInspector.AuditRule.Severity.Warning); + this.addRule(new WebInspector.AuditRules.StaticCookielessRule(5), WebInspector.AuditRule.Severity.Warning); + this.addRule(new WebInspector.AuditRules.CombineJsResourcesRule(2), WebInspector.AuditRule.Severity.Severe); + this.addRule(new WebInspector.AuditRules.CombineCssResourcesRule(2), WebInspector.AuditRule.Severity.Severe); + this.addRule(new WebInspector.AuditRules.MinimizeDnsLookupsRule(4), WebInspector.AuditRule.Severity.Warning); + this.addRule(new WebInspector.AuditRules.ParallelizeDownloadRule(4, 10, 0.5), WebInspector.AuditRule.Severity.Warning); + this.addRule(new WebInspector.AuditRules.BrowserCacheControlRule(), WebInspector.AuditRule.Severity.Severe); + this.addRule(new WebInspector.AuditRules.ProxyCacheControlRule(), WebInspector.AuditRule.Severity.Warning); } } diff --git a/WebCore/inspector/front-end/AuditLauncherView.js b/WebCore/inspector/front-end/AuditLauncherView.js index f2a2fd2..60b7c32 100644 --- a/WebCore/inspector/front-end/AuditLauncherView.js +++ b/WebCore/inspector/front-end/AuditLauncherView.js @@ -42,6 +42,8 @@ WebInspector.AuditLauncherView = function(categoriesById, runnerCallback) this._contentElement.className = "audit-launcher-view-content"; this.element.appendChild(this._contentElement); + this._resetResourceCount(); + function categorySortFunction(a, b) { var aTitle = a.displayName || ""; @@ -67,6 +69,9 @@ WebInspector.AuditLauncherView.prototype = { { if (!this._auditPresentStateLabelElement) return; + + this._resetResourceCount(); + if (isTracking) { this._auditPresentStateLabelElement.nodeValue = WebInspector.UIString("Audit Present State"); this._auditPresentStateElement.disabled = false; @@ -79,12 +84,63 @@ WebInspector.AuditLauncherView.prototype = { } }, + get totalResources() + { + return this._totalResources; + }, + + set totalResources(x) + { + if (this._totalResources === x) + return; + this._totalResources = x; + this._updateResourceProgress(); + }, + + get loadedResources() + { + return this._loadedResources; + }, + + set loadedResources(x) + { + if (this._loadedResources === x) + return; + this._loadedResources = x; + this._updateResourceProgress(); + }, + + _resetResourceCount: function() + { + this.loadedResources = 0; + + // We never receive a resourceStarted notification for the main resource + // (see InspectorController.willSendRequest()) + this.totalResources = 1; + }, + + resourceStarted: function(resource) + { + ++this.totalResources; + }, + + resourceFinished: function(resource) + { + ++this.loadedResources; + }, + + reset: function() + { + this._resetResourceCount(); + }, + _setAuditRunning: function(auditRunning) { if (this._auditRunning === auditRunning) return; this._auditRunning = auditRunning; this._updateButton(); + this._updateResourceProgress(); }, _launchButtonClicked: function(event) @@ -95,12 +151,8 @@ WebInspector.AuditLauncherView.prototype = { if (this._categoriesById[id]._checkboxElement.checked) catIds.push(id); } - function profilingFinishedCallback() - { - this._setAuditRunning(false); - } this._setAuditRunning(true); - this._runnerCallback(catIds, this._auditPresentStateElement.checked, profilingFinishedCallback.bind(this)); + this._runnerCallback(catIds, this._auditPresentStateElement.checked, this._setAuditRunning.bind(this, false)); }, _selectAllClicked: function(checkCategories) @@ -121,11 +173,10 @@ WebInspector.AuditLauncherView.prototype = { _createCategoryElement: function(title, id) { - var element; var labelElement = document.createElement("label"); labelElement.id = this._categoryIdPrefix + id; - element = document.createElement("input"); + var element = document.createElement("input"); element.type = "checkbox"; labelElement.appendChild(element); labelElement.appendChild(document.createTextNode(title)); @@ -166,6 +217,10 @@ WebInspector.AuditLauncherView.prototype = { this._totalCategoriesCount = this._categoriesElement.childNodes.length; this._currentCategoriesCount = 0; + var flexibleSpaceElement = document.createElement("div"); + flexibleSpaceElement.className = "flexible-space"; + this._contentElement.appendChild(flexibleSpaceElement); + this._buttonContainerElement = document.createElement("div"); this._buttonContainerElement.className = "button-container"; @@ -188,10 +243,19 @@ WebInspector.AuditLauncherView.prototype = { this._buttonContainerElement.appendChild(labelElement); this._launchButton = document.createElement("button"); - this._launchButton.setAttribute("type", "button"); + this._launchButton.type = "button"; + this._launchButton.textContent = WebInspector.UIString("Run"); this._launchButton.addEventListener("click", this._launchButtonClicked.bind(this), false); this._buttonContainerElement.appendChild(this._launchButton); + this._resourceProgressContainer = document.createElement("span"); + this._resourceProgressContainer.className = "resource-progress"; + var resourceProgressImage = document.createElement("img"); + this._resourceProgressContainer.appendChild(resourceProgressImage); + this._resourceProgressTextElement = document.createElement("span"); + this._resourceProgressContainer.appendChild(this._resourceProgressTextElement); + this._buttonContainerElement.appendChild(this._resourceProgressContainer); + this._contentElement.appendChild(this._buttonContainerElement); this._selectAllClicked(this._selectAllCheckboxElement.checked); @@ -199,25 +263,22 @@ WebInspector.AuditLauncherView.prototype = { this._updateButton(); }, - _updateButton: function() + _updateResourceProgress: function() { - this._launchButton.disabled = !this._currentCategoriesCount || this._auditRunning; - if (this._auditRunning) - this._launchButton.textContent = WebInspector.UIString("Running..."); - else - this._launchButton.textContent = WebInspector.UIString("Run"); - }, + if (!this._resourceProgressContainer) + return; - show: function(parentElement) - { - WebInspector.View.prototype.show.call(this, parentElement); - setTimeout(this.resize(), 0); + if (!this._auditRunning) + this._resourceProgressContainer.addStyleClass("hidden"); + else { + this._resourceProgressContainer.removeStyleClass("hidden"); + this._resourceProgressTextElement.textContent = WebInspector.UIString("Loading (%d of %d)", this.loadedResources, this.totalResources); + } }, - resize: function() + _updateButton: function() { - if (this._categoriesElement) - this._categoriesElement.style.height = (this._buttonContainerElement.totalOffsetTop - this._categoriesElement.totalOffsetTop) + "px"; + this._launchButton.disabled = !this._currentCategoriesCount || this._auditRunning; } } diff --git a/WebCore/inspector/front-end/AuditResultView.js b/WebCore/inspector/front-end/AuditResultView.js index ac818fb..2f4afbd 100644 --- a/WebCore/inspector/front-end/AuditResultView.js +++ b/WebCore/inspector/front-end/AuditResultView.js @@ -31,24 +31,14 @@ WebInspector.AuditResultView = function(categoryResults) { WebInspector.View.call(this); + this.element.className = "audit-result-view"; - this.element.id = "audit-result-view"; - - function entrySortFunction(a, b) - { - var result = b.type - a.type; - if (!result) - result = (a.value || "").localeCompare(b.value || ""); - return result; - } - - for (var i = 0; i < categoryResults.length; ++i) { - var entries = categoryResults[i].entries; - if (entries) { - entries.sort(entrySortFunction); - this.element.appendChild(new WebInspector.AuditCategoryResultPane(categoryResults[i]).element); - } + function categorySorter(a, b) { + return (a.title || "").localeCompare(b.title || ""); } + categoryResults.sort(categorySorter); + for (var i = 0; i < categoryResults.length; ++i) + this.element.appendChild(new WebInspector.AuditCategoryResultPane(categoryResults[i]).element); } WebInspector.AuditResultView.prototype.__proto__ = WebInspector.View.prototype; @@ -57,82 +47,60 @@ WebInspector.AuditResultView.prototype.__proto__ = WebInspector.View.prototype; WebInspector.AuditCategoryResultPane = function(categoryResult) { WebInspector.SidebarPane.call(this, categoryResult.title); - this.expand(); - for (var i = 0; i < categoryResult.entries.length; ++i) - this.bodyElement.appendChild(new WebInspector.AuditRuleResultPane(categoryResult.entries[i]).element); -} - -WebInspector.AuditCategoryResultPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; - - -WebInspector.AuditRuleResultPane = function(ruleResult) -{ - WebInspector.SidebarPane.call(this, ruleResult.value); - if (!ruleResult.children) - return; - - this._decorateRuleResult(ruleResult); + var treeOutlineElement = document.createElement("ol"); + this.bodyElement.addStyleClass("audit-result-tree"); + this.bodyElement.appendChild(treeOutlineElement); - for (var i = 0; i < ruleResult.children.length; ++i) { - var section = new WebInspector.AuditRuleResultChildSection(ruleResult.children[i]); - this.bodyElement.appendChild(section.element); - } -} + this._treeOutline = new TreeOutline(treeOutlineElement); + this._treeOutline.expandTreeElementsWhenArrowing = true; -WebInspector.AuditRuleResultPane.prototype = { - _decorateRuleResult: function(ruleResult) + function ruleSorter(a, b) { - if (ruleResult.type == WebInspector.AuditRuleResult.Type.NA) - return; - - var scoreElement = document.createElement("img"); - scoreElement.className = "score"; - var className = (ruleResult.type == WebInspector.AuditRuleResult.Type.Violation) ? "red" : "green"; - scoreElement.addStyleClass(className); - this.element.insertBefore(scoreElement, this.element.firstChild); + var result = WebInspector.AuditRule.SeverityOrder[a.severity || 0] - WebInspector.AuditRule.SeverityOrder[b.severity || 0]; + if (!result) + result = (a.value || "").localeCompare(b.value || ""); + return result; } -} -WebInspector.AuditRuleResultPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; + categoryResult.ruleResults.sort(ruleSorter); + for (var i = 0; i < categoryResult.ruleResults.length; ++i) { + var ruleResult = categoryResult.ruleResults[i]; + var treeElement = this._appendResult(this._treeOutline, ruleResult); + treeElement.listItemElement.addStyleClass("audit-result"); -WebInspector.AuditRuleResultChildSection = function(entry) -{ - WebInspector.Section.call(this, entry.value); - var children = entry.children; - this._hasChildren = !!children && children.length; - if (!this._hasChildren) - this.element.addStyleClass("blank-section"); - else { - this.contentElement = document.createElement("div"); - this.contentElement.addStyleClass("section-content"); - for (var i = 0; i < children.length; ++i) { - var paraElement = document.createElement("p"); - paraElement.innerHTML = children[i].value; - this.contentElement.appendChild(paraElement); + if (ruleResult.severity) { + var severityElement = document.createElement("img"); + severityElement.className = "severity-" + ruleResult.severity; + treeElement.listItemElement.appendChild(severityElement); } - this.contentElement.appendChild(paraElement); - this.element.appendChild(this.contentElement); } + this.expand(); } -WebInspector.AuditRuleResultChildSection.prototype = { - - // title is considered pure HTML - set title(x) - { - if (this._title === x) - return; - this._title = x; - - this.titleElement.innerHTML = x; - }, - - expand: function() +WebInspector.AuditCategoryResultPane.prototype = { + _appendResult: function(parentTreeElement, result) { - if (this._hasChildren) - WebInspector.Section.prototype.expand.call(this); + var title = result.value; + if (result.violationCount) + title = String.sprintf("%s (%d)", title, result.violationCount); + + var treeElement = new TreeElement(title, null, !!result.children); + parentTreeElement.appendChild(treeElement); + + if (result.className) + treeElement.listItemElement.addStyleClass(result.className); + if (result.children) { + for (var i = 0; i < result.children.length; ++i) + this._appendResult(treeElement, result.children[i]); + } + if (result.expanded) { + treeElement.listItemElement.removeStyleClass("parent"); + treeElement.listItemElement.addStyleClass("parent-expanded"); + treeElement.expand(); + } + return treeElement; } } -WebInspector.AuditRuleResultChildSection.prototype.__proto__ = WebInspector.Section.prototype; +WebInspector.AuditCategoryResultPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; diff --git a/WebCore/inspector/front-end/AuditRules.js b/WebCore/inspector/front-end/AuditRules.js index 210b8a9..d2c5f09 100644 --- a/WebCore/inspector/front-end/AuditRules.js +++ b/WebCore/inspector/front-end/AuditRules.js @@ -42,27 +42,6 @@ WebInspector.AuditRules.CacheableResponseCodes = 304: true // Underlying resource is cacheable } -/** - * @param {Array} array Array of Elements (outerHTML is used) or strings (plain value is used as innerHTML) - */ -WebInspector.AuditRules.arrayAsUL = function(array, shouldLinkify) -{ - if (!array.length) - return ""; - var ulElement = document.createElement("ul"); - for (var i = 0; i < array.length; ++i) { - var liElement = document.createElement("li"); - if (array[i] instanceof Element) - liElement.appendChild(array[i]); - else if (shouldLinkify) - liElement.appendChild(WebInspector.linkifyURLAsNode(array[i])); - else - liElement.innerHTML = array[i]; - ulElement.appendChild(liElement); - } - return ulElement.outerHTML; -} - WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, regexp, needFullResources) { var domainToResourcesMap = {}; @@ -84,9 +63,9 @@ WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, reg return domainToResourcesMap; } -WebInspector.AuditRules.evaluateInTargetWindow = function(func, callback) +WebInspector.AuditRules.evaluateInTargetWindow = function(func, args, callback) { - InjectedScriptAccess.getDefault().evaluateOnSelf(func.toString(), callback); + InjectedScriptAccess.getDefault().evaluateOnSelf(func.toString(), args, callback); } @@ -98,43 +77,29 @@ WebInspector.AuditRules.GzipRule = function() WebInspector.AuditRules.GzipRule.prototype = { doRun: function(resources, result, callback) { - try { - var commonMessage = undefined; - var totalSavings = 0; - var compressedSize = 0 - var candidateSize = 0 - var outputResources = []; - for (var i = 0, length = resources.length; i < length; ++i) { - var resource = resources[i]; - if (this._shouldCompress(resource)) { - var size = resource.contentLength; - candidateSize += size; - if (this._isCompressed(resource)) { - compressedSize += size; - continue; - } - if (!commonMessage) - commonMessage = result.appendChild(""); - var savings = 2 * size / 3; - totalSavings += savings; - outputResources.push( - String.sprintf("Compressing %s could save ~%s", - WebInspector.linkifyURL(resource.url), Number.bytesToString(savings))); + var totalSavings = 0; + var compressedSize = 0; + var candidateSize = 0; + var summary = result.addChild("", true); + for (var i = 0, length = resources.length; i < length; ++i) { + var resource = resources[i]; + if (this._shouldCompress(resource)) { + var size = resource.resourceSize; + candidateSize += size; + if (this._isCompressed(resource)) { + compressedSize += size; + continue; } + var savings = 2 * size / 3; + totalSavings += savings; + summary.addChild(String.sprintf("%s could save ~%s", WebInspector.AuditRuleResult.linkifyDisplayName(resource.url), Number.bytesToString(savings))); + result.violationCount++; } - if (commonMessage) { - commonMessage.value = - String.sprintf("Compressing the following resources with gzip could reduce their " + - "transfer size by about two thirds (~%s):", Number.bytesToString(totalSavings)); - commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources)); - result.score = 100 * compressedSize / candidateSize; - result.type = WebInspector.AuditRuleResult.Type.Violation; - } - } catch(e) { - console.log(e); - } finally { - callback(result); } + if (!totalSavings) + return callback(null); + summary.value = String.sprintf("Compressing the following resources with gzip could reduce their transfer size by about two thirds (~%s):", Number.bytesToString(totalSavings)); + callback(result); }, _isCompressed: function(resource) @@ -145,105 +110,100 @@ WebInspector.AuditRules.GzipRule.prototype = { _shouldCompress: function(resource) { - return WebInspector.Resource.Type.isTextType(resource.type) && resource.domain && resource.contentLength !== undefined && resource.contentLength > 150; + return WebInspector.Resource.Type.isTextType(resource.type) && resource.domain && resource.resourceSize !== undefined && resource.resourceSize > 150; } } WebInspector.AuditRules.GzipRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, resourceTypeName, parametersObject) +WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, resourceTypeName, allowedPerDomain) { - WebInspector.AuditRule.call(this, id, name, parametersObject); + WebInspector.AuditRule.call(this, id, name); this._type = type; this._resourceTypeName = resourceTypeName; + this._allowedPerDomain = allowedPerDomain; } WebInspector.AuditRules.CombineExternalResourcesRule.prototype = { doRun: function(resources, result, callback) { - try { - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [this._type], WebInspector.URLRegExp); - var penalizedResourceCount = 0; - // TODO: refactor according to the chosen i18n approach - for (var domain in domainToResourcesMap) { - var domainResources = domainToResourcesMap[domain]; - var extraResourceCount = domainResources.length - this.getValue("AllowedPerDomain"); - if (extraResourceCount <= 0) - continue; - penalizedResourceCount += extraResourceCount - 1; - result.appendChild( - String.sprintf("There are %d %s files served from %s. Consider combining them into as few files as possible.", - domainResources.length, this._resourceTypeName, domain)); - } - result.score = 100 - (penalizedResourceCount * this.getValue("ScorePerResource")); - result.type = WebInspector.AuditRuleResult.Type.Hint; - } catch(e) { - console.log(e); - } finally { - callback(result); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, [this._type], WebInspector.URLRegExp); + var penalizedResourceCount = 0; + // TODO: refactor according to the chosen i18n approach + var summary = result.addChild("", true); + for (var domain in domainToResourcesMap) { + var domainResources = domainToResourcesMap[domain]; + var extraResourceCount = domainResources.length - this._allowedPerDomain; + if (extraResourceCount <= 0) + continue; + penalizedResourceCount += extraResourceCount - 1; + summary.addChild(String.sprintf("%d %s resources served from %s.", domainResources.length, this._resourceTypeName, WebInspector.AuditRuleResult.resourceDomain(domain))); + result.violationCount += domainResources.length; } + if (!penalizedResourceCount) + return callback(null); + + summary.value = "There are multiple resources served from same domain. Consider combining them into as few files as possible."; + callback(result); } -}; +} WebInspector.AuditRules.CombineExternalResourcesRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CombineJsResourcesRule = function(parametersObject) { - WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externaljs", "Combine external JavaScript", WebInspector.Resource.Type.Script, "JS", parametersObject); +WebInspector.AuditRules.CombineJsResourcesRule = function(allowedPerDomain) { + WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externaljs", "Combine external JavaScript", WebInspector.Resource.Type.Script, "JavaScript", allowedPerDomain); } WebInspector.AuditRules.CombineJsResourcesRule.prototype.__proto__ = WebInspector.AuditRules.CombineExternalResourcesRule.prototype; -WebInspector.AuditRules.CombineCssResourcesRule = function(parametersObject) { - WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externalcss", "Combine external CSS", WebInspector.Resource.Type.Stylesheet, "CSS", parametersObject); +WebInspector.AuditRules.CombineCssResourcesRule = function(allowedPerDomain) { + WebInspector.AuditRules.CombineExternalResourcesRule.call(this, "page-externalcss", "Combine external CSS", WebInspector.Resource.Type.Stylesheet, "CSS", allowedPerDomain); } WebInspector.AuditRules.CombineCssResourcesRule.prototype.__proto__ = WebInspector.AuditRules.CombineExternalResourcesRule.prototype; -WebInspector.AuditRules.MinimizeDnsLookupsRule = function(parametersObject) { - WebInspector.AuditRule.call(this, "network-minimizelookups", "Minimize DNS lookups", parametersObject); +WebInspector.AuditRules.MinimizeDnsLookupsRule = function(hostCountThreshold) { + WebInspector.AuditRule.call(this, "network-minimizelookups", "Minimize DNS lookups"); + this._hostCountThreshold = hostCountThreshold; } WebInspector.AuditRules.MinimizeDnsLookupsRule.prototype = { doRun: function(resources, result, callback) { - try { - var violationDomains = []; - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, undefined, WebInspector.URLRegExp); - for (var domain in domainToResourcesMap) { - if (domainToResourcesMap[domain].length > 1) - continue; - var match = domain.match(WebInspector.URLRegExp); - if (!match) - continue; - if (!match[2].search(WebInspector.AuditRules.IPAddressRegexp)) - continue; // an IP address - violationDomains.push(match[2]); - } - if (violationDomains.length <= this.getValue("HostCountThreshold")) - return; - var commonMessage = result.appendChild( - "The following domains only serve one resource each. If possible, avoid the extra DNS " + - "lookups by serving these resources from existing domains."); - commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(violationDomains)); - result.score = 100 - violationDomains.length * this.getValue("ViolationDomainScore"); - } catch(e) { - console.log(e); - } finally { - callback(result); + var summary = result.addChild(""); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(resources, undefined, WebInspector.URLRegExp); + for (var domain in domainToResourcesMap) { + if (domainToResourcesMap[domain].length > 1) + continue; + var match = domain.match(WebInspector.URLRegExp); + if (!match) + continue; + if (!match[2].search(WebInspector.AuditRules.IPAddressRegexp)) + continue; // an IP address + summary.addSnippet(match[2]); + result.violationCount++; } + if (!summary.children || summary.children.length <= this._hostCountThreshold) + return callback(null); + + summary.value = "The following domains only serve one resource each. If possible, avoid the extra DNS lookups by serving these resources from existing domains."; + callback(result); } } WebInspector.AuditRules.MinimizeDnsLookupsRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.ParallelizeDownloadRule = function(parametersObject) +WebInspector.AuditRules.ParallelizeDownloadRule = function(optimalHostnameCount, minRequestThreshold, minBalanceThreshold) { - WebInspector.AuditRule.call(this, "network-parallelizehosts", "Parallelize downloads across hostnames", parametersObject); + WebInspector.AuditRule.call(this, "network-parallelizehosts", "Parallelize downloads across hostnames"); + this._optimalHostnameCount = optimalHostnameCount; + this._minRequestThreshold = minRequestThreshold; + this._minBalanceThreshold = minBalanceThreshold; } @@ -257,65 +217,50 @@ WebInspector.AuditRules.ParallelizeDownloadRule.prototype = { return (aCount < bCount) ? 1 : (aCount == bCount) ? 0 : -1; } - try { - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap( - resources, - [WebInspector.Resource.Type.Stylesheet, WebInspector.Resource.Type.Image], - WebInspector.URLRegExp, - true); - - var hosts = []; - for (var url in domainToResourcesMap) - hosts.push(url); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap( + resources, + [WebInspector.Resource.Type.Stylesheet, WebInspector.Resource.Type.Image], + WebInspector.URLRegExp, + true); - if (!hosts.length) - return; // no hosts (local file or something) + var hosts = []; + for (var url in domainToResourcesMap) + hosts.push(url); - hosts.sort(hostSorter); + if (!hosts.length) + return callback(null); // no hosts (local file or something) - var optimalHostnameCount = this.getValue("OptimalHostnameCount"); - if (hosts.length > optimalHostnameCount) - hosts.splice(optimalHostnameCount); + hosts.sort(hostSorter); - var busiestHostResourceCount = domainToResourcesMap[hosts[0]].length; - var resourceCountAboveThreshold = busiestHostResourceCount - this.getValue("MinRequestThreshold"); - if (resourceCountAboveThreshold <= 0) - return; + var optimalHostnameCount = this._optimalHostnameCount; + if (hosts.length > optimalHostnameCount) + hosts.splice(optimalHostnameCount); - var avgResourcesPerHost = 0; - for (var i = 0, size = hosts.length; i < size; ++i) - avgResourcesPerHost += domainToResourcesMap[hosts[i]].length; + var busiestHostResourceCount = domainToResourcesMap[hosts[0]].length; + var resourceCountAboveThreshold = busiestHostResourceCount - this._minRequestThreshold; + if (resourceCountAboveThreshold <= 0) + return callback(null); - // Assume optimal parallelization. - avgResourcesPerHost /= optimalHostnameCount; + var avgResourcesPerHost = 0; + for (var i = 0, size = hosts.length; i < size; ++i) + avgResourcesPerHost += domainToResourcesMap[hosts[i]].length; - avgResourcesPerHost = Math.max(avgResourcesPerHost, 1); + // Assume optimal parallelization. + avgResourcesPerHost /= optimalHostnameCount; + avgResourcesPerHost = Math.max(avgResourcesPerHost, 1); - var pctAboveAvg = (resourceCountAboveThreshold / avgResourcesPerHost) - 1.0; + var pctAboveAvg = (resourceCountAboveThreshold / avgResourcesPerHost) - 1.0; + var minBalanceThreshold = this._minBalanceThreshold; + if (pctAboveAvg < minBalanceThreshold) + return callback(null); - var minBalanceThreshold = this.getValue("MinBalanceThreshold"); - if (pctAboveAvg < minBalanceThreshold) { - result.score = 100; - return; - } + var resourcesOnBusiestHost = domainToResourcesMap[hosts[0]]; + var entry = result.addChild(String.sprintf("This page makes %d parallelizable requests to %s. Increase download parallelization by distributing the following requests across multiple hostnames.", busiestHostResourceCount, hosts[0]), true); + for (var i = 0; i < resourcesOnBusiestHost.length; ++i) + entry.addURL(resourcesOnBusiestHost[i].url); - result.score = (1 - (pctAboveAvg - minBalanceThreshold)) * 100; - result.type = WebInspector.AuditRuleResult.Type.Hint; - - var resourcesOnBusiestHost = domainToResourcesMap[hosts[0]]; - var commonMessage = result.appendChild( - String.sprintf("This page makes %d parallelizable requests to %s" + - ". Increase download parallelization by distributing the following" + - " requests across multiple hostnames.", busiestHostResourceCount, hosts[0])); - var outputResources = []; - for (var i = 0, size = resourcesOnBusiestHost.length; i < size; ++i) - outputResources.push(resourcesOnBusiestHost[i].url); - commonMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources, true)); - } catch(e) { - console.log(e); - } finally { - callback(result); - } + result.violationCount = resourcesOnBusiestHost.length; + callback(result); } } @@ -324,156 +269,132 @@ WebInspector.AuditRules.ParallelizeDownloadRule.prototype.__proto__ = WebInspect // The reported CSS rule size is incorrect (parsed != original in WebKit), // so use percentages instead, which gives a better approximation. -WebInspector.AuditRules.UnusedCssRule = function(parametersObject) +WebInspector.AuditRules.UnusedCssRule = function() { - WebInspector.AuditRule.call(this, "page-unusedcss", "Remove unused CSS", parametersObject); + WebInspector.AuditRule.call(this, "page-unusedcss", "Remove unused CSS rules"); } WebInspector.AuditRules.UnusedCssRule.prototype = { - _getUnusedStylesheetRatioMessage: function(unusedLength, type, location, styleSheetLength) - { - var url = type === "href" - ? WebInspector.linkifyURL(location) - : String.sprintf("Inline block #%s", location); - var pctUnused = Math.round(unusedLength / styleSheetLength * 100); - return String.sprintf("%s: %f%% (estimated) is not used by the current page.", url, pctUnused); - }, - - _getUnusedTotalRatioMessage: function(unusedLength, totalLength) - { - var pctUnused = Math.round(unusedLength / totalLength * 100); - return String.sprintf("%d%% of CSS (estimated) is not used by the current page.", pctUnused); - }, - doRun: function(resources, result, callback) { var self = this; - function evalCallback(evalResult, isException) { - try { - if (isException) - return; - - var totalLength = 0; - var totalUnusedLength = 0; - var topMessage; - var styleSheetMessage; - for (var i = 0; i < evalResult.length; ) { - var type = evalResult[i++]; - if (type === "totalLength") { - totalLength = evalResult[i++]; - continue; - } - - var styleSheetLength = evalResult[i++]; - var location = evalResult[i++]; - var unusedRules = evalResult[i++]; - styleSheetMessage = undefined; - if (!topMessage) - topMessage = result.appendChild(""); - - var totalUnusedRuleLength = 0; - var ruleSelectors = []; - for (var j = 0; j < unusedRules.length; ++j) { - var rule = unusedRules[j]; - totalUnusedRuleLength += parseInt(rule[1]); - if (!styleSheetMessage) - styleSheetMessage = result.appendChild(""); - ruleSelectors.push(rule[0]); - } - styleSheetMessage.appendChild(WebInspector.AuditRules.arrayAsUL(ruleSelectors)); - - styleSheetMessage.value = self._getUnusedStylesheetRatioMessage(totalUnusedRuleLength, type, location, styleSheetLength); - totalUnusedLength += totalUnusedRuleLength; - } - if (totalUnusedLength) { - var totalUnusedPercent = totalUnusedLength / totalLength; - topMessage.value = self._getUnusedTotalRatioMessage(totalUnusedLength, totalLength); - var pctMultiplier = Math.log(Math.max(200, totalUnusedLength - 800)) / 7 - 0.6; - result.score = (1 - totalUnusedPercent * pctMultiplier) * 100; - result.type = WebInspector.AuditRuleResult.Type.Hint; - } else - result.score = 100; - } catch(e) { - console.log(e); - } finally { - callback(result); - } - } + function evalCallback(styleSheets) { + if (!styleSheets.length) + return callback(null); - function routine() - { - var styleSheets = document.styleSheets; - if (!styleSheets) - return {}; - var styleSheetToUnusedRules = []; - var inlineBlockOrdinal = 0; - var totalCSSLength = 0; - var pseudoSelectorRegexp = /:hover|:link|:active|:visited|:focus/; + var pseudoSelectorRegexp = /:hover|:link|:active|:visited|:focus|:before|:after/; + var selectors = []; + var testedSelectors = {}; for (var i = 0; i < styleSheets.length; ++i) { var styleSheet = styleSheets[i]; - if (!styleSheet.cssRules) - continue; - var currentStyleSheetSize = 0; - var unusedRules = []; for (var curRule = 0; curRule < styleSheet.cssRules.length; ++curRule) { var rule = styleSheet.cssRules[curRule]; - var textLength = rule.cssText ? rule.cssText.length : 0; - currentStyleSheetSize += textLength; - totalCSSLength += textLength; - if (rule.type !== 1 || rule.selectorText.match(pseudoSelectorRegexp)) + if (rule.selectorText.match(pseudoSelectorRegexp)) continue; - var nodes = document.querySelectorAll(rule.selectorText); - if (nodes && nodes.length) + selectors.push(rule.selectorText); + testedSelectors[rule.selectorText] = 1; + } + } + + function selectorsCallback(callback, styleSheets, testedSelectors, foundSelectors) + { + var inlineBlockOrdinal = 0; + var totalStylesheetSize = 0; + var totalUnusedStylesheetSize = 0; + var summary; + + for (var i = 0; i < styleSheets.length; ++i) { + var styleSheet = styleSheets[i]; + var stylesheetSize = 0; + var unusedStylesheetSize = 0; + var unusedRules = []; + for (var curRule = 0; curRule < styleSheet.cssRules.length; ++curRule) { + var rule = styleSheet.cssRules[curRule]; + var textLength = rule.cssText ? rule.cssText.length : 0; + stylesheetSize += textLength; + if (!testedSelectors[rule.selectorText] || foundSelectors[rule.selectorText]) + continue; + unusedStylesheetSize += textLength; + unusedRules.push(rule.selectorText); + } + totalStylesheetSize += stylesheetSize; + totalUnusedStylesheetSize += unusedStylesheetSize; + + if (!unusedRules.length) continue; - unusedRules.push([rule.selectorText, textLength]); + + var url = styleSheet.href ? WebInspector.AuditRuleResult.linkifyDisplayName(styleSheet.href) : String.sprintf("Inline block #%d", ++inlineBlockOrdinal); + var pctUnused = Math.round(100 * unusedStylesheetSize / stylesheetSize); + if (!summary) + summary = result.addChild("", true); + var entry = summary.addChild(String.sprintf("%s: %d%% (estimated) is not used by the current page.", url, pctUnused)); + + for (var j = 0; j < unusedRules.length; ++j) + entry.addSnippet(unusedRules[j]); + + result.violationCount += unusedRules.length; } - if (unusedRules.length) { - styleSheetToUnusedRules.push(styleSheet.href ? "href" : "inline"); - styleSheetToUnusedRules.push(currentStyleSheetSize); - styleSheetToUnusedRules.push(styleSheet.href ? styleSheet.href : ++inlineBlockOrdinal); - styleSheetToUnusedRules.push(unusedRules); + + if (!totalUnusedStylesheetSize) + return callback(null); + + var totalUnusedPercent = Math.round(100 * totalUnusedStylesheetSize / totalStylesheetSize); + summary.value = String.sprintf("%d%% of CSS (estimated) is not used by the current page.", totalUnusedPercent); + + callback(result); + } + + function routine(selectorArray) + { + var result = {}; + for (var i = 0; i < selectorArray.length; ++i) { + try { + var nodes = document.querySelectorAll(selectorArray[i]); + if (nodes && nodes.length) + result[selectorArray[i]] = true; + } catch(e) { + // ignore and mark as unused + } } + return result; } - styleSheetToUnusedRules.push("totalLength"); - styleSheetToUnusedRules.push(totalCSSLength); - return styleSheetToUnusedRules; + + WebInspector.AuditRules.evaluateInTargetWindow(routine, [selectors], selectorsCallback.bind(null, callback, styleSheets, testedSelectors)); } - WebInspector.AuditRules.evaluateInTargetWindow(routine, evalCallback); + function routine() + { + var styleSheets = document.styleSheets; + if (!styleSheets) + return false; + + return routineResult; + } + + InspectorBackend.getAllStyles(WebInspector.Callback.wrap(evalCallback)); } } WebInspector.AuditRules.UnusedCssRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CacheControlRule = function(id, name, parametersObject) +WebInspector.AuditRules.CacheControlRule = function(id, name) { - WebInspector.AuditRule.call(this, id, name, parametersObject); + WebInspector.AuditRule.call(this, id, name); } WebInspector.AuditRules.CacheControlRule.MillisPerMonth = 1000 * 60 * 60 * 24 * 30; WebInspector.AuditRules.CacheControlRule.prototype = { - InfoCheck: -1, - FailCheck: 0, - WarningCheck: 1, - SevereCheck: 2, - doRun: function(resources, result, callback) { - try { - var cacheableAndNonCacheableResources = this._cacheableAndNonCacheableResources(resources); - if (cacheableAndNonCacheableResources[0].length) { - result.score = 100; - this.runChecks(cacheableAndNonCacheableResources[0], result); - } - this.handleNonCacheableResources(cacheableAndNonCacheableResources[1], result); - } catch(e) { - console.log(e); - } finally { - callback(result); - } + var cacheableAndNonCacheableResources = this._cacheableAndNonCacheableResources(resources); + if (cacheableAndNonCacheableResources[0].length) + this.runChecks(cacheableAndNonCacheableResources[0], result); + this.handleNonCacheableResources(cacheableAndNonCacheableResources[1], result); + + callback(result); }, handleNonCacheableResources: function() @@ -495,36 +416,19 @@ WebInspector.AuditRules.CacheControlRule.prototype = { return processedResources; }, - execCheck: function(messageText, resourceCheckFunction, resources, severity, result) + execCheck: function(messageText, resourceCheckFunction, resources, result) { - var topMessage; - var failingResources = 0; var resourceCount = resources.length; - var outputResources = []; + var urls = []; for (var i = 0; i < resourceCount; ++i) { - if (resourceCheckFunction.call(this, resources[i])) { - ++failingResources; - if (!topMessage) - topMessage = result.appendChild(messageText); - outputResources.push(resources[i].url); - } + if (resourceCheckFunction.call(this, resources[i])) + urls.push(resources[i].url); } - if (topMessage) - topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources, true)); - if (failingResources) { - switch (severity) { - case this.FailCheck: - result.score = 0; - result.type = WebInspector.AuditRuleResult.Type.Violation; - break; - case this.SevereCheck: - case this.WarningCheck: - result.score -= 50 * severity * failingResources / resourceCount; - result.type = WebInspector.AuditRuleResult.Type.Hint; - break; - } + if (urls.length) { + var entry = result.addChild(messageText, true); + entry.addURLs(urls); + result.violationCount += urls.length; } - return topMessage; }, freshnessLifetimeGreaterThan: function(resource, timeMs) @@ -612,47 +516,34 @@ WebInspector.AuditRules.CacheControlRule.prototype = { WebInspector.AuditRules.CacheControlRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.BrowserCacheControlRule = function(parametersObject) +WebInspector.AuditRules.BrowserCacheControlRule = function() { - WebInspector.AuditRules.CacheControlRule.call(this, "http-browsercache", "Leverage browser caching", parametersObject); + WebInspector.AuditRules.CacheControlRule.call(this, "http-browsercache", "Leverage browser caching"); } WebInspector.AuditRules.BrowserCacheControlRule.prototype = { handleNonCacheableResources: function(resources, result) { if (resources.length) { - var message = result.appendChild( - "The following resources are explicitly non-cacheable. Consider making them cacheable if possible:"); - var resourceOutput = []; + var entry = result.addChild("The following resources are explicitly non-cacheable. Consider making them cacheable if possible:", true); + result.violationCount += resources.length; for (var i = 0; i < resources.length; ++i) - resourceOutput.push(resources[i].url); - message.appendChild(WebInspector.AuditRules.arrayAsUL(resourceOutput, true)); + entry.addURL(resources[i].url); } }, runChecks: function(resources, result, callback) { - this.execCheck( - "The following resources are missing a cache expiration." + - " Resources that do not specify an expiration may not be" + - " cached by browsers:", - this._missingExpirationCheck, resources, this.SevereCheck, result); - this.execCheck( - "The following resources specify a \"Vary\" header that" + - " disables caching in most versions of Internet Explorer:", - this._varyCheck, resources, this.SevereCheck, result); - this.execCheck( - "The following cacheable resources have a short" + - " freshness lifetime:", - this._oneMonthExpirationCheck, resources, this.WarningCheck, result); + this.execCheck("The following resources are missing a cache expiration. Resources that do not specify an expiration may not be cached by browsers:", + this._missingExpirationCheck, resources, result); + this.execCheck("The following resources specify a \"Vary\" header that disables caching in most versions of Internet Explorer:", + this._varyCheck, resources, result); + this.execCheck("The following cacheable resources have a short freshness lifetime:", + this._oneMonthExpirationCheck, resources, result); // Unable to implement the favicon check due to the WebKit limitations. - - this.execCheck( - "To further improve cache hit rate, specify an expiration" + - " one year in the future for the following cacheable" + - " resources:", - this._oneYearExpirationCheck, resources, this.InfoCheck, result); + this.execCheck("To further improve cache hit rate, specify an expiration one year in the future for the following cacheable resources:", + this._oneYearExpirationCheck, resources, result); }, _missingExpirationCheck: function(resource) @@ -691,26 +582,19 @@ WebInspector.AuditRules.BrowserCacheControlRule.prototype = { WebInspector.AuditRules.BrowserCacheControlRule.prototype.__proto__ = WebInspector.AuditRules.CacheControlRule.prototype; -WebInspector.AuditRules.ProxyCacheControlRule = function(parametersObject) { - WebInspector.AuditRules.CacheControlRule.call(this, "http-proxycache", "Leverage proxy caching", parametersObject); +WebInspector.AuditRules.ProxyCacheControlRule = function() { + WebInspector.AuditRules.CacheControlRule.call(this, "http-proxycache", "Leverage proxy caching"); } WebInspector.AuditRules.ProxyCacheControlRule.prototype = { runChecks: function(resources, result, callback) { - this.execCheck( - "Resources with a \"?\" in the URL are not cached by most" + - " proxy caching servers:", - this._questionMarkCheck, resources, this.WarningCheck, result); - this.execCheck( - "Consider adding a \"Cache-Control: public\" header to the" + - " following resources:", - this._publicCachingCheck, resources, this.InfoCheck, result); - this.execCheck( - "The following publicly cacheable resources contain" + - " a Set-Cookie header. This security vulnerability" + - " can cause cookies to be shared by multiple users.", - this._setCookieCacheableCheck, resources, this.FailCheck, result); + this.execCheck("Resources with a \"?\" in the URL are not cached by most proxy caching servers:", + this._questionMarkCheck, resources, result); + this.execCheck("Consider adding a \"Cache-Control: public\" header to the following resources:", + this._publicCachingCheck, resources, result); + this.execCheck("The following publicly cacheable resources contain a Set-Cookie header. This security vulnerability can cause cookies to be shared by multiple users.", + this._setCookieCacheableCheck, resources, result); }, _questionMarkCheck: function(resource) @@ -735,102 +619,104 @@ WebInspector.AuditRules.ProxyCacheControlRule.prototype = { WebInspector.AuditRules.ProxyCacheControlRule.prototype.__proto__ = WebInspector.AuditRules.CacheControlRule.prototype; -WebInspector.AuditRules.ImageDimensionsRule = function(parametersObject) +WebInspector.AuditRules.ImageDimensionsRule = function() { - WebInspector.AuditRule.call(this, "page-imagedims", "Specify image dimensions", parametersObject); + WebInspector.AuditRule.call(this, "page-imagedims", "Specify image dimensions"); } WebInspector.AuditRules.ImageDimensionsRule.prototype = { doRun: function(resources, result, callback) { - function evalCallback(evalResult, isException) + function doneCallback(context) { - try { - if (isException) - return; - if (!evalResult || !evalResult.totalImages) - return; - result.score = 100; - var topMessage = result.appendChild( - "A width and height should be specified for all images in order to " + - "speed up page display. The following image(s) are missing a width and/or height:"); - var map = evalResult.map; - var outputResources = []; - for (var url in map) { - var value = WebInspector.linkifyURL(url); - if (map[url] > 1) - value += " (" + map[url] + " uses)"; - outputResources.push(value); - result.score -= this.getValue("ScorePerImageUse") * map[url]; - result.type = WebInspector.AuditRuleResult.Type.Hint; - } - topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputResources)); - } catch(e) { - console.log(e); - } finally { - callback(result); + var map = context.urlToNoDimensionCount; + for (var url in map) { + var entry = entry || result.addChild("A width and height should be specified for all images in order to speed up page display. The following image(s) are missing a width and/or height:", true); + var value = WebInspector.AuditRuleResult.linkifyDisplayName(url); + if (map[url] > 1) + value += String.sprintf(" (%d uses)", map[url]); + entry.addChild(value); + result.violationCount++; } + callback(entry ? result : null); } - function routine() + function imageStylesReady(imageId, context, styles) { - var images = document.getElementsByTagName("img"); - const widthRegExp = /width[^:;]*:/gim; - const heightRegExp = /height[^:;]*:/gim; - - function hasDimension(element, cssText, rules, regexp, attributeName) { - if (element.attributes.getNamedItem(attributeName) != null || (cssText && cssText.match(regexp))) - return true; - - if (!rules) - return false; - for (var i = 0; i < rules.length; ++i) { - if (rules.item(i).style.cssText.match(regexp)) - return true; + --context.imagesLeft; + + const node = WebInspector.domAgent.nodeForId(imageId); + var src = node.getAttribute("src"); + for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) { + if (frameOwnerCandidate.documentURL) { + var completeSrc = WebInspector.completeURL(frameOwnerCandidate.documentURL, src); + break; } - return false; } + if (completeSrc) + src = completeSrc; - function hasWidth(element, cssText, rules) { - return hasDimension(element, cssText, rules, widthRegExp, "width"); + const computedStyle = new WebInspector.CSSStyleDeclaration(styles.computedStyle); + if (computedStyle.getPropertyValue("position") === "absolute") { + if (!context.imagesLeft) + doneCallback(context); + return; } - function hasHeight(element, cssText, rules) { - return hasDimension(element, cssText, rules, heightRegExp, "height"); + var widthFound = "width" in styles.styleAttributes; + var heightFound = "height" in styles.styleAttributes; + + for (var i = styles.matchedCSSRules.length - 1; i >= 0 && !(widthFound && heightFound); --i) { + var style = WebInspector.CSSStyleDeclaration.parseRule(styles.matchedCSSRules[i]).style; + if (style.getPropertyValue("width") !== "") + widthFound = true; + if (style.getPropertyValue("height") !== "") + heightFound = true; + } + + if (!widthFound || !heightFound) { + if (src in context.urlToNoDimensionCount) + ++context.urlToNoDimensionCount[src]; + else + context.urlToNoDimensionCount[src] = 1; } - var urlToNoDimensionCount = {}; - var found = false; - for (var i = 0; i < images.length; ++i) { - var image = images[i]; - if (!image.src) - continue; - var position = document.defaultView.getComputedStyle(image).getPropertyValue("position"); - if (position === "absolute") + if (!context.imagesLeft) + doneCallback(context); + } + + function receivedImages(imageIds) + { + if (!imageIds || !imageIds.length) + return callback(null); + var context = {imagesLeft: imageIds.length, urlToNoDimensionCount: {}}; + for (var i = imageIds.length - 1; i >= 0; --i) + InspectorBackend.getStyles(WebInspector.Callback.wrap(imageStylesReady.bind(this, imageIds[i], context)), imageIds[i], true); + } + + function pushImageNodes() + { + const nodeIds = []; + var nodes = document.getElementsByTagName("img"); + for (var i = 0; i < nodes.length; ++i) { + if (!nodes[i].src) continue; - var cssText = (image.style && image.style.cssText) ? image.style.cssText : ""; - var rules = document.defaultView.getMatchedCSSRules(image, "", true); - if (!hasWidth(image, cssText, rules) || !hasHeight(image, cssText, rules)) { - found = true; - if (urlToNoDimensionCount.hasOwnProperty(image.src)) - ++urlToNoDimensionCount[image.src]; - else - urlToNoDimensionCount[image.src] = 1; - } + var nodeId = this.getNodeId(nodes[i]); + nodeIds.push(nodeId); } - return found ? {totalImages: images.length, map: urlToNoDimensionCount} : null; + return nodeIds; } - WebInspector.AuditRules.evaluateInTargetWindow(routine, evalCallback.bind(this)); + WebInspector.AuditRules.evaluateInTargetWindow(pushImageNodes, null, receivedImages); } } WebInspector.AuditRules.ImageDimensionsRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CssInHeadRule = function(parametersObject) +WebInspector.AuditRules.CssInHeadRule = function() { - WebInspector.AuditRule.call(this, "page-cssinhead", "Put CSS in the document head", parametersObject); + WebInspector.AuditRule.call(this, "page-cssinhead", "Put CSS in the document head"); } WebInspector.AuditRules.CssInHeadRule.prototype = { @@ -838,36 +724,24 @@ WebInspector.AuditRules.CssInHeadRule.prototype = { { function evalCallback(evalResult, isException) { - try { - if (isException) - return; - if (!evalResult) - return; - result.score = 100; - var outputMessages = []; - for (var url in evalResult) { - var urlViolations = evalResult[url]; - var topMessage = result.appendChild( - String.sprintf("CSS in the %s document body adversely impacts rendering performance.", - WebInspector.linkifyURL(url))); - if (urlViolations[0]) { - outputMessages.push( - String.sprintf("%s style block(s) in the body should be moved to the document head.", urlViolations[0])); - result.score -= this.getValue("InlineURLScore") * urlViolations[0]; - } - for (var i = 0; i < urlViolations[1].length; ++i) { - outputMessages.push( - String.sprintf("Link node %s should be moved to the document head", WebInspector.linkifyURL(urlViolations[1]))); - } - result.score -= this.getValue("InlineStylesheetScore") * urlViolations[1]; - result.type = WebInspector.AuditRuleResult.Type.Hint; + if (isException || !evalResult) + return callback(null); + + var summary = result.addChild(""); + + var outputMessages = []; + for (var url in evalResult) { + var urlViolations = evalResult[url]; + if (urlViolations[0]) { + result.addChild(String.sprintf("%s style block(s) in the %s body should be moved to the document head.", urlViolations[0], WebInspector.AuditRuleResult.linkifyDisplayName(url))); + result.violationCount += urlViolations[0]; } - topMessage.appendChild(WebInspector.AuditRules.arrayAsUL(outputMessages)); - } catch(e) { - console.log(e); - } finally { - callback(result); + for (var i = 0; i < urlViolations[1].length; ++i) + result.addChild(String.sprintf("Link node %s should be moved to the document head in %s", WebInspector.AuditRuleResult.linkifyDisplayName(urlViolations[1][i]), WebInspector.AuditRuleResult.linkifyDisplayName(url))); + result.violationCount += urlViolations[1].length; } + summary.value = String.sprintf("CSS in the document body adversely impacts rendering performance."); + callback(result); } function routine() @@ -891,112 +765,81 @@ WebInspector.AuditRules.CssInHeadRule.prototype = { var urlToViolationsArray = {}; var found = false; for (var i = 0; i < views.length; ++i) { - var view = views[i]; - if (!view.document) - continue; - - var inlineStyles = view.document.querySelectorAll("body style"); - var inlineStylesheets = view.document.querySelectorAll( - "body link[rel~='stylesheet'][href]"); - if (!inlineStyles.length && !inlineStylesheets.length) - continue; - - found = true; - var inlineStylesheetHrefs = []; - for (var j = 0; j < inlineStylesheets.length; ++j) - inlineStylesheetHrefs.push(inlineStylesheets[j].href); - - urlToViolationsArray[view.location.href] = - [inlineStyles.length, inlineStylesheetHrefs]; + var view = views[i]; + if (!view.document) + continue; + + var inlineStyles = view.document.querySelectorAll("body style"); + var inlineStylesheets = view.document.querySelectorAll("body link[rel~='stylesheet'][href]"); + if (!inlineStyles.length && !inlineStylesheets.length) + continue; + + found = true; + var inlineStylesheetHrefs = []; + for (var j = 0; j < inlineStylesheets.length; ++j) + inlineStylesheetHrefs.push(inlineStylesheets[j].href); + urlToViolationsArray[view.location.href] = [inlineStyles.length, inlineStylesheetHrefs]; } return found ? urlToViolationsArray : null; } - WebInspector.AuditRules.evaluateInTargetWindow(routine, evalCallback); + WebInspector.AuditRules.evaluateInTargetWindow(routine, null, evalCallback); } } WebInspector.AuditRules.CssInHeadRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.StylesScriptsOrderRule = function(parametersObject) +WebInspector.AuditRules.StylesScriptsOrderRule = function() { - WebInspector.AuditRule.call(this, "page-stylescriptorder", "Optimize the order of styles and scripts", parametersObject); + WebInspector.AuditRule.call(this, "page-stylescriptorder", "Optimize the order of styles and scripts"); } WebInspector.AuditRules.StylesScriptsOrderRule.prototype = { doRun: function(resources, result, callback) { - function evalCallback(evalResult, isException) + function evalCallback(resultValue, isException) { - try { - if (isException) - return; - if (!evalResult) - return; - - result.score = 100; - var lateCssUrls = evalResult['late']; - if (lateCssUrls) { - var lateMessage = result.appendChild( - 'The following external CSS files were included after ' + - 'an external JavaScript file in the document head. To ' + - 'ensure CSS files are downloaded in parallel, always ' + - 'include external CSS before external JavaScript.'); - lateMessage.appendChild(WebInspector.AuditRules.arrayAsUL(lateCssUrls, true)); - result.score -= this.getValue("InlineBetweenResourcesScore") * lateCssUrls.length; - result.type = WebInspector.AuditRuleResult.Type.Violation; - } - if (evalResult['cssBeforeInlineCount']) { - var count = evalResult['cssBeforeInlineCount']; - result.appendChild(count + ' inline script block' + - (count > 1 ? 's were' : ' was') + ' found in the head between an ' + - 'external CSS file and another resource. To allow parallel ' + - 'downloading, move the inline script before the external CSS ' + - 'file, or after the next resource.'); - result.score -= this.getValue("CSSAfterJSURLScore") * count; - result.type = WebInspector.AuditRuleResult.Type.Violation; - } - } catch(e) { - console.log(e); - } finally { - callback(result); + if (isException || !resultValue) + return callback(null); + + var lateCssUrls = resultValue[0]; + var cssBeforeInlineCount = resultValue[1]; + + var entry = result.addChild("The following external CSS files were included after an external JavaScript file in the document head. To ensure CSS files are downloaded in parallel, always include external CSS before external JavaScript.", true); + entry.addURLs(lateCssUrls); + result.violationCount += lateCssUrls.length; + + if (cssBeforeInlineCount) { + result.addChild(String.sprintf(" %d inline script block%s found in the head between an external CSS file and another resource. To allow parallel downloading, move the inline script before the external CSS file, or after the next resource.", cssBeforeInlineCount, cssBeforeInlineCount > 1 ? "s were" : " was")); + result.violationCount += cssBeforeInlineCount; } + callback(result); } function routine() { - var lateStyles = document.querySelectorAll( - "head script[src] ~ link[rel~='stylesheet'][href]"); - var stylesBeforeInlineScript = document.querySelectorAll( - "head link[rel~='stylesheet'][href] ~ script:not([src])"); - - var resultObject; - if (!lateStyles.length && !stylesBeforeInlineScript.length) - resultObject = null; - else { - resultObject = {}; - if (lateStyles.length) { - lateStyleUrls = []; - for (var i = 0; i < lateStyles.length; ++i) - lateStyleUrls.push(lateStyles[i].href); - resultObject["late"] = lateStyleUrls; - } - resultObject["cssBeforeInlineCount"] = stylesBeforeInlineScript.length; - } - return resultObject; + var lateStyles = document.querySelectorAll("head script[src] ~ link[rel~='stylesheet'][href]"); + var cssBeforeInlineCount = document.querySelectorAll("head link[rel~='stylesheet'][href] ~ script:not([src])").length; + if (!lateStyles.length && !cssBeforeInlineCount) + return null; + + var lateStyleUrls = []; + for (var i = 0; i < lateStyles.length; ++i) + lateStyleUrls.push(lateStyles[i].href); + return [ lateStyleUrls, cssBeforeInlineCount ]; } - WebInspector.AuditRules.evaluateInTargetWindow(routine, evalCallback.bind(this)); + WebInspector.AuditRules.evaluateInTargetWindow(routine, null, evalCallback.bind(this)); } } WebInspector.AuditRules.StylesScriptsOrderRule.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CookieRuleBase = function(id, name, parametersObject) +WebInspector.AuditRules.CookieRuleBase = function(id, name) { - WebInspector.AuditRule.call(this, id, name, parametersObject); + WebInspector.AuditRule.call(this, id, name); } WebInspector.AuditRules.CookieRuleBase.prototype = { @@ -1004,13 +847,8 @@ WebInspector.AuditRules.CookieRuleBase.prototype = { { var self = this; function resultCallback(receivedCookies, isAdvanced) { - try { - self.processCookies(isAdvanced ? receivedCookies : [], resources, result); - } catch(e) { - console.log(e); - } finally { - callback(result); - } + self.processCookies(isAdvanced ? receivedCookies : [], resources, result); + callback(result); } WebInspector.Cookies.getCookiesAsync(resultCallback); }, @@ -1039,9 +877,11 @@ WebInspector.AuditRules.CookieRuleBase.prototype = { WebInspector.AuditRules.CookieRuleBase.prototype.__proto__ = WebInspector.AuditRule.prototype; -WebInspector.AuditRules.CookieSizeRule = function(parametersObject) +WebInspector.AuditRules.CookieSizeRule = function(avgBytesThreshold) { - WebInspector.AuditRules.CookieRuleBase.call(this, "http-cookiesize", "Minimize cookie size", parametersObject); + WebInspector.AuditRules.CookieRuleBase.call(this, "http-cookiesize", "Minimize cookie size"); + this._avgBytesThreshold = avgBytesThreshold; + this._maxBytesThreshold = 1000; } WebInspector.AuditRules.CookieSizeRule.prototype = { @@ -1097,7 +937,6 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { var matchingResourceData = {}; this.mapResourceCookies(domainToResourcesMap, allCookies, collectorCallback.bind(this)); - result.score = 100; for (var resourceDomain in cookiesPerResourceDomain) { var cookies = cookiesPerResourceDomain[resourceDomain]; sortedCookieSizes.push({ @@ -1111,13 +950,10 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { var hugeCookieDomains = []; sortedCookieSizes.sort(maxSizeSorter); - var maxBytesThreshold = this.getValue("MaxBytesThreshold"); - var minBytesThreshold = this.getValue("MinBytesThreshold"); - for (var i = 0, len = sortedCookieSizes.length; i < len; ++i) { var maxCookieSize = sortedCookieSizes[i].maxCookieSize; - if (maxCookieSize > maxBytesThreshold) - hugeCookieDomains.push(sortedCookieSizes[i].domain + ": " + Number.bytesToString(maxCookieSize)); + if (maxCookieSize > this._maxBytesThreshold) + hugeCookieDomains.push(WebInspector.AuditRuleResult.resourceDomain(sortedCookieSizes[i].domain) + ": " + Number.bytesToString(maxCookieSize)); } var bigAvgCookieDomains = []; @@ -1125,45 +961,33 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { for (var i = 0, len = sortedCookieSizes.length; i < len; ++i) { var domain = sortedCookieSizes[i].domain; var avgCookieSize = sortedCookieSizes[i].avgCookieSize; - if (avgCookieSize > minBytesThreshold && avgCookieSize < maxBytesThreshold) - bigAvgCookieDomains.push(domain + ": " + Number.bytesToString(avgCookieSize)); + if (avgCookieSize > this._avgBytesThreshold && avgCookieSize < this._maxBytesThreshold) + bigAvgCookieDomains.push(WebInspector.AuditRuleResult.resourceDomain(domain) + ": " + Number.bytesToString(avgCookieSize)); } - result.appendChild("The average cookie size for all requests on this page is " + Number.bytesToString(avgAllCookiesSize)); + result.addChild(String.sprintf("The average cookie size for all requests on this page is %s", Number.bytesToString(avgAllCookiesSize))); var message; if (hugeCookieDomains.length) { - result.score = 75; - result.type = WebInspector.AuditRuleResult.Type.Violation; - message = result.appendChild( - String.sprintf("The following domains have a cookie size in excess of %d " + - " bytes. This is harmful because requests with cookies larger than 1KB" + - " typically cannot fit into a single network packet.", maxBytesThreshold)); - message.appendChild(WebInspector.AuditRules.arrayAsUL(hugeCookieDomains)); + var entry = result.addChild("The following domains have a cookie size in excess of 1KB. This is harmful because requests with cookies larger than 1KB typically cannot fit into a single network packet.", true); + entry.addURLs(hugeCookieDomains); + result.violationCount += hugeCookieDomains.length; } if (bigAvgCookieDomains.length) { - this.score -= Math.max(0, avgAllCookiesSize - minBytesThreshold) / - (minBytesThreshold - minBytesThreshold) / this.getValue("TotalPoints"); - if (!result.type) - result.type = WebInspector.AuditRuleResult.Type.Hint; - message = result.appendChild( - String.sprintf("The following domains have an average cookie size in excess of %d" + - " bytes. Reducing the size of cookies" + - " for these domains can reduce the time it takes to send requests.", minBytesThreshold)); - message.appendChild(WebInspector.AuditRules.arrayAsUL(bigAvgCookieDomains)); + var entry = result.addChild(String.sprintf("The following domains have an average cookie size in excess of %d bytes. Reducing the size of cookies for these domains can reduce the time it takes to send requests.", this._avgBytesThreshold), true); + entry.addURLs(bigAvgCookieDomains); + result.violationCount += bigAvgCookieDomains.length; } - - if (!bigAvgCookieDomains.length && !hugeCookieDomains.length) - result.score = WebInspector.AuditCategoryResult.ScoreNA; } } WebInspector.AuditRules.CookieSizeRule.prototype.__proto__ = WebInspector.AuditRules.CookieRuleBase.prototype; -WebInspector.AuditRules.StaticCookielessRule = function(parametersObject) +WebInspector.AuditRules.StaticCookielessRule = function(minResources) { - WebInspector.AuditRules.CookieRuleBase.call(this, "http-staticcookieless", "Serve static content from a cookieless domain", parametersObject); + WebInspector.AuditRules.CookieRuleBase.call(this, "http-staticcookieless", "Serve static content from a cookieless domain"); + this._minResources = minResources; } WebInspector.AuditRules.StaticCookielessRule.prototype = { @@ -1175,10 +999,9 @@ WebInspector.AuditRules.StaticCookielessRule.prototype = { WebInspector.URLRegExp, true); var totalStaticResources = 0; - var minResources = this.getValue("MinResources"); for (var domain in domainToResourcesMap) totalStaticResources += domainToResourcesMap[domain].length; - if (totalStaticResources < minResources) + if (totalStaticResources < this._minResources) return; var matchingResourceData = {}; this.mapResourceCookies(domainToResourcesMap, allCookies, this._collectorCallback.bind(this, matchingResourceData)); @@ -1189,19 +1012,12 @@ WebInspector.AuditRules.StaticCookielessRule.prototype = { badUrls.push(url); cookieBytes += matchingResourceData[url] } - if (badUrls.length < minResources) + if (badUrls.length < this._minResources) return; - result.score = 100; - var badPoints = cookieBytes / 75; - var violationPct = Math.max(badUrls.length / totalStaticResources, 0.6); - badPoints *= violationPct; - result.score -= badPoints; - result.score = Math.max(result.score, 0); - result.type = WebInspector.AuditRuleResult.Type.Violation; - result.appendChild(String.sprintf("%s of cookies were sent with the following static resources.", Number.bytesToString(cookieBytes))); - var message = result.appendChild("Serve these static resources from a domain that does not set cookies:"); - message.appendChild(WebInspector.AuditRules.arrayAsUL(badUrls, true)); + var entry = result.addChild(String.sprintf("%s of cookies were sent with the following static resources. Serve these static resources from a domain that does not set cookies:", Number.bytesToString(cookieBytes)), true); + entry.addURLs(badUrls); + result.violationCount = badUrls.length; }, _collectorCallback: function(matchingResourceData, resource, cookie) diff --git a/WebCore/inspector/front-end/AuditsPanel.js b/WebCore/inspector/front-end/AuditsPanel.js index fcadb82..c3318ce 100644 --- a/WebCore/inspector/front-end/AuditsPanel.js +++ b/WebCore/inspector/front-end/AuditsPanel.js @@ -37,6 +37,7 @@ WebInspector.AuditsPanel = function() this.createSidebar(); this.auditsTreeElement = new WebInspector.SidebarSectionTreeElement("", {}, true); this.sidebarTree.appendChild(this.auditsTreeElement); + this.auditsTreeElement.listItemElement.addStyleClass("hidden"); this.auditsTreeElement.expand(); this.auditsItemTreeElement = new WebInspector.AuditsSidebarTreeElement(); @@ -54,6 +55,8 @@ WebInspector.AuditsPanel = function() this.viewsContainerElement = document.createElement("div"); this.viewsContainerElement.id = "audit-views"; this.element.appendChild(this.viewsContainerElement); + + this._launcherView = new WebInspector.AuditLauncherView(this.categoriesById, this.initiateAudit.bind(this)); } WebInspector.AuditsPanel.prototype = { @@ -95,9 +98,14 @@ WebInspector.AuditsPanel.prototype = { return this._auditCategoriesById; }, - get visibleView() + resourceStarted: function(resource) { - return this._visibleView; + this._launcherView.resourceStarted(resource); + }, + + resourceFinished: function(resource) + { + this._launcherView.resourceFinished(resource); }, _constructCategories: function() @@ -125,8 +133,8 @@ WebInspector.AuditsPanel.prototype = { function ruleResultReadyCallback(categoryResult, ruleResult) { - if (ruleResult.children) - categoryResult.entries.push(ruleResult); + if (ruleResult && ruleResult.children) + categoryResult.addRuleResult(ruleResult); --rulesRemaining; @@ -188,18 +196,18 @@ WebInspector.AuditsPanel.prototype = { { this._resourceTrackingCallback = callback; - if (!InspectorBackend.resourceTrackingEnabled()) { + if (!WebInspector.panels.resources.resourceTrackingEnabled) { InspectorBackend.enableResourceTracking(false); this._updateLauncherViewControls(true); } else - InjectedScriptAccess.getDefault().evaluate("window.location.reload()", switchCallback); + InspectorBackend.reloadPage(); }, _didMainResourceLoad: function() { if (this._resourceTrackingCallback) { var callback = this._resourceTrackingCallback; - this._resourceTrackingCallback = null; + delete this._resourceTrackingCallback; callback(); } }, @@ -209,36 +217,42 @@ WebInspector.AuditsPanel.prototype = { if (!categoryResults._resultView) categoryResults._resultView = new WebInspector.AuditResultView(categoryResults); - this.showView(categoryResults._resultView); + this.visibleView = categoryResults._resultView; }, showLauncherView: function() { - if (!this._launcherView) - this._launcherView = new WebInspector.AuditLauncherView(this.categoriesById, this.initiateAudit.bind(this)); - - this.showView(this._launcherView); + this.visibleView = this._launcherView; + }, + + get visibleView() + { + return this._visibleView; }, - showView: function(view) + set visibleView(x) { - if (view) { - if (this._visibleView === view) - return; - this._closeVisibleView(); - this._visibleView = view; - } - var visibleView = this.visibleView; - if (visibleView) - visibleView.show(this.viewsContainerElement); + if (this._visibleView === x) + return; + + if (this._visibleView) + this._visibleView.hide(); + + this._visibleView = x; + + if (x) + x.show(this.viewsContainerElement); }, show: function() { WebInspector.Panel.prototype.show.call(this); + this._updateLauncherViewControls(WebInspector.panels.resources.resourceTrackingEnabled); + }, - this.showView(); - this._updateLauncherViewControls(InspectorBackend.resourceTrackingEnabled()); + reset: function() + { + this._launcherView.reset(); }, attach: function() @@ -264,12 +278,6 @@ WebInspector.AuditsPanel.prototype = { this.auditsItemTreeElement.reveal(); this.auditsItemTreeElement.select(); this.auditResultsTreeElement.removeChildren(); - }, - - _closeVisibleView: function() - { - if (this.visibleView) - this.visibleView.hide(); } } @@ -301,8 +309,9 @@ WebInspector.AuditCategory.prototype = { return this._rules.length; }, - addRule: function(rule) + addRule: function(rule, severity) { + rule.severity = severity; this._rules.push(rule); }, @@ -324,11 +333,22 @@ WebInspector.AuditCategory.prototype = { } -WebInspector.AuditRule = function(id, displayName, parametersObject) +WebInspector.AuditRule = function(id, displayName) { this._id = id; this._displayName = displayName; - this._parametersObject = parametersObject; +} + +WebInspector.AuditRule.Severity = { + Info: "info", + Warning: "warning", + Severe: "severe" +} + +WebInspector.AuditRule.SeverityOrder = { + "info": 3, + "warning": 2, + "severe": 1 } WebInspector.AuditRule.prototype = { @@ -342,83 +362,82 @@ WebInspector.AuditRule.prototype = { return this._displayName; }, - run: function(resources, callback) + set severity(severity) { - this.doRun(resources, new WebInspector.AuditRuleResult(this.displayName), callback); + this._severity = severity; }, - doRun: function(resources, result, callback) + run: function(resources, callback) { - throw new Error("doRun() not implemented"); + var result = new WebInspector.AuditRuleResult(this.displayName); + result.severity = this._severity; + this.doRun(resources, result, callback); }, - getValue: function(key) + doRun: function(resources, result, callback) { - if (key in this._parametersObject) - return this._parametersObject[key]; - else - throw new Error(key + " not found in rule parameters"); + throw new Error("doRun() not implemented"); } } - WebInspector.AuditCategoryResult = function(category) { this.title = category.displayName; - this.entries = []; + this.ruleResults = []; } WebInspector.AuditCategoryResult.prototype = { - addEntry: function(value) + addRuleResult: function(ruleResult) { - var entry = new WebInspector.AuditRuleResult(value); - this.entries.push(entry); - return entry; + this.ruleResults.push(ruleResult); } } -/** - * @param {string} value The result message HTML contents. - */ -WebInspector.AuditRuleResult = function(value) +WebInspector.AuditRuleResult = function(value, expanded, className) { this.value = value; - this.type = WebInspector.AuditRuleResult.Type.NA; + this.className = className; + this.expanded = expanded; + this.violationCount = 0; } -WebInspector.AuditRuleResult.Type = { - // Does not denote a discovered flaw but rather represents an informational message. - NA: 0, - - // Denotes a minor impact on the checked metric. - Hint: 1, +WebInspector.AuditRuleResult.linkifyDisplayName = function(url) +{ + return WebInspector.linkifyURL(url, WebInspector.displayNameForURL(url)); +} - // Denotes a major impact on the checked metric. - Violation: 2 +WebInspector.AuditRuleResult.resourceDomain = function(domain) +{ + return domain || WebInspector.UIString("[empty domain]"); } WebInspector.AuditRuleResult.prototype = { - appendChild: function(value) + addChild: function(value, expanded, className) { if (!this.children) this.children = []; - var entry = new WebInspector.AuditRuleResult(value); + var entry = new WebInspector.AuditRuleResult(value, expanded, className); this.children.push(entry); return entry; }, - set type(x) + addURL: function(url) { - this._type = x; + return this.addChild(WebInspector.AuditRuleResult.linkifyDisplayName(url)); }, - get type() + addURLs: function(urls) { - return this._type; + for (var i = 0; i < urls.length; ++i) + this.addURL(urls[i]); + }, + + addSnippet: function(snippet) + { + return this.addChild(snippet, false, "source-code"); } } - WebInspector.AuditsSidebarTreeElement = function() { this.small = false; diff --git a/WebCore/inspector/front-end/Breakpoint.js b/WebCore/inspector/front-end/Breakpoint.js index 5d46cc9..7f3ef17 100644 --- a/WebCore/inspector/front-end/Breakpoint.js +++ b/WebCore/inspector/front-end/Breakpoint.js @@ -89,7 +89,7 @@ WebInspector.Breakpoint.prototype = { this.dispatchEventToListeners("condition-changed"); if (this.enabled) - InspectorBackend.updateBreakpoint(this.sourceID, this.line, c); + InspectorBackend.setBreakpoint(this.sourceID, this.line, this.enabled, this.condition); } } diff --git a/WebCore/inspector/front-end/BreakpointsSidebarPane.js b/WebCore/inspector/front-end/BreakpointsSidebarPane.js index 8865f0b..fcfd2ab 100644 --- a/WebCore/inspector/front-end/BreakpointsSidebarPane.js +++ b/WebCore/inspector/front-end/BreakpointsSidebarPane.js @@ -40,6 +40,16 @@ WebInspector.BreakpointsSidebarPane = function() } WebInspector.BreakpointsSidebarPane.prototype = { + reset: function() + { + this.breakpoints = {}; + this.listElement.removeChildren(); + if (this.listElement.parentElement) { + this.bodyElement.removeChild(this.listElement); + this.bodyElement.appendChild(this.emptyElement); + } + }, + addBreakpoint: function(breakpoint) { if (this.breakpoints[breakpoint.id]) @@ -58,11 +68,7 @@ WebInspector.BreakpointsSidebarPane.prototype = { this.bodyElement.appendChild(this.listElement); } - if (!InspectorBackend.debuggerEnabled() || !breakpoint.sourceID) - return; - - if (breakpoint.enabled) - InspectorBackend.addBreakpoint(breakpoint.sourceID, breakpoint.line, breakpoint.condition); + InspectorBackend.setBreakpoint(breakpoint.sourceID, breakpoint.line, breakpoint.enabled, breakpoint.condition); }, _appendBreakpointElement: function(breakpoint) @@ -77,9 +83,7 @@ WebInspector.BreakpointsSidebarPane.prototype = { function breakpointClicked() { - var script = WebInspector.panels.scripts.scriptOrResourceForID(breakpoint.sourceID); - if (script) - WebInspector.panels.scripts.showScript(script, breakpoint.line); + WebInspector.panels.scripts.showSourceLine(breakpoint.url, breakpoint.line); } var breakpointElement = document.createElement("li"); @@ -135,9 +139,6 @@ WebInspector.BreakpointsSidebarPane.prototype = { this.bodyElement.appendChild(this.emptyElement); } - if (!InspectorBackend.debuggerEnabled() || !breakpoint.sourceID) - return; - InspectorBackend.removeBreakpoint(breakpoint.sourceID, breakpoint.line); }, @@ -147,14 +148,7 @@ WebInspector.BreakpointsSidebarPane.prototype = { var checkbox = breakpoint._breakpointListElement.firstChild; checkbox.checked = breakpoint.enabled; - - if (!InspectorBackend.debuggerEnabled() || !breakpoint.sourceID) - return; - - if (breakpoint.enabled) - InspectorBackend.addBreakpoint(breakpoint.sourceID, breakpoint.line, breakpoint.condition); - else - InspectorBackend.removeBreakpoint(breakpoint.sourceID, breakpoint.line); + InspectorBackend.setBreakpoint(breakpoint.sourceID, breakpoint.line, breakpoint.enabled, breakpoint.condition); }, _breakpointTextChanged: function(event) diff --git a/WebCore/inspector/front-end/Checkbox.js b/WebCore/inspector/front-end/Checkbox.js new file mode 100644 index 0000000..b30da70 --- /dev/null +++ b/WebCore/inspector/front-end/Checkbox.js @@ -0,0 +1,56 @@ +/* + * 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. ``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 + * 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.Checkbox = function(label, callback, checked, className, tooltip) +{ + this.element = document.createElement('label'); + this._inputElement = document.createElement('input'); + + function callbackWrapper(event) + { + if (callback) + callback(event); + event.stopPropagation(); + return true; + } + this._inputElement.type = "checkbox"; + this._inputElement.checked = checked; + this._inputElement.addEventListener("click", callbackWrapper, false); + + this.element.className = className; + this.element.appendChild(this._inputElement); + this.element.appendChild(document.createTextNode(label)); + if (tooltip) + this.element.title = tooltip; + this.element.addEventListener("click", callbackWrapper, false); +} + +WebInspector.Checkbox.prototype = { + checked: function() + { + return this._inputElement.checked; + } +} + diff --git a/WebCore/inspector/front-end/ConsoleView.js b/WebCore/inspector/front-end/ConsoleView.js index 07d9812..f840a81 100644 --- a/WebCore/inspector/front-end/ConsoleView.js +++ b/WebCore/inspector/front-end/ConsoleView.js @@ -48,6 +48,7 @@ WebInspector.ConsoleView = function(drawer) this.promptElement.className = "source-code"; this.promptElement.addEventListener("keydown", this._promptKeyDown.bind(this), true); this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), ExpressionStopCharacters + "."); + WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this); this.topGroup = new WebInspector.ConsoleGroup(null, 0); this.messagesElement.insertBefore(this.topGroup.element, this.promptElement); @@ -92,11 +93,13 @@ WebInspector.ConsoleView = function(drawer) this._shortcuts = {}; var shortcut; - var clearConsoleHandler = this.requestClearMessages.bind(this); shortcut = WebInspector.KeyboardShortcut.makeKey("k", WebInspector.KeyboardShortcut.Modifiers.Meta); - this._shortcuts[shortcut] = clearConsoleHandler; + // This case requires a separate bound function as its isMacOnly property should not be shared among different shortcut handlers. + this._shortcuts[shortcut] = this.requestClearMessages.bind(this); this._shortcuts[shortcut].isMacOnly = true; + + var clearConsoleHandler = this.requestClearMessages.bind(this); shortcut = WebInspector.KeyboardShortcut.makeKey("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl); this._shortcuts[shortcut] = clearConsoleHandler; @@ -115,6 +118,10 @@ WebInspector.ConsoleView = function(drawer) } WebInspector.ConsoleView.prototype = { + _settingsLoaded: function() + { + this.prompt.history = WebInspector.settings.consoleHistory; + }, _updateFilter: function(e) { @@ -215,6 +222,19 @@ WebInspector.ConsoleView.prototype = { this.toggleConsoleButton.title = WebInspector.UIString("Show console."); }, + _scheduleScrollIntoView: function() + { + if (this._scrollIntoViewTimer) + return; + + function scrollIntoView() + { + this.promptElement.scrollIntoView(false); + delete this._scrollIntoViewTimer; + } + this._scrollIntoViewTimer = setTimeout(scrollIntoView.bind(this), 20); + }, + addMessage: function(msg) { if (msg instanceof WebInspector.ConsoleMessage && !(msg instanceof WebInspector.ConsoleCommandResult)) { @@ -256,7 +276,7 @@ WebInspector.ConsoleView.prototype = { this.currentGroup.addMessage(msg); } - this.promptElement.scrollIntoView(false); + this._scheduleScrollIntoView(); }, updateMessageRepeatCount: function(count) @@ -491,6 +511,9 @@ WebInspector.ConsoleView.prototype = { self.prompt.history.push(str); self.prompt.historyOffset = 0; self.prompt.text = ""; + + WebInspector.settings.consoleHistory = self.prompt.history.slice(-30); + self.addMessage(new WebInspector.ConsoleCommandResult(result, exception, commandMessage)); } this.evalInInspectedWindow(str, "console", printResult); @@ -504,7 +527,7 @@ WebInspector.ConsoleView.prototype = { var formatter = this._customFormatters[type]; if (!formatter || !isProxy) { formatter = this._formatvalue; - output = output.description || output; + output = output.description; } var span = document.createElement("span"); @@ -568,7 +591,7 @@ WebInspector.ConsoleView.prototype = { for (var i = 0; i < properties.length; ++i) { var name = properties[i].name; if (name == parseInt(name)) - elements[name] = this._format(properties[i].value); + elements[name] = this._formatAsArrayEntry(properties[i].value); } elem.appendChild(document.createTextNode("[")); @@ -582,6 +605,13 @@ WebInspector.ConsoleView.prototype = { elem.appendChild(document.createTextNode(", ")); } elem.appendChild(document.createTextNode("]")); + }, + + _formatAsArrayEntry: function(output) + { + var type = Object.proxyType(output); + // Prevent infinite expansion of cross-referencing arrays. + return this._format(output, type === "array"); } } diff --git a/WebCore/inspector/front-end/CookieItemsView.js b/WebCore/inspector/front-end/CookieItemsView.js index b5674b8..1baf4a6 100644 --- a/WebCore/inspector/front-end/CookieItemsView.js +++ b/WebCore/inspector/front-end/CookieItemsView.js @@ -122,7 +122,7 @@ WebInspector.CookieItemsView.prototype = { for (var id in WebInspector.resources) { var resource = WebInspector.resources[id]; - var match = resource.documentURL.match(WebInspector.URLRegExp); + var match = resource.documentURL.match(WebInspector.GenericURLRegExp); if (match && match[2] === this._cookieDomain) resourceURLsForDocumentURL.push(resource.url); } diff --git a/WebCore/inspector/front-end/DOMAgent.js b/WebCore/inspector/front-end/DOMAgent.js index 834f527..46ff0bf 100644 --- a/WebCore/inspector/front-end/DOMAgent.js +++ b/WebCore/inspector/front-end/DOMAgent.js @@ -211,30 +211,6 @@ WebInspector.DOMNode.prototype = { }; this._attributesMap[name] = attr; this.attributes.push(attr); - }, - - _setStyles: function(computedStyle, inlineStyle, styleAttributes, matchedCSSRules) - { - this._computedStyle = new WebInspector.CSSStyleDeclaration(computedStyle); - this.style = new WebInspector.CSSStyleDeclaration(inlineStyle); - - for (var name in styleAttributes) { - if (this._attributesMap[name]) - this._attributesMap[name].style = new WebInspector.CSSStyleDeclaration(styleAttributes[name]); - } - - this._matchedCSSRules = []; - for (var i = 0; i < matchedCSSRules.length; i++) - this._matchedCSSRules.push(WebInspector.CSSStyleDeclaration.parseRule(matchedCSSRules[i])); - }, - - _clearStyles: function() - { - this.computedStyle = null; - this.style = null; - for (var name in this._attributesMap) - this._attributesMap[name].style = null; - this._matchedCSSRules = null; } } @@ -308,16 +284,6 @@ WebInspector.DOMWindow.prototype = { Object: function() { - }, - - getComputedStyle: function(node) - { - return node._computedStyle; - }, - - getMatchedCSSRules: function(node, pseudoElement, authorOnly) - { - return node._matchedCSSRules; } } @@ -489,7 +455,7 @@ WebInspector.Cookies.buildCookiesFromString = function(rawCookieString) WebInspector.Cookies.cookieMatchesResourceURL = function(cookie, resourceURL) { - var match = resourceURL.match(WebInspector.URLRegExp); + var match = resourceURL.match(WebInspector.GenericURLRegExp); if (!match) return false; // See WebInspector.URLRegExp for definitions of the group index constants. @@ -523,13 +489,20 @@ WebInspector.EventListeners.getEventListenersForNodeAsync = function(node, callb WebInspector.CSSStyleDeclaration = function(payload) { this.id = payload.id; - this.injectedScriptId = payload.injectedScriptId; this.width = payload.width; this.height = payload.height; - this.__disabledProperties = payload.__disabledProperties; - this.__disabledPropertyValues = payload.__disabledPropertyValues; - this.__disabledPropertyPriorities = payload.__disabledPropertyPriorities; - this.uniqueStyleProperties = payload.uniqueStyleProperties; + this.__disabledProperties = {}; + this.__disabledPropertyValues = {}; + this.__disabledPropertyPriorities = {}; + if (payload.disabled) { + for (var i = 0; i < payload.disabled.length; ++i) { + var property = payload.disabled[i]; + this.__disabledProperties[property.name] = true; + this.__disabledPropertyValues[property.name] = property.value; + this.__disabledPropertyPriorities[property.name] = property.priority; + } + } + this._shorthandValues = payload.shorthandValues; this._propertyMap = {}; this._longhandProperties = {}; @@ -540,12 +513,8 @@ WebInspector.CSSStyleDeclaration = function(payload) var name = property.name; this[i] = name; this._propertyMap[name] = property; - } - // Index longhand properties. - for (var i = 0; i < this.uniqueStyleProperties.length; ++i) { - var name = this.uniqueStyleProperties[i]; - var property = this._propertyMap[name]; + // Index longhand properties. if (property.shorthand) { var longhands = this._longhandProperties[property.shorthand]; if (!longhands) { @@ -566,13 +535,13 @@ WebInspector.CSSStyleDeclaration.parseRule = function(payload) { var rule = {}; rule.id = payload.id; - rule.injectedScriptId = payload.injectedScriptId; rule.selectorText = payload.selectorText; rule.style = new WebInspector.CSSStyleDeclaration(payload.style); rule.style.parentRule = rule; rule.isUserAgent = payload.isUserAgent; rule.isUser = payload.isUser; rule.isViaInspector = payload.isViaInspector; + rule.sourceLine = payload.sourceLine; if (payload.parentStyleSheet) rule.parentStyleSheet = { href: payload.parentStyleSheet.href }; @@ -697,4 +666,17 @@ WebInspector.didPerformSearch = WebInspector.Callback.processCallback; WebInspector.didApplyDomChange = WebInspector.Callback.processCallback; WebInspector.didRemoveAttribute = WebInspector.Callback.processCallback; WebInspector.didSetTextNodeValue = WebInspector.Callback.processCallback; +WebInspector.didRemoveNode = WebInspector.Callback.processCallback; +WebInspector.didChangeTagName = WebInspector.Callback.processCallback; WebInspector.didGetEventListenersForNode = WebInspector.Callback.processCallback; + +WebInspector.didGetStyles = WebInspector.Callback.processCallback; +WebInspector.didGetAllStyles = WebInspector.Callback.processCallback; +WebInspector.didGetInlineStyle = WebInspector.Callback.processCallback; +WebInspector.didGetComputedStyle = WebInspector.Callback.processCallback; +WebInspector.didApplyStyleText = WebInspector.Callback.processCallback; +WebInspector.didSetStyleText = WebInspector.Callback.processCallback; +WebInspector.didSetStyleProperty = WebInspector.Callback.processCallback; +WebInspector.didToggleStyleEnabled = WebInspector.Callback.processCallback; +WebInspector.didSetRuleSelector = WebInspector.Callback.processCallback; +WebInspector.didAddRule = WebInspector.Callback.processCallback; diff --git a/WebCore/inspector/front-end/DatabaseQueryView.js b/WebCore/inspector/front-end/DatabaseQueryView.js index cc902e7..38c8df4 100644 --- a/WebCore/inspector/front-end/DatabaseQueryView.js +++ b/WebCore/inspector/front-end/DatabaseQueryView.js @@ -140,13 +140,15 @@ WebInspector.DatabaseQueryView.prototype = { _queryFinished: function(query, result) { var dataGrid = WebInspector.panels.storage.dataGridForResult(result); - if (!dataGrid) - return; - dataGrid.element.addStyleClass("inline"); - this._appendQueryResult(query, dataGrid.element); - dataGrid.autoSizeColumns(5); + var trimmedQuery = query.trim(); + + if (dataGrid) { + dataGrid.element.addStyleClass("inline"); + this._appendQueryResult(trimmedQuery, dataGrid.element); + dataGrid.autoSizeColumns(5); + } - if (query.match(/^create /i) || query.match(/^drop table /i)) + if (trimmedQuery.match(/^create /i) || trimmedQuery.match(/^drop table /i)) WebInspector.panels.storage.updateDatabaseTables(this.database); }, diff --git a/WebCore/inspector/front-end/ElementsPanel.js b/WebCore/inspector/front-end/ElementsPanel.js index 897fdd1..e2ca838 100644 --- a/WebCore/inspector/front-end/ElementsPanel.js +++ b/WebCore/inspector/front-end/ElementsPanel.js @@ -58,10 +58,6 @@ WebInspector.ElementsPanel = function() this.panel.updateProperties(); this.panel.updateEventListeners(); - if (InspectorBackend.searchingForNode()) { - InspectorBackend.toggleNodeSearch(); - this.panel.nodeSearchButton.toggled = false; - } if (this._focusedDOMNode) InjectedScriptAccess.get(this._focusedDOMNode.injectedScriptId).addInspectedNode(this._focusedDOMNode.id, function() {}); }; @@ -102,10 +98,8 @@ WebInspector.ElementsPanel = function() this.sidebarResizeElement.className = "sidebar-resizer-vertical"; this.sidebarResizeElement.addEventListener("mousedown", this.rightSidebarResizerDragStart.bind(this), false); - this.nodeSearchButton = new WebInspector.StatusBarButton(WebInspector.UIString("Select an element in the page to inspect it."), "node-search-status-bar-item"); - this.nodeSearchButton.addEventListener("click", this._nodeSearchButtonClicked.bind(this), false); - - this.searchingForNode = false; + this._nodeSearchButton = new WebInspector.StatusBarButton(WebInspector.UIString("Select an element in the page to inspect it."), "node-search-status-bar-item"); + this._nodeSearchButton.addEventListener("click", this._nodeSearchButtonClicked.bind(this), false); this.element.appendChild(this.contentElement); this.element.appendChild(this.sidebarElement); @@ -126,7 +120,7 @@ WebInspector.ElementsPanel.prototype = { get statusBarItems() { - return [this.nodeSearchButton.element, this.crumbsElement]; + return [this._nodeSearchButton.element, this.crumbsElement]; }, get defaultFocusedElement() @@ -154,11 +148,7 @@ WebInspector.ElementsPanel.prototype = { WebInspector.Panel.prototype.hide.call(this); WebInspector.hoveredDOMNode = null; - - if (InspectorBackend.searchingForNode()) { - InspectorBackend.toggleNodeSearch(); - this.nodeSearchButton.toggled = false; - } + InspectorBackend.disableSearchingForNode(); }, resize: function() @@ -185,11 +175,6 @@ WebInspector.ElementsPanel.prototype = { WebInspector.hoveredDOMNode = null; - if (InspectorBackend.searchingForNode()) { - InspectorBackend.toggleNodeSearch(); - this.nodeSearchButton.toggled = false; - } - this.recentlyModifiedNodes = []; delete this.currentQuery; @@ -268,7 +253,17 @@ WebInspector.ElementsPanel.prototype = { this._matchesCountUpdateTimeout = null; this._searchQuery = query; - InjectedScriptAccess.getDefault().performSearch(whitespaceTrimmedQuery, function() {}); + InjectedScriptAccess.getDefault().performSearch(whitespaceTrimmedQuery, false, function() {}); + }, + + searchingForNodeWasEnabled: function() + { + this._nodeSearchButton.toggled = true; + }, + + searchingForNodeWasDisabled: function() + { + this._nodeSearchButton.toggled = false; }, _updateMatchesCount: function() @@ -678,47 +673,7 @@ WebInspector.ElementsPanel.prototype = { var crumbTitle; switch (current.nodeType) { case Node.ELEMENT_NODE: - crumbTitle = current.nodeName.toLowerCase(); - - var nameElement = document.createElement("span"); - nameElement.textContent = crumbTitle; - crumb.appendChild(nameElement); - - var idAttribute = current.getAttribute("id"); - if (idAttribute) { - var idElement = document.createElement("span"); - crumb.appendChild(idElement); - - var part = "#" + idAttribute; - crumbTitle += part; - idElement.appendChild(document.createTextNode(part)); - - // Mark the name as extra, since the ID is more important. - nameElement.className = "extra"; - } - - var classAttribute = current.getAttribute("class"); - if (classAttribute) { - var classes = classAttribute.split(/\s+/); - var foundClasses = {}; - - if (classes.length) { - var classesElement = document.createElement("span"); - classesElement.className = "extra"; - crumb.appendChild(classesElement); - - for (var i = 0; i < classes.length; ++i) { - var className = classes[i]; - if (className && !(className in foundClasses)) { - var part = "." + className; - crumbTitle += part; - classesElement.appendChild(document.createTextNode(part)); - foundClasses[className] = true; - } - } - } - } - + this.decorateNodeLabel(current, crumb); break; case Node.TEXT_NODE: @@ -737,17 +692,16 @@ WebInspector.ElementsPanel.prototype = { break; default: - crumbTitle = current.nodeName.toLowerCase(); + crumbTitle = this.treeOutline.nodeNameToCorrectCase(current.nodeName); } if (!crumb.childNodes.length) { var nameElement = document.createElement("span"); nameElement.textContent = crumbTitle; crumb.appendChild(nameElement); + crumb.title = crumbTitle; } - crumb.title = crumbTitle; - if (foundRoot) crumb.addStyleClass("dimmed"); if (current === this.focusedDOMNode) @@ -764,6 +718,51 @@ WebInspector.ElementsPanel.prototype = { this.updateBreadcrumbSizes(); }, + decorateNodeLabel: function(node, parentElement) + { + var title = this.treeOutline.nodeNameToCorrectCase(node.nodeName); + + var nameElement = document.createElement("span"); + nameElement.textContent = title; + parentElement.appendChild(nameElement); + + var idAttribute = node.getAttribute("id"); + if (idAttribute) { + var idElement = document.createElement("span"); + parentElement.appendChild(idElement); + + var part = "#" + idAttribute; + title += part; + idElement.appendChild(document.createTextNode(part)); + + // Mark the name as extra, since the ID is more important. + nameElement.className = "extra"; + } + + var classAttribute = node.getAttribute("class"); + if (classAttribute) { + var classes = classAttribute.split(/\s+/); + var foundClasses = {}; + + if (classes.length) { + var classesElement = document.createElement("span"); + classesElement.className = "extra"; + parentElement.appendChild(classesElement); + + for (var i = 0; i < classes.length; ++i) { + var className = classes[i]; + if (className && !(className in foundClasses)) { + var part = "." + className; + title += part; + classesElement.appendChild(document.createTextNode(part)); + foundClasses[className] = true; + } + } + } + } + parentElement.title = title; + }, + updateBreadcrumbSizes: function(focusedCrumb) { if (!this.visible) @@ -1108,9 +1107,10 @@ WebInspector.ElementsPanel.prototype = { _nodeSearchButtonClicked: function(event) { - InspectorBackend.toggleNodeSearch(); - - this.nodeSearchButton.toggled = InspectorBackend.searchingForNode(); + if (!this._nodeSearchButton.toggled) + InspectorBackend.enableSearchingForNode(); + else + InspectorBackend.disableSearchingForNode(); } } diff --git a/WebCore/inspector/front-end/ElementsTreeOutline.js b/WebCore/inspector/front-end/ElementsTreeOutline.js index fe7ae53..1add6cc 100644 --- a/WebCore/inspector/front-end/ElementsTreeOutline.js +++ b/WebCore/inspector/front-end/ElementsTreeOutline.js @@ -69,6 +69,11 @@ WebInspector.ElementsTreeOutline.prototype = { return this._isXMLMimeType; }, + nodeNameToCorrectCase: function(nodeName) + { + return this.isXMLMimeType ? nodeName : nodeName.toLowerCase(); + }, + get focusedDOMNode() { return this._focusedDOMNode; @@ -177,9 +182,16 @@ WebInspector.ElementsTreeOutline.prototype = { return null; }, + set suppressRevealAndSelect(x) + { + if (this._suppressRevealAndSelect === x) + return; + this._suppressRevealAndSelect = x; + }, + revealAndSelectNode: function(node) { - if (!node) + if (!node || this._suppressRevealAndSelect) return; var treeElement = this.createTreeElementFor(node); @@ -213,7 +225,7 @@ WebInspector.ElementsTreeOutline.prototype = { return element; }, - + _keyDown: function(event) { if (event.target !== this.treeOutline.element) @@ -225,6 +237,9 @@ WebInspector.ElementsTreeOutline.prototype = { if (event.keyCode === WebInspector.KeyboardShortcut.KeyCodes.Backspace || event.keyCode === WebInspector.KeyboardShortcut.KeyCodes.Delete) { + var startTagTreeElement = this.findTreeElement(selectedElement.representedObject); + if (selectedElement !== startTagTreeElement) + selectedElement = startTagTreeElement; selectedElement.remove(); event.preventDefault(); event.stopPropagation(); @@ -267,12 +282,12 @@ WebInspector.ElementsTreeOutline.prototype = { delete this._previousHoveredElement; } - if (element && !element.elementCloseTag) { + if (element) { element.hovered = true; this._previousHoveredElement = element; } - WebInspector.hoveredDOMNode = (element && !element.elementCloseTag ? element.representedObject : null); + WebInspector.hoveredDOMNode = (element ? element.representedObject : null); }, _onmouseout: function(event) @@ -309,14 +324,15 @@ WebInspector.ElementsTreeOutline.prototype = { WebInspector.ElementsTreeOutline.prototype.__proto__ = TreeOutline.prototype; -WebInspector.ElementsTreeElement = function(node) +WebInspector.ElementsTreeElement = function(node, elementCloseTag) { - var hasChildrenOverride = node.hasChildNodes() && !this._showInlineText(node); + this._elementCloseTag = elementCloseTag; + var hasChildrenOverride = !elementCloseTag && node.hasChildNodes() && !this._showInlineText(node); // The title will be updated in onattach. TreeElement.call(this, "", node, hasChildrenOverride); - if (this.representedObject.nodeType == Node.ELEMENT_NODE) + if (this.representedObject.nodeType == Node.ELEMENT_NODE && !elementCloseTag) this._canAddAttributes = true; this._searchQuery = null; this._expandedChildrenLimit = WebInspector.ElementsTreeElement.InitialChildrenLimit; @@ -332,6 +348,11 @@ WebInspector.ElementsTreeElement.ForbiddenClosingTagElements = [ "hr", "img", "input", "isindex", "keygen", "link", "meta", "param", "source" ].keySet(); +// These tags we do not allow editing their tag name. +WebInspector.ElementsTreeElement.EditTagBlacklist = [ + "html", "head", "body" +].keySet(); + WebInspector.ElementsTreeElement.prototype = { highlightSearchResults: function(searchQuery) { @@ -382,7 +403,7 @@ WebInspector.ElementsTreeElement.prototype = { get expandedChildCount() { var count = this.children.length; - if (count && this.children[count - 1].elementCloseTag) + if (count && this.children[count - 1]._elementCloseTag) count--; if (count && this.children[count - 1].expandAllButton) count--; @@ -391,6 +412,9 @@ WebInspector.ElementsTreeElement.prototype = { showChild: function(index) { + if (this._elementCloseTag) + return; + if (index >= this.expandedChildrenLimit) { this._expandedChildrenLimit = index + 1; this._updateChildren(true); @@ -402,6 +426,9 @@ WebInspector.ElementsTreeElement.prototype = { createTooltipForImageNode: function(node, callback) { + if (this._elementCloseTag) + return; + function createTooltipThenCallback(properties) { if (!properties) { @@ -443,8 +470,6 @@ WebInspector.ElementsTreeElement.prototype = { onattach: function() { - this.listItemElement.addEventListener("mousedown", this.onmousedown.bind(this), false); - if (this._hovered) { this.updateSelection(); this.listItemElement.addStyleClass("hovered"); @@ -467,20 +492,23 @@ WebInspector.ElementsTreeElement.prototype = { onpopulate: function() { - if (this.children.length || this._showInlineText(this.representedObject)) + if (this.children.length || this._showInlineText(this.representedObject) || this._elementCloseTag) return; this.updateChildren(); }, - + updateChildren: function(fullRefresh) { + if (this._elementCloseTag) + return; + WebInspector.domAgent.getChildNodesAsync(this.representedObject, this._updateChildren.bind(this, fullRefresh)); }, - insertChildElement: function(child, index) + insertChildElement: function(child, index, closingTag) { - var newElement = new WebInspector.ElementsTreeElement(child); + var newElement = new WebInspector.ElementsTreeElement(child, closingTag); newElement.selectable = this.treeOutline.selectEnabled; this.insertChild(newElement, index); return newElement; @@ -489,10 +517,10 @@ WebInspector.ElementsTreeElement.prototype = { moveChild: function(child, targetIndex) { var wasSelected = child.selected; - treeElement.removeChild(child); - treeElement.insertChild(child, targetIndex); + this.removeChild(child); + this.insertChild(child, targetIndex); if (wasSelected) - existingTreeElement.select(); + child.select(); }, _updateChildren: function(fullRefresh) @@ -554,9 +582,6 @@ WebInspector.ElementsTreeElement.prototype = { // Remove any tree elements that no longer have this node (or this node's contentDocument) as their parent. for (var i = (this.children.length - 1); i >= 0; --i) { - if ("elementCloseTag" in this.children[i]) - continue; - var currentChild = this.children[i]; var currentNode = currentChild.representedObject; var currentParentNode = currentNode.parentNode; @@ -575,13 +600,8 @@ WebInspector.ElementsTreeElement.prototype = { this.adjustCollapsedRange(false); var lastChild = this.children[this.children.length - 1]; - if (this.representedObject.nodeType == Node.ELEMENT_NODE && (!lastChild || !lastChild.elementCloseTag)) { - var title = "<span class=\"webkit-html-tag close\"></" + this.representedObject.nodeName.toLowerCase().escapeHTML() + "></span>"; - var item = new TreeElement(title, null, false); - item.selectable = false; - item.elementCloseTag = true; - this.appendChild(item); - } + if (this.representedObject.nodeType == Node.ELEMENT_NODE && (!lastChild || !lastChild._elementCloseTag)) + this.insertChildElement(this.representedObject, this.children.length, true); // We want to restore the original selection and tree scroll position after a full refresh, if possible. if (fullRefresh && elementToSelect) { @@ -635,12 +655,18 @@ WebInspector.ElementsTreeElement.prototype = { onexpand: function() { + if (this._elementCloseTag) + return; + this.updateTitle(); this.treeOutline.updateSelection(); }, oncollapse: function() { + if (this._elementCloseTag) + return; + this.updateTitle(); this.treeOutline.updateSelection(); }, @@ -653,19 +679,20 @@ WebInspector.ElementsTreeElement.prototype = { onselect: function() { + this.treeOutline.suppressRevealAndSelect = true; this.treeOutline.focusedDOMNode = this.representedObject; this.updateSelection(); + this.treeOutline.suppressRevealAndSelect = false; }, - onmousedown: function(event) + selectOnMouseDown: function(event) { - if (this._editing) - return; + TreeElement.prototype.selectOnMouseDown.call(this, event); - if (this.isEventWithinDisclosureTriangle(event)) + if (this._editing) return; - if (this.treeOutline.showInElementsPanelEnabled) { + if (this.treeOutline.showInElementsPanelEnabled) { WebInspector.showElementsPanel(); WebInspector.panels.elements.focusedDOMNode = this.representedObject; } @@ -677,10 +704,10 @@ WebInspector.ElementsTreeElement.prototype = { ondblclick: function(event) { - if (this._editing) + if (this._editing || this._elementCloseTag) return; - if (this._startEditingFromEvent(event)) + if (this._startEditingTarget(event.target)) return; if (this.hasChildren && !this.expanded) @@ -702,7 +729,7 @@ WebInspector.ElementsTreeElement.prototype = { this.updateSelection(); }, - _startEditingFromEvent: function(event) + _startEditingTarget: function(eventTarget) { if (this.treeOutline.focusedDOMNode != this.representedObject) return; @@ -710,15 +737,19 @@ WebInspector.ElementsTreeElement.prototype = { if (this.representedObject.nodeType != Node.ELEMENT_NODE && this.representedObject.nodeType != Node.TEXT_NODE) return false; - var textNode = event.target.enclosingNodeOrSelfWithClass("webkit-html-text-node"); + var textNode = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-text-node"); if (textNode) return this._startEditingTextNode(textNode); - var attribute = event.target.enclosingNodeOrSelfWithClass("webkit-html-attribute"); + var attribute = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-attribute"); if (attribute) - return this._startEditingAttribute(attribute, event.target); + return this._startEditingAttribute(attribute, eventTarget); - var newAttribute = event.target.enclosingNodeOrSelfWithClass("add-attribute"); + var tagName = eventTarget.enclosingNodeOrSelfWithClass("webkit-html-tag-name"); + if (tagName) + return this._startEditingTagName(tagName); + + var newAttribute = eventTarget.enclosingNodeOrSelfWithClass("add-attribute"); if (newAttribute) return this._addNewAttribute(); @@ -736,7 +767,7 @@ WebInspector.ElementsTreeElement.prototype = { contextMenu.appendItem(WebInspector.UIString("Edit Attribute"), this._startEditingAttribute.bind(this, attribute, event.target)); contextMenu.appendSeparator(); - // Add node-related actions. + // Add free-form node-related actions. contextMenu.appendItem(WebInspector.UIString("Edit as HTML"), this._editAsHTML.bind(this)); contextMenu.appendItem(WebInspector.UIString("Copy as HTML"), this._copyHTML.bind(this)); contextMenu.appendItem(WebInspector.UIString("Delete Node"), this.remove.bind(this)); @@ -772,17 +803,13 @@ WebInspector.ElementsTreeElement.prototype = { _addNewAttribute: function() { - var attr = document.createElement("span"); - attr.className = "webkit-html-attribute"; + // Cannot just convert the textual html into an element without + // a parent node. Use a temporary span container for the HTML. + var container = document.createElement("span"); + container.innerHTML = this._attributeHTML(" ", ""); + var attr = container.firstChild; attr.style.marginLeft = "2px"; // overrides the .editing margin rule attr.style.marginRight = "2px"; // overrides the .editing margin rule - var name = document.createElement("span"); - name.className = "webkit-html-attribute-name new-attribute"; - name.textContent = " "; - var value = document.createElement("span"); - value.className = "webkit-html-attribute-value"; - attr.appendChild(name); - attr.appendChild(value); var tag = this.listItemElement.getElementsByClassName("webkit-html-tag")[0]; this._insertInLastAttributePosition(tag, attr); @@ -799,7 +826,7 @@ WebInspector.ElementsTreeElement.prototype = { continue; if (elem.hasStyleClass("webkit-html-attribute-value")) - return this._startEditingAttribute(attributeElements[i].parentNode, elem); + return this._startEditingAttribute(elem.parentNode, elem); } } } @@ -833,9 +860,7 @@ WebInspector.ElementsTreeElement.prototype = { // Remove zero-width spaces that were added by nodeTitleInfo. removeZeroWidthSpaceRecursive(attribute); - this._editing = true; - - WebInspector.startEditing(attribute, this._attributeEditingCommitted.bind(this), this._editingCancelled.bind(this), attributeName); + this._editing = WebInspector.startEditing(attribute, this._attributeEditingCommitted.bind(this), this._editingCancelled.bind(this), attributeName); window.getSelection().setBaseAndExtent(elementForSelection, 0, elementForSelection, 1); return true; @@ -846,21 +871,59 @@ WebInspector.ElementsTreeElement.prototype = { if (WebInspector.isBeingEdited(textNode)) return true; - this._editing = true; - - WebInspector.startEditing(textNode, this._textNodeEditingCommitted.bind(this), this._editingCancelled.bind(this)); + this._editing = WebInspector.startEditing(textNode, this._textNodeEditingCommitted.bind(this), this._editingCancelled.bind(this)); window.getSelection().setBaseAndExtent(textNode, 0, textNode, 1); return true; }, + _startEditingTagName: function(tagNameElement) + { + if (!tagNameElement) { + tagNameElement = this.listItemElement.getElementsByClassName("webkit-html-tag-name")[0]; + if (!tagNameElement) + return false; + } + + var tagName = tagNameElement.textContent; + if (WebInspector.ElementsTreeElement.EditTagBlacklist[tagName.toLowerCase()]) + return false; + + if (WebInspector.isBeingEdited(tagNameElement)) + return true; + + var closingTagElement = this._distinctClosingTagElement(); + + function keyupListener(event) + { + if (closingTagElement) + closingTagElement.textContent = "</" + tagNameElement.textContent + ">"; + } + + function editingComitted(element, newTagName) + { + tagNameElement.removeEventListener('keyup', keyupListener, false); + this._tagNameEditingCommitted.apply(this, arguments); + } + + function editingCancelled() + { + tagNameElement.removeEventListener('keyup', keyupListener, false); + this._editingCancelled.apply(this, arguments); + } + + tagNameElement.addEventListener('keyup', keyupListener, false); + + this._editing = WebInspector.startEditing(tagNameElement, editingComitted.bind(this), editingCancelled.bind(this), tagName); + window.getSelection().setBaseAndExtent(tagNameElement, 0, tagNameElement, 1); + return true; + }, + _startEditingAsHTML: function(commitCallback, initialValue) { if (this._htmlEditElement && WebInspector.isBeingEdited(this._htmlEditElement)) return true; - this._editing = true; - this._htmlEditElement = document.createElement("div"); this._htmlEditElement.className = "source-code elements-tree-editor"; this._htmlEditElement.textContent = initialValue; @@ -905,7 +968,7 @@ WebInspector.ElementsTreeElement.prototype = { this.updateSelection(); } - WebInspector.startEditing(this._htmlEditElement, commit.bind(this), dispose.bind(this), null, true); + this._editing = WebInspector.startEditing(this._htmlEditElement, commit.bind(this), dispose.bind(this), null, true); }, _attributeEditingCommitted: function(element, newText, oldText, attributeName, moveDirection) @@ -914,34 +977,59 @@ WebInspector.ElementsTreeElement.prototype = { // Before we do anything, determine where we should move // next based on the current element's settings - var moveToAttribute; - var newAttribute; + var moveToAttribute, moveToTagName, moveToNewAttribute; if (moveDirection) { var found = false; + + // Search for the attribute's position, and then decide where to move to. var attributes = this.representedObject.attributes; - for (var i = 0, len = attributes.length; i < len; ++i) { + for (var i = 0; i < attributes.length; ++i) { if (attributes[i].name === attributeName) { found = true; - if (moveDirection === "backward" && i > 0) - moveToAttribute = attributes[i - 1].name; - else if (moveDirection === "forward" && i < attributes.length - 1) - moveToAttribute = attributes[i + 1].name; - else if (moveDirection === "forward" && i === attributes.length - 1) - newAttribute = true; + if (moveDirection === "backward") { + if (i === 0) + moveToTagName = true; + else + moveToAttribute = attributes[i - 1].name; + } else if (moveDirection === "forward") { + if (i === attributes.length - 1) + moveToNewAttribute = true; + else + moveToAttribute = attributes[i + 1].name; + } } } - if (!found && moveDirection === "backward" && attributes.length > 0) - moveToAttribute = attributes[attributes.length - 1].name; - else if (!found && moveDirection === "forward" && !/^\s*$/.test(newText)) - newAttribute = true; + // Moving From the "New Attribute" position. + if (!found) { + if (moveDirection === "backward" && attributes.length > 0) + moveToAttribute = attributes[attributes.length - 1].name; + else if (moveDirection === "forward" && !/^\s*$/.test(newText)) + moveToNewAttribute = true; + } } - function moveToNextAttributeIfNeeded() { + function moveToNextAttributeIfNeeded() + { + // Cleanup empty new attribute sections. + if (element.textContent.trim().length === 0) + element.parentNode.removeChild(element); + + // Make the move. if (moveToAttribute) this._triggerEditAttribute(moveToAttribute); - else if (newAttribute) - this._addNewAttribute(this.listItemElement); + else if (moveToNewAttribute) + this._addNewAttribute(); + else if (moveToTagName) + this._startEditingTagName(); + } + + function regenerateStyledAttribute(name, value) + { + var previous = element.previousSibling; + if (!previous || previous.nodeType !== Node.TEXT_NODE) + element.parentNode.insertBefore(document.createTextNode(" "), element); + element.outerHTML = this._attributeHTML(name, value); } var parseContainerElement = document.createElement("span"); @@ -966,6 +1054,7 @@ WebInspector.ElementsTreeElement.prototype = { foundOriginalAttribute = foundOriginalAttribute || attr.name === attributeName; try { this.representedObject.setAttribute(attr.name, attr.value); + regenerateStyledAttribute.call(this, attr.name, attr.value); } catch(e) {} // ignore invalid attribute (innerHTML doesn't throw errors, but this can) } @@ -977,6 +1066,64 @@ WebInspector.ElementsTreeElement.prototype = { moveToNextAttributeIfNeeded.call(this); }, + _tagNameEditingCommitted: function(element, newText, oldText, tagName, moveDirection) + { + delete this._editing; + var self = this; + + function cancel() + { + var closingTagElement = self._distinctClosingTagElement(); + if (closingTagElement) + closingTagElement.textContent = "</" + tagName + ">"; + + self._editingCancelled(element, tagName); + moveToNextAttributeIfNeeded.call(self); + } + + function moveToNextAttributeIfNeeded() + { + if (moveDirection !== "forward") + return; + + var attributes = this.representedObject.attributes; + if (attributes.length > 0) + this._triggerEditAttribute(attributes[0].name); + else + this._addNewAttribute(); + } + + newText = newText.trim(); + if (newText === oldText) { + cancel(); + return; + } + + var treeOutline = this.treeOutline; + var wasExpanded = this.expanded; + + function changeTagNameCallback(nodeId) + { + if (nodeId === -1) { + cancel(); + return; + } + + // Select it and expand if necessary. We force tree update so that it processes dom events and is up to date. + WebInspector.panels.elements.updateModifiedNodes(); + + WebInspector.updateFocusedNode(nodeId); + var newTreeItem = treeOutline.findTreeElement(WebInspector.domAgent.nodeForId(nodeId)); + if (wasExpanded) + newTreeItem.expand(); + + moveToNextAttributeIfNeeded.call(newTreeItem); + } + + var callId = WebInspector.Callback.wrap(changeTagNameCallback); + InspectorBackend.changeTagName(callId, this.representedObject.id, newText, wasExpanded); + }, + _textNodeEditingCommitted: function(element, newText) { delete this._editing; @@ -1003,6 +1150,24 @@ WebInspector.ElementsTreeElement.prototype = { this.updateTitle(); }, + _distinctClosingTagElement: function() + { + // FIXME: Improve the Tree Element / Outline Abstraction to prevent crawling the DOM + + // For an expanded element, it will be the last element with class "close" + // in the child element list. + if (this.expanded) { + var closers = this._childrenListNode.querySelectorAll(".close"); + return closers[closers.length-1]; + } + + // Remaining cases are single line non-expanded elements with a closing + // tag, or HTML elements without a closing tag (such as <br>). Return + // null in the case where there isn't a closing tag. + var tags = this.listItemElement.getElementsByClassName("webkit-html-tag"); + return (tags.length === 1 ? null : tags[tags.length-1]); + }, + updateTitle: function() { // If we are editing, return early to prevent canceling the edit. @@ -1051,92 +1216,117 @@ WebInspector.ElementsTreeElement.prototype = { return hrefValue; }, + _attributeHTML: function(name, value, node, linkify, tooltipText) + { + var hasText = (value.length > 0); + var html = "<span class=\"webkit-html-attribute\"><span class=\"webkit-html-attribute-name\">" + name.escapeHTML() + "</span>"; + + if (hasText) + html += "=​\""; + + if (linkify && (name === "src" || name === "href")) { + value = value.replace(/([\/;:\)\]\}])/g, "$1\u200B"); + html += linkify(this._rewriteAttrHref(node, value), value, "webkit-html-attribute-value", node.nodeName.toLowerCase() === "a", tooltipText); + } else { + value = value.escapeHTML().replace(/([\/;:\)\]\}])/g, "$1​"); + html += "<span class=\"webkit-html-attribute-value\">" + value + "</span>"; + } + + if (hasText) + html += "\""; + + html += "</span>"; + return html; + }, + + _tagHTML: function(tagName, isClosingTag, isDistinctTreeElement, linkify, tooltipText) + { + var node = this.representedObject; + var result = "<span class=\"webkit-html-tag" + (isClosingTag && isDistinctTreeElement ? " close" : "") + "\"><"; + result += "<span " + (isClosingTag ? "" : "class=\"webkit-html-tag-name\"") + ">" + (isClosingTag ? "/" : "") + tagName + "</span>"; + if (!isClosingTag && node.hasAttributes()) { + for (var i = 0; i < node.attributes.length; ++i) { + var attr = node.attributes[i]; + result += " " + this._attributeHTML(attr.name, attr.value, node, linkify, tooltipText); + } + } + result += "></span>​"; + + return result; + }, + _nodeTitleInfo: function(linkify, tooltipText) { var node = this.representedObject; var info = {title: "", hasChildren: this.hasChildren}; - + switch (node.nodeType) { case Node.DOCUMENT_NODE: info.title = "Document"; break; - + case Node.DOCUMENT_FRAGMENT_NODE: info.title = "Document Fragment"; break; case Node.ELEMENT_NODE: - var tagName = node.nodeName.toLowerCase().escapeHTML(); - info.title = "<span class=\"webkit-html-tag\"><" + tagName; - - if (node.hasAttributes()) { - for (var i = 0; i < node.attributes.length; ++i) { - var attr = node.attributes[i]; - info.title += " <span class=\"webkit-html-attribute\"><span class=\"webkit-html-attribute-name\">" + attr.name.escapeHTML() + "</span>=​\""; - - var value = attr.value; - if (linkify && (attr.name === "src" || attr.name === "href")) { - var value = value.replace(/([\/;:\)\]\}])/g, "$1\u200B"); - info.title += linkify(this._rewriteAttrHref(node, attr.value), value, "webkit-html-attribute-value", node.nodeName.toLowerCase() == "a", tooltipText); - } else { - var value = value.escapeHTML(); - value = value.replace(/([\/;:\)\]\}])/g, "$1​"); - info.title += "<span class=\"webkit-html-attribute-value\">" + value + "</span>"; - } - info.title += "\"</span>"; - } + var tagName = this.treeOutline.nodeNameToCorrectCase(node.nodeName).escapeHTML(); + if (this._elementCloseTag) { + info.title = this._tagHTML(tagName, true, true); + info.hasChildren = false; + break; } - info.title += "></span>​"; - - const closingTagHTML = "<span class=\"webkit-html-tag\"></" + tagName + "></span>​"; + + info.title = this._tagHTML(tagName, false, false, linkify, tooltipText); + var textChild = onlyTextChild.call(node); var showInlineText = textChild && textChild.textContent.length < Preferences.maxInlineTextChildLength; if (!this.expanded && (!showInlineText && (this.treeOutline.isXMLMimeType || !WebInspector.ElementsTreeElement.ForbiddenClosingTagElements[tagName]))) { if (this.hasChildren) info.title += "<span class=\"webkit-html-text-node\">…</span>​"; - info.title += closingTagHTML; + info.title += this._tagHTML(tagName, true, false); } // If this element only has a single child that is a text node, // just show that text and the closing tag inline rather than // create a subtree for them if (showInlineText) { - info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>​" + closingTagHTML; + info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>​" + this._tagHTML(tagName, true, false); info.hasChildren = false; } break; - + case Node.TEXT_NODE: if (isNodeWhitespace.call(node)) info.title = "(whitespace)"; else { - if (node.parentNode && node.parentNode.nodeName.toLowerCase() == "script") { + if (node.parentNode && node.parentNode.nodeName.toLowerCase() === "script") { var newNode = document.createElement("span"); newNode.textContent = node.textContent; var javascriptSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter("text/javascript"); javascriptSyntaxHighlighter.syntaxHighlightNode(newNode); - + info.title = "<span class=\"webkit-html-text-node webkit-html-js-node\">" + newNode.innerHTML.replace(/^[\n\r]*/, "").replace(/\s*$/, "") + "</span>"; - } else if (node.parentNode && node.parentNode.nodeName.toLowerCase() == "style") { + } else if (node.parentNode && node.parentNode.nodeName.toLowerCase() === "style") { var newNode = document.createElement("span"); newNode.textContent = node.textContent; - + var cssSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter("text/css"); cssSyntaxHighlighter.syntaxHighlightNode(newNode); - + info.title = "<span class=\"webkit-html-text-node webkit-html-css-node\">" + newNode.innerHTML.replace(/^[\n\r]*/, "").replace(/\s*$/, "") + "</span>"; } else { - info.title = "\"<span class=\"webkit-html-text-node\">" + node.nodeValue.escapeHTML() + "</span>\""; + info.title = "\"<span class=\"webkit-html-text-node\">" + node.nodeValue.escapeHTML() + "</span>\""; } - } + } break; - + case Node.COMMENT_NODE: info.title = "<span class=\"webkit-html-comment\"><!--" + node.nodeValue.escapeHTML() + "--></span>"; break; - + case Node.DOCUMENT_TYPE_NODE: info.title = "<span class=\"webkit-html-doctype\"><!DOCTYPE " + node.nodeName; if (node.publicId) { @@ -1150,9 +1340,9 @@ WebInspector.ElementsTreeElement.prototype = { info.title += "></span>"; break; default: - info.title = node.nodeName.toLowerCase().collapseWhitespace().escapeHTML(); + info.title = this.treeOutline.nodeNameToCorrectCase(node.nodeName).collapseWhitespace().escapeHTML(); } - + return info; }, @@ -1165,7 +1355,7 @@ WebInspector.ElementsTreeElement.prototype = { } return false; }, - + remove: function() { var parentElement = this.parent; @@ -1242,5 +1432,3 @@ WebInspector.ElementsTreeElement.prototype = { } WebInspector.ElementsTreeElement.prototype.__proto__ = TreeElement.prototype; - -WebInspector.didRemoveNode = WebInspector.Callback.processCallback; diff --git a/WebCore/inspector/front-end/ImageView.js b/WebCore/inspector/front-end/ImageView.js index 96e1a6e..c13c9a5 100644 --- a/WebCore/inspector/front-end/ImageView.js +++ b/WebCore/inspector/front-end/ImageView.js @@ -31,45 +31,48 @@ WebInspector.ImageView = function(resource) WebInspector.ResourceView.call(this, resource); this.element.addStyleClass("image"); +} - var container = document.createElement("div"); - container.className = "image"; - this.contentElement.appendChild(container); +WebInspector.ImageView.prototype = { + contentTabSelected: function() + { + if (this._container) + return; + this._container = document.createElement("div"); + this._container.className = "image"; + this.contentElement.appendChild(this._container); - this.imagePreviewElement = document.createElement("img"); - this.imagePreviewElement.addStyleClass("resource-image-view"); - this.imagePreviewElement.setAttribute("src", this.resource.url); + this.imagePreviewElement = document.createElement("img"); + this.imagePreviewElement.addStyleClass("resource-image-view"); + this.imagePreviewElement.setAttribute("src", this.resource.url); - container.appendChild(this.imagePreviewElement); + this._container.appendChild(this.imagePreviewElement); - container = document.createElement("div"); - container.className = "info"; - this.contentElement.appendChild(container); + this._container = document.createElement("div"); + this._container.className = "info"; + this.contentElement.appendChild(this._container); - var imageNameElement = document.createElement("h1"); - imageNameElement.className = "title"; - imageNameElement.textContent = this.resource.displayName; - container.appendChild(imageNameElement); + var imageNameElement = document.createElement("h1"); + imageNameElement.className = "title"; + imageNameElement.textContent = this.resource.displayName; + this._container.appendChild(imageNameElement); - var infoListElement = document.createElement("dl"); - infoListElement.className = "infoList"; + var infoListElement = document.createElement("dl"); + infoListElement.className = "infoList"; - var imageProperties = [ - { name: WebInspector.UIString("Dimensions"), value: WebInspector.UIString("%d × %d", this.imagePreviewElement.naturalWidth, this.imagePreviewElement.height) }, - { name: WebInspector.UIString("File size"), value: Number.bytesToString(this.resource.contentLength, WebInspector.UIString.bind(WebInspector)) }, - { name: WebInspector.UIString("MIME type"), value: this.resource.mimeType } - ]; + var imageProperties = [ + { name: WebInspector.UIString("Dimensions"), value: WebInspector.UIString("%d × %d", this.imagePreviewElement.naturalWidth, this.imagePreviewElement.height) }, + { name: WebInspector.UIString("File size"), value: Number.bytesToString(this.resource.resourceSize, WebInspector.UIString.bind(WebInspector)) }, + { name: WebInspector.UIString("MIME type"), value: this.resource.mimeType } + ]; - var listHTML = ''; - for (var i = 0; i < imageProperties.length; ++i) - listHTML += "<dt>" + imageProperties[i].name + "</dt><dd>" + imageProperties[i].value + "</dd>"; + var listHTML = ''; + for (var i = 0; i < imageProperties.length; ++i) + listHTML += "<dt>" + imageProperties[i].name + "</dt><dd>" + imageProperties[i].value + "</dd>"; - infoListElement.innerHTML = listHTML; - container.appendChild(infoListElement); -} - -WebInspector.ImageView.prototype = { - + infoListElement.innerHTML = listHTML; + this._container.appendChild(infoListElement); + } } WebInspector.ImageView.prototype.__proto__ = WebInspector.ResourceView.prototype; diff --git a/WebCore/inspector/front-end/Images/auditsIcon.png b/WebCore/inspector/front-end/Images/auditsIcon.png Binary files differnew file mode 100644 index 0000000..8d1f752 --- /dev/null +++ b/WebCore/inspector/front-end/Images/auditsIcon.png diff --git a/WebCore/inspector/front-end/Images/breakpointBorder.png b/WebCore/inspector/front-end/Images/breakpointBorder.png Binary files differnew file mode 100644 index 0000000..0b1b550 --- /dev/null +++ b/WebCore/inspector/front-end/Images/breakpointBorder.png diff --git a/WebCore/inspector/front-end/Images/breakpointConditionalBorder.png b/WebCore/inspector/front-end/Images/breakpointConditionalBorder.png Binary files differnew file mode 100644 index 0000000..430e37e --- /dev/null +++ b/WebCore/inspector/front-end/Images/breakpointConditionalBorder.png diff --git a/WebCore/inspector/front-end/Images/breakpointConditionalCounterBorder.png b/WebCore/inspector/front-end/Images/breakpointConditionalCounterBorder.png Binary files differnew file mode 100644 index 0000000..b4a5030 --- /dev/null +++ b/WebCore/inspector/front-end/Images/breakpointConditionalCounterBorder.png diff --git a/WebCore/inspector/front-end/Images/breakpointCounterBorder.png b/WebCore/inspector/front-end/Images/breakpointCounterBorder.png Binary files differnew file mode 100644 index 0000000..8b77b61 --- /dev/null +++ b/WebCore/inspector/front-end/Images/breakpointCounterBorder.png diff --git a/WebCore/inspector/front-end/Images/breakpointsActivateButtonGlyph.png b/WebCore/inspector/front-end/Images/breakpointsActivateButtonGlyph.png Binary files differnew file mode 100644 index 0000000..ce49aac --- /dev/null +++ b/WebCore/inspector/front-end/Images/breakpointsActivateButtonGlyph.png diff --git a/WebCore/inspector/front-end/Images/breakpointsDeactivateButtonGlyph.png b/WebCore/inspector/front-end/Images/breakpointsDeactivateButtonGlyph.png Binary files differnew file mode 100644 index 0000000..5c5fcf6 --- /dev/null +++ b/WebCore/inspector/front-end/Images/breakpointsDeactivateButtonGlyph.png diff --git a/WebCore/inspector/front-end/Images/programCounterBorder.png b/WebCore/inspector/front-end/Images/programCounterBorder.png Binary files differnew file mode 100644 index 0000000..fed2f3e --- /dev/null +++ b/WebCore/inspector/front-end/Images/programCounterBorder.png diff --git a/WebCore/inspector/front-end/Images/spinner.gif b/WebCore/inspector/front-end/Images/spinner.gif Binary files differnew file mode 100644 index 0000000..5f68c02 --- /dev/null +++ b/WebCore/inspector/front-end/Images/spinner.gif diff --git a/WebCore/inspector/front-end/InjectedFakeWorker.js b/WebCore/inspector/front-end/InjectedFakeWorker.js new file mode 100644 index 0000000..7176844 --- /dev/null +++ b/WebCore/inspector/front-end/InjectedFakeWorker.js @@ -0,0 +1,345 @@ +/* + * 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. + */ + +var InjectedFakeWorker = function(InjectedScriptHost, inspectedWindow, injectedScriptId) +{ + +Worker = function(url) +{ + var impl = new FakeWorker(this, url); + if (impl === null) + return null; + + this.isFake = true; + this.postMessage = bind(impl.postMessage, impl); + this.terminate = bind(impl.terminate, impl); + + function onmessageGetter() + { + return impl.channel.port1.onmessage; + } + function onmessageSetter(callback) + { + impl.channel.port1.onmessage = callback; + } + this.__defineGetter__("onmessage", onmessageGetter); + this.__defineSetter__("onmessage", onmessageSetter); + this.addEventListener = bind(impl.channel.port1.addEventListener, impl.channel.port1); + this.removeEventListener = bind(impl.channel.port1.removeEventListener, impl.channel.port1); + this.dispatchEvent = bind(impl.channel.port1.dispatchEvent, impl.channel.port1); +} + +function FakeWorker(worker, url) +{ + var scriptURL = this._expandURLAndCheckOrigin(document.baseURI, location.href, url); + + this._worker = worker; + this._id = InjectedScriptHost.nextWorkerId(); + this.channel = new MessageChannel(); + this._listeners = []; + this._buildWorker(scriptURL); + + InjectedScriptHost.didCreateWorker(this._id, scriptURL.url, false); +} + +FakeWorker.prototype = { + postMessage: function(msg, opt_ports) + { + if (this._frame != null) + this.channel.port1.postMessage.apply(this.channel.port1, arguments); + else if (this._pendingMessages) + this._pendingMessages.push(arguments) + else + this._pendingMessages = [ arguments ]; + }, + + terminate: function() + { + InjectedScriptHost.didDestroyWorker(this._id); + + this.channel.port1.close(); + this.channel.port2.close(); + if (this._frame != null) + this._frame.frameElement.parentNode.removeChild(this._frame.frameElement); + this._frame = null; + this._worker = null; // Break reference loop. + }, + + _buildWorker: function(url) + { + var code = this._loadScript(url.url); + var iframeElement = document.createElement("iframe"); + iframeElement.style.display = "none"; + + this._document = document; + iframeElement.onload = bind(this._onWorkerFrameLoaded, this, iframeElement, url, code); + + if (document.body) + this._attachWorkerFrameToDocument(iframeElement, url, code); + else + window.addEventListener("load", bind(this._attachWorkerFrameToDocument, this, iframeElement), false); + }, + + _attachWorkerFrameToDocument: function(iframeElement) + { + document.body.appendChild(iframeElement); + }, + + _onWorkerFrameLoaded: function(iframeElement, url, code) + { + var frame = iframeElement.contentWindow; + this._frame = frame; + this._setupWorkerContext(frame, url); + + var frameContents = '(function() { var location = __devtools.location; var window; ' + code + '})();\n' + '//@ sourceURL=' + url.url; + + frame.eval(frameContents); + if (this._pendingMessages) { + for (var msg = 0; msg < this._pendingMessages.length; ++msg) + this.postMessage.apply(this, this._pendingMessages[msg]); + delete this._pendingMessages; + } + }, + + _setupWorkerContext: function(workerFrame, url) + { + workerFrame.__devtools = { + handleException: bind(this._handleException, this), + location: url.mockLocation() + }; + + var self = this; + + function onmessageGetter() + { + return self.channel.port2.onmessage ? self.channel.port2.onmessage.originalCallback : null; + } + + function onmessageSetter(callback) + { + var wrappedCallback = bind(self._callbackWrapper, self, callback); + wrappedCallback.originalCallback = callback; + self.channel.port2.onmessage = wrappedCallback; + } + + workerFrame.__defineGetter__("onmessage", onmessageGetter); + workerFrame.__defineSetter__("onmessage", onmessageSetter); + workerFrame.addEventListener = bind(this._addEventListener, this); + workerFrame.removeEventListener = bind(this._removeEventListener, this); + workerFrame.dispatchEvent = bind(this.channel.port2.dispatchEvent, this.channel.port2); + workerFrame.postMessage = bind(this.channel.port2.postMessage, this.channel.port2); + workerFrame.importScripts = bind(this._importScripts, this, workerFrame); + workerFrame.close = bind(this.terminate, this); + }, + + _addEventListener: function(type, callback, useCapture) + { + var wrappedCallback = bind(this._callbackWrapper, this, callback); + wrappedCallback.originalCallback = callback; + wrappedCallback.type = type; + wrappedCallback.useCapture = Boolean(useCapture); + + this.channel.port2.addEventListener(type, wrappedCallback, useCapture); + this._listeners.push(wrappedCallback); + }, + + _removeEventListener: function(type, callback, useCapture) + { + var listeners = this._listeners; + for (var i = 0; i < listeners.length; ++i) { + if (listeners[i].originalCallback === callback && + listeners[i].type === type && + listeners[i].useCapture === Boolean(useCapture)) { + this.channel.port2.removeEventListener(type, listeners[i], useCapture); + listeners[i] = listeners[listeners.length - 1]; + listeners.pop(); + break; + } + } + }, + + _callbackWrapper: function(callback, msg) + { + // Shortcut -- if no exception handlers installed, avoid try/catch so as not to obscure line number. + if (!this._frame.onerror && !this._worker.onerror) { + callback(msg); + return; + } + + try { + callback(msg); + } catch (e) { + this._handleException(e, this._frame.onerror, this._worker.onerror); + } + }, + + _handleException: function(e) + { + // NB: it should be an ErrorEvent, but creating it from script is not + // currently supported, so emulate it on top of plain vanilla Event. + var errorEvent = this._document.createEvent("Event"); + errorEvent.initEvent("Event", false, false); + errorEvent.message = "Uncaught exception"; + + for (var i = 1; i < arguments.length; ++i) { + if (arguments[i] && arguments[i](errorEvent)) + return; + } + + throw e; + }, + + _importScripts: function(targetFrame) + { + for (var i = 1; i < arguments.length; ++i) { + var workerOrigin = targetFrame.__devtools.location.href; + var url = this._expandURLAndCheckOrigin(workerOrigin, workerOrigin, arguments[i]); + targetFrame.eval(this._loadScript(url.url) + "\n//@ sourceURL= " + url.url); + } + }, + + _loadScript: function(url) + { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.send(null); + + var text = xhr.responseText; + if (xhr.status != 0 && xhr.status/100 !== 2) { // We're getting status === 0 when using file://. + console.error("Failed to load worker: " + url + "[" + xhr.status + "]"); + text = ""; // We've got error message, not worker code. + } + return text; + }, + + _expandURLAndCheckOrigin: function(baseURL, origin, url) + { + var scriptURL = new URL(baseURL).completeWith(url); + + if (!scriptURL.sameOrigin(origin)) + throw new DOMCoreException("SECURITY_ERR",18); + return scriptURL; + } +}; + +function URL(url) +{ + this.url = url; + this.split(); +} + +URL.prototype = { + urlRegEx: (/^(http[s]?|file):\/\/([^\/:]*)(:[\d]+)?(?:(\/[^#?]*)(\?[^#]*)?(?:#(.*))?)?$/i), + + split: function() + { + function emptyIfNull(str) + { + return str == null ? "" : str; + } + var parts = this.urlRegEx.exec(this.url); + + this.schema = parts[1]; + this.host = parts[2]; + this.port = emptyIfNull(parts[3]); + this.path = emptyIfNull(parts[4]); + this.query = emptyIfNull(parts[5]); + this.fragment = emptyIfNull(parts[6]); + }, + + mockLocation: function() + { + var host = this.host.replace(/^[^@]*@/, ""); + + return { + href: this.url, + protocol: this.schema + ":", + host: host, + hostname: host, + port: this.port, + pathname: this.path, + search: this.query, + hash: this.fragment + }; + }, + + completeWith: function(url) + { + if (url === "" || /^[^/]*:/.exec(url)) // If given absolute url, return as is now. + return new URL(url); + + var relParts = /^([^#?]*)(.*)$/.exec(url); // => [ url, path, query-andor-fragment ] + + var path = (relParts[1].slice(0, 1) === "/" ? "" : this.path.replace(/[^/]*$/, "")) + relParts[1]; + path = path.replace(/(\/\.)+(\/|$)/g, "/").replace(/[^/]*\/\.\.(\/|$)/g, ""); + + return new URL(this.schema + "://" + this.host + this.port + path + relParts[2]); + }, + + sameOrigin: function(url) + { + function normalizePort(schema, port) + { + var portNo = port.slice(1); + return (schema === "https" && portNo == 443 || schema === "http" && portNo == 80) ? "" : port; + } + + var other = new URL(url); + + return this.schema === other.schema && + this.host === other.host && + normalizePort(this.schema, this.port) === normalizePort(other.schema, other.port); + } +}; + +function DOMCoreException(name, code) +{ + function formatError() + { + return "Error: " + this.message; + } + + this.name = name; + this.message = name + ": DOM Exception " + code; + this.code = code; + this.toString = bind(formatError, this); +} + +function bind(func, thisObject) +{ + var args = Array.prototype.slice.call(arguments, 2); + return function() { return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))); }; +} + +function noop() +{ +} + +} diff --git a/WebCore/inspector/front-end/InjectedScript.js b/WebCore/inspector/front-end/InjectedScript.js index 8d8fa88..8984d0e 100644 --- a/WebCore/inspector/front-end/InjectedScript.js +++ b/WebCore/inspector/front-end/InjectedScript.js @@ -26,28 +26,43 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var injectedScriptConstructor = (function (InjectedScriptHost, inspectedWindow, injectedScriptId) { +var injectedScriptConstructor = (function (InjectedScriptHost, inspectedWindow, injectedScriptId, jsEngine) { var InjectedScript = {}; InjectedScript.lastBoundObjectId = 1; InjectedScript.idToWrappedObject = {}; InjectedScript.objectGroups = {}; + +InjectedScript.wrapObjectForConsole = function(object, canAccessInspectedWindow) +{ + if (canAccessInspectedWindow) + return InjectedScript.wrapObject(object, "console"); + var result = {}; + result.type = typeof object; + result.description = InjectedScript._toString(object); + return result; +} + InjectedScript.wrapObject = function(object, objectGroupName) { - var objectId; - if (typeof object === "object" || typeof object === "function" || - (typeof object === "undefined" && object instanceof inspectedWindow.HTMLAllCollection)) { // FIXME(33716) - var id = InjectedScript.lastBoundObjectId++; - objectId = "object#" + id; - InjectedScript.idToWrappedObject[objectId] = object; - - var group = InjectedScript.objectGroups[objectGroupName]; - if (!group) { - group = []; - InjectedScript.objectGroups[objectGroupName] = group; + try { + var objectId; + if (typeof object === "object" || typeof object === "function" || InjectedScript._isHTMLAllCollection(object)) { + var id = InjectedScript.lastBoundObjectId++; + objectId = "object#" + id; + InjectedScript.idToWrappedObject[objectId] = object; + + var group = InjectedScript.objectGroups[objectGroupName]; + if (!group) { + group = []; + InjectedScript.objectGroups[objectGroupName] = group; + } + group.push(objectId); } - group.push(objectId); + return InjectedScript.createProxyObject(object, objectId); + } catch (e) { + return InjectedScript.createProxyObject("[ Exception: " + e.toString() + " ]"); } return InjectedScript.createProxyObject(object, objectId); }; @@ -68,10 +83,6 @@ InjectedScript.releaseWrapperObjectGroup = function(objectGroupName) { // Called from within InspectorController on the 'inspected page' side. InjectedScript.reset = function() { - InjectedScript._styles = {}; - InjectedScript._styleRules = {}; - InjectedScript._lastStyleId = 0; - InjectedScript._lastStyleRuleId = 0; InjectedScript._searchResults = []; InjectedScript._includedInSearchResultsPropertyName = "__includedInInspectorSearchResults"; } @@ -85,384 +96,12 @@ InjectedScript.dispatch = function(methodName, args, callId) argsArray.splice(0, 0, callId); // Methods that run asynchronously have a call back id parameter. var result = InjectedScript[methodName].apply(InjectedScript, argsArray); if (typeof result === "undefined") { - InjectedScript._window().console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName); + inspectedWindow.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName); result = null; } return result; } -InjectedScript.getStyles = function(nodeId, authorOnly) -{ - var node = InjectedScript._nodeForId(nodeId); - if (!node) - return false; - var defaultView = node.ownerDocument.defaultView; - var matchedRules = defaultView.getMatchedCSSRules(node, "", authorOnly); - var matchedCSSRules = []; - for (var i = 0; matchedRules && i < matchedRules.length; ++i) - matchedCSSRules.push(InjectedScript._serializeRule(matchedRules[i])); - - var styleAttributes = {}; - var attributes = node.attributes; - for (var i = 0; attributes && i < attributes.length; ++i) { - if (attributes[i].style) - styleAttributes[attributes[i].name] = InjectedScript._serializeStyle(attributes[i].style, true); - } - var result = {}; - result.inlineStyle = InjectedScript._serializeStyle(node.style, true); - result.computedStyle = InjectedScript._serializeStyle(defaultView.getComputedStyle(node)); - result.matchedCSSRules = matchedCSSRules; - result.styleAttributes = styleAttributes; - return result; -} - -InjectedScript.getComputedStyle = function(nodeId) -{ - var node = InjectedScript._nodeForId(nodeId); - if (!node) - return false; - return InjectedScript._serializeStyle(node.ownerDocument.defaultView.getComputedStyle(node)); -} - -InjectedScript.getInlineStyle = function(nodeId) -{ - var node = InjectedScript._nodeForId(nodeId); - if (!node) - return false; - return InjectedScript._serializeStyle(node.style, true); -} - -InjectedScript.applyStyleText = function(styleId, styleText, propertyName) -{ - var style = InjectedScript._styles[styleId]; - if (!style) - return false; - - var styleTextLength = styleText.length; - - // Create a new element to parse the user input CSS. - var parseElement = document.createElement("span"); - parseElement.setAttribute("style", styleText); - - var tempStyle = parseElement.style; - if (tempStyle.length || !styleTextLength) { - // The input was parsable or the user deleted everything, so remove the - // original property from the real style declaration. If this represents - // a shorthand remove all the longhand properties. - if (style.getPropertyShorthand(propertyName)) { - var longhandProperties = InjectedScript._getLonghandProperties(style, propertyName); - for (var i = 0; i < longhandProperties.length; ++i) - style.removeProperty(longhandProperties[i]); - } else - style.removeProperty(propertyName); - } - - // Notify caller that the property was successfully deleted. - if (!styleTextLength) - return [null, [propertyName]]; - - if (!tempStyle.length) - return false; - - // 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. - var foundShorthands = {}; - var changedProperties = []; - var uniqueProperties = InjectedScript._getUniqueStyleProperties(tempStyle); - for (var i = 0; i < uniqueProperties.length; ++i) { - var name = uniqueProperties[i]; - var shorthand = tempStyle.getPropertyShorthand(name); - - if (shorthand && shorthand in foundShorthands) - continue; - - if (shorthand) { - var value = InjectedScript._getShorthandValue(tempStyle, shorthand); - var priority = InjectedScript._getShorthandPriority(tempStyle, shorthand); - foundShorthands[shorthand] = true; - } else { - var value = tempStyle.getPropertyValue(name); - var priority = tempStyle.getPropertyPriority(name); - } - - // Set the property on the real style declaration. - style.setProperty((shorthand || name), value, priority); - changedProperties.push(shorthand || name); - } - return [InjectedScript._serializeStyle(style, true), changedProperties]; -} - -InjectedScript.setStyleText = function(style, cssText) -{ - style.cssText = cssText; - return true; -} - -InjectedScript.toggleStyleEnabled = function(styleId, propertyName, disabled) -{ - var style = InjectedScript._styles[styleId]; - if (!style) - return false; - - if (disabled) { - if (!style.__disabledPropertyValues || !style.__disabledPropertyPriorities) { - style.__disabledProperties = {}; - style.__disabledPropertyValues = {}; - style.__disabledPropertyPriorities = {}; - } - - style.__disabledPropertyValues[propertyName] = style.getPropertyValue(propertyName); - style.__disabledPropertyPriorities[propertyName] = style.getPropertyPriority(propertyName); - - if (style.getPropertyShorthand(propertyName)) { - var longhandProperties = InjectedScript._getLonghandProperties(style, propertyName); - for (var i = 0; i < longhandProperties.length; ++i) { - style.__disabledProperties[longhandProperties[i]] = true; - style.removeProperty(longhandProperties[i]); - } - } else { - style.__disabledProperties[propertyName] = true; - style.removeProperty(propertyName); - } - } else if (style.__disabledProperties && style.__disabledProperties[propertyName]) { - var value = style.__disabledPropertyValues[propertyName]; - var priority = style.__disabledPropertyPriorities[propertyName]; - - style.setProperty(propertyName, value, priority); - delete style.__disabledProperties[propertyName]; - delete style.__disabledPropertyValues[propertyName]; - delete style.__disabledPropertyPriorities[propertyName]; - } - return InjectedScript._serializeStyle(style, true); -} - -InjectedScript.applyStyleRuleText = function(ruleId, newContent, selectedNodeId) -{ - var rule = InjectedScript._styleRules[ruleId]; - if (!rule) - return false; - - var selectedNode = InjectedScript._nodeForId(selectedNodeId); - - try { - var stylesheet = rule.parentStyleSheet; - stylesheet.addRule(newContent); - var newRule = stylesheet.cssRules[stylesheet.cssRules.length - 1]; - newRule.style.cssText = rule.style.cssText; - - var parentRules = stylesheet.cssRules; - for (var i = 0; i < parentRules.length; ++i) { - if (parentRules[i] === rule) { - rule.parentStyleSheet.removeRule(i); - break; - } - } - - return [InjectedScript._serializeRule(newRule), InjectedScript._doesSelectorAffectNode(newContent, selectedNode)]; - } catch(e) { - // Report invalid syntax. - return false; - } -} - -InjectedScript.addStyleSelector = function(newContent, selectedNodeId) -{ - var selectedNode = InjectedScript._nodeForId(selectedNodeId); - if (!selectedNode) - return false; - var ownerDocument = selectedNode.ownerDocument; - - var stylesheet = ownerDocument.__stylesheet; - if (!stylesheet) { - var head = ownerDocument.head; - var styleElement = ownerDocument.createElement("style"); - styleElement.type = "text/css"; - head.appendChild(styleElement); - stylesheet = ownerDocument.styleSheets[ownerDocument.styleSheets.length - 1]; - ownerDocument.__stylesheet = stylesheet; - } - - try { - stylesheet.addRule(newContent); - } catch (e) { - // Invalid Syntax for a Selector - return false; - } - - var rule = stylesheet.cssRules[stylesheet.cssRules.length - 1]; - rule.__isViaInspector = true; - - return [ InjectedScript._serializeRule(rule), InjectedScript._doesSelectorAffectNode(newContent, selectedNode) ]; -} - -InjectedScript._doesSelectorAffectNode = function(selectorText, node) -{ - if (!node) - return false; - var nodes = node.ownerDocument.querySelectorAll(selectorText); - for (var i = 0; i < nodes.length; ++i) { - if (nodes[i] === node) { - return true; - } - } - return false; -} - -InjectedScript.setStyleProperty = function(styleId, name, value) -{ - var style = InjectedScript._styles[styleId]; - if (!style) - return false; - - style.setProperty(name, value, ""); - return true; -} - -InjectedScript._serializeRule = function(rule) -{ - var parentStyleSheet = rule.parentStyleSheet; - - var ruleValue = {}; - ruleValue.selectorText = rule.selectorText; - if (parentStyleSheet) { - ruleValue.parentStyleSheet = {}; - ruleValue.parentStyleSheet.href = parentStyleSheet.href; - } - ruleValue.isUserAgent = parentStyleSheet && !parentStyleSheet.ownerNode && !parentStyleSheet.href; - ruleValue.isUser = parentStyleSheet && parentStyleSheet.ownerNode && parentStyleSheet.ownerNode.nodeName == "#document"; - ruleValue.isViaInspector = !!rule.__isViaInspector; - - // Bind editable scripts only. - var doBind = !ruleValue.isUserAgent && !ruleValue.isUser; - ruleValue.style = InjectedScript._serializeStyle(rule.style, doBind); - - if (doBind) { - if (!rule.id) { - rule.id = InjectedScript._lastStyleRuleId++; - InjectedScript._styleRules[rule.id] = rule; - } - ruleValue.id = rule.id; - ruleValue.injectedScriptId = injectedScriptId; - } - return ruleValue; -} - -InjectedScript._serializeStyle = function(style, doBind) -{ - var result = {}; - result.width = style.width; - result.height = style.height; - result.__disabledProperties = style.__disabledProperties; - result.__disabledPropertyValues = style.__disabledPropertyValues; - result.__disabledPropertyPriorities = style.__disabledPropertyPriorities; - result.properties = []; - result.shorthandValues = {}; - var foundShorthands = {}; - for (var i = 0; i < style.length; ++i) { - var property = {}; - var name = style[i]; - property.name = name; - property.priority = style.getPropertyPriority(name); - property.implicit = style.isPropertyImplicit(name); - var shorthand = style.getPropertyShorthand(name); - property.shorthand = shorthand; - if (shorthand && !(shorthand in foundShorthands)) { - foundShorthands[shorthand] = true; - result.shorthandValues[shorthand] = InjectedScript._getShorthandValue(style, shorthand); - } - property.value = style.getPropertyValue(name); - result.properties.push(property); - } - result.uniqueStyleProperties = InjectedScript._getUniqueStyleProperties(style); - - if (doBind) { - if (!style.id) { - style.id = InjectedScript._lastStyleId++; - InjectedScript._styles[style.id] = style; - } - result.id = style.id; - result.injectedScriptId = injectedScriptId; - } - return result; -} - -InjectedScript._getUniqueStyleProperties = function(style) -{ - var properties = []; - var foundProperties = {}; - - for (var i = 0; i < style.length; ++i) { - var property = style[i]; - if (property in foundProperties) - continue; - foundProperties[property] = true; - properties.push(property); - } - - return properties; -} - - -InjectedScript._getLonghandProperties = function(style, shorthandProperty) -{ - var properties = []; - var foundProperties = {}; - - for (var i = 0; i < style.length; ++i) { - var individualProperty = style[i]; - if (individualProperty in foundProperties || style.getPropertyShorthand(individualProperty) !== shorthandProperty) - continue; - foundProperties[individualProperty] = true; - properties.push(individualProperty); - } - - return properties; -} - -InjectedScript._getShorthandValue = function(style, shorthandProperty) -{ - var value = style.getPropertyValue(shorthandProperty); - if (!value) { - // Some shorthands (like border) return a null value, so compute a shorthand value. - // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15823 is fixed. - - var foundProperties = {}; - for (var i = 0; i < style.length; ++i) { - var individualProperty = style[i]; - if (individualProperty in foundProperties || style.getPropertyShorthand(individualProperty) !== shorthandProperty) - continue; - - var individualValue = style.getPropertyValue(individualProperty); - if (style.isPropertyImplicit(individualProperty) || individualValue === "initial") - continue; - - foundProperties[individualProperty] = true; - - if (!value) - value = ""; - else if (value.length) - value += " "; - value += individualValue; - } - } - return value; -} - -InjectedScript._getShorthandPriority = function(style, shorthandProperty) -{ - var priority = style.getPropertyPriority(shorthandProperty); - if (!priority) { - for (var i = 0; i < style.length; ++i) { - var individualProperty = style[i]; - if (style.getPropertyShorthand(individualProperty) !== shorthandProperty) - continue; - priority = style.getPropertyPriority(individualProperty); - break; - } - } - return priority; -} - InjectedScript.getPrototypes = function(nodeId) { var node = InjectedScript._nodeForId(nodeId); @@ -486,10 +125,32 @@ InjectedScript.getProperties = function(objectProxy, ignoreHasOwnProperty, abbre if (!InjectedScript._isDefined(object)) return false; var properties = []; + var propertyNames = ignoreHasOwnProperty ? InjectedScript._getPropertyNames(object) : Object.getOwnPropertyNames(object); if (!ignoreHasOwnProperty && object.__proto__) propertyNames.push("__proto__"); + if (jsEngine === "v8") { + // Check if the object is a scope. + if (InjectedScript._isScopeProxy(objectProxy)) { + propertyNames = []; + for (var name in object) + propertyNames.push(name); + } else { + // FIXME(http://crbug.com/41243): Object.getOwnPropertyNames may return duplicated names. + var a = []; + propertyNames.sort(); + var prev; + for (var i = 0; i < propertyNames.length; i++) { + var n = propertyNames[i]; + if (n != prev) + a.push(n); + prev = n; + } + propertyNames = a; + } + } + // Go over properties, prepare results. for (var i = 0; i < propertyNames.length; ++i) { var propertyName = propertyNames[i]; @@ -498,7 +159,7 @@ InjectedScript.getProperties = function(objectProxy, ignoreHasOwnProperty, abbre property.name = propertyName + ""; property.parentObjectProxy = objectProxy; var isGetter = object["__lookupGetter__"] && object.__lookupGetter__(propertyName); - if (!property.isGetter) { + if (!isGetter) { try { var childObject = object[propertyName]; var childObjectProxy = new InjectedScript.createProxyObject(childObject, objectProxy.objectId, abbreviate); @@ -519,6 +180,12 @@ InjectedScript.getProperties = function(objectProxy, ignoreHasOwnProperty, abbre return properties; } +InjectedScript._isScopeProxy = function(objectProxy) +{ + var objectId = objectProxy.objectId; + return typeof objectId === "object" && !objectId.thisObject; +} + InjectedScript.setPropertyValue = function(objectProxy, propertyName, expression) { var object = InjectedScript._resolveObject(objectProxy); @@ -538,13 +205,13 @@ InjectedScript.setPropertyValue = function(objectProxy, propertyName, expression // There is a regression introduced here: eval is now happening against global object, // not call frame while on a breakpoint. // TODO: bring evaluation against call frame back. - var result = InjectedScript._window().eval("(" + expression + ")"); + var result = inspectedWindow.eval("(" + expression + ")"); // Store the result in the property. object[propertyName] = result; return true; } catch(e) { try { - var result = InjectedScript._window().eval("\"" + InjectedScript._escapeCharacters(expression, "\"") + "\""); + var result = inspectedWindow.eval("\"" + InjectedScript._escapeCharacters(expression, "\"") + "\""); object[propertyName] = result; return true; } catch(e) { @@ -606,7 +273,7 @@ InjectedScript.getCompletions = function(expression, includeInspectorCommandLine if (!callFrame) return props; if (expression) - expressionResult = InjectedScript._evaluateOn(callFrame.evaluate, callFrame, expression); + expressionResult = InjectedScript._evaluateOn(callFrame.evaluate, callFrame, expression, true); else { // Evaluate into properties in scope of the selected call frame. var scopeChain = callFrame.scopeChain; @@ -616,12 +283,12 @@ InjectedScript.getCompletions = function(expression, includeInspectorCommandLine } else { if (!expression) expression = "this"; - expressionResult = InjectedScript._evaluateOn(InjectedScript._window().eval, InjectedScript._window(), expression); + expressionResult = InjectedScript._evaluateOn(inspectedWindow.eval, inspectedWindow, expression); } if (typeof expressionResult == "object") InjectedScript._populatePropertyNames(expressionResult, props); if (includeInspectorCommandLineAPI) - for (var prop in InjectedScript._window().console._inspectorCommandLineAPI) + for (var prop in inspectedWindow.console._inspectorCommandLineAPI) if (prop.charAt(0) !== '_') props[prop] = true; } catch(e) { @@ -631,14 +298,14 @@ InjectedScript.getCompletions = function(expression, includeInspectorCommandLine InjectedScript.evaluate = function(expression, objectGroup) { - return InjectedScript._evaluateAndWrap(InjectedScript._window().eval, InjectedScript._window(), expression, objectGroup); + return InjectedScript._evaluateAndWrap(inspectedWindow.eval, inspectedWindow, expression, objectGroup); } -InjectedScript._evaluateAndWrap = function(evalFunction, object, expression, objectGroup) +InjectedScript._evaluateAndWrap = function(evalFunction, object, expression, objectGroup, dontUseCommandLineAPI) { var result = {}; try { - result.value = InjectedScript.wrapObject(InjectedScript._evaluateOn(evalFunction, object, expression), objectGroup); + result.value = InjectedScript.wrapObject(InjectedScript._evaluateOn(evalFunction, object, expression, dontUseCommandLineAPI), objectGroup); // Handle error that might have happened while describing result. if (result.value.errorText) { @@ -652,12 +319,14 @@ InjectedScript._evaluateAndWrap = function(evalFunction, object, expression, obj return result; } -InjectedScript._evaluateOn = function(evalFunction, object, expression) +InjectedScript._evaluateOn = function(evalFunction, object, expression, dontUseCommandLineAPI) { InjectedScript._ensureCommandLineAPIInstalled(evalFunction, object); // 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. - expression = "with (window.console._inspectorCommandLineAPI) { with (window) {\n" + expression + "\n} }"; + if (!dontUseCommandLineAPI) + expression = "with (window.console._inspectorCommandLineAPI) { with (window) {\n" + expression + "\n} }"; + var value = evalFunction.call(object, expression); // When evaluating on call frame error is not thrown, but returned as a value. @@ -673,15 +342,20 @@ InjectedScript.addInspectedNode = function(nodeId) if (!node) return false; - InjectedScript._ensureCommandLineAPIInstalled(InjectedScript._window().eval, InjectedScript._window()); - var inspectedNodes = InjectedScript._window().console._inspectorCommandLineAPI._inspectedNodes; + InjectedScript._ensureCommandLineAPIInstalled(inspectedWindow.eval, inspectedWindow); + var inspectedNodes = inspectedWindow.console._inspectorCommandLineAPI._inspectedNodes; inspectedNodes.unshift(node); if (inspectedNodes.length >= 5) inspectedNodes.pop(); return true; } -InjectedScript.performSearch = function(whitespaceTrimmedQuery) +InjectedScript.getNodeId = function(node) +{ + return InjectedScriptHost.pushNodePathToFrontend(node, false, false); +} + +InjectedScript.performSearch = function(whitespaceTrimmedQuery, runSynchronously) { // FIXME: Few things are missing here: // 1) Search works with node granularity - number of matches within node is not calculated. @@ -830,7 +504,7 @@ InjectedScript.performSearch = function(whitespaceTrimmedQuery) delete InjectedScript._searchResults[i][searchResultsProperty]; } - const mainFrameDocument = InjectedScript._window().document; + const mainFrameDocument = inspectedWindow.document; const searchDocuments = [mainFrameDocument]; var searchFunctions; if (tagNameQuery && startTagFound && endTagFound) @@ -849,8 +523,7 @@ InjectedScript.performSearch = function(whitespaceTrimmedQuery) searchFunctions = [matchExactItems, matchStyleSelector, matchPartialTagNamesAndAttributeValues, matchPlainText, matchXPathQuery]; // Find all frames, iframes and object elements to search their documents. - const querySelectorAllFunction = InjectedScript._window().Document.prototype.querySelectorAll; - const subdocumentResult = querySelectorAllFunction.call(mainFrameDocument, "iframe, frame, object"); + const subdocumentResult = mainFrameDocument.querySelectorAll("iframe, frame, object"); for (var i = 0; i < subdocumentResult.length; ++i) { var element = subdocumentResult.item(i); @@ -879,26 +552,27 @@ InjectedScript.performSearch = function(whitespaceTrimmedQuery) delete panel._currentSearchChunkIntervalIdentifier; clearInterval(chunkIntervalIdentifier); finishedSearching.call(panel); - return; + return false; } searchDocument = searchDocuments[documentIndex]; } - if (!searchDocument || !searchFunction) - return; - try { searchFunction.call(panel, searchDocument); } catch(err) { // ignore any exceptions. the query might be malformed, but we allow that. } + return true; } - processChunk(); - - chunkIntervalIdentifier = setInterval(processChunk, 25); - InjectedScript._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier; + if (runSynchronously) + while (processChunk()) {} + else { + processChunk(); + chunkIntervalIdentifier = setInterval(processChunk, 25); + InjectedScript._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier; + } return true; } @@ -926,7 +600,7 @@ InjectedScript.openInInspectedWindow = function(url) { // Don't call window.open on wrapper - popup blocker mutes it. // URIs should have no double quotes. - InjectedScript._window().eval("window.open(\"" + url + "\")"); + inspectedWindow.eval("window.open(\"" + url + "\")"); return true; } @@ -950,7 +624,7 @@ InjectedScript.evaluateInCallFrame = function(callFrameId, code, objectGroup) var callFrame = InjectedScript._callFrameForId(callFrameId); if (!callFrame) return false; - return InjectedScript._evaluateAndWrap(callFrame.evaluate, callFrame, code, objectGroup); + return InjectedScript._evaluateAndWrap(callFrame.evaluate, callFrame, code, objectGroup, true); } InjectedScript._callFrameForId = function(id) @@ -1088,13 +762,6 @@ InjectedScript._resolveObject = function(objectProxy) return object; } -InjectedScript._window = function() -{ - // TODO: replace with 'return window;' once this script is injected into - // the page's context. - return inspectedWindow; -} - InjectedScript._nodeForId = function(nodeId) { if (!nodeId) @@ -1158,9 +825,10 @@ InjectedScript.createProxyObject = function(object, objectId, abbreviate) return result; } -InjectedScript.evaluateOnSelf = function(funcBody) +InjectedScript.evaluateOnSelf = function(funcBody, args) { - return window.eval("(" + funcBody + ")();"); + var func = window.eval("(" + funcBody + ")"); + return func.apply(this, args || []); } InjectedScript.CallFrameProxy = function(id, callFrame) @@ -1173,6 +841,59 @@ InjectedScript.CallFrameProxy = function(id, callFrame) this.scopeChain = this._wrapScopeChain(callFrame); } +// FIXME(37663): unify scope chain representation and remove this if. +if (jsEngine === "v8") { + +InjectedScript.CallFrameProxy.prototype = { + _wrapScopeChain: function(callFrame) + { + const GLOBAL_SCOPE = 0; + const LOCAL_SCOPE = 1; + const WITH_SCOPE = 2; + const CLOSURE_SCOPE = 3; + const CATCH_SCOPE = 4; + + var scopeChain = callFrame.scopeChain; + var scopeChainProxy = []; + for (var i = 0; i < scopeChain.length; i++) { + var scopeType = callFrame.scopeType(i); + var scopeObject = scopeChain[i]; + var scopeObjectProxy = InjectedScript.createProxyObject(scopeObject, { callFrame: this.id, chainIndex: i }, true); + + var foundLocalScope = false; + switch(scopeType) { + case LOCAL_SCOPE: { + foundLocalScope = true; + scopeObjectProxy.isLocal = true; + scopeObjectProxy.thisObject = InjectedScript.createProxyObject(callFrame.thisObject, { callFrame: this.id, thisObject: true }, true); + break; + } + case CLOSURE_SCOPE: { + scopeObjectProxy.isClosure = true; + break; + } + case WITH_SCOPE: + case CATCH_SCOPE: { + scopeObjectProxy.isWithBlock = true; + break; + } + } + + if (foundLocalScope) { + if (scopeObject instanceof inspectedWindow.Element) + scopeObjectProxy.isElement = true; + else if (scopeObject instanceof inspectedWindow.Document) + scopeObjectProxy.isDocument = true; + } + + scopeChainProxy.push(scopeObjectProxy); + } + return scopeChainProxy; + } +} + +} else { + InjectedScript.CallFrameProxy.prototype = { _wrapScopeChain: function(callFrame) { @@ -1190,9 +911,9 @@ InjectedScript.CallFrameProxy.prototype = { scopeObjectProxy.isClosure = true; foundLocalScope = true; scopeObjectProxy.isLocal = true; - } else if (foundLocalScope && scopeObject instanceof InjectedScript._window().Element) + } else if (foundLocalScope && scopeObject instanceof inspectedWindow.Element) scopeObjectProxy.isElement = true; - else if (foundLocalScope && scopeObject instanceof InjectedScript._window().Document) + else if (foundLocalScope && scopeObject instanceof inspectedWindow.Document) scopeObjectProxy.isDocument = true; else if (!foundLocalScope) scopeObjectProxy.isWithBlock = true; @@ -1202,6 +923,8 @@ InjectedScript.CallFrameProxy.prototype = { } } +} + InjectedScript.executeSql = function(callId, databaseId, query) { function successCallback(tx, result) @@ -1243,7 +966,13 @@ InjectedScript.executeSql = function(callId, databaseId, query) InjectedScript._isDefined = function(object) { - return object || object instanceof inspectedWindow.HTMLAllCollection; + return object || InjectedScript._isHTMLAllCollection(object); +} + +InjectedScript._isHTMLAllCollection = function(object) +{ + // document.all is reported as undefined, but we still want to process it. + return (typeof object === "undefined") && inspectedWindow.HTMLAllCollection && object instanceof inspectedWindow.HTMLAllCollection; } InjectedScript._type = function(obj) @@ -1251,35 +980,38 @@ InjectedScript._type = function(obj) if (obj === null) return "null"; - // FIXME(33716): typeof document.all is always 'undefined'. - if (obj instanceof inspectedWindow.HTMLAllCollection) - return "array"; - var type = typeof obj; - if (type !== "object" && type !== "function") + if (type !== "object" && type !== "function") { + // FIXME(33716): typeof document.all is always 'undefined'. + if (InjectedScript._isHTMLAllCollection(obj)) + return "array"; return type; + } - var win = InjectedScript._window(); + // If owning frame has navigated to somewhere else window properties will be undefined. + // In this case just return result of the typeof. + if (!inspectedWindow.document) + return type; - if (obj instanceof win.Node) + if (obj instanceof inspectedWindow.Node) return (obj.nodeType === undefined ? type : "node"); - if (obj instanceof win.String) + if (obj instanceof inspectedWindow.String) return "string"; - if (obj instanceof win.Array) + if (obj instanceof inspectedWindow.Array) return "array"; - if (obj instanceof win.Boolean) + if (obj instanceof inspectedWindow.Boolean) return "boolean"; - if (obj instanceof win.Number) + if (obj instanceof inspectedWindow.Number) return "number"; - if (obj instanceof win.Date) + if (obj instanceof inspectedWindow.Date) return "date"; - if (obj instanceof win.RegExp) + if (obj instanceof inspectedWindow.RegExp) return "regexp"; - if (obj instanceof win.NodeList) + if (obj instanceof inspectedWindow.NodeList) return "array"; - if (obj instanceof win.HTMLCollection || obj instanceof win.HTMLAllCollection) + if (obj instanceof inspectedWindow.HTMLCollection) return "array"; - if (obj instanceof win.Error) + if (obj instanceof inspectedWindow.Error) return "error"; return type; } @@ -1301,20 +1033,27 @@ InjectedScript._describe = function(obj, abbreviated) return "\"" + obj.substring(0, 100) + "\u2026\""; return "\"" + obj + "\""; case "function": - var objectText = String(obj); + var objectText = InjectedScript._toString(obj); if (!/^function /.test(objectText)) objectText = (type2 == "object") ? type1 : type2; else if (abbreviated) objectText = /.*/.exec(obj)[0].replace(/ +$/g, ""); return objectText; default: - return String(obj); + return InjectedScript._toString(obj); } } +InjectedScript._toString = function(obj) +{ + // We don't use String(obj) because inspectedWindow.String is undefined if owning frame navigated to another page. + return "" + obj; +} + InjectedScript._className = function(obj) { - return Object.prototype.toString.call(obj).replace(/^\[object (.*)\]$/i, "$1") + var str = inspectedWindow.Object ? inspectedWindow.Object.prototype.toString.call(obj) : InjectedScript._toString(obj); + return str.replace(/^\[object (.*)\]$/i, "$1"); } InjectedScript._escapeCharacters = function(str, chars) diff --git a/WebCore/inspector/front-end/InspectorBackendStub.js b/WebCore/inspector/front-end/InspectorBackendStub.js index ed03f73..4670af1 100644 --- a/WebCore/inspector/front-end/InspectorBackendStub.js +++ b/WebCore/inspector/front-end/InspectorBackendStub.js @@ -34,9 +34,6 @@ WebInspector.InspectorBackendStub = function() { this._searchingForNode = false; this._attachedWindowHeight = 0; - this._debuggerEnabled = true; - this._profilerEnabled = true; - this._resourceTrackingEnabled = false; this._timelineEnabled = false; } @@ -126,39 +123,42 @@ WebInspector.InspectorBackendStub.prototype = { return ""; }, - debuggerEnabled: function() - { - return this._debuggerEnabled; - }, - enableResourceTracking: function() { - this._resourceTrackingEnabled = true; WebInspector.resourceTrackingWasEnabled(); }, disableResourceTracking: function() { - this._resourceTrackingEnabled = false; WebInspector.resourceTrackingWasDisabled(); }, - resourceTrackingEnabled: function() + + enableSearchingForNode: function() + { + WebInspector.searchingForNodeWasEnabled(); + }, + + disableSearchingForNode: function() + { + WebInspector.searchingForNodeWasDisabled(); + }, + + reloadPage: function() { - return this._resourceTrackingEnabled; }, enableDebugger: function() { - this._debuggerEnabled = true; + WebInspector.debuggerWasEnabled(); }, disableDebugger: function() { - this._debuggerEnabled = false; + WebInspector.debuggerWasDisabled(); }, - addBreakpoint: function(sourceID, line, condition) + setBreakpoint: function(sourceID, line, enabled, condition) { }, @@ -166,40 +166,37 @@ WebInspector.InspectorBackendStub.prototype = { { }, - updateBreakpoint: function(sourceID, line, condition) + activateBreakpoints: function() { + this._breakpointsActivated = true; }, - pauseInDebugger: function() + deactivateBreakpoints: function() { + this._breakpointsActivated = false; }, - pauseOnExceptionsState: function() + pauseInDebugger: function() { - return 0; }, setPauseOnExceptionsState: function(value) { + WebInspector.updatePauseOnExceptionsState(value); }, resumeDebugger: function() { }, - profilerEnabled: function() - { - return true; - }, - enableProfiler: function() { - this._profilerEnabled = true; + WebInspector.profilerWasEnabled(); }, disableProfiler: function() { - this._profilerEnabled = false; + WebInspector.profilerWasDisabled(); }, startProfiling: function() @@ -217,10 +214,6 @@ WebInspector.InspectorBackendStub.prototype = { getProfile: function(callId, uid) { - if (WebInspector.__fullProfiles && (uid in WebInspector.__fullProfiles)) - { - WebInspector.didGetProfile(callId, WebInspector.__fullProfiles[uid]); - } }, takeHeapSnapshot: function() @@ -258,6 +251,14 @@ WebInspector.InspectorBackendStub.prototype = { setInjectedScriptSource: function() { + }, + + addScriptToEvaluateOnLoad: function() + { + }, + + removeAllScriptsToEvaluateOnLoad: function() + { } } diff --git a/WebCore/inspector/front-end/InspectorFrontendHostStub.js b/WebCore/inspector/front-end/InspectorFrontendHostStub.js index f1decb6..5456069 100644 --- a/WebCore/inspector/front-end/InspectorFrontendHostStub.js +++ b/WebCore/inspector/front-end/InspectorFrontendHostStub.js @@ -48,6 +48,11 @@ WebInspector.InspectorFrontendHostStub.prototype = { return "unknown"; }, + bringToFront: function() + { + this._windowVisible = true; + }, + closeWindow: function() { this._windowVisible = false; @@ -87,12 +92,17 @@ WebInspector.InspectorFrontendHostStub.prototype = { return ""; }, - windowUnloading: function() + inspectedURLChanged: function(url) { }, copyText: function() { + }, + + canAttachWindow: function() + { + return false; } } diff --git a/WebCore/inspector/front-end/MetricsSidebarPane.js b/WebCore/inspector/front-end/MetricsSidebarPane.js index 767da1f..ed5a7ec 100644 --- a/WebCore/inspector/front-end/MetricsSidebarPane.js +++ b/WebCore/inspector/front-end/MetricsSidebarPane.js @@ -30,7 +30,6 @@ WebInspector.MetricsSidebarPane = function() { WebInspector.SidebarPane.call(this, WebInspector.UIString("Metrics")); this._inlineStyleId = null; - this._inlineStyleInjectedScriptId = null; } WebInspector.MetricsSidebarPane.prototype = { @@ -53,15 +52,14 @@ WebInspector.MetricsSidebarPane.prototype = { var style = WebInspector.CSSStyleDeclaration.parseStyle(stylePayload); self._update(style); }; - InjectedScriptAccess.get(node.injectedScriptId).getComputedStyle(node.id, callback); + InspectorBackend.getComputedStyle(WebInspector.Callback.wrap(callback), node.id); var inlineStyleCallback = function(stylePayload) { if (!stylePayload) return; self._inlineStyleId = stylePayload.id; - self._inlineStyleInjectedScriptId = stylePayload.injectedScriptId; }; - InjectedScriptAccess.get(node.injectedScriptId).getInlineStyle(node.id, inlineStyleCallback); + InspectorBackend.getInlineStyle(WebInspector.Callback.wrap(inlineStyleCallback), node.id); }, _update: function(style) @@ -187,6 +185,11 @@ WebInspector.MetricsSidebarPane.prototype = { editingCommitted: function(element, userInput, previousContent, context) { + if (!this._inlineStyleId) { + // Element has no renderer. + return this.editingCancelled(element, context); // nothing changed, so cancel + } + if (userInput === previousContent) return this.editingCancelled(element, context); // nothing changed, so cancel @@ -206,7 +209,8 @@ WebInspector.MetricsSidebarPane.prototype = { self.dispatchEventToListeners("metrics edited"); self.update(); }; - InjectedScriptAccess.get(this._inlineStyleInjectedScriptId).setStyleProperty(this._inlineStyleId, context.styleProperty, userInput, callback); + + InspectorBackend.setStyleProperty(WebInspector.Callback.wrap(callback), this._inlineStyleId, context.styleProperty, userInput); } } diff --git a/WebCore/inspector/front-end/Panel.js b/WebCore/inspector/front-end/Panel.js index 5b01191..b916708 100644 --- a/WebCore/inspector/front-end/Panel.js +++ b/WebCore/inspector/front-end/Panel.js @@ -373,12 +373,12 @@ WebInspector.Panel.prototype = { visibleView.resize(); }, - canShowSourceLineForURL: function(url) + canShowSourceLine: function(url, line) { return false; }, - showSourceLineForURL: function(url, line) + showSourceLine: function(url, line) { return false; }, diff --git a/WebCore/inspector/front-end/Popover.js b/WebCore/inspector/front-end/Popover.js index 70e4ac9..848c99e 100644 --- a/WebCore/inspector/front-end/Popover.js +++ b/WebCore/inspector/front-end/Popover.js @@ -38,6 +38,8 @@ WebInspector.Popover = function(contentElement) this.element.appendChild(this._popupArrowElement); this.contentElement = contentElement; + this._contentDiv = document.createElement("div"); + this._contentDiv.className = "content"; } WebInspector.Popover.prototype = { @@ -54,25 +56,28 @@ WebInspector.Popover.prototype = { var preferredWidth = preferredWidth || this.contentElement.offsetWidth; var preferredHeight = preferredHeight || this.contentElement.offsetHeight; - this.contentElement.addStyleClass("content"); - this.element.appendChild(this.contentElement); + this._contentDiv.appendChild(this.contentElement); + this.element.appendChild(this._contentDiv); document.body.appendChild(this.element); this._positionElement(anchor, preferredWidth, preferredHeight); }, hide: function() { - delete WebInspector.Popover._popoverElement; - document.body.removeChild(this.element); + if (WebInspector.Popover._popoverElement) { + delete WebInspector.Popover._popoverElement; + document.body.removeChild(this.element); + } }, _positionElement: function(anchorElement, preferredWidth, preferredHeight) { const borderWidth = 25; const scrollerWidth = 11; - const arrowHeight = 10; - const arrowOffset = 15; - + const arrowHeight = 15; + const arrowOffset = 10; + const borderRadius = 10; + // Skinny tooltips are not pretty, their arrow location is not nice. preferredWidth = Math.max(preferredWidth, 50); const totalWidth = window.innerWidth; @@ -87,7 +92,7 @@ WebInspector.Popover.prototype = { anchorElement = anchorElement.parentElement; } - var newElementPosition = { x: 0, y: 0, width: preferredWidth + borderWidth * 2, height: preferredHeight + borderWidth * 2 }; + var newElementPosition = { x: 0, y: 0, width: preferredWidth + scrollerWidth, height: preferredHeight }; var verticalAlignment; var roomAbove = anchorBox.y; @@ -95,53 +100,140 @@ WebInspector.Popover.prototype = { if (roomAbove > roomBelow) { // Positioning above the anchor. - if (anchorBox.y > newElementPosition.height) - newElementPosition.y = anchorBox.y - newElementPosition.height; + if (anchorBox.y > newElementPosition.height + arrowHeight + borderRadius) + newElementPosition.y = anchorBox.y - newElementPosition.height - arrowHeight; else { - newElementPosition.y = 0; - newElementPosition.height = anchorBox.y - newElementPosition.y; - // Reserve room for vertical scroller anyways. - newElementPosition.width += scrollerWidth; + newElementPosition.y = borderRadius * 2; + newElementPosition.height = anchorBox.y - borderRadius * 2 - arrowHeight; } verticalAlignment = "bottom"; } else { // Positioning below the anchor. - newElementPosition.y = anchorBox.y + anchorBox.height; - if (newElementPosition.y + newElementPosition.height >= totalHeight) { - newElementPosition.height = totalHeight - anchorBox.y - anchorBox.height; - // Reserve room for vertical scroller. - newElementPosition.width += scrollerWidth; - } + newElementPosition.y = anchorBox.y + anchorBox.height + arrowHeight; + if (newElementPosition.y + newElementPosition.height + arrowHeight - borderWidth >= totalHeight) + newElementPosition.height = totalHeight - anchorBox.y - anchorBox.height - borderRadius * 2 - arrowHeight; // Align arrow. - newElementPosition.y -= arrowHeight; verticalAlignment = "top"; } var horizontalAlignment; if (anchorBox.x + newElementPosition.width < totalWidth) { - newElementPosition.x = Math.max(0, anchorBox.x) - borderWidth - arrowOffset; + newElementPosition.x = Math.max(borderRadius, anchorBox.x - borderRadius - arrowOffset); horizontalAlignment = "left"; - } else if (newElementPosition.width < totalWidth) { - newElementPosition.x = totalWidth - newElementPosition.width; + } else if (newElementPosition.width + borderRadius * 2 < totalWidth) { + newElementPosition.x = totalWidth - newElementPosition.width - borderRadius; horizontalAlignment = "right"; // Position arrow accurately. - this._popupArrowElement.style.right = totalWidth - anchorBox.x - borderWidth - anchorBox.width + "px"; + var arrowRightPosition = Math.max(0, totalWidth - anchorBox.x - anchorBox.width - borderRadius - arrowOffset); + arrowRightPosition += anchorBox.width / 2; + this._popupArrowElement.style.right = arrowRightPosition + "px"; } else { - newElementPosition.x = 0; - newElementPosition.width = totalWidth; + newElementPosition.x = borderRadius; + newElementPosition.width = totalWidth - borderRadius * 2; + newElementPosition.height += scrollerWidth; horizontalAlignment = "left"; if (verticalAlignment === "bottom") newElementPosition.y -= scrollerWidth; // Position arrow accurately. - this._popupArrowElement.style.left = anchorBox.x - borderWidth + "px"; + this._popupArrowElement.style.left = Math.max(0, anchorBox.x - borderRadius * 2 - arrowOffset) + "px"; + this._popupArrowElement.style.left += anchorBox.width / 2; } - // Reserve room for horizontal scroller. - newElementPosition.height += scrollerWidth; - this.element.className = "popover " + verticalAlignment + "-" + horizontalAlignment + "-arrow"; - this.element.positionAt(newElementPosition.x, newElementPosition.y); - this.element.style.width = newElementPosition.width + "px"; - this.element.style.height = newElementPosition.height + "px"; + this.element.positionAt(newElementPosition.x - borderWidth, newElementPosition.y - borderWidth); + this.element.style.width = newElementPosition.width + borderWidth * 2 + "px"; + this.element.style.height = newElementPosition.height + borderWidth * 2 + "px"; + } +} + +WebInspector.PopoverHelper = function(panelElement, getAnchor, showPopup, showOnClick, onHide) +{ + this._panelElement = panelElement; + this._getAnchor = getAnchor; + this._showPopup = showPopup; + this._showOnClick = showOnClick; + this._onHide = onHide; + panelElement.addEventListener("mousedown", this._mouseDown.bind(this), false); + panelElement.addEventListener("mousemove", this._mouseMove.bind(this), false); +} + +WebInspector.PopoverHelper.prototype = { + _mouseDown: function(event) + { + this._killHidePopupTimer(); + this._handleMouseAction(event, true); + }, + + _mouseMove: function(event) + { + // Pretend that nothing has happened. + if (this._hoverElement === event.target || (this._hoverElement && this._hoverElement.isAncestor(event.target))) + return; + + // User has 500ms to reach the popup. + if (this._popup && !this._hidePopupTimer) { + var self = this; + function doHide() + { + self.hidePopup(); + delete self._hidePopupTimer; + } + this._hidePopupTimer = setTimeout(doHide, 500); + } + + this._handleMouseAction(event); + }, + + _handleMouseAction: function(event, isMouseDown) + { + this._resetHoverTimer(); + + this._hoverElement = this._getAnchor(event.target); + if (!this._hoverElement) + return; + + const toolTipDelay = isMouseDown ? 0 : (this._popup ? 600 : 1000); + this._hoverTimer = setTimeout(this._mouseHover.bind(this, this._hoverElement), toolTipDelay); + }, + + _resetHoverTimer: function() + { + if (this._hoverTimer) { + clearTimeout(this._hoverTimer); + delete this._hoverTimer; + } + }, + + hidePopup: function() + { + if (!this._popup) + return; + + if (this._onHide) + this._onHide(); + + this._popup.hide(); + delete this._popup; + }, + + _mouseHover: function(element) + { + delete this._hoverTimer; + + this._popup = this._showPopup(element); + if (this._popup) + this._popup.contentElement.addEventListener("mousemove", this._killHidePopupTimer.bind(this), true); + }, + + _killHidePopupTimer: function() + { + if (this._hidePopupTimer) { + clearTimeout(this._hidePopupTimer); + delete this._hidePopupTimer; + + // We know that we reached the popup, but we might have moved over other elements. + // Discard pending command. + this._resetHoverTimer(); + } } } diff --git a/WebCore/inspector/front-end/ProfileView.js b/WebCore/inspector/front-end/ProfileView.js index 1efa0dc..817f1f5 100644 --- a/WebCore/inspector/front-end/ProfileView.js +++ b/WebCore/inspector/front-end/ProfileView.js @@ -83,7 +83,7 @@ WebInspector.CPUProfileView = function(profile) var self = this; function profileCallback(profile) { - self.profile = profile; + self.profile.head = profile.head; self._assignParentsInProfile(); self.profileDataGridTree = self.bottomUpProfileDataGridTree; @@ -601,7 +601,7 @@ WebInspector.CPUProfileType.prototype = { get welcomeMessage() { - return WebInspector.UIString("Start CPU profiling by pressing<br>the %s button on the status bar."); + return WebInspector.UIString("Control CPU profiling by pressing the %s button on the status bar."); }, setRecordingProfile: function(isProfiling) diff --git a/WebCore/inspector/front-end/ProfilesPanel.js b/WebCore/inspector/front-end/ProfilesPanel.js index dd049a1..2f9d38d 100644 --- a/WebCore/inspector/front-end/ProfilesPanel.js +++ b/WebCore/inspector/front-end/ProfilesPanel.js @@ -120,6 +120,7 @@ WebInspector.ProfilesPanel = function() this.element.appendChild(this.welcomeView.element); this._profiles = []; + this._profilerEnabled = Preferences.profilerAlwaysEnabled; this.reset(); } @@ -163,6 +164,7 @@ WebInspector.ProfilesPanel.prototype = { populateInterface: function() { + this.reset(); if (this.visible) this._populateProfiles(); else @@ -171,12 +173,19 @@ WebInspector.ProfilesPanel.prototype = { profilerWasEnabled: function() { - this.reset(); + if (this._profilerEnabled) + return; + + this._profilerEnabled = true; this.populateInterface(); }, profilerWasDisabled: function() { + if (!this._profilerEnabled) + return; + + this._profilerEnabled = false; this.reset(); }, @@ -230,7 +239,6 @@ WebInspector.ProfilesPanel.prototype = { container.appendChild(part1); var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption); - button.element.addEventListener("click", profileType.buttonClicked.bind(profileType), false); container.appendChild(button.element); var part2 = document.createElement("span"); @@ -429,7 +437,7 @@ WebInspector.ProfilesPanel.prototype = { _updateInterface: function() { // FIXME: Replace ProfileType-specific button visibility changes by a single ProfileType-agnostic "combo-button" visibility change. - if (InspectorBackend.profilerEnabled()) { + if (this._profilerEnabled) { this.enableToggleButton.title = WebInspector.UIString("Profiling enabled. Click to disable."); this.enableToggleButton.toggled = true; for (var typeId in this._profileTypeButtonsByIdMap) @@ -448,14 +456,14 @@ WebInspector.ProfilesPanel.prototype = { _enableProfiling: function() { - if (InspectorBackend.profilerEnabled()) + if (this._profilerEnabled) return; this._toggleProfiling(this.panelEnablerView.alwaysEnabled); }, _toggleProfiling: function(optionalAlways) { - if (InspectorBackend.profilerEnabled()) + if (this._profilerEnabled) InspectorBackend.disableProfiler(true); else InspectorBackend.enableProfiler(!!optionalAlways); diff --git a/WebCore/inspector/front-end/Resource.js b/WebCore/inspector/front-end/Resource.js index 9860300..4ee5f28 100644 --- a/WebCore/inspector/front-end/Resource.js +++ b/WebCore/inspector/front-end/Resource.js @@ -272,22 +272,28 @@ WebInspector.Resource.prototype = { return this._responseReceivedTime - this._startTime; }, - get contentLength() + get resourceSize() { - return this._contentLength || 0; + return this._resourceSize || 0; }, - set contentLength(x) + set resourceSize(x) { - if (this._contentLength === x) + if (this._resourceSize === x) return; - this._contentLength = x; + this._resourceSize = x; if (WebInspector.panels.resources) WebInspector.panels.resources.refreshResource(this); }, + get transferSize() + { + // FIXME: this is wrong for chunked-encoding resources. + return this.cached ? 0 : Number(this.responseHeaders["Content-Length"] || this.resourceSize || 0); + }, + get expectedContentLength() { return this._expectedContentLength || 0; @@ -603,9 +609,15 @@ WebInspector.Resource.CompareByLatency = function(a, b) WebInspector.Resource.CompareBySize = function(a, b) { - return a.contentLength - b.contentLength; + return a.resourceSize - b.resourceSize; } +WebInspector.Resource.CompareByTransferSize = function(a, b) +{ + return a.transferSize - b.transferSize; +} + + WebInspector.Resource.StatusTextForCode = function(code) { return code ? code + " " + WebInspector.Resource.StatusText[code] : ""; diff --git a/WebCore/inspector/front-end/ResourceView.js b/WebCore/inspector/front-end/ResourceView.js index b7b01ac..618a935 100644 --- a/WebCore/inspector/front-end/ResourceView.js +++ b/WebCore/inspector/front-end/ResourceView.js @@ -47,7 +47,7 @@ WebInspector.ResourceView = function(resource) this.tabsElement.appendChild(this.contentTabElement); this.headersTabElement.addEventListener("click", this._selectHeadersTab.bind(this), false); - this.contentTabElement.addEventListener("click", this._selectContentTab.bind(this), false); + this.contentTabElement.addEventListener("click", this.selectContentTab.bind(this), false); this.headersElement = document.createElement("div"); this.headersElement.className = "resource-view-headers"; @@ -155,7 +155,7 @@ WebInspector.ResourceView.prototype = { if (WebInspector.settings.resourceViewTab === "headers") this._selectHeadersTab(); else - this._selectContentTab(); + this.selectContentTab(); } else this._innerSelectContentTab(); }, @@ -169,7 +169,7 @@ WebInspector.ResourceView.prototype = { this.contentElement.addStyleClass("hidden"); }, - _selectContentTab: function() + selectContentTab: function() { WebInspector.settings.resourceViewTab = "content"; this._innerSelectContentTab(); @@ -183,8 +183,17 @@ WebInspector.ResourceView.prototype = { this.headersElement.addStyleClass("hidden"); if ("resize" in this) this.resize(); - if ("contentTabSelected" in this) - this.contentTabSelected(); + this.contentTabSelected(); + }, + + contentTabSelected: function() + { + if (!this._contentPlaceholderElement) { + this._contentPlaceholderElement = document.createElement("div"); + this._contentPlaceholderElement.className = "resource-content-unavailable"; + this._contentPlaceholderElement.textContent = WebInspector.UIString("No Content Available"); + this.contentElement.appendChild(this._contentPlaceholderElement); + } }, _refreshURL: function() diff --git a/WebCore/inspector/front-end/ResourcesPanel.js b/WebCore/inspector/front-end/ResourcesPanel.js index 19325bb..5284e68 100644 --- a/WebCore/inspector/front-end/ResourcesPanel.js +++ b/WebCore/inspector/front-end/ResourcesPanel.js @@ -47,6 +47,7 @@ WebInspector.ResourcesPanel = function() this.reset(); this.filter(this.filterAllElement, false); this.graphsTreeElement.children[0].select(); + this._resourceTrackingEnabled = false; } WebInspector.ResourcesPanel.prototype = { @@ -98,6 +99,7 @@ WebInspector.ResourcesPanel.prototype = { { name: WebInspector.UIString("Sort by Latency"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency, calculator: transferDurationCalculator }, ]; + timeGraphItem.isBarOpaqueAtLeft = false; timeGraphItem.selectedSortingOptionIndex = 1; var sizeGraphItem = new WebInspector.SidebarTreeElement("resources-size-graph-sidebar-item", WebInspector.UIString("Size")); @@ -105,9 +107,11 @@ WebInspector.ResourcesPanel.prototype = { var transferSizeCalculator = new WebInspector.ResourceTransferSizeCalculator(); sizeGraphItem.sortingOptions = [ + { name: WebInspector.UIString("Sort by Transfer Size"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingTransferSize, calculator: transferSizeCalculator }, { name: WebInspector.UIString("Sort by Size"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize, calculator: transferSizeCalculator }, ]; + sizeGraphItem.isBarOpaqueAtLeft = true; sizeGraphItem.selectedSortingOptionIndex = 0; this.graphsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("GRAPHS"), {}, true); @@ -123,6 +127,11 @@ WebInspector.ResourcesPanel.prototype = { this.itemsTreeElement.expand(); }, + get resourceTrackingEnabled() + { + return this._resourceTrackingEnabled; + }, + _createPanelEnabler: function() { var panelEnablerHeading = WebInspector.UIString("You need to enable resource tracking to use this panel."); @@ -279,7 +288,7 @@ WebInspector.ResourcesPanel.prototype = { { if (this.visibleResource) return this.visibleResource._resourcesView; - return InspectorBackend.resourceTrackingEnabled() ? null : this.panelEnablerView; + return this._resourceTrackingEnabled ? null : this.panelEnablerView; }, get sortingFunction() @@ -308,11 +317,13 @@ WebInspector.ResourcesPanel.prototype = { resourceTrackingWasEnabled: function() { + this._resourceTrackingEnabled = true; this.reset(); }, resourceTrackingWasDisabled: function() { + this._resourceTrackingEnabled = false; this.reset(); }, @@ -344,7 +355,7 @@ WebInspector.ResourcesPanel.prototype = { this.summaryBar.reset(); - if (InspectorBackend.resourceTrackingEnabled()) { + if (this._resourceTrackingEnabled) { this.enableToggleButton.title = WebInspector.UIString("Resource tracking enabled. Click to disable."); this.enableToggleButton.toggled = true; this.largerResourcesButton.visible = true; @@ -450,14 +461,16 @@ WebInspector.ResourcesPanel.prototype = { if (oldViewParentNode) newView.show(oldViewParentNode); + + WebInspector.panels.scripts.viewRecreated(oldView, newView); }, - canShowSourceLineForURL: function(url) + canShowSourceLine: function(url, line) { - return !!WebInspector.resourceForURL(url); + return this._resourceTrackingEnabled && !!WebInspector.resourceForURL(url); }, - showSourceLineForURL: function(url, line) + showSourceLine: function(url, line) { this.showResource(WebInspector.resourceForURL(url), line); }, @@ -477,6 +490,7 @@ WebInspector.ResourcesPanel.prototype = { view.show(this.viewsContainerElement); if (line) { + view.selectContentTab(); if (view.revealLine) view.revealLine(line); if (view.highlightLine) @@ -564,7 +578,7 @@ WebInspector.ResourcesPanel.prototype = { var percent = this.calculator.computePercentageFromEventTime(this.mainResourceLoadTime); var loadDivider = document.createElement("div"); - loadDivider.className = "resources-onload-divider"; + loadDivider.className = "resources-event-divider resources-red-divider"; var loadDividerPadding = document.createElement("div"); loadDividerPadding.className = "resources-event-divider-padding"; @@ -579,7 +593,7 @@ WebInspector.ResourcesPanel.prototype = { var percent = this.calculator.computePercentageFromEventTime(this.mainResourceDOMContentTime); var domContentDivider = document.createElement("div"); - domContentDivider.className = "resources-ondomcontent-divider"; + domContentDivider.className = "resources-event-divider resources-blue-divider"; var domContentDividerPadding = document.createElement("div"); domContentDividerPadding.className = "resources-event-divider-padding"; @@ -685,16 +699,18 @@ WebInspector.ResourcesPanel.prototype = { _enableResourceTracking: function() { - if (InspectorBackend.resourceTrackingEnabled()) + if (this._resourceTrackingEnabled) return; this._toggleResourceTracking(this.panelEnablerView.alwaysEnabled); }, _toggleResourceTracking: function(optionalAlways) { - if (InspectorBackend.resourceTrackingEnabled()) { + if (this._resourceTrackingEnabled) { this.largerResourcesButton.visible = false; this.sortingSelectElement.visible = false; + WebInspector.resources = {}; + WebInspector.resourceURLMap = {}; InspectorBackend.disableResourceTracking(true); } else { this.largerResourcesButton.visible = true; @@ -824,18 +840,20 @@ WebInspector.ResourceTimeCalculator.prototype = { computeBarGraphLabels: function(resource) { - var leftLabel = ""; - if (resource.latency > 0) - leftLabel = this.formatValue(resource.latency); - var rightLabel = ""; if (resource.responseReceivedTime !== -1 && resource.endTime !== -1) rightLabel = this.formatValue(resource.endTime - resource.responseReceivedTime); - if (leftLabel && rightLabel) { + var hasLatency = resource.latency > 0; + if (hasLatency) + var leftLabel = this.formatValue(resource.latency); + else + var leftLabel = rightLabel; + + if (hasLatency && rightLabel) { var total = this.formatValue(resource.duration); var tooltip = WebInspector.UIString("%s latency, %s download (%s total)", leftLabel, rightLabel, total); - } else if (leftLabel) + } else if (hasLatency) var tooltip = WebInspector.UIString("%s latency", leftLabel); else if (rightLabel) var tooltip = WebInspector.UIString("%s download", rightLabel); @@ -939,16 +957,39 @@ WebInspector.ResourceTransferSizeCalculator = function() WebInspector.ResourceTransferSizeCalculator.prototype = { computeBarGraphLabels: function(resource) { - const label = this.formatValue(this._value(resource)); - var tooltip = label; + 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: label, right: label, tooltip: tooltip}; + return {left: left, right: right, tooltip: tooltip}; + }, + + computeBarGraphPercentages: function(item) + { + 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}; }, _value: function(resource) { - return resource.contentLength; + return resource.resourceSize; + }, + + _networkBytes: function(resource) + { + return resource.transferSize; }, formatValue: function(value) @@ -1139,6 +1180,11 @@ WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize = function(a, b) return -1 * WebInspector.Resource.CompareBySize(a.resource, b.resource); } +WebInspector.ResourceSidebarTreeElement.CompareByDescendingTransferSize = function(a, b) +{ + return -1 * WebInspector.Resource.CompareByTransferSize(a.resource, b.resource); +} + WebInspector.ResourceSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype; WebInspector.ResourceGraph = function(resource) @@ -1196,8 +1242,15 @@ WebInspector.ResourceGraph.prototype = { const labelPadding = 10; const barRightElementOffsetWidth = this._barRightElement.offsetWidth; const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth; - const rightBarWidth = (barRightElementOffsetWidth - labelPadding); - const leftBarWidth = ((barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding); + + if (this._isBarOpaqueAtLeft) { + var leftBarWidth = barLeftElementOffsetWidth - labelPadding; + var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding; + } else { + var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding; + var rightBarWidth = barRightElementOffsetWidth - labelPadding; + } + const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth; const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth; @@ -1205,8 +1258,22 @@ WebInspector.ResourceGraph.prototype = { const labelAfter = (labelRightElementOffsetWidth > rightBarWidth); const graphElementOffsetWidth = this._graphElement.offsetWidth; + if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10)) + var leftHidden = true; + + if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10)) + var rightHidden = true; + + if (barLeftElementOffsetWidth == barRightElementOffsetWidth) { + // The left/right label data are the same, so a before/after label can be replaced by an on-bar label. + if (labelBefore && !labelAfter) + leftHidden = true; + else if (labelAfter && !labelBefore) + rightHidden = true; + } + if (labelBefore) { - if ((graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10)) + if (leftHidden) this._labelLeftElement.addStyleClass("hidden"); this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%"); this._labelLeftElement.addStyleClass("before"); @@ -1216,7 +1283,7 @@ WebInspector.ResourceGraph.prototype = { } if (labelAfter) { - if ((graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10)) + if (rightHidden) this._labelRightElement.addStyleClass("hidden"); this._labelRightElement.style.setProperty("left", this._percentages.end + "%"); this._labelRightElement.addStyleClass("after"); @@ -1226,7 +1293,7 @@ WebInspector.ResourceGraph.prototype = { } }, - refresh: function(calculator) + refresh: function(calculator, isBarOpaqueAtLeft) { var percentages = calculator.computeBarGraphPercentages(this.resource); var labels = calculator.computeBarGraphLabels(this.resource); @@ -1241,11 +1308,32 @@ WebInspector.ResourceGraph.prototype = { } this._barLeftElement.style.setProperty("left", percentages.start + "%"); - this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%"); - - this._barRightElement.style.setProperty("left", percentages.middle + "%"); this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%"); + if (!isBarOpaqueAtLeft) { + this._barLeftElement.style.setProperty("right", (100 - percentages.end) + "%"); + this._barRightElement.style.setProperty("left", percentages.middle + "%"); + + if (this._isBarOpaqueAtLeft != isBarOpaqueAtLeft) { + this._barLeftElement.addStyleClass("waiting"); + this._barRightElement.removeStyleClass("waiting-right"); + this._labelLeftElement.addStyleClass("waiting"); + this._labelRightElement.removeStyleClass("waiting-right"); + } + } else { + this._barLeftElement.style.setProperty("right", (100 - percentages.middle) + "%"); + this._barRightElement.style.setProperty("left", percentages.start + "%"); + + if (this._isBarOpaqueAtLeft != isBarOpaqueAtLeft) { + this._barLeftElement.removeStyleClass("waiting"); + this._barRightElement.addStyleClass("waiting-right"); + this._labelLeftElement.removeStyleClass("waiting"); + this._labelRightElement.addStyleClass("waiting-right"); + } + } + + this._isBarOpaqueAtLeft = isBarOpaqueAtLeft; + this._labelLeftElement.textContent = labels.left; this._labelRightElement.textContent = labels.right; diff --git a/WebCore/inspector/front-end/Script.js b/WebCore/inspector/front-end/Script.js index e6413a9..79004f3 100644 --- a/WebCore/inspector/front-end/Script.js +++ b/WebCore/inspector/front-end/Script.js @@ -42,9 +42,20 @@ WebInspector.Script = function(sourceID, sourceURL, source, startingLine, errorL var match = pattern.exec(source); if (match) - this.sourceURL = WebInspector.UIString("(program): %s", match[1]); + this.sourceURL = match[1]; } } WebInspector.Script.prototype = { + get linesCount() + { + if (!this.source) + return 0; + this._linesCount = 0; + var lastIndex = this.source.indexOf("\n"); + while (lastIndex !== -1) { + lastIndex = this.source.indexOf("\n", lastIndex + 1) + this._linesCount++; + } + } } diff --git a/WebCore/inspector/front-end/ScriptView.js b/WebCore/inspector/front-end/ScriptView.js index c5a8b81..e55a685 100644 --- a/WebCore/inspector/front-end/ScriptView.js +++ b/WebCore/inspector/front-end/ScriptView.js @@ -33,7 +33,8 @@ WebInspector.ScriptView = function(script) this._frameNeedsSetup = true; this._sourceFrameSetup = false; - this.sourceFrame = new WebInspector.SourceFrame(this.element, this._addBreakpoint.bind(this), this._removeBreakpoint.bind(this)); + var canEditScripts = WebInspector.panels.scripts.canEditScripts(); + this.sourceFrame = new WebInspector.SourceFrame(this.element, this._addBreakpoint.bind(this), this._removeBreakpoint.bind(this), canEditScripts ? this._editLine.bind(this) : null); } WebInspector.ScriptView.prototype = { @@ -52,11 +53,18 @@ WebInspector.ScriptView.prototype = { this.attach(); - this.sourceFrame.setContent("text/javascript", this.script.source); + this.sourceFrame.setContent("text/javascript", this._prependWhitespace(this.script.source)); this._sourceFrameSetup = true; delete this._frameNeedsSetup; }, + _prependWhitespace: function(content) { + var prefix = ""; + for (var i = 0; i < this.script.startingLine - 1; ++i) + prefix += "\n"; + return prefix + content; + }, + attach: function() { if (!this.element.parentNode) @@ -69,6 +77,17 @@ WebInspector.ScriptView.prototype = { WebInspector.panels.scripts.addBreakpoint(breakpoint); }, + _editLine: function(line, newContent) + { + WebInspector.panels.scripts.editScriptLine(this.script.sourceID, line, newContent, this._editLineComplete.bind(this)); + }, + + _editLineComplete: function(newBody) + { + this.script.source = newBody; + this.sourceFrame.updateContent(this._prependWhitespace(newBody)); + }, + // The follow methods are pulled from SourceView, since they are // generic and work with ScriptView just fine. diff --git a/WebCore/inspector/front-end/ScriptsPanel.js b/WebCore/inspector/front-end/ScriptsPanel.js index da24ed2..987bdf2 100644 --- a/WebCore/inspector/front-end/ScriptsPanel.js +++ b/WebCore/inspector/front-end/ScriptsPanel.js @@ -105,6 +105,12 @@ WebInspector.ScriptsPanel = function() this.stepOutButton.appendChild(document.createElement("img")); this.sidebarButtonsElement.appendChild(this.stepOutButton); + this.toggleBreakpointsButton = new WebInspector.StatusBarButton("", "toggle-breakpoints"); + this.toggleBreakpointsButton.addEventListener("click", this._toggleBreakpointsClicked.bind(this), false); + this.sidebarButtonsElement.appendChild(this.toggleBreakpointsButton.element); + // Breakpoints should be activated by default, so emulate a click to toggle on. + this._toggleBreakpointsClicked(); + this.debuggerStatusElement = document.createElement("div"); this.debuggerStatusElement.id = "scripts-debugger-status"; this.sidebarButtonsElement.appendChild(this.debuggerStatusElement); @@ -129,6 +135,7 @@ WebInspector.ScriptsPanel = function() this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane(); this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane(); this.sidebarPanes.breakpoints = new WebInspector.BreakpointsSidebarPane(); + this.sidebarPanes.workers = new WebInspector.WorkersSidebarPane(); for (var pane in this.sidebarPanes) this.sidebarElement.appendChild(this.sidebarPanes[pane].element); @@ -154,10 +161,9 @@ WebInspector.ScriptsPanel = function() this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item"); this.enableToggleButton.addEventListener("click", this._toggleDebugging.bind(this), false); - this.pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3); - this.pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false); - - this._breakpointsURLMap = {}; + this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3); + this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false); + this._pauseOnExceptionButton.state = WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions; this._shortcuts = {}; var handler, shortcut; @@ -191,10 +197,13 @@ WebInspector.ScriptsPanel = function() shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.Semicolon, WebInspector.KeyboardShortcut.Modifiers.Shift, platformSpecificModifier); this._shortcuts[shortcut] = handler; + this._debuggerEnabled = Preferences.debuggerAlwaysEnabled; + if (Preferences.debuggerAlwaysEnabled) + this._attachDebuggerWhenShown = true; this.reset(); } -// Keep these in sync with WebCore::JavaScriptDebugServer +// Keep these in sync with WebCore::ScriptDebugServer WebInspector.ScriptsPanel.PauseOnExceptionsState = { DontPauseOnExceptions : 0, PauseOnAllExceptions : 1, @@ -211,7 +220,7 @@ WebInspector.ScriptsPanel.prototype = { get statusBarItems() { - return [this.enableToggleButton.element, this.pauseOnExceptionButton.element]; + return [this.enableToggleButton.element, this._pauseOnExceptionButton.element]; }, get defaultFocusedElement() @@ -234,16 +243,6 @@ WebInspector.ScriptsPanel.prototype = { this.visibleView.headersVisible = false; this.visibleView.show(this.viewsContainerElement); } - // Hide any views that are visible that are not this panel's current visible view. - // This can happen when a ResourceView is visible in the Resources panel then switched - // to the this panel. - for (var sourceID in this._sourceIDMap) { - var scriptOrResource = this._sourceIDMap[sourceID]; - var view = this._sourceViewForScriptOrResource(scriptOrResource); - if (!view || view === this.visibleView) - continue; - view.visible = false; - } if (this._attachDebuggerWhenShown) { InspectorBackend.enableDebugger(false); delete this._attachDebuggerWhenShown; @@ -252,89 +251,74 @@ WebInspector.ScriptsPanel.prototype = { get searchableViews() { - var views = []; - - const visibleView = this.visibleView; - if (visibleView && visibleView.performSearch) { - visibleView.alreadySearching = true; - views.push(visibleView); - } - - for (var sourceID in this._sourceIDMap) { - var scriptOrResource = this._sourceIDMap[sourceID]; - var view = this._sourceViewForScriptOrResource(scriptOrResource); - if (!view || !view.performSearch || view.alreadySearching) - continue; - - view.alreadySearching = true; - views.push(view); - } - - for (var i = 0; i < views.length; ++i) - delete views[i].alreadySearching; + return [ this.visibleView ]; + }, - return views; + get breakpointsActivated() + { + return this.toggleBreakpointsButton.toggled; }, addScript: function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage) { var script = new WebInspector.Script(sourceID, sourceURL, source, startingLine, errorLine, errorMessage); - - if (sourceURL in WebInspector.resourceURLMap) { - var resource = WebInspector.resourceURLMap[sourceURL]; - resource.addScript(script); - } - - sourceURL = script.sourceURL; - - if (sourceID) - this._sourceIDMap[sourceID] = (resource || script); - - if (sourceURL in this._breakpointsURLMap && sourceID) { - var breakpoints = this._breakpointsURLMap[sourceURL]; - var breakpointsLength = breakpoints.length; - for (var i = 0; i < breakpointsLength; ++i) { - var breakpoint = breakpoints[i]; - - if (startingLine <= breakpoint.line) { - // remove and add the breakpoint, to clean up things like the sidebar - this.removeBreakpoint(breakpoint); - breakpoint.sourceID = sourceID; - this.addBreakpoint(breakpoint); - - if (breakpoint.enabled) - InspectorBackend.addBreakpoint(breakpoint.sourceID, breakpoint.line, breakpoint.condition); + this._sourceIDMap[sourceID] = script; + + var resource = WebInspector.resourceURLMap[sourceURL]; + if (resource) { + if (resource.finished) { + // Resource is finished, bind the script right away. + resource.addScript(script); + this._sourceIDMap[sourceID] = resource; + } else { + // Resource is not finished, bind the script later. + if (!resource._scriptsPendingResourceLoad) { + resource._scriptsPendingResourceLoad = []; + resource.addEventListener("finished", this._resourceLoadingFinished, this); } + resource._scriptsPendingResourceLoad.push(script); } } - this._addScriptToFilesMenu(script); }, - scriptOrResourceForID: function(id) - { - return this._sourceIDMap[id]; - }, - - scriptForURL: function(url) + _resourceLoadingFinished: function(e) { - return this._scriptsForURLsInFilesSelect[url]; + var resource = e.target; + for (var i = 0; i < resource._scriptsPendingResourceLoad.length; ++i) { + // Bind script to resource. + var script = resource._scriptsPendingResourceLoad[i]; + resource.addScript(script); + this._sourceIDMap[script.sourceID] = resource; + + // Remove script from the files list. + script.filesSelectOption.parentElement.removeChild(script.filesSelectOption); + + // Move breakpoints to the resource's frame. + if (script._scriptView) { + var sourceFrame = script._scriptView.sourceFrame; + var resourceFrame = this._sourceFrameForScriptOrResource(resource); + for (var j = 0; j < sourceFrame.breakpoints; ++j) + resourceFrame.addBreakpoint(sourceFrame.breakpoints[j]); + } + } + // Adding first script will add resource. + this._addScriptToFilesMenu(resource._scriptsPendingResourceLoad[0]); + delete resource._scriptsPendingResourceLoad; }, addBreakpoint: function(breakpoint) { + if (!this.breakpointsActivated) + this._toggleBreakpointsClicked(); + this.sidebarPanes.breakpoints.addBreakpoint(breakpoint); var sourceFrame; if (breakpoint.url) { - if (!(breakpoint.url in this._breakpointsURLMap)) - this._breakpointsURLMap[breakpoint.url] = []; - this._breakpointsURLMap[breakpoint.url].unshift(breakpoint); - - if (breakpoint.url in WebInspector.resourceURLMap) { - var resource = WebInspector.resourceURLMap[breakpoint.url]; + var resource = WebInspector.resourceURLMap[breakpoint.url]; + if (resource && resource.finished) sourceFrame = this._sourceFrameForScriptOrResource(resource); - } } if (breakpoint.sourceID && !sourceFrame) { @@ -351,16 +335,10 @@ WebInspector.ScriptsPanel.prototype = { this.sidebarPanes.breakpoints.removeBreakpoint(breakpoint); var sourceFrame; - if (breakpoint.url && breakpoint.url in this._breakpointsURLMap) { - var breakpoints = this._breakpointsURLMap[breakpoint.url]; - breakpoints.remove(breakpoint); - if (!breakpoints.length) - delete this._breakpointsURLMap[breakpoint.url]; - - if (breakpoint.url in WebInspector.resourceURLMap) { - var resource = WebInspector.resourceURLMap[breakpoint.url]; + if (breakpoint.url) { + var resource = WebInspector.resourceURLMap[breakpoint.url]; + if (resource && resource.finished) sourceFrame = this._sourceFrameForScriptOrResource(resource); - } } if (breakpoint.sourceID && !sourceFrame) { @@ -372,6 +350,40 @@ WebInspector.ScriptsPanel.prototype = { sourceFrame.removeBreakpoint(breakpoint); }, + canEditScripts: function() + { + return !!InspectorBackend.editScriptLine; + }, + + editScriptLine: function(sourceID, line, newContent, callback) + { + if (!this.canEditScripts()) + return; + + // Need to clear breakpoints and re-create them later when editing source. + var breakpointsPanel = this.sidebarPanes.breakpoints; + var newBreakpoints = []; + for (var id in breakpointsPanel.breakpoints) { + var breakpoint = breakpointsPanel.breakpoints[id]; + breakpointsPanel.removeBreakpoint(breakpoint); + newBreakpoints.push(breakpoint); + } + + var linesCountToShift = newContent.split("\n").length - 1; + function mycallback(newBody) + { + callback(newBody); + for (var i = 0; i < newBreakpoints.length; ++i) { + var breakpoint = newBreakpoints[i]; + if (breakpoint.line >= line) + breakpoint.line += linesCountToShift; + this.addBreakpoint(breakpoint); + } + }; + var callbackId = WebInspector.Callback.wrap(mycallback.bind(this)) + InspectorBackend.editScriptLine(callbackId, sourceID, line, newContent); + }, + selectedCallFrameId: function() { var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame; @@ -444,22 +456,30 @@ WebInspector.ScriptsPanel.prototype = { debuggerWasEnabled: function() { - this.reset(); + if (this._debuggerEnabled) + return; + + this._debuggerEnabled = true; + this.reset(true); }, debuggerWasDisabled: function() { - this.reset(); + if (!this._debuggerEnabled) + return; + + this._debuggerEnabled = false; + this.reset(true); }, - reset: function() + reset: function(preserveWorkers) { this.visibleView = null; delete this.currentQuery; this.searchCanceled(); - if (!InspectorBackend.debuggerEnabled()) { + if (!this._debuggerEnabled) { this._paused = false; this._waitingToPause = false; this._stepping = false; @@ -471,7 +491,7 @@ WebInspector.ScriptsPanel.prototype = { this._currentBackForwardIndex = -1; this._updateBackAndForwardButtons(); - this._scriptsForURLsInFilesSelect = {}; + this._resourceForURLInFilesSelect = {}; this.filesSelectElement.removeChildren(); this.functionsSelectElement.removeChildren(); this.viewsContainerElement.removeChildren(); @@ -487,6 +507,9 @@ WebInspector.ScriptsPanel.prototype = { this._sourceIDMap = {}; this.sidebarPanes.watchExpressions.refreshExpressions(); + this.sidebarPanes.breakpoints.reset(); + if (!preserveWorkers) + this.sidebarPanes.workers.reset(); }, get visibleView() @@ -508,36 +531,50 @@ WebInspector.ScriptsPanel.prototype = { x.show(this.viewsContainerElement); }, - canShowSourceLineForURL: function(url) + viewRecreated: function(oldView, newView) { - return InspectorBackend.debuggerEnabled() && - !!(WebInspector.resourceForURL(url) || this.scriptForURL(url)); + if (this._visibleView === oldView) + this._visibleView = newView; }, - showSourceLineForURL: function(url, line) + canShowSourceLine: function(url, line) { - var resource = WebInspector.resourceForURL(url); - if (resource) - this.showResource(resource, line); - else - this.showScript(this.scriptForURL(url), line); + if (!this._debuggerEnabled) + return false; + return !!this._scriptOrResourceForURLAndLine(url, line); }, - showScript: function(script, line) + showSourceLine: function(url, line) { - this._showScriptOrResource(script, {line: line, shouldHighlightLine: true}); + var scriptOrResource = this._scriptOrResourceForURLAndLine(url, line); + this._showScriptOrResource(scriptOrResource, {line: line, shouldHighlightLine: true}); }, - showResource: function(resource, line) + _scriptOrResourceForURLAndLine: function(url, line) { - this._showScriptOrResource(resource, {line: line, shouldHighlightLine: true}); + var scriptWithMatchingUrl = null; + for (var sourceID in this._sourceIDMap) { + var scriptOrResource = this._sourceIDMap[sourceID]; + if (scriptOrResource instanceof WebInspector.Script) { + if (scriptOrResource.sourceURL !== url) + continue; + scriptWithMatchingUrl = scriptOrResource; + if (scriptWithMatchingUrl.startingLine <= line && scriptWithMatchingUrl.startingLine + scriptWithMatchingUrl.linesCount > line) + return scriptWithMatchingUrl; + } else { + var resource = scriptOrResource; + if (resource.url === url) + return resource; + } + } + return scriptWithMatchingUrl; }, showView: function(view) { if (!view) return; - this._showScriptOrResource((view.resource || view.script)); + this._showScriptOrResource(view.resource || view.script); }, handleShortcut: function(event) @@ -574,24 +611,10 @@ WebInspector.ScriptsPanel.prototype = { return view.sourceFrame; }, - _sourceViewForScriptOrResource: function(scriptOrResource) - { - if (scriptOrResource instanceof WebInspector.Resource) { - if (!WebInspector.panels.resources) - return null; - return WebInspector.panels.resources.resourceViewForResource(scriptOrResource); - } - if (scriptOrResource instanceof WebInspector.Script) - return this.scriptViewForScript(scriptOrResource); - }, - _sourceFrameForScriptOrResource: function(scriptOrResource) { - if (scriptOrResource instanceof WebInspector.Resource) { - if (!WebInspector.panels.resources) - return null; + if (scriptOrResource instanceof WebInspector.Resource) return WebInspector.panels.resources.sourceFrameForResource(scriptOrResource); - } if (scriptOrResource instanceof WebInspector.Script) return this.sourceFrameForScript(scriptOrResource); }, @@ -610,15 +633,13 @@ WebInspector.ScriptsPanel.prototype = { if (!WebInspector.panels.resources) return null; view = WebInspector.panels.resources.resourceViewForResource(scriptOrResource); - view.headersVisible = false; - - if (scriptOrResource.url in this._breakpointsURLMap) { - var sourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource); - if (sourceFrame && !sourceFrame.breakpoints.length) { - var breakpoints = this._breakpointsURLMap[scriptOrResource.url]; - var breakpointsLength = breakpoints.length; - for (var i = 0; i < breakpointsLength; ++i) - sourceFrame.addBreakpoint(breakpoints[i]); + view.headersVisible = false; + var breakpoints = this.sidebarPanes.breakpoints.breakpoints; + for (var breakpointId in breakpoints) { + var breakpoint = breakpoints[breakpointId]; + if (breakpoint.url === scriptOrResource.url) { + var sourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource); + sourceFrame.addBreakpoint(breakpoint); } } } else if (scriptOrResource instanceof WebInspector.Script) @@ -666,55 +687,50 @@ WebInspector.ScriptsPanel.prototype = { // hasn't been added yet - happens for stepping in evals, // so use the force option to force the script into the menu. if (!option) { - this._addScriptToFilesMenu(scriptOrResource, {force: true}); + this._addScriptToFilesMenu(scriptOrResource, true); option = scriptOrResource.filesSelectOption; } console.assert(option); - } else { - var script = this.scriptForURL(url); - if (script) - option = script.filesSelectOption; - } + } else + option = scriptOrResource.filesSelectOption; if (option) this.filesSelectElement.selectedIndex = option.index; }, - _addScriptToFilesMenu: function(script, options) + _addScriptToFilesMenu: function(script, force) { - var force = options && options.force; - if (!script.sourceURL && !force) return; - if (script.resource && this._scriptsForURLsInFilesSelect[script.sourceURL]) - return; - - this._scriptsForURLsInFilesSelect[script.sourceURL] = script; + if (script.resource) { + if (this._resourceForURLInFilesSelect[script.resource.url]) + return; + this._resourceForURLInFilesSelect[script.resource.url] = script.resource; + } + + var displayName = script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)"); var select = this.filesSelectElement; - var option = document.createElement("option"); - option.representedObject = (script.resource || script); - option.text = (script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)")); + option.representedObject = script.resource || script; + option.url = displayName; + option.startingLine = script.startingLine; + option.text = script.resource || script.startingLine === 1 ? displayName : String.sprintf("%s:%d", displayName, script.startingLine); function optionCompare(a, b) { - var aTitle = a.text.toLowerCase(); - var bTitle = b.text.toLowerCase(); - if (aTitle < bTitle) + if (a.url < b.url) return -1; - else if (aTitle > bTitle) + else if (a.url > b.url) return 1; - var aSourceID = a.representedObject.sourceID; - var bSourceID = b.representedObject.sourceID; - if (aSourceID < bSourceID) + if (typeof a.startingLine !== "number") return -1; - else if (aSourceID > bSourceID) - return 1; - return 0; + if (typeof b.startingLine !== "number") + return -1; + return a.startingLine - b.startingLine; } var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare); @@ -723,7 +739,10 @@ WebInspector.ScriptsPanel.prototype = { else select.insertBefore(option, select.childNodes.item(insertionIndex)); - script.filesSelectOption = option; + if (script.resource) + script.resource.filesSelectOption = option; + else + script.filesSelectOption = option; // Call _showScriptOrResource if the option we just appended ended up being selected. // This will happen for the first item added to the menu. @@ -803,35 +822,32 @@ WebInspector.ScriptsPanel.prototype = { event.preventDefault(); }, - _updatePauseOnExceptionsButton: function() - { - if (InspectorBackend.pauseOnExceptionsState() == WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions) - this.pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions."); - else if (InspectorBackend.pauseOnExceptionsState() == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnAllExceptions) - this.pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions."); - else if (InspectorBackend.pauseOnExceptionsState() == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions) - this.pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions."); - - this.pauseOnExceptionButton.state = InspectorBackend.pauseOnExceptionsState(); - + updatePauseOnExceptionsState: function(pauseOnExceptionsState) + { + if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions) + this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions."); + else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnAllExceptions) + this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions."); + else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions) + this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions."); + + this._pauseOnExceptionButton.state = pauseOnExceptionsState; }, _updateDebuggerButtons: function() { - if (InspectorBackend.debuggerEnabled()) { + if (this._debuggerEnabled) { this.enableToggleButton.title = WebInspector.UIString("Debugging enabled. Click to disable."); this.enableToggleButton.toggled = true; - this.pauseOnExceptionButton.visible = true; + this._pauseOnExceptionButton.visible = true; this.panelEnablerView.visible = false; } else { this.enableToggleButton.title = WebInspector.UIString("Debugging disabled. Click to enable."); this.enableToggleButton.toggled = false; - this.pauseOnExceptionButton.visible = false; + this._pauseOnExceptionButton.visible = false; this.panelEnablerView.visible = true; } - this._updatePauseOnExceptionsButton(); - if (this._paused) { this.pauseButton.addStyleClass("paused"); @@ -897,7 +913,7 @@ WebInspector.ScriptsPanel.prototype = { _enableDebugging: function() { - if (InspectorBackend.debuggerEnabled()) + if (this._debuggerEnabled) return; this._toggleDebugging(this.panelEnablerView.alwaysEnabled); }, @@ -908,7 +924,7 @@ WebInspector.ScriptsPanel.prototype = { this._waitingToPause = false; this._stepping = false; - if (InspectorBackend.debuggerEnabled()) + if (this._debuggerEnabled) InspectorBackend.disableDebugger(true); else InspectorBackend.enableDebugger(!!optionalAlways); @@ -916,8 +932,7 @@ WebInspector.ScriptsPanel.prototype = { _togglePauseOnExceptions: function() { - InspectorBackend.setPauseOnExceptionsState((InspectorBackend.pauseOnExceptionsState() + 1) % this.pauseOnExceptionButton.states); - this._updatePauseOnExceptionsButton(); + InspectorBackend.setPauseOnExceptionsState((this._pauseOnExceptionButton.state + 1) % this._pauseOnExceptionButton.states); }, _togglePause: function() @@ -963,7 +978,23 @@ WebInspector.ScriptsPanel.prototype = { this._clearInterface(); InspectorBackend.stepOutOfFunctionInDebugger(); + }, + + _toggleBreakpointsClicked: function() + { + this.toggleBreakpointsButton.toggled = !this.toggleBreakpointsButton.toggled; + if (this.toggleBreakpointsButton.toggled) { + InspectorBackend.activateBreakpoints(); + this.toggleBreakpointsButton.title = WebInspector.UIString("Deactivate all breakpoints."); + document.getElementById("main-panels").removeStyleClass("breakpoints-deactivated"); + } else { + InspectorBackend.deactivateBreakpoints(); + this.toggleBreakpointsButton.title = WebInspector.UIString("Activate all breakpoints."); + document.getElementById("main-panels").addStyleClass("breakpoints-deactivated"); + } } } WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype; + +WebInspector.didEditScriptLine = WebInspector.Callback.processCallback; diff --git a/WebCore/inspector/front-end/Settings.js b/WebCore/inspector/front-end/Settings.js index e6fc0c3..b9b5f75 100644 --- a/WebCore/inspector/front-end/Settings.js +++ b/WebCore/inspector/front-end/Settings.js @@ -38,7 +38,10 @@ var Preferences = { styleRulesExpandedState: {}, showMissingLocalizedStrings: false, samplingCPUProfiler: false, - showColorNicknames: true + showColorNicknames: true, + debuggerAlwaysEnabled: false, + profilerAlwaysEnabled: false, + auditsPanelEnabled: true } WebInspector.populateFrontendSettings = function(settingsString) @@ -68,6 +71,7 @@ WebInspector.Settings.prototype = { this._installSetting("showInheritedComputedStyleProperties", "show-inherited-computed-style-properties", false); this._installSetting("showUserAgentStyles", "show-user-agent-styles", true); this._installSetting("resourceViewTab", "resource-view-tab", "content"); + this._installSetting("consoleHistory", "console-history", []); this.dispatchEventToListeners("loaded"); }, diff --git a/WebCore/inspector/front-end/SourceCSSTokenizer.js b/WebCore/inspector/front-end/SourceCSSTokenizer.js index 599cfb3..f7d7d51 100644 --- a/WebCore/inspector/front-end/SourceCSSTokenizer.js +++ b/WebCore/inspector/front-end/SourceCSSTokenizer.js @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Thu Jan 28 20:49:22 2010 */ +/* Generated by re2c 0.13.5 on Thu Feb 25 21:44:55 2010 */ /* * Copyright (C) 2009 Google Inc. All rights reserved. * @@ -99,35 +99,35 @@ WebInspector.SourceCSSTokenizer = function() this._valueKeywords = [ "above", "absolute", "activeborder", "activecaption", "afar", "after-white-space", "ahead", "alias", "all", "all-scroll", - "alternate", "always","amharic", "amharic-abegede", "antialiased", "appworkspace", "aqua", "armenian", "auto", "avoid", - "background", "backwards", "baseline", "below", "bidi-override", "black", "blink", "block", "block-axis", "blue", "bold", - "bolder", "border", "border-box", "both", "bottom", "break-all", "break-word", "button", "button-bevel", "buttonface", - "buttonhighlight", "buttonshadow", "buttontext", "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", "cell", - "center", "checkbox", "circle", "cjk-earthly-branch", "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", - "col-resize", "collapse", "compact", "condensed", "contain", "content", "content-box", "context-menu", "continuous", "copy", - "cover", "crop", "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", "decimal-leading-zero", "default", - "default-button", "destination-atop", "destination-in", "destination-out", "destination-over", "disc", "discard", "document", + "alternate", "always","amharic", "amharic-abegede", "antialiased", "appworkspace", "aqua", "arabic-indic", "armenian", + "auto", "avoid", "background", "backwards", "baseline", "below", "bidi-override", "binary", "bengali", "black", "blink", + "block", "block-axis", "blue", "bold", "bolder", "border", "border-box", "both", "bottom", "break-all", "break-word", "button", + "button-bevel", "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", "capitalize", "caps-lock-indicator", + "caption", "captiontext", "caret", "cell", "center", "checkbox", "circle", "cjk-earthly-branch", "cjk-heavenly-stem", "cjk-ideographic", + "clear", "clip", "close-quote", "col-resize", "collapse", "compact", "condensed", "contain", "content", "content-box", "context-menu", + "continuous", "copy", "cover", "crop", "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", "decimal-leading-zero", "default", + "default-button", "destination-atop", "destination-in", "destination-out", "destination-over", "devanagari", "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", "ethiopic-halehame-gez", "ethiopic-halehame-om-et", "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "forwards", "from", "fuchsia", "geometricPrecision", - "georgian", "gray", "graytext", "green", "grey", "groove", "hand", "hangul", "hangul-consonant", "hebrew", "help", "hidden", "hide", - "higher", "highlight", "highlighttext", "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", + "georgian", "gray", "graytext", "green", "grey", "groove", "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", "help", + "hidden", "hide", "higher", "highlight", "highlighttext", "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext", "inherit", "initial", "inline", - "inline-axis", "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", "italic", "justify", "katakana", - "katakana-iroha", "landscape", "large", "larger", "left", "level", "lighter", "lime", "line-through", "linear", "lines", - "list-button", "list-item", "listbox", "listitem", "local", "logical", "loud", "lower", "lower-alpha", "lower-greek", "lower-latin", - "lower-norwegian", "lower-roman", "lowercase", "ltr", "maroon", "match", "media-controls-background", "media-current-time-display", + "inline-axis", "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", "italic", "justify", "kannada", "katakana", + "katakana-iroha", "khmer", "landscape", "lao", "large", "larger", "left", "level", "lighter", "lime", "line-through", "linear", "lines", + "list-button", "list-item", "listbox", "listitem", "local", "logical", "loud", "lower", "lower-alpha", "lower-greek", "lower-hexadecimal", "lower-latin", + "lower-norwegian", "lower-roman", "lowercase", "ltr", "malayalam", "maroon", "match", "media-controls-background", "media-current-time-display", "media-fullscreen-button", "media-mute-button", "media-play-button", "media-return-to-realtime-button", "media-rewind-button", "media-seek-back-button", "media-seek-forward-button", "media-slider", "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", "media-volume-slider-container", "media-volume-sliderthumb", "medium", "menu", "menulist", "menulist-button", - "menulist-text", "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", "mix", "monospace", "move", "multiple", - "n-resize", "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none", - "normal", "not-allowed", "nowrap", "ns-resize", "nw-resize", "nwse-resize", "oblique", "olive", "open-quote", "optimizeLegibility", - "optimizeSpeed", "orange", "oromo", "outset", "outside", "overlay", "overline", "padding", "padding-box", "painted", "paused", - "plus-darker", "plus-lighter", "pointer", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "purple", + "menulist-text", "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", "mix", "mongolian", "monospace", "move", "multiple", + "myanmar", "n-resize", "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none", + "normal", "not-allowed", "nowrap", "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "olive", "open-quote", "optimizeLegibility", + "optimizeSpeed", "orange", "oriya", "oromo", "outset", "outside", "overlay", "overline", "padding", "padding-box", "painted", "paused", + "persian", "plus-darker", "plus-lighter", "pointer", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "purple", "push-button", "radio", "read-only", "read-write", "read-write-plaintext-only", "red", "relative", "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button", @@ -136,15 +136,15 @@ WebInspector.SourceCSSTokenizer = function() "small", "small-caps", "small-caption", "smaller", "solid", "somali", "source-atop", "source-in", "source-out", "source-over", "space", "square", "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub", "subpixel-antialiased", "super", "sw-resize", "table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group", - "table-row", "table-row-group", "teal", "text", "text-bottom", "text-top", "textarea", "textfield", "thick", "thin", "threeddarkshadow", - "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "tigre", "tigrinya-er", "tigrinya-er-abegede", "tigrinya-et", - "tigrinya-et-abegede", "to", "top", "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", "upper-alpha", "upper-greek", - "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "url", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", - "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", "window", "windowframe", "windowtext", "x-large", "x-small", - "xor", "xx-large", "xx-small", "yellow", "-wap-marquee", "-webkit-activelink", "-webkit-auto", "-webkit-baseline-middle", "-webkit-body", - "-webkit-box", "-webkit-center", "-webkit-control", "-webkit-focus-ring-color", "-webkit-grab", "-webkit-grabbing", "-webkit-gradient", "-webkit-inline-box", - "-webkit-left", "-webkit-link", "-webkit-marquee", "-webkit-mini-control", "-webkit-nowrap", "-webkit-right", "-webkit-small-control", - "-webkit-text", "-webkit-xxx-large", "-webkit-zoom-in", "-webkit-zoom-out", + "table-row", "table-row-group", "teal", "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", "thick", "thin", + "threeddarkshadow", "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", "tigrinya-er-abegede", + "tigrinya-et", "tigrinya-et-abegede", "to", "top", "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", "upper-alpha", "upper-greek", + "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", "vertical", "vertical-text", "visible", + "visibleFill", "visiblePainted", "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", "window", "windowframe", "windowtext", + "x-large", "x-small", "xor", "xx-large", "xx-small", "yellow", "-wap-marquee", "-webkit-activelink", "-webkit-auto", "-webkit-baseline-middle", + "-webkit-body", "-webkit-box", "-webkit-center", "-webkit-control", "-webkit-focus-ring-color", "-webkit-grab", "-webkit-grabbing", + "-webkit-gradient", "-webkit-inline-box", "-webkit-left", "-webkit-link", "-webkit-marquee", "-webkit-mini-control", "-webkit-nowrap", "-webkit-right", + "-webkit-small-control", "-webkit-text", "-webkit-xxx-large", "-webkit-zoom-in", "-webkit-zoom-out", ].keySet(); this._mediaTypes = ["all", "aural", "braille", "embossed", "handheld", "import", "print", "projection", "screen", "tty", "tv"].keySet(); @@ -183,7 +183,7 @@ WebInspector.SourceCSSTokenizer.prototype = { _isPropertyValue: function() { - return this._parseCondition === this._parseConditions.PROPERTY_VALUE || this._parseCondition === this._parseConditions.AT_RULE; + return this._condition.parseCondition === this._parseConditions.PROPERTY_VALUE || this._condition.parseCondition === this._parseConditions.AT_RULE; }, nextToken: function(cursor) @@ -423,18 +423,18 @@ case 31: case 32: { var token = this._line.substring(cursorOnEnter, cursor); - if (this._parseCondition === this._parseConditions.INITIAL) { + if (this._condition.parseCondition === this._parseConditions.INITIAL) { if (token === "@import" || token === "@media") { this.tokenType = "css-at-rule"; - this._parseCondition = this._parseConditions.AT_RULE; + this._condition.parseCondition = this._parseConditions.AT_RULE; } else if (token.indexOf("@") === 0) this.tokenType = "css-at-rule"; else this.tokenType = "css-selector"; } - else if (this._parseCondition === this._parseConditions.AT_RULE && token in this._mediaTypes) + else if (this._condition.parseCondition === this._parseConditions.AT_RULE && token in this._mediaTypes) this.tokenType = "css-keyword"; - else if (this._parseCondition === this._parseConditions.PROPERTY && token in this._propertyKeywords) + else if (this._condition.parseCondition === this._parseConditions.PROPERTY && token in this._propertyKeywords) this.tokenType = "css-property"; else if (this._isPropertyValue() && token in this._valueKeywords) this.tokenType = "css-keyword"; @@ -647,35 +647,35 @@ case 40: ++cursor; { this.tokenType = null; - if (this._parseCondition === this._parseConditions.PROPERTY) - this._parseCondition = this._parseConditions.PROPERTY_VALUE; + if (this._condition.parseCondition === this._parseConditions.PROPERTY) + this._condition.parseCondition = this._parseConditions.PROPERTY_VALUE; return cursor; } case 42: ++cursor; { this.tokenType = null; - if (this._parseCondition === this._parseConditions.AT_RULE) - this._parseCondition = this._parseConditions.INITIAL; + if (this._condition.parseCondition === this._parseConditions.AT_RULE) + this._condition.parseCondition = this._parseConditions.INITIAL; else - this._parseCondition = this._parseConditions.PROPERTY; + this._condition.parseCondition = this._parseConditions.PROPERTY; return cursor; } case 44: ++cursor; { this.tokenType = null; - if (this._parseCondition === this._parseConditions.AT_RULE) - this._parseCondition = this._parseConditions.INITIAL; + if (this._condition.parseCondition === this._parseConditions.AT_RULE) + this._condition.parseCondition = this._parseConditions.INITIAL; else - this._parseCondition = this._parseConditions.PROPERTY; + this._condition.parseCondition = this._parseConditions.PROPERTY; return cursor; } case 46: ++cursor; { this.tokenType = null; - this._parseCondition = this._parseConditions.INITIAL; + this._condition.parseCondition = this._parseConditions.INITIAL; return cursor; } case 48: diff --git a/WebCore/inspector/front-end/SourceCSSTokenizer.re2js b/WebCore/inspector/front-end/SourceCSSTokenizer.re2js index ac22bd4..6ba9f60 100644 --- a/WebCore/inspector/front-end/SourceCSSTokenizer.re2js +++ b/WebCore/inspector/front-end/SourceCSSTokenizer.re2js @@ -182,7 +182,7 @@ WebInspector.SourceCSSTokenizer.prototype = { _isPropertyValue: function() { - return this._parseCondition === this._parseConditions.PROPERTY_VALUE || this._parseCondition === this._parseConditions.AT_RULE; + return this._condition.parseCondition === this._parseConditions.PROPERTY_VALUE || this._condition.parseCondition === this._parseConditions.AT_RULE; }, nextToken: function(cursor) @@ -244,35 +244,35 @@ WebInspector.SourceCSSTokenizer.prototype = { <INITIAL> OpenCurlyBracket { this.tokenType = null; - if (this._parseCondition === this._parseConditions.AT_RULE) - this._parseCondition = this._parseConditions.INITIAL; + if (this._condition.parseCondition === this._parseConditions.AT_RULE) + this._condition.parseCondition = this._parseConditions.INITIAL; else - this._parseCondition = this._parseConditions.PROPERTY; + this._condition.parseCondition = this._parseConditions.PROPERTY; return cursor; } <INITIAL> CloseCurlyBracket { this.tokenType = null; - this._parseCondition = this._parseConditions.INITIAL; + this._condition.parseCondition = this._parseConditions.INITIAL; return cursor; } <INITIAL> Colon { this.tokenType = null; - if (this._parseCondition === this._parseConditions.PROPERTY) - this._parseCondition = this._parseConditions.PROPERTY_VALUE; + if (this._condition.parseCondition === this._parseConditions.PROPERTY) + this._condition.parseCondition = this._parseConditions.PROPERTY_VALUE; return cursor; } <INITIAL> Semicolon { this.tokenType = null; - if (this._parseCondition === this._parseConditions.AT_RULE) - this._parseCondition = this._parseConditions.INITIAL; + if (this._condition.parseCondition === this._parseConditions.AT_RULE) + this._condition.parseCondition = this._parseConditions.INITIAL; else - this._parseCondition = this._parseConditions.PROPERTY; + this._condition.parseCondition = this._parseConditions.PROPERTY; return cursor; } @@ -288,18 +288,18 @@ WebInspector.SourceCSSTokenizer.prototype = { <INITIAL> Identifier { var token = this._line.substring(cursorOnEnter, cursor); - if (this._parseCondition === this._parseConditions.INITIAL) { + if (this._condition.parseCondition === this._parseConditions.INITIAL) { if (token === "@import" || token === "@media") { this.tokenType = "css-at-rule"; - this._parseCondition = this._parseConditions.AT_RULE; + this._condition.parseCondition = this._parseConditions.AT_RULE; } else if (token.indexOf("@") === 0) this.tokenType = "css-at-rule"; else this.tokenType = "css-selector"; } - else if (this._parseCondition === this._parseConditions.AT_RULE && token in this._mediaTypes) + else if (this._condition.parseCondition === this._parseConditions.AT_RULE && token in this._mediaTypes) this.tokenType = "css-keyword"; - else if (this._parseCondition === this._parseConditions.PROPERTY && token in this._propertyKeywords) + else if (this._condition.parseCondition === this._parseConditions.PROPERTY && token in this._propertyKeywords) this.tokenType = "css-property"; else if (this._isPropertyValue() && token in this._valueKeywords) this.tokenType = "css-keyword"; diff --git a/WebCore/inspector/front-end/SourceFrame.js b/WebCore/inspector/front-end/SourceFrame.js index 799628e..99280fc 100644 --- a/WebCore/inspector/front-end/SourceFrame.js +++ b/WebCore/inspector/front-end/SourceFrame.js @@ -28,7 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.SourceFrame = function(parentElement, addBreakpointDelegate, removeBreakpointDelegate) +WebInspector.SourceFrame = function(parentElement, addBreakpointDelegate, removeBreakpointDelegate, editDelegate) { this._parentElement = parentElement; @@ -45,6 +45,7 @@ WebInspector.SourceFrame = function(parentElement, addBreakpointDelegate, remove this._addBreakpointDelegate = addBreakpointDelegate; this._removeBreakpointDelegate = removeBreakpointDelegate; + this._editDelegate = editDelegate; this._popoverObjectGroup = "popover"; } @@ -143,6 +144,11 @@ WebInspector.SourceFrame.prototype = { this._createViewerIfNeeded(); }, + updateContent: function(content) + { + this._textModel.setText(null, content); + }, + highlightLine: function(line) { if (this._textViewer) @@ -192,6 +198,8 @@ WebInspector.SourceFrame.prototype = { delete this._lineToHighlight; } this._textViewer.endUpdates(); + if (this._editDelegate) + this._textViewer.editCallback = this._editDelegate; }, findSearchMatches: function(query) @@ -288,8 +296,6 @@ WebInspector.SourceFrame.prototype = { if (!this._executionLine) return; - this._drawProgramCounterImageIfNeeded(); - if (this._executionLine < this._textModel.linesCount) this._textViewer.addDecoration(this._executionLine - 1, "webkit-execution-line"); }, @@ -369,7 +375,6 @@ WebInspector.SourceFrame.prototype = { this._textModel.setAttribute(lineNumber, "breakpoint", breakpoint); breakpoint.sourceText = this._textModel.line(breakpoint.line - 1); - this._drawBreakpointImagesIfNeeded(); this._textViewer.beginUpdates(); this._textViewer.addDecoration(lineNumber, "webkit-breakpoint"); @@ -393,9 +398,10 @@ WebInspector.SourceFrame.prototype = { _contextMenu: function(event) { - if (event.target.className !== "webkit-line-number") + var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number"); + if (!target) return; - var row = event.target.parentElement; + var row = target.parentElement; var lineNumber = row.lineNumber; var contextMenu = new WebInspector.ContextMenu(); @@ -435,18 +441,22 @@ WebInspector.SourceFrame.prototype = { { this._resetHoverTimer(); this._hidePopup(); - if (event.button != 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) + if (event.button != 0 || event.altKey || event.ctrlKey || event.metaKey) return; - if (event.target.className !== "webkit-line-number") + var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number"); + if (!target) return; - var row = event.target.parentElement; + var row = target.parentElement; var lineNumber = row.lineNumber; var breakpoint = this._textModel.getAttribute(lineNumber, "breakpoint"); - if (breakpoint) - this._removeBreakpointDelegate(breakpoint); - else if (this._addBreakpointDelegate) + if (breakpoint) { + if (event.shiftKey) + breakpoint.enabled = !breakpoint.enabled; + else + this._removeBreakpointDelegate(breakpoint); + } else this._addBreakpointDelegate(lineNumber + 1); event.preventDefault(); }, @@ -454,11 +464,10 @@ WebInspector.SourceFrame.prototype = { _mouseMove: function(event) { // Pretend that nothing has happened. - if (this._hoverElement === event.target) + if (this._hoverElement === event.target || event.target.hasStyleClass("source-frame-eval-expression")) return; this._resetHoverTimer(); - // User has 500ms to reach the popup. if (this._popup) { var self = this; @@ -483,7 +492,7 @@ WebInspector.SourceFrame.prototype = { } else if (!this._hoverElement.hasStyleClass("webkit-javascript-ident")) return; - const toolTipDelay = 1500; + const toolTipDelay = this._popup ? 600 : 1000; this._hoverTimer = setTimeout(this._mouseHover.bind(this, this._hoverElement), toolTipDelay); }, @@ -497,11 +506,22 @@ WebInspector.SourceFrame.prototype = { _hidePopup: function() { - if (this._popup) { - this._popup.hide(); - delete this._popup; - InspectorBackend.releaseWrapperObjectGroup(0, this._popoverObjectGroup); + if (!this._popup) + return; + + // Replace higlight element with its contents inplace. + var parentElement = this._popup.highlightElement.parentElement; + var child = this._popup.highlightElement.firstChild; + while (child) { + var nextSibling = child.nextSibling; + parentElement.insertBefore(child, this._popup.highlightElement); + child = nextSibling; } + parentElement.removeChild(this._popup.highlightElement); + + this._popup.hide(); + delete this._popup; + InspectorBackend.releaseWrapperObjectGroup(0, this._popoverObjectGroup); }, _mouseHover: function(element) @@ -515,27 +535,26 @@ WebInspector.SourceFrame.prototype = { if (!lineRow) return; - // Find text offset of the hovered node (iterate over text nodes until we hit ours). - var offset = 0; - var node = lineRow.lastChild.traverseNextTextNode(lineRow.lastChild); - while (node && node !== element.firstChild) { - offset += node.nodeValue.length; - node = node.traverseNextTextNode(lineRow.lastChild); + // Collect tokens belonging to evaluated exression. + var tokens = [ element ]; + var token = element.previousSibling; + while (token && (token.className === "webkit-javascript-ident" || token.className === "webkit-javascript-keyword" || token.textContent.trim() === ".")) { + tokens.push(token); + token = token.previousSibling; } + tokens.reverse(); - // Imagine that the line is "foo(A.B.C.D)" and we hit C. Following code goes through following steps: - // "foo(A.B.C" -> "C.B.A(oof" -> "C.B.A" -> "A.B.C" (target eval expression). - var lineNumber = lineRow.lineNumber; - var prefix = this._textModel.line(lineNumber).substring(0, offset + element.textContent.length); - var reversedPrefix = prefix.split("").reverse().join(""); - var match = /[a-zA-Z\x80-\xFF\_$0-9.]+/.exec(reversedPrefix); - if (!match) - return; - var expression = match[0].split("").reverse().join(""); - this._showPopup(element, expression); + // Wrap them with highlight element. + var parentElement = element.parentElement; + var nextElement = element.nextSibling; + var container = document.createElement("span"); + for (var i = 0; i < tokens.length; ++i) + container.appendChild(tokens[i]); + parentElement.insertBefore(container, nextElement); + this._showPopup(container); }, - _showPopup: function(element, expression) + _showPopup: function(element) { function killHidePopupTimer() { @@ -549,20 +568,6 @@ WebInspector.SourceFrame.prototype = { } } - function showTextPopup(text) - { - if (!WebInspector.panels.scripts.paused) - return; - - var popupContentElement = document.createElement("span"); - popupContentElement.className = "monospace"; - popupContentElement.style.whiteSpace = "pre"; - popupContentElement.textContent = text; - this._popup = new WebInspector.Popover(popupContentElement); - this._popup.show(element); - popupContentElement.addEventListener("mousemove", killHidePopupTimer.bind(this), true); - } - function showObjectPopup(result) { if (!WebInspector.panels.scripts.paused) @@ -595,6 +600,8 @@ WebInspector.SourceFrame.prototype = { const popupHeight = 250; this._popup.show(element, popupWidth, popupHeight); } + this._popup.highlightElement = element; + this._popup.highlightElement.addStyleClass("source-frame-eval-expression"); popupContentElement.addEventListener("mousemove", killHidePopupTimer.bind(this), true); } @@ -606,7 +613,7 @@ WebInspector.SourceFrame.prototype = { return; showObjectPopup.call(this, result); } - WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, this._popoverObjectGroup, evaluateCallback.bind(this)); + WebInspector.panels.scripts.evaluateInSelectedCallFrame(element.textContent, false, this._popoverObjectGroup, evaluateCallback.bind(this)); }, _editBreakpointCondition: function(breakpoint) @@ -695,134 +702,6 @@ WebInspector.SourceFrame.prototype = { { if (this._textViewer) this._textViewer.resize(); - }, - - _drawProgramCounterInContext: function(ctx, glow) - { - if (glow) - ctx.save(); - - ctx.beginPath(); - ctx.moveTo(17, 2); - ctx.lineTo(19, 2); - ctx.lineTo(19, 0); - ctx.lineTo(21, 0); - ctx.lineTo(26, 5.5); - ctx.lineTo(21, 11); - ctx.lineTo(19, 11); - ctx.lineTo(19, 9); - ctx.lineTo(17, 9); - ctx.closePath(); - ctx.fillStyle = "rgb(142, 5, 4)"; - - if (glow) { - ctx.shadowBlur = 4; - ctx.shadowColor = "rgb(255, 255, 255)"; - ctx.shadowOffsetX = -1; - ctx.shadowOffsetY = 0; - } - - ctx.fill(); - ctx.fill(); // Fill twice to get a good shadow and darker anti-aliased pixels. - - if (glow) - ctx.restore(); - }, - - _drawProgramCounterImageIfNeeded: function() - { - if (!this._needsProgramCounterImage) - return; - - var ctx = document.getCSSCanvasContext("2d", "program-counter", 26, 11); - ctx.clearRect(0, 0, 26, 11); - this._drawProgramCounterInContext(ctx, true); - - delete this._needsProgramCounterImage; - }, - - _drawBreakpointImagesIfNeeded: function(conditional) - { - if (!this._needsBreakpointImages) - return; - - function drawBreakpoint(ctx, disabled, conditional) - { - ctx.beginPath(); - ctx.moveTo(0, 2); - ctx.lineTo(2, 0); - ctx.lineTo(21, 0); - ctx.lineTo(26, 5.5); - ctx.lineTo(21, 11); - ctx.lineTo(2, 11); - ctx.lineTo(0, 9); - ctx.closePath(); - ctx.fillStyle = conditional ? "rgb(217, 142, 1)" : "rgb(1, 142, 217)"; - ctx.strokeStyle = conditional ? "rgb(205, 103, 0)" : "rgb(0, 103, 205)"; - ctx.lineWidth = 3; - ctx.fill(); - ctx.save(); - ctx.clip(); - ctx.stroke(); - ctx.restore(); - - if (!disabled) - return; - - ctx.save(); - ctx.globalCompositeOperation = "destination-out"; - ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; - ctx.fillRect(0, 0, 26, 11); - ctx.restore(); - } - - - // Unconditional breakpoints. - - var ctx = document.getCSSCanvasContext("2d", "breakpoint", 26, 11); - ctx.clearRect(0, 0, 26, 11); - drawBreakpoint(ctx); - - var ctx = document.getCSSCanvasContext("2d", "breakpoint-program-counter", 26, 11); - ctx.clearRect(0, 0, 26, 11); - drawBreakpoint(ctx); - ctx.clearRect(20, 0, 6, 11); - this._drawProgramCounterInContext(ctx, true); - - var ctx = document.getCSSCanvasContext("2d", "breakpoint-disabled", 26, 11); - ctx.clearRect(0, 0, 26, 11); - drawBreakpoint(ctx, true); - - var ctx = document.getCSSCanvasContext("2d", "breakpoint-disabled-program-counter", 26, 11); - ctx.clearRect(0, 0, 26, 11); - drawBreakpoint(ctx, true); - ctx.clearRect(20, 0, 6, 11); - this._drawProgramCounterInContext(ctx, true); - - - // Conditional breakpoints. - - var ctx = document.getCSSCanvasContext("2d", "breakpoint-conditional", 26, 11); - ctx.clearRect(0, 0, 26, 11); - drawBreakpoint(ctx, false, true); - - var ctx = document.getCSSCanvasContext("2d", "breakpoint-conditional-program-counter", 26, 11); - ctx.clearRect(0, 0, 26, 11); - drawBreakpoint(ctx, false, true); - ctx.clearRect(20, 0, 6, 11); - this._drawProgramCounterInContext(ctx, true); - - var ctx = document.getCSSCanvasContext("2d", "breakpoint-disabled-conditional", 26, 11); - ctx.clearRect(0, 0, 26, 11); - drawBreakpoint(ctx, true, true); - - var ctx = document.getCSSCanvasContext("2d", "breakpoint-disabled-conditional-program-counter", 26, 11); - ctx.clearRect(0, 0, 26, 11); - drawBreakpoint(ctx, true, true); - ctx.clearRect(20, 0, 6, 11); - this._drawProgramCounterInContext(ctx, true); - - delete this._needsBreakpointImages; } } diff --git a/WebCore/inspector/front-end/SourceHTMLTokenizer.js b/WebCore/inspector/front-end/SourceHTMLTokenizer.js index 3c9bd8c..cfbc44f 100644 --- a/WebCore/inspector/front-end/SourceHTMLTokenizer.js +++ b/WebCore/inspector/front-end/SourceHTMLTokenizer.js @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Mon Feb 15 19:30:21 2010 */ +/* Generated by re2c 0.13.5 on Thu Feb 25 21:44:55 2010 */ /* * Copyright (C) 2009 Google Inc. All rights reserved. * @@ -71,31 +71,43 @@ WebInspector.SourceHTMLTokenizer = function() }; this.initialCondition = { lexCondition: this._lexConditions.INITIAL, parseCondition: this._parseConditions.INITIAL }; + this.condition = this.initialCondition; } WebInspector.SourceHTMLTokenizer.prototype = { + set line(line) { + if (this._internalJavaScriptTokenizer) { + var match = /<\/script/i.exec(line); + if (match) { + this._internalJavaScriptTokenizer.line = line.substring(0, match.index); + } else + this._internalJavaScriptTokenizer.line = line; + } + this._line = line; + }, + _isExpectingAttribute: function() { - return this._parseCondition & this._parseConditions.ATTRIBUTE; + return this._condition.parseCondition & this._parseConditions.ATTRIBUTE; }, _isExpectingAttributeValue: function() { - return this._parseCondition & this._parseConditions.ATTRIBUTE_VALUE; + return this._condition.parseCondition & this._parseConditions.ATTRIBUTE_VALUE; }, _setExpectingAttribute: function() { if (this._isExpectingAttributeValue()) - this._parseCondition ^= this._parseConditions.ATTRIBUTE_VALUE; - this._parseCondition |= this._parseConditions.ATTRIBUTE; + this._condition.parseCondition ^= this._parseConditions.ATTRIBUTE_VALUE; + this._condition.parseCondition |= this._parseConditions.ATTRIBUTE; }, _setExpectingAttributeValue: function() { if (this._isExpectingAttribute()) - this._parseCondition ^= this._parseConditions.ATTRIBUTE; - this._parseCondition |= this._parseConditions.ATTRIBUTE_VALUE; + this._condition.parseCondition ^= this._parseConditions.ATTRIBUTE; + this._condition.parseCondition |= this._parseConditions.ATTRIBUTE_VALUE; }, _stringToken: function(cursor, stringEnds) @@ -112,8 +124,8 @@ WebInspector.SourceHTMLTokenizer.prototype = { _attrValueTokenType: function() { - if (this._parseCondition & this._parseConditions.LINKIFY) { - if (this._parseCondition & this._parseConditions.A_NODE) + if (this._condition.parseCondition & this._parseConditions.LINKIFY) { + if (this._condition.parseCondition & this._parseConditions.A_NODE) return "html-external-link"; return "html-resource-link"; } @@ -122,6 +134,20 @@ WebInspector.SourceHTMLTokenizer.prototype = { nextToken: function(cursor) { + if (this._internalJavaScriptTokenizer) { + // Re-set line to force </script> detection first. + this.line = this._line; + if (cursor !== this._internalJavaScriptTokenizer._line.length) { + // Tokenizer is stateless, so restore its condition before tokenizing and save it after. + this._internalJavaScriptTokenizer.condition = this._condition.internalJavaScriptTokenizerCondition; + var result = this._internalJavaScriptTokenizer.nextToken(cursor); + this.tokenType = this._internalJavaScriptTokenizer.tokenType; + this._condition.internalJavaScriptTokenizerCondition = this._internalJavaScriptTokenizer.condition; + return result; + } else if (cursor !== this._line.length) + delete this._internalJavaScriptTokenizer; + } + var cursorOnEnter = cursor; var gotoCase = 1; while (1) { @@ -305,14 +331,14 @@ case 39: case 40: this.setLexCondition(this._lexConditions.TAG); { - if (this._parseCondition & this._parseConditions.SCRIPT) { + if (this._condition.parseCondition & this._parseConditions.SCRIPT) { // Do not tokenize script tag contents, keep lexer state although processing "<". this.setLexCondition(this._lexConditions.INITIAL); this.tokenType = null; return cursor; } - this._parseCondition = this._parseConditions.INITIAL; + this._condition.parseCondition = this._parseConditions.INITIAL; this.tokenType = "html-tag"; return cursor; } @@ -413,14 +439,14 @@ case 66: ++cursor; this.setLexCondition(this._lexConditions.TAG); { - if (this._parseCondition & this._parseConditions.SCRIPT) { + if (this._condition.parseCondition & this._parseConditions.SCRIPT) { // Do not tokenize script tag contents, keep lexer state although processing "<". this.setLexCondition(this._lexConditions.INITIAL); this.tokenType = null; return cursor; } this.tokenType = "html-tag"; - this._parseCondition = this._parseConditions.SCRIPT; + this._condition.parseCondition = this._parseConditions.SCRIPT; this._setExpectingAttribute(); return cursor; } @@ -449,7 +475,7 @@ case 73: this.setLexCondition(this._lexConditions.TAG); { this.tokenType = "html-tag"; - this._parseCondition = this._parseConditions.INITIAL; + this._condition.parseCondition = this._parseConditions.INITIAL; return cursor; } /* *********************************** */ @@ -527,26 +553,26 @@ case this.case_TAG: { gotoCase = 109; continue; }; case 89: { - if (this._parseCondition === this._parseConditions.SCRIPT) { + if (this._condition.parseCondition === this._parseConditions.SCRIPT) { // Fall through if expecting attributes. this.tokenType = null; return cursor; } - if (this._parseCondition === this._parseConditions.INITIAL) { + if (this._condition.parseCondition === this._parseConditions.INITIAL) { this.tokenType = "html-tag"; this._setExpectingAttribute(); var token = this._line.substring(cursorOnEnter, cursor); if (token === "a") - this._parseCondition |= this._parseConditions.A_NODE; - else if (this._parseCondition & this._parseConditions.A_NODE) - this._parseCondition ^= this._parseConditions.A_NODE; + this._condition.parseCondition |= this._parseConditions.A_NODE; + else if (this._condition.parseCondition & this._parseConditions.A_NODE) + this._condition.parseCondition ^= this._parseConditions.A_NODE; } else if (this._isExpectingAttribute()) { var token = this._line.substring(cursorOnEnter, cursor); if (token === "href" || token === "src") - this._parseCondition |= this._parseConditions.LINKIFY; - else if (this._parseCondition |= this._parseConditions.LINKIFY) - this._parseCondition ^= this._parseConditions.LINKIFY; + this._condition.parseCondition |= this._parseConditions.LINKIFY; + else if (this._condition.parseCondition |= this._parseConditions.LINKIFY) + this._condition.parseCondition ^= this._parseConditions.LINKIFY; this.tokenType = "html-attribute-name"; } else if (this._isExpectingAttributeValue()) this.tokenType = this._attrValueTokenType(); @@ -577,14 +603,17 @@ case 96: ++cursor; this.setLexCondition(this._lexConditions.INITIAL); { - if (this._parseCondition & this._parseConditions.SCRIPT) { + this.tokenType = "html-tag"; + if (this._condition.parseCondition & this._parseConditions.SCRIPT) { + if (!this._internalJavaScriptTokenizer) { + this._internalJavaScriptTokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/javascript"); + this._condition.internalJavaScriptTokenizerCondition = this._internalJavaScriptTokenizer.initialCondition; + } // Do not tokenize script tag contents. - this.tokenType = null; return cursor; } - this._parseCondition = this._parseConditions.INITIAL; - this.tokenType = "html-tag"; + this._condition.parseCondition = this._parseConditions.INITIAL; return cursor; } case 98: diff --git a/WebCore/inspector/front-end/SourceHTMLTokenizer.re2js b/WebCore/inspector/front-end/SourceHTMLTokenizer.re2js index cfa8834..44c62b3 100644 --- a/WebCore/inspector/front-end/SourceHTMLTokenizer.re2js +++ b/WebCore/inspector/front-end/SourceHTMLTokenizer.re2js @@ -70,31 +70,43 @@ WebInspector.SourceHTMLTokenizer = function() }; this.initialCondition = { lexCondition: this._lexConditions.INITIAL, parseCondition: this._parseConditions.INITIAL }; + this.condition = this.initialCondition; } WebInspector.SourceHTMLTokenizer.prototype = { + set line(line) { + if (this._internalJavaScriptTokenizer) { + var match = /<\/script/i.exec(line); + if (match) { + this._internalJavaScriptTokenizer.line = line.substring(0, match.index); + } else + this._internalJavaScriptTokenizer.line = line; + } + this._line = line; + }, + _isExpectingAttribute: function() { - return this._parseCondition & this._parseConditions.ATTRIBUTE; + return this._condition.parseCondition & this._parseConditions.ATTRIBUTE; }, _isExpectingAttributeValue: function() { - return this._parseCondition & this._parseConditions.ATTRIBUTE_VALUE; + return this._condition.parseCondition & this._parseConditions.ATTRIBUTE_VALUE; }, _setExpectingAttribute: function() { if (this._isExpectingAttributeValue()) - this._parseCondition ^= this._parseConditions.ATTRIBUTE_VALUE; - this._parseCondition |= this._parseConditions.ATTRIBUTE; + this._condition.parseCondition ^= this._parseConditions.ATTRIBUTE_VALUE; + this._condition.parseCondition |= this._parseConditions.ATTRIBUTE; }, _setExpectingAttributeValue: function() { if (this._isExpectingAttribute()) - this._parseCondition ^= this._parseConditions.ATTRIBUTE; - this._parseCondition |= this._parseConditions.ATTRIBUTE_VALUE; + this._condition.parseCondition ^= this._parseConditions.ATTRIBUTE; + this._condition.parseCondition |= this._parseConditions.ATTRIBUTE_VALUE; }, _stringToken: function(cursor, stringEnds) @@ -111,8 +123,8 @@ WebInspector.SourceHTMLTokenizer.prototype = { _attrValueTokenType: function() { - if (this._parseCondition & this._parseConditions.LINKIFY) { - if (this._parseCondition & this._parseConditions.A_NODE) + if (this._condition.parseCondition & this._parseConditions.LINKIFY) { + if (this._condition.parseCondition & this._parseConditions.A_NODE) return "html-external-link"; return "html-resource-link"; } @@ -121,6 +133,20 @@ WebInspector.SourceHTMLTokenizer.prototype = { nextToken: function(cursor) { + if (this._internalJavaScriptTokenizer) { + // Re-set line to force </script> detection first. + this.line = this._line; + if (cursor !== this._internalJavaScriptTokenizer._line.length) { + // Tokenizer is stateless, so restore its condition before tokenizing and save it after. + this._internalJavaScriptTokenizer.condition = this._condition.internalJavaScriptTokenizerCondition; + var result = this._internalJavaScriptTokenizer.nextToken(cursor); + this.tokenType = this._internalJavaScriptTokenizer.tokenType; + this._condition.internalJavaScriptTokenizerCondition = this._internalJavaScriptTokenizer.condition; + return result; + } else if (cursor !== this._line.length) + delete this._internalJavaScriptTokenizer; + } + var cursorOnEnter = cursor; var gotoCase = 1; while (1) { @@ -174,14 +200,14 @@ WebInspector.SourceHTMLTokenizer.prototype = { <INITIAL> ScriptStart => TAG { - if (this._parseCondition & this._parseConditions.SCRIPT) { + if (this._condition.parseCondition & this._parseConditions.SCRIPT) { // Do not tokenize script tag contents, keep lexer state although processing "<". this.setLexCondition(this._lexConditions.INITIAL); this.tokenType = null; return cursor; } this.tokenType = "html-tag"; - this._parseCondition = this._parseConditions.SCRIPT; + this._condition.parseCondition = this._parseConditions.SCRIPT; this._setExpectingAttribute(); return cursor; } @@ -189,34 +215,37 @@ WebInspector.SourceHTMLTokenizer.prototype = { <INITIAL> ScriptEnd => TAG { this.tokenType = "html-tag"; - this._parseCondition = this._parseConditions.INITIAL; + this._condition.parseCondition = this._parseConditions.INITIAL; return cursor; } <INITIAL> LT => TAG { - if (this._parseCondition & this._parseConditions.SCRIPT) { + if (this._condition.parseCondition & this._parseConditions.SCRIPT) { // Do not tokenize script tag contents, keep lexer state although processing "<". this.setLexCondition(this._lexConditions.INITIAL); this.tokenType = null; return cursor; } - this._parseCondition = this._parseConditions.INITIAL; + this._condition.parseCondition = this._parseConditions.INITIAL; this.tokenType = "html-tag"; return cursor; } - + <TAG> GT => INITIAL { - if (this._parseCondition & this._parseConditions.SCRIPT) { + this.tokenType = "html-tag"; + if (this._condition.parseCondition & this._parseConditions.SCRIPT) { + if (!this._internalJavaScriptTokenizer) { + this._internalJavaScriptTokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/javascript"); + this._condition.internalJavaScriptTokenizerCondition = this._internalJavaScriptTokenizer.initialCondition; + } // Do not tokenize script tag contents. - this.tokenType = null; return cursor; } - this._parseCondition = this._parseConditions.INITIAL; - this.tokenType = "html-tag"; + this._condition.parseCondition = this._parseConditions.INITIAL; return cursor; } @@ -238,26 +267,26 @@ WebInspector.SourceHTMLTokenizer.prototype = { <TAG> Identifier { - if (this._parseCondition === this._parseConditions.SCRIPT) { + if (this._condition.parseCondition === this._parseConditions.SCRIPT) { // Fall through if expecting attributes. this.tokenType = null; return cursor; } - if (this._parseCondition === this._parseConditions.INITIAL) { + if (this._condition.parseCondition === this._parseConditions.INITIAL) { this.tokenType = "html-tag"; this._setExpectingAttribute(); var token = this._line.substring(cursorOnEnter, cursor); if (token === "a") - this._parseCondition |= this._parseConditions.A_NODE; - else if (this._parseCondition & this._parseConditions.A_NODE) - this._parseCondition ^= this._parseConditions.A_NODE; + this._condition.parseCondition |= this._parseConditions.A_NODE; + else if (this._condition.parseCondition & this._parseConditions.A_NODE) + this._condition.parseCondition ^= this._parseConditions.A_NODE; } else if (this._isExpectingAttribute()) { var token = this._line.substring(cursorOnEnter, cursor); if (token === "href" || token === "src") - this._parseCondition |= this._parseConditions.LINKIFY; - else if (this._parseCondition |= this._parseConditions.LINKIFY) - this._parseCondition ^= this._parseConditions.LINKIFY; + this._condition.parseCondition |= this._parseConditions.LINKIFY; + else if (this._condition.parseCondition |= this._parseConditions.LINKIFY) + this._condition.parseCondition ^= this._parseConditions.LINKIFY; this.tokenType = "html-attribute-name"; } else if (this._isExpectingAttributeValue()) this.tokenType = this._attrValueTokenType(); diff --git a/WebCore/inspector/front-end/SourceJavaScriptTokenizer.js b/WebCore/inspector/front-end/SourceJavaScriptTokenizer.js index 75abeee..fbd44d7 100644 --- a/WebCore/inspector/front-end/SourceJavaScriptTokenizer.js +++ b/WebCore/inspector/front-end/SourceJavaScriptTokenizer.js @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Thu Jan 28 20:49:23 2010 */ +/* Generated by re2c 0.13.5 on Thu Feb 25 21:44:55 2010 */ /* * Copyright (C) 2009 Google Inc. All rights reserved. * @@ -69,6 +69,7 @@ WebInspector.SourceJavaScriptTokenizer = function() this.case_REGEX = 1005; this.initialCondition = { lexCondition: this._lexConditions.NODIV } + this.condition = this.initialCondition; } WebInspector.SourceJavaScriptTokenizer.prototype = { diff --git a/WebCore/inspector/front-end/SourceJavaScriptTokenizer.re2js b/WebCore/inspector/front-end/SourceJavaScriptTokenizer.re2js index 053c82f..ae71efe 100644 --- a/WebCore/inspector/front-end/SourceJavaScriptTokenizer.re2js +++ b/WebCore/inspector/front-end/SourceJavaScriptTokenizer.re2js @@ -68,6 +68,7 @@ WebInspector.SourceJavaScriptTokenizer = function() this.case_REGEX = 1005; this.initialCondition = { lexCondition: this._lexConditions.NODIV } + this.condition = this.initialCondition; } WebInspector.SourceJavaScriptTokenizer.prototype = { diff --git a/WebCore/inspector/front-end/SourceTokenizer.js b/WebCore/inspector/front-end/SourceTokenizer.js index d498028..d30744c 100644 --- a/WebCore/inspector/front-end/SourceTokenizer.js +++ b/WebCore/inspector/front-end/SourceTokenizer.js @@ -40,28 +40,27 @@ WebInspector.SourceTokenizer.prototype = { set condition(condition) { - this._lexCondition = condition.lexCondition; - this._parseCondition = condition.parseCondition; + this._condition = condition; }, get condition() { - return { lexCondition: this._lexCondition, parseCondition: this._parseCondition }; + return this._condition; }, - hasCondition: function(condition) + get subTokenizer() { - return this._lexCondition === condition.lexCondition && this._parseCondition === condition.parseCondition; + return this._condition.subTokenizer; }, getLexCondition: function() { - return this._lexCondition; + return this.condition.lexCondition; }, setLexCondition: function(lexCondition) { - this._lexCondition = lexCondition; + this.condition.lexCondition = lexCondition; }, _charAt: function(cursor) @@ -76,9 +75,7 @@ WebInspector.SourceTokenizer.Registry = function() { this._tokenizerConstructors = { "text/css": "SourceCSSTokenizer", "text/html": "SourceHTMLTokenizer", - "text/javascript": "SourceJavaScriptTokenizer", - "application/javascript": "SourceJavaScriptTokenizer", - "application/x-javascript": "SourceJavaScriptTokenizer" + "text/javascript": "SourceJavaScriptTokenizer" }; } diff --git a/WebCore/inspector/front-end/SourceView.js b/WebCore/inspector/front-end/SourceView.js index b401c12..9fbd161 100644 --- a/WebCore/inspector/front-end/SourceView.js +++ b/WebCore/inspector/front-end/SourceView.js @@ -37,6 +37,14 @@ WebInspector.SourceView = function(resource) this._frameNeedsSetup = true; } +// This is a map from resource.type to mime types +// found in WebInspector.SourceTokenizer.Registry. +WebInspector.SourceView.DefaultMIMETypeForResourceType = { + 0: "text/html", + 1: "text/css", + 4: "text/javascript" +} + WebInspector.SourceView.prototype = { show: function(parentElement) { @@ -76,10 +84,16 @@ WebInspector.SourceView.prototype = { _contentLoaded: function(content) { - this.sourceFrame.setContent(this.resource.mimeType, content, this.resource.url); + var mimeType = this._canonicalMimeType(this.resource); + this.sourceFrame.setContent(mimeType, content, this.resource.url); this._sourceFrameSetupFinished(); }, + _canonicalMimeType: function(resource) + { + return WebInspector.SourceView.DefaultMIMETypeForResourceType[resource.type] || resource.mimeType; + }, + _resourceLoadingFinished: function(event) { this._frameNeedsSetup = true; diff --git a/WebCore/inspector/front-end/StylesSidebarPane.js b/WebCore/inspector/front-end/StylesSidebarPane.js index 265e488..eb5e012 100644 --- a/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/WebCore/inspector/front-end/StylesSidebarPane.js @@ -65,6 +65,33 @@ WebInspector.StylesSidebarPane = function() this.titleElement.appendChild(this.settingsSelectElement); } +// Taken from http://www.w3.org/TR/CSS21/propidx.html. +WebInspector.StylesSidebarPane.InheritedProperties = [ + "azimuth", "border-collapse", "border-spacing", "caption-side", "color", "cursor", "direction", "elevation", + "empty-cells", "font-family", "font-size", "font-style", "font-variant", "font-weight", "font", "letter-spacing", + "line-height", "list-style-image", "list-style-position", "list-style-type", "list-style", "orphans", "pitch-range", + "pitch", "quotes", "richness", "speak-header", "speak-numeral", "speak-punctuation", "speak", "speech-rate", "stress", + "text-align", "text-indent", "text-transform", "visibility", "voice-family", "volume", "white-space", "widows", "word-spacing" +].keySet(); + +// Keep in sync with RenderStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes. +// First item is empty due to its artificial NOPSEUDO nature in the enum. +// FIXME: find a way of generating this mapping or getting it from combination of RenderStyleConstants and CSSSelector.cpp at +// runtime. +WebInspector.StylesSidebarPane.PseudoIdNames = [ + "", "first-line", "first-letter", "before", "after", "selection", "", "-webkit-scrollbar", "-webkit-file-upload-button", + "-webkit-input-placeholder", "-webkit-slider-thumb", "-webkit-search-cancel-button", "-webkit-search-decoration", + "-webkit-search-results-decoration", "-webkit-search-results-button", "-webkit-media-controls-panel", + "-webkit-media-controls-play-button", "-webkit-media-controls-mute-button", "-webkit-media-controls-timeline", + "-webkit-media-controls-timeline-container", "-webkit-media-controls-volume-slider", + "-webkit-media-controls-volume-slider-container", "-webkit-media-controls-current-time-display", + "-webkit-media-controls-time-remaining-display", "-webkit-media-controls-seek-back-button", "-webkit-media-controls-seek-forward-button", + "-webkit-media-controls-fullscreen-button", "-webkit-media-controls-rewind-button", "-webkit-media-controls-return-to-realtime-button", + "-webkit-media-controls-toggle-closed-captions-button", "-webkit-media-controls-status-display", "-webkit-scrollbar-thumb", + "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner", + "-webkit-resizer", "-webkit-input-list-button", "-webkit-inner-spin-button", "-webkit-outer-spin-button" +]; + WebInspector.StylesSidebarPane.prototype = { _settingsLoaded: function() { @@ -102,76 +129,173 @@ WebInspector.StylesSidebarPane.prototype = { if (!node) { body.removeChildren(); - this.sections = []; + this.sections = {}; return; } - var self = this; - function callback(styles) + function getStylesCallback(styles) { - if (!styles) - return; - node._setStyles(styles.computedStyle, styles.inlineStyle, styles.styleAttributes, styles.matchedCSSRules); - self._update(refresh, body, node, editedSection, forceUpdate); + if (styles) + this._rebuildUpdate(node, styles); } - InjectedScriptAccess.get(node.injectedScriptId).getStyles(node.id, !WebInspector.settings.showUserAgentStyles, callback); + function getComputedStyleCallback(computedStyle) + { + if (computedStyle) + this._refreshUpdate(node, computedStyle, editedSection); + }; + + if (refresh) + InspectorBackend.getComputedStyle(WebInspector.Callback.wrap(getComputedStyleCallback.bind(this)), node.id); + else + InspectorBackend.getStyles(WebInspector.Callback.wrap(getStylesCallback.bind(this)), node.id, !WebInspector.settings.showUserAgentStyles); }, - _update: function(refresh, body, node, editedSection, forceUpdate) + _refreshUpdate: function(node, computedStyle, editedSection) { - if (!refresh) { - body.removeChildren(); - this.sections = []; + for (var pseudoId in this.sections) { + var styleRules = this._refreshStyleRules(this.sections[pseudoId], computedStyle); + var usedProperties = {}; + var disabledComputedProperties = {}; + this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties); + this._refreshSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, editedSection); } + }, - var styleRules = []; + _rebuildUpdate: function(node, styles) + { + this.bodyElement.removeChildren(); + var styleRules = this._rebuildStyleRules(node, styles); + var usedProperties = {}; + var disabledComputedProperties = {}; + this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties); + this.sections[0] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, 0); - if (refresh) { - for (var i = 0; i < this.sections.length; ++i) { - var section = this.sections[i]; - if (section instanceof WebInspector.BlankStylePropertiesSection) - continue; - if (section.computedStyle) - section.styleRule.style = node.ownerDocument.defaultView.getComputedStyle(node); - var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule }; - styleRules.push(styleRule); - } - } else { - var computedStyle = node.ownerDocument.defaultView.getComputedStyle(node); - styleRules.push({ computedStyle: true, selectorText: WebInspector.UIString("Computed Style"), style: computedStyle, editable: false }); - - var nodeName = node.nodeName.toLowerCase(); - for (var i = 0; i < node.attributes.length; ++i) { - var attr = node.attributes[i]; - if (attr.style) { - var attrStyle = { style: attr.style, editable: false }; - attrStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", attr.name); - attrStyle.selectorText = nodeName + "[" + attr.name; - if (attr.value.length) - attrStyle.selectorText += "=" + attr.value; - attrStyle.selectorText += "]"; - styleRules.push(attrStyle); - } + for (var i = 0; i < styles.pseudoElements.length; ++i) { + var pseudoElementCSSRules = styles.pseudoElements[i]; + + styleRules = []; + var pseudoId = pseudoElementCSSRules.pseudoId; + + var entry = { isStyleSeparator: true, pseudoId: pseudoId }; + styleRules.push(entry); + + // Add rules in reverse order to match the cascade order. + for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) { + var rule = WebInspector.CSSStyleDeclaration.parseRule(pseudoElementCSSRules.rules[j]); + styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule }); } + usedProperties = {}; + disabledComputedProperties = {}; + this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties); + this.sections[pseudoId] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, pseudoId); + } + }, - // Always Show element's Style Attributes - if (node.nodeType === Node.ELEMENT_NODE) { - var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: node.style, isAttribute: true }; - inlineStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", "style"); - styleRules.push(inlineStyle); + _refreshStyleRules: function(sections, computedStyle) + { + var nodeComputedStyle = new WebInspector.CSSStyleDeclaration(computedStyle); + var styleRules = []; + for (var i = 0; sections && i < sections.length; ++i) { + var section = sections[i]; + if (section instanceof WebInspector.BlankStylePropertiesSection) + continue; + if (section.computedStyle) + section.styleRule.style = nodeComputedStyle; + var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule }; + styleRules.push(styleRule); + } + return styleRules; + }, + + _rebuildStyleRules: function(node, styles) + { + var nodeComputedStyle = new WebInspector.CSSStyleDeclaration(styles.computedStyle); + this.sections = {}; + + var styleRules = []; + + styleRules.push({ computedStyle: true, selectorText: WebInspector.UIString("Computed Style"), style: nodeComputedStyle, editable: false }); + + var styleAttributes = {}; + for (var name in styles.styleAttributes) { + var attrStyle = { style: new WebInspector.CSSStyleDeclaration(styles.styleAttributes[name]), editable: false }; + attrStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", name); + attrStyle.selectorText = WebInspector.panels.elements.treeOutline.nodeNameToCorrectCase(node.nodeName) + "[" + name; + if (node.getAttribute(name)) + attrStyle.selectorText += "=" + node.getAttribute(name); + attrStyle.selectorText += "]"; + styleRules.push(attrStyle); + } + + // Show element's Style Attributes + if (styles.inlineStyle && node.nodeType === Node.ELEMENT_NODE) { + var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: new WebInspector.CSSStyleDeclaration(styles.inlineStyle), isAttribute: true }; + inlineStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", "style"); + styleRules.push(inlineStyle); + } + + // Add rules in reverse order to match the cascade order. + for (var i = styles.matchedCSSRules.length - 1; i >= 0; --i) { + var rule = WebInspector.CSSStyleDeclaration.parseRule(styles.matchedCSSRules[i]); + styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule }); + } + + // Collect used properties first in order to identify inherited ones. + var userPropertyNames = {}; + for (var i = 0; i < styleRules.length; ++i) { + var styleRule = styleRules[i]; + if (styleRule.computedStyle) + continue; + var style = styleRule.style; + for (var j = 0; j < style.length; ++j) + userPropertyNames[style[j]] = true; + } + + // Walk the node structure and identify styles with inherited properties. + var parentStyles = styles.parent; + var parentNode = node.parentNode; + function insertInheritedNodeSeparator(node) + { + var entry = {}; + entry.isStyleSeparator = true; + entry.node = node; + styleRules.push(entry); + } + + while (parentStyles) { + var separatorInserted = false; + if (parentStyles.inlineStyle) { + if (this._containsInherited(parentStyles.inlineStyle)) { + var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: new WebInspector.CSSStyleDeclaration(parentStyles.inlineStyle), isAttribute: true, isInherited: true }; + inlineStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", "style"); + if (!separatorInserted) { + insertInheritedNodeSeparator(parentNode); + separatorInserted = true; + } + styleRules.push(inlineStyle); + } } - var matchedStyleRules = node.ownerDocument.defaultView.getMatchedCSSRules(node, "", !WebInspector.settings.showUserAgentStyles); - if (matchedStyleRules) { - // Add rules in reverse order to match the cascade order. - for (var i = (matchedStyleRules.length - 1); i >= 0; --i) { - var rule = matchedStyleRules[i]; - styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule }); + for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) { + var rulePayload = parentStyles.matchedCSSRules[i]; + if (!this._containsInherited(rulePayload.style)) + continue; + var rule = WebInspector.CSSStyleDeclaration.parseRule(rulePayload); + if (!separatorInserted) { + insertInheritedNodeSeparator(parentNode); + separatorInserted = true; } + styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule, isInherited: true }); } + parentStyles = parentStyles.parent; + parentNode = parentNode.parentNode; } + return styleRules; + }, + _markUsedProperties: function(styleRules, usedProperties, disabledComputedProperties) + { function deleteDisabledProperty(style, name) { if (!style || !name) @@ -184,14 +308,12 @@ WebInspector.StylesSidebarPane.prototype = { delete style.__disabledProperties[name]; } - var usedProperties = {}; - var disabledComputedProperties = {}; var priorityUsed = false; // Walk the style rules and make a list of all used and overloaded properties. for (var i = 0; i < styleRules.length; ++i) { var styleRule = styleRules[i]; - if (styleRule.computedStyle) + if (styleRule.computedStyle || styleRule.isStyleSeparator) continue; if (styleRule.section && styleRule.section.noAffect) continue; @@ -245,13 +367,12 @@ WebInspector.StylesSidebarPane.prototype = { // Walk in reverse to match the order !important overrides. for (var i = (styleRules.length - 1); i >= 0; --i) { - if (styleRules[i].computedStyle) + if (styleRules[i].computedStyle || styleRules[i].isStyleSeparator) continue; var style = styleRules[i].style; - var uniqueProperties = style.uniqueStyleProperties; - for (var j = 0; j < uniqueProperties.length; ++j) { - var name = uniqueProperties[j]; + for (var j = 0; j < style.length; ++j) { + var name = style[j]; if (style.getPropertyPriority(name).length) { if (!(name in foundPriorityProperties)) styleRules[i].usedProperties[name] = true; @@ -263,58 +384,94 @@ WebInspector.StylesSidebarPane.prototype = { } } } + }, + + _refreshSectionsForStyleRules: function(styleRules, usedProperties, disabledComputedProperties, editedSection) + { + // Walk the style rules and update the sections with new overloaded and used properties. + for (var i = 0; i < styleRules.length; ++i) { + var styleRule = styleRules[i]; + var section = styleRule.section; + if (styleRule.computedStyle) + section.disabledComputedProperties = disabledComputedProperties; + section._usedProperties = (styleRule.usedProperties || usedProperties); + section.update((section === editedSection) || styleRule.computedStyle); + } + }, - if (refresh) { - // Walk the style rules and update the sections with new overloaded and used properties. - for (var i = 0; i < styleRules.length; ++i) { - var styleRule = styleRules[i]; - var section = styleRule.section; - if (styleRule.computedStyle) - section.disabledComputedProperties = disabledComputedProperties; - section._usedProperties = (styleRule.usedProperties || usedProperties); - section.update((section === editedSection) || styleRule.computedStyle); + _rebuildSectionsForStyleRules: function(styleRules, usedProperties, disabledComputedProperties, pseudoId) + { + // Make a property section for each style rule. + var sections = []; + for (var i = 0; i < styleRules.length; ++i) { + var styleRule = styleRules[i]; + if (styleRule.isStyleSeparator) { + var separatorElement = document.createElement("div"); + separatorElement.className = "styles-sidebar-separator"; + if (styleRule.node) { + var link = document.createElement("a"); + link.href = ""; + link.addEventListener("mousedown", this._selectNode.bind(this, styleRule.node.id), false); + WebInspector.panels.elements.decorateNodeLabel(styleRule.node, link); + separatorElement.appendChild(document.createTextNode(WebInspector.UIString("Inherited from") + " ")); + separatorElement.appendChild(link); + } else if ("pseudoId" in styleRule) { + var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[styleRule.pseudoId]; + if (pseudoName) + separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName); + else + separatorElement.textContent = WebInspector.UIString("Pseudo element"); + } + this.bodyElement.appendChild(separatorElement); + continue; } - } else { - // Make a property section for each style rule. - for (var i = 0; i < styleRules.length; ++i) { - var styleRule = styleRules[i]; - var subtitle = styleRule.subtitle; - delete styleRule.subtitle; - - var computedStyle = styleRule.computedStyle; - delete styleRule.computedStyle; - - var ruleUsedProperties = styleRule.usedProperties; - delete styleRule.usedProperties; - - var editable = styleRule.editable; - delete styleRule.editable; - - var isAttribute = styleRule.isAttribute; - delete styleRule.isAttribute; - - // Default editable to true if it was omitted. - if (typeof editable === "undefined") - editable = true; - - var section = new WebInspector.StylePropertiesSection(styleRule, subtitle, computedStyle, (ruleUsedProperties || usedProperties), editable); - if (computedStyle) - section.disabledComputedProperties = disabledComputedProperties; - section.pane = this; - - if (Preferences.styleRulesExpandedState && section.identifier in Preferences.styleRulesExpandedState) - section.expanded = Preferences.styleRulesExpandedState[section.identifier]; - else if (computedStyle) - section.collapse(true); - else if (isAttribute && styleRule.style.length === 0) - section.collapse(true); - else - section.expand(true); + var computedStyle = styleRule.computedStyle; + + // Default editable to true if it was omitted. + var editable = styleRule.editable; + if (typeof editable === "undefined") + editable = true; + + var section = new WebInspector.StylePropertiesSection(styleRule, styleRule.subtitle, styleRule.computedStyle, (styleRule.usedProperties || usedProperties), editable, styleRule.isInherited); + if (computedStyle) + section.disabledComputedProperties = disabledComputedProperties; + section.pane = this; + + if (Preferences.styleRulesExpandedState && section.identifier in Preferences.styleRulesExpandedState) + section.expanded = Preferences.styleRulesExpandedState[section.identifier]; + else if (computedStyle) + section.collapse(true); + else if (styleRule.isAttribute && styleRule.style.length === 0) + section.collapse(true); + else if (styleRule.isInherited) + section.collapse(true); + else + section.expand(true); - body.appendChild(section.element); - this.sections.push(section); - } + this.bodyElement.appendChild(section.element); + sections.push(section); + } + return sections; + }, + + _selectNode: function(nodeId, e) + { + WebInspector.updateFocusedNode(nodeId); + e.preventDefault(); + }, + + _containsInherited: function(payload) + { + var properties = []; + for (var i = 0; i < payload.properties.length; ++i) { + var property = payload.properties[i]; + // Does this style contain non-overriden inherited property? + if (property.name in WebInspector.StylesSidebarPane.InheritedProperties) + return true; } + if (payload.disabled && this._containsInherited(payload.disabled)) + return true; + return false; }, _changeSetting: function(event) @@ -340,8 +497,11 @@ WebInspector.StylesSidebarPane.prototype = { var selectedOption = this.settingsSelectElement[this.settingsSelectElement.selectedIndex]; WebInspector.settings.colorFormat = selectedOption.value; - for (var i = 0; i < this.sections.length; ++i) - this.sections[i].update(true); + for (var pseudoId in this.sections) { + var sections = this.sections[pseudoId]; + for (var i = 0; i < sections.length; ++i) + sections[i].update(true); + } }, _createNewRule: function(event) @@ -354,39 +514,43 @@ WebInspector.StylesSidebarPane.prototype = { var blankSection = new WebInspector.BlankStylePropertiesSection(appropriateSelectorForNode(this.node, true)); blankSection.pane = this; - var elementStyleSection = this.sections[1]; + var elementStyleSection = this.sections[0][1]; this.bodyElement.insertBefore(blankSection.element, elementStyleSection.element.nextSibling); - this.sections.splice(2, 0, blankSection); + this.sections[0].splice(2, 0, blankSection); return blankSection; }, removeSection: function(section) { - var index = this.sections.indexOf(section); - if (index === -1) - return; - this.sections.splice(index, 1); - if (section.element.parentNode) - section.element.parentNode.removeChild(section.element); + for (var pseudoId in this.sections) { + var sections = this.sections[pseudoId]; + var index = sections.indexOf(section); + if (index === -1) + continue; + sections.splice(index, 1); + if (section.element.parentNode) + section.element.parentNode.removeChild(section.element); + } } } WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; -WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyle, usedProperties, editable) +WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyle, usedProperties, editable, isInherited) { WebInspector.PropertiesSection.call(this, styleRule.selectorText); - this.titleElement.addEventListener("click", this._clickSelector.bind(this), false); - this.titleElement.addEventListener("dblclick", this._dblclickSelector.bind(this), false); - this.element.addEventListener("dblclick", this._dblclickEmptySpace.bind(this), false); + this.titleElement.addEventListener("dblclick", this._handleSelectorDoubleClick.bind(this), false); + this.titleElement.addEventListener("click", this._handleSelectorClick.bind(this), false); + this.element.addEventListener("dblclick", this._handleEmptySpaceDoubleClick.bind(this), false); this.styleRule = styleRule; this.rule = this.styleRule.rule; this.computedStyle = computedStyle; this.editable = (editable && !computedStyle); + this.isInherited = isInherited; // Prevent editing the user agent and user rules. var isUserAgent = this.rule && this.rule.isUserAgent; @@ -398,38 +562,34 @@ WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyl this._usedProperties = usedProperties; + if (this.rule) + this.titleElement.addStyleClass("styles-selector"); + if (computedStyle) { this.element.addStyleClass("computed-style"); if (WebInspector.settings.showInheritedComputedStyleProperties) this.element.addStyleClass("show-inherited"); - var showInheritedLabel = document.createElement("label"); - var showInheritedInput = document.createElement("input"); - showInheritedInput.type = "checkbox"; - showInheritedInput.checked = WebInspector.settings.showInheritedComputedStyleProperties; - var computedStyleSection = this; var showInheritedToggleFunction = function(event) { - WebInspector.settings.showInheritedComputedStyleProperties = showInheritedInput.checked; + WebInspector.settings.showInheritedComputedStyleProperties = computedStyleSection._showInheritedCheckbox.checked(); if (WebInspector.settings.showInheritedComputedStyleProperties) computedStyleSection.element.addStyleClass("show-inherited"); else computedStyleSection.element.removeStyleClass("show-inherited"); - event.stopPropagation(); }; - showInheritedLabel.addEventListener("click", showInheritedToggleFunction, false); + this._showInheritedCheckbox = new WebInspector.Checkbox(WebInspector.UIString("Show inherited"), + showInheritedToggleFunction, + WebInspector.settings.showInheritedComputedStyleProperties); - showInheritedLabel.appendChild(showInheritedInput); - showInheritedLabel.appendChild(document.createTextNode(WebInspector.UIString("Show inherited"))); - this.subtitleElement.appendChild(showInheritedLabel); + this.subtitleElement.appendChild(this._showInheritedCheckbox.element); } else { if (!subtitle) { if (this.styleRule.parentStyleSheet && this.styleRule.parentStyleSheet.href) { var url = this.styleRule.parentStyleSheet.href; - subtitle = WebInspector.linkifyURL(url, WebInspector.displayNameForURL(url)); - this.subtitleElement.addStyleClass("file"); + this.subtitleElement.appendChild(WebInspector.linkifyResourceAsNode(url, "resources", this.rule.sourceLine + 1)); } else if (isUserAgent) subtitle = WebInspector.UIString("user agent stylesheet"); else if (isUser) @@ -439,8 +599,10 @@ WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyl else subtitle = WebInspector.UIString("inline stylesheet"); } - - this.subtitle = subtitle; + if (isInherited) + this.element.addStyleClass("show-inherited"); + if (subtitle) + this.subtitle = subtitle; } this.identifier = styleRule.selectorText; @@ -484,6 +646,12 @@ WebInspector.StylePropertiesSection.prototype = { isPropertyInherited: function(property) { + if (this.isInherited) { + // While rendering inherited stylesheet, reverse meaning of this property. + // Render truly inherited properties with black, i.e. return them as non-inherited. + return !(property in WebInspector.StylesSidebarPane.InheritedProperties); + } + if (!this.computedStyle || !this._usedProperties || this.noAffect) return false; // These properties should always show for Computed Style. @@ -496,6 +664,11 @@ WebInspector.StylePropertiesSection.prototype = { if (this.computedStyle || !this._usedProperties || this.noAffect) return false; + if (this.isInherited && !(property in WebInspector.StylesSidebarPane.InheritedProperties)) { + // In the inherited sections, only show overrides for the potentially inherited properties. + return false; + } + var used = (property in this.usedProperties); if (used || !shorthand) return !used; @@ -512,11 +685,6 @@ WebInspector.StylePropertiesSection.prototype = { return true; }, - isInspectorStylesheet: function() - { - return (this.styleRule.parentStyleSheet === WebInspector.panels.elements.stylesheet); - }, - update: function(full) { if (full || this.computedStyle) { @@ -546,9 +714,12 @@ WebInspector.StylePropertiesSection.prototype = { var style = this.styleRule.style; var foundShorthands = {}; - var uniqueProperties = style.uniqueStyleProperties; var disabledProperties = style.__disabledPropertyValues || {}; + var uniqueProperties = []; + for (var i = 0; i < style.length; ++i) + uniqueProperties.push(style[i]); + for (var name in disabledProperties) uniqueProperties.push(name); @@ -599,26 +770,28 @@ WebInspector.StylePropertiesSection.prototype = { return item; }, - _clickSelector: function(event) + _handleEmptySpaceDoubleClick: function(event) { - event.stopPropagation(); - - // Don't search "Computed Styles", "Style Attribute", or Mapped Attributes. - if (this.computedStyle || !this.rule || this.rule.isUser) + if (event.target.hasStyleClass("header")) { + event.stopPropagation(); return; + } + this.expand(); + this.addNewBlankProperty().startEditing(); + }, - var searchElement = document.getElementById("search"); - searchElement.value = this.styleRule.selectorText; - WebInspector.performSearch({ target: searchElement }); + _handleSelectorClick: function(event) + { + event.stopPropagation(); }, - _dblclickEmptySpace: function(event) + _handleSelectorDoubleClick: function(event) { - this.expand(); - this.addNewBlankProperty().startEditing(); + this._startEditingOnMouseEvent(); + event.stopPropagation(); }, - _dblclickSelector: function(event) + _startEditingOnMouseEvent: function() { if (!this.editable) return; @@ -633,7 +806,6 @@ WebInspector.StylePropertiesSection.prototype = { return; this.startEditingSelector(); - event.stopPropagation(); }, startEditingSelector: function() @@ -665,16 +837,14 @@ WebInspector.StylePropertiesSection.prototype = { return moveToNextIfNeeded.call(this); var self = this; - function callback(result) + function callback(newRulePayload, doesAffectSelectedNode) { - if (!result) { + if (!newRulePayload) { // Invalid Syntax for a Selector moveToNextIfNeeded.call(self); return; } - var newRulePayload = result[0]; - var doesAffectSelectedNode = result[1]; if (!doesAffectSelectedNode) { self.noAffect = true; self.element.addStyleClass("no-affect"); @@ -697,7 +867,7 @@ WebInspector.StylePropertiesSection.prototype = { moveToNextIfNeeded.call(self); } - InjectedScriptAccess.get(this.rule.injectedScriptId).applyStyleRuleText(this.rule.id, newContent, this.pane.node.id, callback); + InspectorBackend.setRuleSelector(WebInspector.Callback.wrap(callback), this.rule.id, newContent, this.pane.node.id); }, editingSelectorCancelled: function() @@ -724,17 +894,14 @@ WebInspector.BlankStylePropertiesSection.prototype = { editingSelectorCommitted: function(element, newContent, oldContent, context) { var self = this; - function callback(result) + function callback(rule, doesSelectorAffectSelectedNode) { - if (!result) { + if (!rule) { // Invalid Syntax for a Selector self.editingSelectorCancelled(); return; } - var rule = result[0]; - var doesSelectorAffectSelectedNode = result[1]; - var styleRule = WebInspector.CSSStyleDeclaration.parseRule(rule); styleRule.rule = rule; @@ -751,7 +918,7 @@ WebInspector.BlankStylePropertiesSection.prototype = { self.addNewBlankProperty().startEditing(); } - InjectedScriptAccess.get(this.pane.node.injectedScriptId).addStyleSelector(newContent, this.pane.node.id, callback); + InspectorBackend.addRule(WebInspector.Callback.wrap(callback), newContent, this.pane.node.id); }, editingSelectorCancelled: function() @@ -994,7 +1161,7 @@ WebInspector.StylePropertyTreeElement.prototype = { return container; } - var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b)/g; + var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g; var colorProcessor = processValue.bind(window, colorRegex, processColor, null); valueElement.appendChild(processValue(/url\(([^)]+)\)/g, linkifyURL, colorProcessor, value)); @@ -1058,7 +1225,7 @@ WebInspector.StylePropertyTreeElement.prototype = { self.updateAll(true); } - InjectedScriptAccess.get(this.style.injectedScriptId).toggleStyleEnabled(this.style.id, this.name, disabled, callback); + InspectorBackend.toggleStyleEnabled(WebInspector.Callback.wrap(callback), this.style.id, this.name, disabled); }, updateState: function() @@ -1211,7 +1378,6 @@ WebInspector.StylePropertyTreeElement.prototype = { selection.removeAllRanges(); selection.addRange(finalSelectionRange); - event.preventDefault(); event.handled = true; if (!this.originalCSSText) { @@ -1221,7 +1387,7 @@ WebInspector.StylePropertyTreeElement.prototype = { } else { // Restore the original CSS text before applying user changes. This is needed to prevent // new properties from sticking around if the user adds one, then removes it. - InjectedScriptAccess.get(this.style.injectedScriptId).setStyleText(this.style.id, this.originalCSSText); + InspectorBackend.setStyleText(WebInspector.Callback.wrap(null), this.style.id, this.originalCSSText); } this.applyStyleText(this.listItemElement.textContent); @@ -1241,7 +1407,7 @@ WebInspector.StylePropertyTreeElement.prototype = { if (this._newProperty) this.treeOutline.removeChild(this); else if (this.originalCSSText) { - InjectedScriptAccess.get(this.style.injectedScriptId).setStyleText(this.style.id, this.originalCSSText); + InspectorBackend.setStyleText(WebInspector.Callback.wrap(null), this.style.id, this.originalCSSText); if (this.treeOutline.section && this.treeOutline.section.pane) this.treeOutline.section.pane.dispatchEventToListeners("style edited"); @@ -1314,7 +1480,8 @@ WebInspector.StylePropertyTreeElement.prototype = { { var section = this.treeOutline.section; var elementsPanel = WebInspector.panels.elements; - var styleTextLength = styleText.trim().length; + styleText = styleText.replace(/\s/g, " ").trim(); // replace with whitespace. + var styleTextLength = styleText.length; if (!styleTextLength && updateInterface) { if (this._newProperty) { // The user deleted everything, so remove the tree element and update. @@ -1327,9 +1494,9 @@ WebInspector.StylePropertyTreeElement.prototype = { } var self = this; - function callback(result) + function callback(success, newPayload, changedProperties) { - if (!result) { + if (!success) { // The user typed something, but it didn't parse. Just abort and restore // the original title for this property. If this was a new attribute and // we couldn't parse, then just remove it. @@ -1342,8 +1509,6 @@ WebInspector.StylePropertyTreeElement.prototype = { return; } - var newPayload = result[0]; - var changedProperties = result[1]; elementsPanel.removeStyleChange(section.identifier, self.style, self.name); if (!styleTextLength) { @@ -1361,12 +1526,8 @@ WebInspector.StylePropertyTreeElement.prototype = { if (updateInterface) self.updateAll(true); - - if (!section.rule) - WebInspector.panels.elements.treeOutline.update(); } - - InjectedScriptAccess.get(this.style.injectedScriptId).applyStyleText(this.style.id, styleText.trim(), this.name, callback); + InspectorBackend.applyStyleText(WebInspector.Callback.wrap(callback), this.style.id, styleText, this.name); } } diff --git a/WebCore/inspector/front-end/TextEditorHighlighter.js b/WebCore/inspector/front-end/TextEditorHighlighter.js index cf0b590..4ac831e 100644 --- a/WebCore/inspector/front-end/TextEditorHighlighter.js +++ b/WebCore/inspector/front-end/TextEditorHighlighter.js @@ -34,24 +34,31 @@ WebInspector.TextEditorHighlighter = function(textModel, damageCallback) this._textModel = textModel; this._tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/html"); this._damageCallback = damageCallback; + this.reset(); } WebInspector.TextEditorHighlighter.prototype = { set mimeType(mimeType) { var tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer(mimeType); - if (tokenizer) + if (tokenizer) { this._tokenizer = tokenizer; + this._tokenizerCondition = this._tokenizer.initialCondition; + } + }, + + reset: function() + { + this._lastHighlightedLine = 0; + this._lastHighlightedColumn = 0; + this._tokenizerCondition = this._tokenizer.initialCondition; }, highlight: function(endLine) { // First check if we have work to do. - var state = this._textModel.getAttribute(endLine - 1, "highlighter-state") - if (state && !state.outOfDate) { - // Last line is highlighted, just exit. + if (endLine <= this._lastHighlightedLine) return; - } this._requestedEndLine = endLine; @@ -60,110 +67,66 @@ WebInspector.TextEditorHighlighter.prototype = { return; } - // We will be highlighting. First rewind to the last highlighted line to gain proper highlighter context. - var startLine = endLine; - while (startLine > 0) { - var state = this._textModel.getAttribute(startLine - 1, "highlighter-state"); - if (state && !state.outOfDate) - break; - startLine--; - } - // Do small highlight synchronously. This will provide instant highlight on PageUp / PageDown, gentle scrolling. - var toLine = Math.min(startLine + 200, endLine); - this._highlightInChunks(startLine, toLine); + this._highlightInChunks(endLine); // Schedule tail highlight if necessary. - if (endLine > toLine) - this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, toLine, endLine), 100); + if (this._lastHighlightedLine < endLine) + this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, endLine), 100); }, - _highlightInChunks: function(startLine, endLine) + _highlightInChunks: function(endLine) { delete this._highlightTimer; // First we always check if we have work to do. Could be that user scrolled back and we can quit. - var state = this._textModel.getAttribute(this._requestedEndLine - 1, "highlighter-state"); - if (state && !state.outOfDate) + if (this._requestedEndLine <= this._lastHighlightedLine) return; if (this._requestedEndLine !== endLine) { // User keeps updating the job in between of our timer ticks. Just reschedule self, don't eat CPU (they must be scrolling). - this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, startLine, this._requestedEndLine), 100); + this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, this._requestedEndLine), 100); return; } - // Highlight 500 lines chunk. - var toLine = Math.min(startLine + 500, this._requestedEndLine); - this._highlightLines(startLine, toLine); + this._highlightLines(this._requestedEndLine); // Schedule tail highlight if necessary. - if (toLine < this._requestedEndLine) - this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, toLine, this._requestedEndLine), 10); + if (this._lastHighlightedLine < this._requestedEndLine) + this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, this._requestedEndLine), 10); }, - updateHighlight: function(startLine, endLine) + _highlightLines: function(endLine) { - // Start line was edited, we should highlight everything until endLine synchronously. - if (startLine) { - var state = this._textModel.getAttribute(startLine - 1, "highlighter-state"); - if (!state || state.outOfDate) { - // Highlighter did not reach this point yet, nothing to update. It will reach it on subsequent timer tick and do the job. - return; - } - } - - var restored = this._highlightLines(startLine, endLine); - - // Set invalidated flag to the subsequent lines. - for (var i = endLine; i < this._textModel.linesCount; ++i) { - var highlighterState = this._textModel.getAttribute(i, "highlighter-state"); - if (highlighterState) - highlighterState.outOfDate = !restored; - else - return; - } - }, - - _highlightLines: function(startLine, endLine) - { - // Restore highlighter context taken from previous line. - var state = this._textModel.getAttribute(startLine - 1, "highlighter-state"); - if (state) - this._tokenizer.condition = state.postCondition; - else - this._tokenizer.condition = this._tokenizer.initialCondition; - - for (var i = startLine; i < endLine; ++i) { - state = {}; - state.preCondition = this._tokenizer.condition; - state.attributes = {}; - - this._lex(this._textModel.line(i), i, state.attributes); - - state.postCondition = this._tokenizer.condition; - this._textModel.setAttribute(i, "highlighter-state", state); - - var nextLineState = this._textModel.getAttribute(i + 1, "highlighter-state"); - if (nextLineState && this._tokenizer.hasCondition(nextLineState.preCondition)) { - // Following lines are up to date, no need re-highlight. - this._damageCallback(startLine, i + 1); - return true; - } + // Tokenizer is stateless and reused accross viewers, restore its condition before highlight and save it after. + this._tokenizer.condition = this._tokenizerCondition; + var tokensCount = 0; + for (var lineNumber = this._lastHighlightedLine; lineNumber < endLine; ++lineNumber) { + var line = this._textModel.line(lineNumber); + this._tokenizer.line = line; + var attributes = this._textModel.getAttribute(lineNumber, "highlight") || {}; + + // Highlight line. + do { + var newColumn = this._tokenizer.nextToken(this._lastHighlightedColumn); + var tokenType = this._tokenizer.tokenType; + if (tokenType) + attributes[this._lastHighlightedColumn] = { length: newColumn - this._lastHighlightedColumn, tokenType: tokenType, subTokenizer: this._tokenizer.subTokenizer }; + this._lastHighlightedColumn = newColumn; + if (++tokensCount > 1000) + break; + } while (this._lastHighlightedColumn < line.length) + + this._textModel.setAttribute(lineNumber, "highlight", attributes); + if (this._lastHighlightedColumn < line.length) { + // Too much work for single chunk - exit. + break; + } else + this._lastHighlightedColumn = 0; } - this._damageCallback(startLine, endLine); - return false; - }, - _lex: function(line, lineNumber, attributes) { - this._tokenizer.line = line; - var column = 0; - do { - var newColumn = this._tokenizer.nextToken(column); - var tokenType = this._tokenizer.tokenType; - if (tokenType) - attributes[column] = { length: newColumn - column, tokenType: tokenType }; - column = newColumn; - } while (column < line.length) + this._damageCallback(this._lastHighlightedLine, lineNumber); + this._tokenizerCondition = this._tokenizer.condition; + this._lastHighlightedLine = lineNumber; } } diff --git a/WebCore/inspector/front-end/TextEditorModel.js b/WebCore/inspector/front-end/TextEditorModel.js index e56c269..f23ce76 100644 --- a/WebCore/inspector/front-end/TextEditorModel.js +++ b/WebCore/inspector/front-end/TextEditorModel.js @@ -200,6 +200,9 @@ WebInspector.TextEditorModel.prototype = { copyRange: function(range) { + if (!range) + range = new WebInspector.TextRange(0, 0, this._lines.length - 1, this._lines[this._lines.length - 1].length); + var clip = []; if (range.startLine === range.endLine) { clip.push(this._lines[range.startLine].substring(range.startColumn, range.endColumn)); diff --git a/WebCore/inspector/front-end/TextPrompt.js b/WebCore/inspector/front-end/TextPrompt.js index 8554a28..d179a9a 100644 --- a/WebCore/inspector/front-end/TextPrompt.js +++ b/WebCore/inspector/front-end/TextPrompt.js @@ -99,6 +99,22 @@ WebInspector.TextPrompt.prototype = { } defaultAction.call(this); break; + case "U+0041": // Ctrl+A = Move caret to the start of prompt on non-Mac. + if (!WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) { + handled = true; + this._moveCaretToStartOfPrompt(); + break; + } + defaultAction.call(this); + break; + case "U+0045": // Ctrl+E = Move caret to the end of prompt on non-Mac. + if (!WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) { + handled = true; + this.moveCaretToEndOfPrompt(); + break; + } + defaultAction.call(this); + break; default: defaultAction.call(this); break; @@ -149,6 +165,7 @@ WebInspector.TextPrompt.prototype = { return; this._userEnteredRange.deleteContents(); + this.element.pruneEmptyTextNodes(); var userTextNode = document.createTextNode(this._userEnteredText); this._userEnteredRange.insertNode(userTextNode); @@ -171,7 +188,7 @@ WebInspector.TextPrompt.prototype = { this._completeTimeout = setTimeout(this.complete.bind(this, true), 250); }, - complete: function(auto) + complete: function(auto, reverse) { this.clearAutoComplete(true); var selection = window.getSelection(); @@ -184,10 +201,10 @@ WebInspector.TextPrompt.prototype = { if (auto && !this.isCaretAtEndOfPrompt()) return; var wordPrefixRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, this.completionStopCharacters, this.element, "backward"); - this.completions(wordPrefixRange, auto, this._completionsReady.bind(this, selection, auto, wordPrefixRange)); + this.completions(wordPrefixRange, auto, this._completionsReady.bind(this, selection, auto, wordPrefixRange, reverse)); }, - _completionsReady: function(selection, auto, originalWordPrefixRange, completions) + _completionsReady: function(selection, auto, originalWordPrefixRange, reverse, completions) { if (!completions || !completions.length) return; @@ -211,10 +228,13 @@ WebInspector.TextPrompt.prototype = { if (completions[i] === currentText) foundIndex = i; - if (foundIndex === null || (foundIndex + 1) >= completions.length) + var nextIndex = foundIndex + (reverse ? -1 : 1); + if (foundIndex === null || nextIndex >= completions.length) var completionText = completions[0]; + else if (nextIndex < 0) + var completionText = completions[completions.length - 1]; else - var completionText = completions[foundIndex + 1]; + var completionText = completions[nextIndex]; } var wordPrefixLength = originalWordPrefixRange.toString().length; @@ -223,6 +243,7 @@ WebInspector.TextPrompt.prototype = { this._userEnteredText = fullWordRange.toString(); fullWordRange.deleteContents(); + this.element.pruneEmptyTextNodes(); var finalSelectionRange = document.createRange(); @@ -334,6 +355,18 @@ WebInspector.TextPrompt.prototype = { return true; }, + _moveCaretToStartOfPrompt: function() + { + var selection = window.getSelection(); + var selectionRange = document.createRange(); + + selectionRange.setStart(this.element, 0); + selectionRange.setEnd(this.element, 0); + + selection.removeAllRanges(); + selection.addRange(selectionRange); + }, + moveCaretToEndOfPrompt: function() { var selection = window.getSelection(); @@ -352,7 +385,7 @@ WebInspector.TextPrompt.prototype = { event.preventDefault(); event.stopPropagation(); - this.complete(); + this.complete(false, event.shiftKey); }, _upKeyPressed: function(event) diff --git a/WebCore/inspector/front-end/TextViewer.js b/WebCore/inspector/front-end/TextViewer.js index 9be6a00..de04641 100644 --- a/WebCore/inspector/front-end/TextViewer.js +++ b/WebCore/inspector/front-end/TextViewer.js @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -39,6 +40,10 @@ WebInspector.TextViewer = function(textModel, platform, url) this.element.tabIndex = 0; this.element.addEventListener("scroll", this._scroll.bind(this), false); + this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false); + this.element.addEventListener("beforecopy", this._beforeCopy.bind(this), false); + this.element.addEventListener("copy", this._copy.bind(this), false); + this.element.addEventListener("dblclick", this._handleDoubleClick.bind(this), false); this._url = url; @@ -75,6 +80,11 @@ WebInspector.TextViewer.prototype = { chunk.element.scrollIntoViewIfNeeded(); }, + set editCallback(editCallback) + { + this._editCallback = editCallback; + }, + addDecoration: function(lineNumber, decoration) { var chunk = this._makeLineAChunk(lineNumber); @@ -134,7 +144,9 @@ WebInspector.TextViewer.prototype = { this._textChunks.push(chunk); this._linesContainerElement.appendChild(chunk.element); } + this._indexChunks(); + this._highlighter.reset(); this._repaintAll(); }, @@ -203,6 +215,74 @@ WebInspector.TextViewer.prototype = { }.bind(this), 50); }, + _handleKeyDown: function() + { + if (this._editingLine || event.metaKey || event.shiftKey || event.ctrlKey || event.altKey) + return; + + var scrollValue = 0; + if (event.keyCode === WebInspector.KeyboardShortcut.KeyCodes.Up) + scrollValue = -1; + else if (event.keyCode == WebInspector.KeyboardShortcut.KeyCodes.Down) + scrollValue = 1; + + if (scrollValue) { + event.preventDefault(); + event.stopPropagation(); + this.element.scrollByLines(scrollValue); + return; + } + + scrollValue = 0; + if (event.keyCode === WebInspector.KeyboardShortcut.KeyCodes.Left) + scrollValue = -40; + else if (event.keyCode == WebInspector.KeyboardShortcut.KeyCodes.Right) + scrollValue = 40; + + if (scrollValue) { + event.preventDefault(); + event.stopPropagation(); + this.element.scrollLeft += scrollValue; + } + }, + + _handleDoubleClick: function(e) + { + if (!this._editCallback) + return; + + var lineRow = e.target.enclosingNodeOrSelfWithNodeName("TR"); + if (!lineRow) + return; + var oldContent = lineRow.lastChild.innerHTML; + this._editingLine = WebInspector.startEditing(lineRow.lastChild, this._commitEditingLine.bind(this, lineRow.lineNumber, lineRow.lastChild), this._cancelEditingLine.bind(this, lineRow.lastChild, oldContent), null, true); + }, + + _commitEditingLine: function(lineNumber, element) + { + this._editCallback(lineNumber, element.textContent) + delete this._editingLine; + }, + + _cancelEditingLine: function(element, oldContent, e) + { + element.innerHTML = oldContent; + delete this._editingLine; + }, + + _beforeCopy: function(e) + { + e.preventDefault(); + }, + + _copy: function(e) + { + var range = this._getSelection(); + var text = this._textModel.copyRange(range); + InspectorFrontendHost.copyText(text); + e.preventDefault(); + }, + beginUpdates: function(enabled) { this._paintCoalescingLevel++; @@ -330,25 +410,25 @@ WebInspector.TextViewer.prototype = { _paintLine: function(lineRow, lineNumber) { var element = lineRow.lastChild; - var highlighterState = this._textModel.getAttribute(lineNumber, "highlighter-state"); - var line = this._textModel.line(lineNumber); - - if (!highlighterState) { + var highlight = this._textModel.getAttribute(lineNumber, "highlight"); + if (!highlight) { if (this._rangeToMark && this._rangeToMark.startLine === lineNumber) this._markedRangeElement = highlightSearchResult(element, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn); return; } element.removeChildren(); + var line = this._textModel.line(lineNumber); var plainTextStart = -1; for (var j = 0; j < line.length;) { if (j > 1000) { // This line is too long - do not waste cycles on minified js highlighting. - plainTextStart = j; + if (plainTextStart === -1) + plainTextStart = j; break; } - var attribute = highlighterState && highlighterState.attributes[j]; + var attribute = highlight[j]; if (!attribute || !attribute.tokenType) { if (plainTextStart === -1) plainTextStart = j; @@ -576,7 +656,18 @@ WebInspector.TextChunk = function(textViewer, startLine, endLine) lineNumbers.push(i + 1); lines.push(this._textModel.line(i)); } - this._lineNumberElement.textContent = lineNumbers.join("\n"); + if (this.linesCount === 1) { + // Single line chunks are typically created for decorations. Host line number in + // the sub-element in order to allow flexible border / margin management. + var innerSpan = document.createElement("span"); + innerSpan.className = "webkit-line-number-inner"; + innerSpan.textContent = startLine + 1; + var outerSpan = document.createElement("div"); + outerSpan.className = "webkit-line-number-outer"; + outerSpan.appendChild(innerSpan); + this._lineNumberElement.appendChild(outerSpan); + } else + this._lineNumberElement.textContent = lineNumbers.join("\n"); this._lineContentElement.textContent = lines.join("\n"); } diff --git a/WebCore/inspector/front-end/TimelineAgent.js b/WebCore/inspector/front-end/TimelineAgent.js index c9e9d64..387d491 100644 --- a/WebCore/inspector/front-end/TimelineAgent.js +++ b/WebCore/inspector/front-end/TimelineAgent.js @@ -48,7 +48,12 @@ WebInspector.TimelineAgent.RecordType = { MarkTimeline : 11, ResourceSendRequest : 12, ResourceReceiveResponse : 13, - ResourceFinish : 14 + ResourceFinish : 14, + FunctionCall : 15, + ResourceReceiveData: 16, + GCEvent : 17, + MarkDOMContentEventType : 18, + MarkLoadEventType : 19 }; WebInspector.addRecordToTimeline = function(record) { diff --git a/WebCore/inspector/front-end/TimelineGrid.js b/WebCore/inspector/front-end/TimelineGrid.js index 12b889a..fb93b8f 100644 --- a/WebCore/inspector/front-end/TimelineGrid.js +++ b/WebCore/inspector/front-end/TimelineGrid.js @@ -37,15 +37,15 @@ WebInspector.TimelineGrid = function() this.element.appendChild(this._itemsGraphsElement); this._dividersElement = document.createElement("div"); - this._dividersElement.id = "resources-dividers"; + this._dividersElement.className = "resources-dividers"; this.element.appendChild(this._dividersElement); this._eventDividersElement = document.createElement("div"); - this._eventDividersElement.id = "resources-event-dividers"; + this._eventDividersElement.className = "resources-event-dividers"; this.element.appendChild(this._eventDividersElement); this._dividersLabelBarElement = document.createElement("div"); - this._dividersLabelBarElement.id = "resources-dividers-label-bar"; + this._dividersLabelBarElement.className = "resources-dividers-label-bar"; this.element.appendChild(this._dividersLabelBarElement); } @@ -135,6 +135,11 @@ WebInspector.TimelineGrid.prototype = { this._eventDividersElement.appendChild(divider); }, + removeEventDividers: function() + { + this._eventDividersElement.removeChildren(); + }, + setScrollAndDividerTop: function(scrollTop, dividersTop) { this._dividersElement.style.top = scrollTop + "px"; diff --git a/WebCore/inspector/front-end/TimelineOverviewPane.js b/WebCore/inspector/front-end/TimelineOverviewPane.js index 8ae8352..7191ef8 100644 --- a/WebCore/inspector/front-end/TimelineOverviewPane.js +++ b/WebCore/inspector/front-end/TimelineOverviewPane.js @@ -92,7 +92,6 @@ WebInspector.TimelineOverviewPane = function(categories) this.windowRight = 1.0; } - WebInspector.TimelineOverviewPane.prototype = { _onCheckboxClicked: function (category, event) { if (event.target.checked) @@ -103,8 +102,9 @@ WebInspector.TimelineOverviewPane.prototype = { this.dispatchEventToListeners("filter changed"); }, - update: function(records) + update: function(records, showShortEvents) { + this._showShortEvents = showShortEvents; // Clear summary bars. var timelines = {}; for (var category in this._categories) { @@ -128,6 +128,8 @@ WebInspector.TimelineOverviewPane.prototype = { function markTimeline(record) { + if (!(this._showShortEvents || record.isLong())) + return; var percentages = this._overviewCalculator.computeBarGraphPercentages(record); var end = Math.round(percentages.end); @@ -161,6 +163,18 @@ WebInspector.TimelineOverviewPane.prototype = { this._overviewGrid.updateDividers(true, this._overviewCalculator); }, + updateEventDividers: function(records, dividerConstructor) + { + this._overviewGrid.removeEventDividers(); + for (var i = 0; i < records.length; ++i) { + var record = records[i]; + var positions = this._overviewCalculator.computeBarGraphPercentages(record); + var divider = dividerConstructor(record); + divider.style.left = positions.start + "%"; + this._overviewGrid.addEventDivider(divider); + } + }, + setSidebarWidth: function(width) { this._overviewSidebarElement.style.width = width + "px"; @@ -175,7 +189,10 @@ WebInspector.TimelineOverviewPane.prototype = { { this.windowLeft = 0.0; this.windowRight = 1.0; - this._setWindowPosition(0, this._overviewGrid.element.clientWidth); + this._overviewWindowElement.style.left = "0%"; + this._overviewWindowElement.style.width = "100%"; + this._leftResizeElement.style.left = "0%"; + this._rightResizeElement.style.left = "100%"; this._overviewCalculator.reset(); this._overviewGrid.updateDividers(true, this._overviewCalculator); }, diff --git a/WebCore/inspector/front-end/TimelinePanel.js b/WebCore/inspector/front-end/TimelinePanel.js index b6d5fde..7b3e3b0 100644 --- a/WebCore/inspector/front-end/TimelinePanel.js +++ b/WebCore/inspector/front-end/TimelinePanel.js @@ -37,6 +37,7 @@ WebInspector.TimelinePanel = function() this._overviewPane.addEventListener("window changed", this._windowChanged, this); this._overviewPane.addEventListener("filter changed", this._refresh, this); this.element.appendChild(this._overviewPane.element); + this.element.tabIndex = 0; this._sidebarBackgroundElement = document.createElement("div"); this._sidebarBackgroundElement.className = "sidebar timeline-sidebar-background"; @@ -75,14 +76,31 @@ WebInspector.TimelinePanel = function() this._bottomGapElement.className = "timeline-gap"; this._itemsGraphsElement.appendChild(this._bottomGapElement); - this._createStatusbarButtons(); - - this._records = []; + this._rootRecord = this._createRootRecord(); this._sendRequestRecords = {}; + this._timerRecords = {}; + this._calculator = new WebInspector.TimelineCalculator(); + this._calculator._showShortEvents = false; + var shortRecordThresholdTitle = Number.secondsToString(WebInspector.TimelinePanel.shortRecordThreshold, WebInspector.UIString.bind(WebInspector)); + this._showShortRecordsTitleText = WebInspector.UIString("Show the records that are shorter than %s", shortRecordThresholdTitle); + this._hideShortRecordsTitleText = WebInspector.UIString("Hide the records that are shorter than %s", shortRecordThresholdTitle); + this._createStatusbarButtons(); + this._boundariesAreValid = true; + this._scrollTop = 0; + + this._popoverHelper = new WebInspector.PopoverHelper(this._containerElement, this._getPopoverAnchor.bind(this), this._showPopover.bind(this), true); + + // Disable short events filter by default. + this.toggleFilterButton.toggled = true; + this._calculator._showShortEvents = this.toggleFilterButton.toggled; + this._markTimelineRecords = []; + this._expandOffset = 15; } +WebInspector.TimelinePanel.shortRecordThreshold = 0.015; + WebInspector.TimelinePanel.prototype = { toolbarItemClass: "timeline", @@ -93,7 +111,7 @@ WebInspector.TimelinePanel.prototype = { get statusBarItems() { - return [this.toggleTimelineButton.element, this.clearButton.element]; + return [this.toggleFilterButton.element, this.toggleTimelineButton.element, this.clearButton.element, this.recordsCounter]; }, get categories() @@ -108,21 +126,117 @@ WebInspector.TimelinePanel.prototype = { return this._categories; }, + get defaultFocusedElement() + { + return this.element; + }, + + get _recordStyles() + { + if (!this._recordStylesArray) { + var recordTypes = WebInspector.TimelineAgent.RecordType; + var recordStyles = {}; + recordStyles[recordTypes.EventDispatch] = { title: WebInspector.UIString("Event"), category: this.categories.scripting }; + recordStyles[recordTypes.Layout] = { title: WebInspector.UIString("Layout"), category: this.categories.rendering }; + recordStyles[recordTypes.RecalculateStyles] = { title: WebInspector.UIString("Recalculate Style"), category: this.categories.rendering }; + recordStyles[recordTypes.Paint] = { title: WebInspector.UIString("Paint"), category: this.categories.rendering }; + recordStyles[recordTypes.ParseHTML] = { title: WebInspector.UIString("Parse"), category: this.categories.loading }; + recordStyles[recordTypes.TimerInstall] = { title: WebInspector.UIString("Install Timer"), category: this.categories.scripting }; + recordStyles[recordTypes.TimerRemove] = { title: WebInspector.UIString("Remove Timer"), category: this.categories.scripting }; + recordStyles[recordTypes.TimerFire] = { title: WebInspector.UIString("Timer Fired"), category: this.categories.scripting }; + recordStyles[recordTypes.XHRReadyStateChange] = { title: WebInspector.UIString("XHR Ready State Change"), category: this.categories.scripting }; + recordStyles[recordTypes.XHRLoad] = { title: WebInspector.UIString("XHR Load"), category: this.categories.scripting }; + recordStyles[recordTypes.EvaluateScript] = { title: WebInspector.UIString("Evaluate Script"), category: this.categories.scripting }; + recordStyles[recordTypes.MarkTimeline] = { title: WebInspector.UIString("Mark"), category: this.categories.scripting }; + recordStyles[recordTypes.ResourceSendRequest] = { title: WebInspector.UIString("Send Request"), category: this.categories.loading }; + recordStyles[recordTypes.ResourceReceiveResponse] = { title: WebInspector.UIString("Receive Response"), category: this.categories.loading }; + recordStyles[recordTypes.ResourceFinish] = { title: WebInspector.UIString("Finish Loading"), category: this.categories.loading }; + recordStyles[recordTypes.FunctionCall] = { title: WebInspector.UIString("Function Call"), category: this.categories.scripting }; + recordStyles[recordTypes.ResourceReceiveData] = { title: WebInspector.UIString("Receive Data"), category: this.categories.loading }; + recordStyles[recordTypes.GCEvent] = { title: WebInspector.UIString("GC Event"), category: this.categories.scripting }; + recordStyles[recordTypes.MarkDOMContentEventType] = { title: WebInspector.UIString("DOMContent event"), category: this.categories.scripting }; + recordStyles[recordTypes.MarkLoadEventType] = { title: WebInspector.UIString("Load event"), category: this.categories.scripting }; + this._recordStylesArray = recordStyles; + } + return this._recordStylesArray; + }, + _createStatusbarButtons: function() { - this.toggleTimelineButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item"); + this.toggleTimelineButton = new WebInspector.StatusBarButton(WebInspector.UIString("Record"), "record-profile-status-bar-item"); this.toggleTimelineButton.addEventListener("click", this._toggleTimelineButtonClicked.bind(this), false); - this.clearButton = new WebInspector.StatusBarButton("", "timeline-clear-status-bar-item"); - this.clearButton.addEventListener("click", this.reset.bind(this), false); + this.clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear"), "timeline-clear-status-bar-item"); + this.clearButton.addEventListener("click", this._clearPanel.bind(this), false); + + this.toggleFilterButton = new WebInspector.StatusBarButton(this._hideShortRecordsTitleText, "timeline-filter-status-bar-item"); + this.toggleFilterButton.addEventListener("click", this._toggleFilterButtonClicked.bind(this), false); + + this.recordsCounter = document.createElement("span"); + this.recordsCounter.className = "timeline-records-counter"; + }, + + _updateRecordsCounter: function() + { + this.recordsCounter.textContent = WebInspector.UIString("%d of %d captured records are visible", this._rootRecord._visibleRecordsCount, this._rootRecord._allRecordsCount); + }, + + _updateEventDividers: function() + { + this._timelineGrid.removeEventDividers(); + var clientWidth = this._graphRowsElement.offsetWidth - this._expandOffset; + for (var i = 0; i < this._markTimelineRecords.length; ++i) { + var record = this._markTimelineRecords[i]; + var positions = this._calculator.computeBarGraphWindowPosition(record, clientWidth); + if (positions.left < 0 || positions.left >= clientWidth) + continue; + + var divider = this._createEventDivider(record); + divider.style.left = (positions.left + this._expandOffset) + "px"; + + this._timelineGrid.addEventDivider(divider); + } + this._overviewPane.updateEventDividers(this._markTimelineRecords, this._createEventDivider.bind(this)); + }, + + _createEventDivider: function(record) + { + var eventDivider = document.createElement("div"); + eventDivider.className = "resources-event-divider"; + var recordTypes = WebInspector.TimelineAgent.RecordType; + + var eventDividerPadding = document.createElement("div"); + eventDividerPadding.className = "resources-event-divider-padding"; + eventDividerPadding.title = record.title; + + if (record.type === recordTypes.MarkDOMContentEventType) + eventDivider.className += " resources-blue-divider"; + else if (record.type === recordTypes.MarkLoadEventType) + eventDivider.className += " resources-red-divider"; + else if (record.type === recordTypes.MarkTimeline) { + eventDivider.className += " resources-orange-divider"; + eventDividerPadding.title = record.data.message; + } + eventDividerPadding.appendChild(eventDivider); + return eventDividerPadding; }, _toggleTimelineButtonClicked: function() { if (this.toggleTimelineButton.toggled) InspectorBackend.stopTimelineProfiler(); - else + else { + this._clearPanel(); InspectorBackend.startTimelineProfiler(); + } + }, + + _toggleFilterButtonClicked: function() + { + this.toggleFilterButton.toggled = !this.toggleFilterButton.toggled; + this._calculator._showShortEvents = this.toggleFilterButton.toggled; + this.toggleFilterButton.element.title = this._calculator._showShortEvents ? this._hideShortRecordsTitleText : this._showShortRecordsTitleText; + this._scheduleRefresh(true); }, timelineWasStarted: function() @@ -137,116 +251,71 @@ WebInspector.TimelinePanel.prototype = { addRecordToTimeline: function(record) { - this._innerAddRecordToTimeline(record, this._records); + if (record.type == WebInspector.TimelineAgent.RecordType.ResourceSendRequest && record.data.isMainResource) { + if (this._mainResourceIdentifier != record.data.identifier) { + // We are loading new main resource -> clear the panel. Check above is necessary since + // there may be several resource loads with main resource marker upon redirects, redirects are reported with + // the original identifier. + this._mainResourceIdentifier = record.data.identifier; + this._clearPanel(); + } + } + this._innerAddRecordToTimeline(record, this._rootRecord); this._scheduleRefresh(); }, - _innerAddRecordToTimeline: function(record, collection) + _findParentRecord: function(record) { - var formattedRecord = this._formatRecord(record); - - // Glue subsequent records with same category and title together if they are closer than 100ms to each other. - if (this._lastRecord && (!record.children || !record.children.length) && - this._lastRecord.category == formattedRecord.category && - this._lastRecord.title == formattedRecord.title && - this._lastRecord.details == formattedRecord.details && - formattedRecord.startTime - this._lastRecord.endTime < 0.1) { - this._lastRecord.endTime = formattedRecord.endTime; - this._lastRecord.count++; - } else { - collection.push(formattedRecord); - for (var i = 0; record.children && i < record.children.length; ++i) { - if (!formattedRecord.children) - formattedRecord.children = []; - var formattedChild = this._innerAddRecordToTimeline(record.children[i], formattedRecord.children); - formattedChild.parent = formattedRecord; - } - this._lastRecord = record.children && record.children.length ? null : formattedRecord; - } - return formattedRecord; + var recordTypes = WebInspector.TimelineAgent.RecordType; + var parentRecord; + if (record.type === recordTypes.ResourceReceiveResponse || + record.type === recordTypes.ResourceFinish || + record.type === recordTypes.ResourceReceiveData) + parentRecord = this._sendRequestRecords[record.data.identifier]; + else if (record.type === recordTypes.TimerFire) + parentRecord = this._timerRecords[record.data.timerId]; + return parentRecord; }, - _formatRecord: function(record) + _innerAddRecordToTimeline: function(record, parentRecord) { - var recordTypes = WebInspector.TimelineAgent.RecordType; - if (!this._recordStyles) { - this._recordStyles = {}; - this._recordStyles[recordTypes.EventDispatch] = { title: WebInspector.UIString("Event"), category: this.categories.scripting }; - this._recordStyles[recordTypes.Layout] = { title: WebInspector.UIString("Layout"), category: this.categories.rendering }; - this._recordStyles[recordTypes.RecalculateStyles] = { title: WebInspector.UIString("Recalculate Style"), category: this.categories.rendering }; - this._recordStyles[recordTypes.Paint] = { title: WebInspector.UIString("Paint"), category: this.categories.rendering }; - this._recordStyles[recordTypes.ParseHTML] = { title: WebInspector.UIString("Parse"), category: this.categories.loading }; - this._recordStyles[recordTypes.TimerInstall] = { title: WebInspector.UIString("Install Timer"), category: this.categories.scripting }; - this._recordStyles[recordTypes.TimerRemove] = { title: WebInspector.UIString("Remove Timer"), category: this.categories.scripting }; - this._recordStyles[recordTypes.TimerFire] = { title: WebInspector.UIString("Timer Fired"), category: this.categories.scripting }; - this._recordStyles[recordTypes.XHRReadyStateChange] = { title: WebInspector.UIString("XHR Ready State Change"), category: this.categories.scripting }; - this._recordStyles[recordTypes.XHRLoad] = { title: WebInspector.UIString("XHR Load"), category: this.categories.scripting }; - this._recordStyles[recordTypes.EvaluateScript] = { title: WebInspector.UIString("Evaluate Script"), category: this.categories.scripting }; - this._recordStyles[recordTypes.MarkTimeline] = { title: WebInspector.UIString("Mark"), category: this.categories.scripting }; - this._recordStyles[recordTypes.ResourceSendRequest] = { title: WebInspector.UIString("Send Request"), category: this.categories.loading }; - this._recordStyles[recordTypes.ResourceReceiveResponse] = { title: WebInspector.UIString("Receive Response"), category: this.categories.loading }; - this._recordStyles[recordTypes.ResourceFinish] = { title: WebInspector.UIString("Finish Loading"), category: this.categories.loading }; + var connectedToOldRecord = false; + if (parentRecord === this._rootRecord) { + var newParentRecord = this._findParentRecord(record); + if (newParentRecord) { + parentRecord = newParentRecord; + connectedToOldRecord = true; + } } + var recordTypes = WebInspector.TimelineAgent.RecordType; - var style = this._recordStyles[record.type]; - if (!style) - style = this._recordStyles[recordTypes.EventDispatch]; - - var formattedRecord = {}; - formattedRecord.category = style.category; - formattedRecord.title = style.title; - formattedRecord.startTime = record.startTime / 1000; - formattedRecord.data = record.data; - formattedRecord.count = 1; - formattedRecord.type = record.type; - formattedRecord.endTime = (typeof record.endTime !== "undefined") ? record.endTime / 1000 : formattedRecord.startTime; - formattedRecord.record = record; - - // Make resource receive record last since request was sent; make finish record last since response received. - if (record.type === WebInspector.TimelineAgent.RecordType.ResourceSendRequest) { - this._sendRequestRecords[record.data.identifier] = formattedRecord; - } else if (record.type === WebInspector.TimelineAgent.RecordType.ResourceReceiveResponse) { - var sendRequestRecord = this._sendRequestRecords[record.data.identifier]; - if (sendRequestRecord) { // False if we started instrumentation in the middle of request. - sendRequestRecord._responseReceivedFormattedTime = formattedRecord.startTime; - formattedRecord.startTime = sendRequestRecord.startTime; - sendRequestRecord.details = this._getRecordDetails(record); - } - } else if (record.type === WebInspector.TimelineAgent.RecordType.ResourceFinish) { - var sendRequestRecord = this._sendRequestRecords[record.data.identifier]; - if (sendRequestRecord) // False for main resource. - formattedRecord.startTime = sendRequestRecord._responseReceivedFormattedTime; + var formattedRecord = new WebInspector.TimelinePanel.FormattedRecord(record, parentRecord, this._recordStyles, this._sendRequestRecords, this._timerRecords); + + if (record.type === recordTypes.MarkDOMContentEventType || record.type === recordTypes.MarkLoadEventType) { + // No bar entry for load events. + this._markTimelineRecords.push(formattedRecord); + return; } - formattedRecord.details = this._getRecordDetails(record); - return formattedRecord; - }, + ++this._rootRecord._allRecordsCount; - _getRecordDetails: function(record) - { - switch (record.type) { - case WebInspector.TimelineAgent.RecordType.EventDispatch: - return record.data ? record.data.type : ""; - case WebInspector.TimelineAgent.RecordType.Paint: - return record.data.width + "\u2009\u00d7\u2009" + record.data.height; - case WebInspector.TimelineAgent.RecordType.TimerInstall: - case WebInspector.TimelineAgent.RecordType.TimerRemove: - case WebInspector.TimelineAgent.RecordType.TimerFire: - return record.data.timerId; - case WebInspector.TimelineAgent.RecordType.XHRReadyStateChange: - case WebInspector.TimelineAgent.RecordType.XHRLoad: - case WebInspector.TimelineAgent.RecordType.EvaluateScript: - case WebInspector.TimelineAgent.RecordType.ResourceSendRequest: - return WebInspector.displayNameForURL(record.data.url); - case WebInspector.TimelineAgent.RecordType.ResourceReceiveResponse: - case WebInspector.TimelineAgent.RecordType.ResourceFinish: - var sendRequestRecord = this._sendRequestRecords[record.data.identifier]; - return sendRequestRecord ? WebInspector.displayNameForURL(sendRequestRecord.data.url) : ""; - case WebInspector.TimelineAgent.RecordType.MarkTimeline: - return record.data.message; - default: - return ""; + if (parentRecord === this._rootRecord) + formattedRecord.collapsed = true; + + for (var i = 0; record.children && i < record.children.length; ++i) + this._innerAddRecordToTimeline(record.children[i], formattedRecord); + + if (connectedToOldRecord) { + var record = formattedRecord; + while (record.parent && record.parent._lastChildEndTime < record._lastChildEndTime) { + record.parent._lastChildEndTime = record._lastChildEndTime; + record = record.parent; + } } + + // Keep bar entry for mark timeline since nesting might be interesting to the user. + if (record.type === recordTypes.MarkTimeline) + this._markTimelineRecords.push(formattedRecord); }, setSidebarWidth: function(width) @@ -263,31 +332,52 @@ WebInspector.TimelinePanel.prototype = { this._overviewPane.updateMainViewWidth(width); }, - resize: function() { + resize: function() + { + this._closeRecordDetails(); this._scheduleRefresh(); }, - reset: function() + _createRootRecord: function() { - this._lastRecord = null; + var rootRecord = {}; + rootRecord.children = []; + rootRecord._visibleRecordsCount = 0; + rootRecord._allRecordsCount = 0; + return rootRecord; + }, + + _clearPanel: function() + { + this._markTimelineRecords = []; this._sendRequestRecords = {}; - this._records = []; + this._timerRecords = {}; + this._rootRecord = this._createRootRecord(); this._boundariesAreValid = false; this._overviewPane.reset(); this._adjustScrollPosition(0); this._refresh(); + this._closeRecordDetails(); }, show: function() { WebInspector.Panel.prototype.show.call(this); - - if (this._needsRefresh) + if (typeof this._scrollTop === "number") + this._containerElement.scrollTop = this._scrollTop; + else if (this._needsRefresh) this._refresh(); }, + hide: function() + { + WebInspector.Panel.prototype.hide.call(this); + this._closeRecordDetails(); + }, + _onScroll: function(event) { + this._closeRecordDetails(); var scrollTop = this._containerElement.scrollTop; var dividersTop = Math.max(0, scrollTop); this._timelineGrid.setScrollAndDividerTop(scrollTop, dividersTop); @@ -296,11 +386,13 @@ WebInspector.TimelinePanel.prototype = { _windowChanged: function() { + this._closeRecordDetails(); this._scheduleRefresh(); }, _scheduleRefresh: function(preserveBoundaries) { + this._closeRecordDetails(); this._boundariesAreValid &= preserveBoundaries; if (this._needsRefresh) return; @@ -321,49 +413,72 @@ WebInspector.TimelinePanel.prototype = { clearTimeout(this._refreshTimeout); delete this._refreshTimeout; } - - if (!this._boundariesAreValid) - this._overviewPane.update(this._records); + + this._overviewPane.update(this._rootRecord.children, this._calculator._showShortEvents); this._refreshRecords(!this._boundariesAreValid); + this._updateRecordsCounter(); + this._updateEventDividers(); this._boundariesAreValid = true; }, - _refreshRecords: function(updateBoundaries) + _updateBoundaries: function() { - if (updateBoundaries) { - this._calculator.reset(); - this._calculator.windowLeft = this._overviewPane.windowLeft; - this._calculator.windowRight = this._overviewPane.windowRight; + this._calculator.reset(); + this._calculator.windowLeft = this._overviewPane.windowLeft; + this._calculator.windowRight = this._overviewPane.windowRight; + + for (var i = 0; i < this._rootRecord.children.length; ++i) + this._calculator.updateBoundaries(this._rootRecord.children[i]); - for (var i = 0; i < this._records.length; ++i) - this._calculator.updateBoundaries(this._records[i]); + this._calculator.calculateWindow(); + }, - this._calculator.calculateWindow(); + _addToRecordsWindow: function(record, recordsWindow, parentIsCollapsed) + { + if (!this._calculator._showShortEvents && !record.isLong()) + return; + var percentages = this._calculator.computeBarGraphPercentages(record); + if (percentages.start < 100 && percentages.endWithChildren >= 0 && !record.category.hidden) { + ++this._rootRecord._visibleRecordsCount; + ++record.parent._invisibleChildrenCount; + if (!parentIsCollapsed) + recordsWindow.push(record); } + var index = recordsWindow.length; + record._invisibleChildrenCount = 0; + for (var i = 0; i < record.children.length; ++i) + this._addToRecordsWindow(record.children[i], recordsWindow, parentIsCollapsed || record.collapsed); + record._visibleChildrenCount = recordsWindow.length - index; + }, + + _filterRecords: function() + { var recordsInWindow = []; - for (var i = 0; i < this._records.length; ++i) { - var record = this._records[i]; - var percentages = this._calculator.computeBarGraphPercentages(record); - if (percentages.start < 100 && percentages.end >= 0 && !record.category.hidden) - this._addToRecordsWindow(record, recordsInWindow); - } + this._rootRecord._visibleRecordsCount = 0; + for (var i = 0; i < this._rootRecord.children.length; ++i) + this._addToRecordsWindow(this._rootRecord.children[i], recordsInWindow); + return recordsInWindow; + }, + + _refreshRecords: function(updateBoundaries) + { + if (updateBoundaries) + this._updateBoundaries(); + + var recordsInWindow = this._filterRecords(); // Calculate the visible area. - var visibleTop = this._containerElement.scrollTop; + this._scrollTop = this._containerElement.scrollTop; + var visibleTop = this._scrollTop; var visibleBottom = visibleTop + this._containerElement.clientHeight; // Define row height, should be in sync with styles for timeline graphs. const rowHeight = 18; - const expandOffset = 15; // Convert visible area to visible indexes. Always include top-level record for a visible nested record. var startIndex = Math.max(0, Math.min(Math.floor(visibleTop / rowHeight) - 1, recordsInWindow.length - 1)); - while (startIndex > 0 && recordsInWindow[startIndex].parent) - startIndex--; var endIndex = Math.min(recordsInWindow.length, Math.ceil(visibleBottom / rowHeight)); - while (endIndex < recordsInWindow.length - 1 && recordsInWindow[endIndex].parent) - endIndex++; // Resize gaps first. const top = (startIndex * rowHeight) + "px"; @@ -378,6 +493,7 @@ WebInspector.TimelinePanel.prototype = { this._itemsGraphsElement.removeChild(this._graphRowsElement); var graphRowElement = this._graphRowsElement.firstChild; var scheduleRefreshCallback = this._scheduleRefresh.bind(this, true); + for (var i = startIndex; i < endIndex; ++i) { var record = recordsInWindow[i]; var isEven = !(i % 2); @@ -391,8 +507,8 @@ WebInspector.TimelinePanel.prototype = { this._graphRowsElement.appendChild(graphRowElement); } - listRowElement.listRow.update(record, isEven); - graphRowElement.graphRow.update(record, isEven, this._calculator, width, expandOffset, i); + listRowElement.row.update(record, isEven, this._calculator, visibleTop); + graphRowElement.row.update(record, isEven, this._calculator, width, this._expandOffset, i); listRowElement = listRowElement.nextSibling; graphRowElement = graphRowElement.nextSibling; @@ -401,45 +517,52 @@ WebInspector.TimelinePanel.prototype = { // Remove extra rows. while (listRowElement) { var nextElement = listRowElement.nextSibling; - listRowElement.listRow.dispose(); + listRowElement.row.dispose(); listRowElement = nextElement; } while (graphRowElement) { var nextElement = graphRowElement.nextSibling; - graphRowElement.graphRow.dispose(); + graphRowElement.row.dispose(); graphRowElement = nextElement; } this._itemsGraphsElement.insertBefore(this._graphRowsElement, this._bottomGapElement); + this.sidebarResizeElement.style.height = this.sidebarElement.clientHeight + "px"; // Reserve some room for expand / collapse controls to the left for records that start at 0ms. - var timelinePaddingLeft = this._calculator.windowLeft === 0 ? expandOffset : 0; + var timelinePaddingLeft = this._calculator.windowLeft === 0 ? this._expandOffset : 0; if (updateBoundaries) this._timelineGrid.updateDividers(true, this._calculator, timelinePaddingLeft); this._adjustScrollPosition((recordsInWindow.length + 1) * rowHeight); }, - _addToRecordsWindow: function(record, recordsWindow) - { - recordsWindow.push(record); - if (!record.collapsed) { - var index = recordsWindow.length; - for (var i = 0; record.children && i < record.children.length; ++i) - this._addToRecordsWindow(record.children[i], recordsWindow); - record.visibleChildrenCount = recordsWindow.length - index; - } - }, - _adjustScrollPosition: function(totalHeight) { // Prevent the container from being scrolled off the end. if ((this._containerElement.scrollTop + this._containerElement.offsetHeight) > totalHeight + 1) this._containerElement.scrollTop = (totalHeight - this._containerElement.offsetHeight); + }, + + _getPopoverAnchor: function(element) + { + return element.enclosingNodeOrSelfWithClass("timeline-graph-bar") || element.enclosingNodeOrSelfWithClass("timeline-tree-item"); + }, + + _showPopover: function(anchor) + { + var record = anchor.row._record; + var popover = new WebInspector.Popover(record._generatePopupContent(this._calculator)); + popover.show(anchor); + return popover; + }, + + _closeRecordDetails: function() + { + this._popoverHelper.hidePopup(); } } WebInspector.TimelinePanel.prototype.__proto__ = WebInspector.Panel.prototype; - WebInspector.TimelineCategory = function(name, title, color) { this.name = name; @@ -447,7 +570,6 @@ WebInspector.TimelineCategory = function(name, title, color) this.color = color; } - WebInspector.TimelineCalculator = function() { this.reset(); @@ -461,7 +583,22 @@ WebInspector.TimelineCalculator.prototype = { { var start = (record.startTime - this.minimumBoundary) / this.boundarySpan * 100; var end = (record.endTime - this.minimumBoundary) / this.boundarySpan * 100; - return {start: start, end: end}; + var endWithChildren = (record._lastChildEndTime - this.minimumBoundary) / this.boundarySpan * 100; + return {start: start, end: end, endWithChildren: endWithChildren}; + }, + + computeBarGraphWindowPosition: function(record, clientWidth) + { + const minWidth = 5; + const borderWidth = 4; + var workingArea = clientWidth - minWidth - borderWidth; + var percentages = this.computeBarGraphPercentages(record); + var left = percentages.start / 100 * workingArea; + var width = (percentages.end - percentages.start) / 100 * workingArea + minWidth; + var widthWithChildren = (percentages.endWithChildren - percentages.start) / 100 * workingArea; + if (percentages.endWithChildren > percentages.end) + widthWithChildren += borderWidth + minWidth; + return {left: left, width: width, widthWithChildren: widthWithChildren}; }, calculateWindow: function() @@ -483,7 +620,9 @@ WebInspector.TimelineCalculator.prototype = { if (this._absoluteMinimumBoundary === -1 || lowerBound < this._absoluteMinimumBoundary) this._absoluteMinimumBoundary = lowerBound; - var upperBound = record.endTime; + const minimumTimeFrame = 0.1; + const minimumDeltaForZeroSizeEvents = 0.01; + var upperBound = Math.max(record._lastChildEndTime + minimumDeltaForZeroSizeEvents, lowerBound + minimumTimeFrame); if (this._absoluteMaximumBoundary === -1 || upperBound > this._absoluteMaximumBoundary) this._absoluteMaximumBoundary = upperBound; }, @@ -498,7 +637,8 @@ WebInspector.TimelineCalculator.prototype = { WebInspector.TimelineRecordListRow = function() { this.element = document.createElement("div"); - this.element.listRow = this; + this.element.row = this; + this.element.style.cursor = "pointer"; var iconElement = document.createElement("span"); iconElement.className = "timeline-tree-icon"; this.element.appendChild(iconElement); @@ -523,18 +663,19 @@ WebInspector.TimelineRecordListRow = function() } WebInspector.TimelineRecordListRow.prototype = { - update: function(record, isEven) + update: function(record, isEven, calculator, offset) { + this._record = record; + this._calculator = calculator; + this._offset = offset; + this.element.className = "timeline-tree-item timeline-category-" + record.category.name + (isEven ? " even" : ""); this._typeElement.textContent = record.title; - if (record.details) { + if (record.details) this._dataElement.textContent = "(" + record.details + ")"; - this._dataElement.title = record.details; - } else { + else this._dataElement.textContent = ""; - this._dataElement.title = ""; - } if (record.count > 1) this._repeatCountElement.textContent = "\u2009\u00d7\u2009" + record.count; @@ -548,18 +689,23 @@ WebInspector.TimelineRecordListRow.prototype = { } } - -WebInspector.TimelineRecordGraphRow = function(graphContainer, refreshCallback, rowHeight) +WebInspector.TimelineRecordGraphRow = function(graphContainer, scheduleRefresh, rowHeight) { this.element = document.createElement("div"); - this.element.graphRow = this; + this.element.row = this; this._barAreaElement = document.createElement("div"); this._barAreaElement.className = "timeline-graph-bar-area"; this.element.appendChild(this._barAreaElement); + this._barWithChildrenElement = document.createElement("div"); + this._barWithChildrenElement.className = "timeline-graph-bar with-children"; + this._barWithChildrenElement.row = this; + this._barAreaElement.appendChild(this._barWithChildrenElement); + this._barElement = document.createElement("div"); this._barElement.className = "timeline-graph-bar"; + this._barElement.row = this; this._barAreaElement.appendChild(this._barElement); this._expandElement = document.createElement("div"); @@ -571,8 +717,9 @@ WebInspector.TimelineRecordGraphRow = function(graphContainer, refreshCallback, this._expandElement.appendChild(leftBorder); this._expandElement.addEventListener("click", this._onClick.bind(this)); - this._refreshCallback = refreshCallback; this._rowHeight = rowHeight; + + this._scheduleRefresh = scheduleRefresh; } WebInspector.TimelineRecordGraphRow.prototype = { @@ -580,18 +727,18 @@ WebInspector.TimelineRecordGraphRow.prototype = { { this._record = record; this.element.className = "timeline-graph-side timeline-category-" + record.category.name + (isEven ? " even" : ""); - var percentages = calculator.computeBarGraphPercentages(record); - var left = percentages.start / 100 * clientWidth; - var width = (percentages.end - percentages.start) / 100 * clientWidth; - this._barElement.style.left = (left + expandOffset) + "px"; - this._barElement.style.width = width + "px"; + var barPosition = calculator.computeBarGraphWindowPosition(record, clientWidth - expandOffset); + this._barWithChildrenElement.style.left = barPosition.left + expandOffset + "px"; + this._barWithChildrenElement.style.width = barPosition.widthWithChildren + "px"; + this._barElement.style.left = barPosition.left + expandOffset + "px"; + this._barElement.style.width = barPosition.width + "px"; - if (record.visibleChildrenCount) { + if (record._visibleChildrenCount || record._invisibleChildrenCount) { this._expandElement.style.top = index * this._rowHeight + "px"; - this._expandElement.style.left = left + "px"; - this._expandElement.style.width = Math.max(12, width + 25) + "px"; + this._expandElement.style.left = barPosition.left + "px"; + this._expandElement.style.width = Math.max(12, barPosition.width + 25) + "px"; if (!record.collapsed) { - this._expandElement.style.height = (record.visibleChildrenCount + 1) * this._rowHeight + "px"; + this._expandElement.style.height = (record._visibleChildrenCount + 1) * this._rowHeight + "px"; this._expandElement.addStyleClass("timeline-expandable-expanded"); this._expandElement.removeStyleClass("timeline-expandable-collapsed"); } else { @@ -608,7 +755,7 @@ WebInspector.TimelineRecordGraphRow.prototype = { _onClick: function(event) { this._record.collapsed = !this._record.collapsed; - this._refreshCallback(); + this._scheduleRefresh(); }, dispose: function() @@ -617,3 +764,199 @@ WebInspector.TimelineRecordGraphRow.prototype = { this._expandElement.parentElement.removeChild(this._expandElement); } } + +WebInspector.TimelinePanel.FormattedRecord = function(record, parentRecord, recordStyles, sendRequestRecords, timerRecords) +{ + var recordTypes = WebInspector.TimelineAgent.RecordType; + var style = recordStyles[record.type]; + + this.parent = parentRecord; + parentRecord.children.push(this); + this.category = style.category; + this.title = style.title; + this.startTime = record.startTime / 1000; + this.data = record.data; + this.count = 1; + this.type = record.type; + this.endTime = (typeof record.endTime !== "undefined") ? record.endTime / 1000 : this.startTime; + this._lastChildEndTime = this.endTime; + this.originalRecordForTests = record; + this.callerScriptName = record.callerScriptName; + this.callerScriptLine = record.callerScriptLine; + this.totalHeapSize = record.totalHeapSize; + this.usedHeapSize = record.usedHeapSize; + + // Make resource receive record last since request was sent; make finish record last since response received. + if (record.type === recordTypes.ResourceSendRequest) { + sendRequestRecords[record.data.identifier] = this; + } else if (record.type === recordTypes.ResourceReceiveResponse) { + var sendRequestRecord = sendRequestRecords[record.data.identifier]; + if (sendRequestRecord) { // False if we started instrumentation in the middle of request. + record.data.url = sendRequestRecord.data.url; + // Now that we have resource in the collection, recalculate details in order to display short url. + sendRequestRecord.details = this._getRecordDetails(sendRequestRecord, sendRequestRecords); + } + } else if (record.type === recordTypes.ResourceReceiveData) { + var sendRequestRecord = sendRequestRecords[record.data.identifier]; + if (sendRequestRecord) // False for main resource. + record.data.url = sendRequestRecord.data.url; + } else if (record.type === recordTypes.ResourceFinish) { + var sendRequestRecord = sendRequestRecords[record.data.identifier]; + if (sendRequestRecord) // False for main resource. + record.data.url = sendRequestRecord.data.url; + } else if (record.type === recordTypes.TimerInstall) { + this.timeout = record.data.timeout; + this.singleShot = record.data.singleShot; + timerRecords[record.data.timerId] = this; + } else if (record.type === recordTypes.TimerFire) { + var timerInstalledRecord = timerRecords[record.data.timerId]; + if (timerInstalledRecord) { + this.callSiteScriptName = timerInstalledRecord.callerScriptName; + this.callSiteScriptLine = timerInstalledRecord.callerScriptLine; + this.timeout = timerInstalledRecord.timeout; + this.singleShot = timerInstalledRecord.singleShot; + } + } + this.details = this._getRecordDetails(record, sendRequestRecords); +} + +WebInspector.TimelinePanel.FormattedRecord.prototype = { + isLong: function() + { + return (this._lastChildEndTime - this.startTime) > WebInspector.TimelinePanel.shortRecordThreshold; + }, + + _createCell: function(content, styleName) + { + var text = document.createElement("label"); + text.appendChild(document.createTextNode(content)); + var cell = document.createElement("td"); + cell.className = "timeline-details"; + if (styleName) + cell.className += " " + styleName; + cell.textContent = content; + return cell; + }, + + get children() + { + if (!this._children) + this._children = []; + return this._children; + }, + + _createRow: function(title, content) + { + var row = document.createElement("tr"); + row.appendChild(this._createCell(title, "timeline-details-row-title")); + row.appendChild(this._createCell(content, "timeline-details-row-data")); + return row; + }, + + _createLinkRow: function(title, content) + { + var row = document.createElement("tr"); + row.appendChild(this._createCell(title, "timeline-details-row-title")); + var cell = document.createElement("td"); + cell.appendChild(content); + row.appendChild(cell); + return row; + }, + + _generatePopupContent: function(calculator) + { + var recordContentTable = document.createElement("table"); + var titleCell = this._createCell(WebInspector.UIString("%s - Details", this.title), "timeline-details-title"); + titleCell.colSpan = 2; + var titleRow = document.createElement("tr"); + titleRow.appendChild(titleCell); + recordContentTable.appendChild(titleRow); + var text = Number.secondsToString(this.endTime - this.startTime) + " (@" + + calculator.formatValue(this.startTime - calculator.minimumBoundary) + ")"; + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Duration"), text)); + + const recordTypes = WebInspector.TimelineAgent.RecordType; + if (this.details) { + if (this.type === recordTypes.GCEvent ) + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Collected"), Number.bytesToString(this.data.usedHeapSizeDelta))); + else if (this.type === recordTypes.TimerInstall || + this.type === recordTypes.TimerFire || + this.type === recordTypes.TimerRemove) { + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Timer Id"), this.data.timerId)); + if (typeof this.timeout === "number") { + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Timeout"), this.timeout)); + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Repeats"), !this.singleShot)); + } + if (typeof this.callSiteScriptLine === "number") { + var link = WebInspector.linkifyResourceAsNode(this.callSiteScriptName, "scripts", this.callSiteScriptLine, "timeline-details"); + recordContentTable.appendChild(this._createLinkRow(WebInspector.UIString("Call Site"), link)); + } + } else if (this.type === recordTypes.FunctionCall) { + var link = WebInspector.linkifyResourceAsNode(this.data.scriptName, "scripts", this.data.scriptLine, "timeline-details"); + recordContentTable.appendChild(this._createLinkRow(WebInspector.UIString("Location"), link)); + } else if (this.type === recordTypes.ResourceSendRequest || + this.type === recordTypes.ResourceReceiveResponse || + this.type === recordTypes.ResourceReceiveData || + this.type === recordTypes.ResourceFinish) { + var link = WebInspector.linkifyResourceAsNode(this.data.url, "resources", null, "timeline-details"); + recordContentTable.appendChild(this._createLinkRow(WebInspector.UIString("Resource"), link)); + if (this.data.requestMethod) + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Request Method"), this.data.requestMethod)); + if (typeof this.data.statusCode === "number") + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Status Code"), this.data.statusCode)); + if (this.data.mimeType) + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Mime Type"), this.data.mimeType)); + if (typeof this.data.expectedContentLength === "number" && this.data.expectedContentLength !== -1) + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Expected Content Length"), this.data.expectedContentLength)); + } else if (this.type === recordTypes.EvaluateScript) { + var link = WebInspector.linkifyResourceAsNode(this.data.url, "scripts", null, "timeline-details"); + recordContentTable.appendChild(this._createLinkRow(WebInspector.UIString("Script"), link)); + } else if (this.type === recordTypes.Paint) { + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Location"), this.data.x + "\u2009\u00d7\u2009" + this.data.y)); + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Dimensions"), this.data.width + "\u2009\u00d7\u2009" + this.data.height)); + } else + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Details"), this.details)); + } + + if (this.type !== recordTypes.GCEvent && this.callerScriptName) { + var link = WebInspector.linkifyResourceAsNode(this.callerScriptName, "scripts", this.callerScriptLine); + recordContentTable.appendChild(this._createLinkRow(WebInspector.UIString("Caller"), link)); + } + if (this.usedHeapSize) { + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Used Heap Size"), Number.bytesToString(this.usedHeapSize))); + recordContentTable.appendChild(this._createRow(WebInspector.UIString("Total Heap Size"), Number.bytesToString(this.totalHeapSize))); + } + return recordContentTable; + }, + + _getRecordDetails: function(record, sendRequestRecords) + { + switch (record.type) { + case WebInspector.TimelineAgent.RecordType.GCEvent: + return WebInspector.UIString("%s collected", Number.bytesToString(record.data.usedHeapSizeDelta)); + case WebInspector.TimelineAgent.RecordType.FunctionCall: + return WebInspector.displayNameForURL(record.data.scriptName) + ":" + record.data.scriptLine; + case WebInspector.TimelineAgent.RecordType.EventDispatch: + return record.data ? record.data.type : ""; + case WebInspector.TimelineAgent.RecordType.Paint: + return record.data.width + "\u2009\u00d7\u2009" + record.data.height; + case WebInspector.TimelineAgent.RecordType.TimerInstall: + case WebInspector.TimelineAgent.RecordType.TimerRemove: + case WebInspector.TimelineAgent.RecordType.TimerFire: + return record.data.timerId; + case WebInspector.TimelineAgent.RecordType.XHRReadyStateChange: + case WebInspector.TimelineAgent.RecordType.XHRLoad: + case WebInspector.TimelineAgent.RecordType.EvaluateScript: + case WebInspector.TimelineAgent.RecordType.ResourceSendRequest: + case WebInspector.TimelineAgent.RecordType.ResourceReceiveData: + case WebInspector.TimelineAgent.RecordType.ResourceReceiveResponse: + case WebInspector.TimelineAgent.RecordType.ResourceFinish: + return WebInspector.displayNameForURL(record.data.url); + case WebInspector.TimelineAgent.RecordType.MarkTimeline: + return record.data.message; + default: + return ""; + } + } +} + diff --git a/WebCore/inspector/front-end/WebKit.qrc b/WebCore/inspector/front-end/WebKit.qrc index 73309d1..47a2e90 100644 --- a/WebCore/inspector/front-end/WebKit.qrc +++ b/WebCore/inspector/front-end/WebKit.qrc @@ -13,6 +13,7 @@ <file>Callback.js</file> <file>CallStackSidebarPane.js</file> <file>ChangesView.js</file> + <file>Checkbox.js</file> <file>Color.js</file> <file>ConsolePanel.js</file> <file>ConsoleView.js</file> @@ -32,6 +33,7 @@ <file>EventListenersSidebarPane.js</file> <file>FontView.js</file> <file>ImageView.js</file> + <file>InjectedFakeWorker.js</file> <file>InjectedScript.js</file> <file>InjectedScriptAccess.js</file> <file>inspector.js</file> @@ -88,12 +90,20 @@ <file>View.js</file> <file>WatchExpressionsSidebarPane.js</file> <file>WelcomeView.js</file> + <file>WorkersSidebarPane.js</file> <file>audits.css</file> <file>inspector.css</file> <file>inspectorSyntaxHighlight.css</file> <file>popover.css</file> <file>textViewer.css</file> + <file>Images/auditsIcon.png</file> <file>Images/back.png</file> + <file>Images/breakpointBorder.png</file> + <file>Images/breakpointConditionalBorder.png</file> + <file>Images/breakpointConditionalCounterBorder.png</file> + <file>Images/breakpointCounterBorder.png</file> + <file>Images/breakpointsActivateButtonGlyph.png</file> + <file>Images/breakpointsDeactivateButtonGlyph.png</file> <file>Images/checker.png</file> <file>Images/clearConsoleButtonGlyph.png</file> <file>Images/closeButtons.png</file> @@ -151,6 +161,7 @@ <file>Images/profilesIcon.png</file> <file>Images/profileSmallIcon.png</file> <file>Images/profilesSilhouette.png</file> + <file>Images/programCounterBorder.png</file> <file>Images/radioDot.png</file> <file>Images/recordButtonGlyph.png</file> <file>Images/recordToggledButtonGlyph.png</file> @@ -178,6 +189,7 @@ <file>Images/segmentSelected.png</file> <file>Images/segmentSelectedEnd.png</file> <file>Images/sessionStorage.png</file> + <file>Images/spinner.gif</file> <file>Images/splitviewDimple.png</file> <file>Images/splitviewDividerBackground.png</file> <file>Images/statusbarBackground.png</file> diff --git a/WebCore/inspector/front-end/WorkersSidebarPane.js b/WebCore/inspector/front-end/WorkersSidebarPane.js new file mode 100644 index 0000000..ed2b1c4 --- /dev/null +++ b/WebCore/inspector/front-end/WorkersSidebarPane.js @@ -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. + */ + +WebInspector.WorkersSidebarPane = function() +{ + WebInspector.SidebarPane.call(this, WebInspector.UIString("Workers")); + + this._workers = {}; + + this._enableWorkersCheckbox = new WebInspector.Checkbox( + WebInspector.UIString("Debug"), + this._onTriggerInstrument.bind(this), + false, + "sidebar-pane-subtitle", + WebInspector.UIString("Allow debugging workers. Enabling this option will replace native workers with the iframe-based JavaScript implementation")); + + this.titleElement.insertBefore(this._enableWorkersCheckbox.element, this.titleElement.firstChild); + + this._listElement = document.createElement("ol"); + this._listElement.className = "workers-list"; + + this.bodyElement.appendChild(this._listElement); + this._treeOutline = new TreeOutline(this._listElement); +} + +WebInspector.WorkersSidebarPane.prototype = { + addWorker: function(id, url, isShared) + { + if (id in this._workers) + return; + var worker = new WebInspector.Worker(id, url, isShared); + this._workers[id] = worker; + + var title = WebInspector.linkifyURL(url, WebInspector.displayNameForURL(url), "worker-item", true, url); + var treeElement = new TreeElement(title, worker, false); + this._treeOutline.appendChild(treeElement); + }, + + removeWorker: function(id) + { + if (id in this._workers) { + this._treeOutline.removeChild(this._treeOutline.findTreeElement(this._workers[id])); + delete this._workers[id]; + } + }, + + setInstrumentation: function(enabled) + { + InspectorBackend.removeAllScriptsToEvaluateOnLoad(); + if (enabled) + InspectorBackend.addScriptToEvaluateOnLoad("(" + InjectedFakeWorker + ")"); + }, + + reset: function() + { + InspectorBackend.removeAllScriptsToEvaluateOnLoad(); + this.setInstrumentation(this._enableWorkersCheckbox.checked()); + this._treeOutline.removeChildren(); + this._workers = {}; + }, + + _onTriggerInstrument: function(event) + { + this.setInstrumentation(this._enableWorkersCheckbox.checked()); + } +}; + +WebInspector.WorkersSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; + +WebInspector.Worker = function(id, url, shared) +{ + this.id = id; + this.url = url; + this.shared = shared; +} + +WebInspector.didCreateWorker = function() +{ + var workersPane = WebInspector.panels.scripts.sidebarPanes.workers; + workersPane.addWorker.apply(workersPane, arguments); +} + +WebInspector.didDestroyWorker = function() +{ + var workersPane = WebInspector.panels.scripts.sidebarPanes.workers; + workersPane.removeWorker.apply(workersPane, arguments); +} diff --git a/WebCore/inspector/front-end/audits.css b/WebCore/inspector/front-end/audits.css index 9d02c80..ad12db3 100644 --- a/WebCore/inspector/front-end/audits.css +++ b/WebCore/inspector/front-end/audits.css @@ -50,104 +50,6 @@ button.clear-audit-results-status-bar-item .glyph { -webkit-mask-image: url(Images/clearConsoleButtonGlyph.png); } -#audit-result-view { - display: none; - overflow: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgb(245, 245, 245); - cursor: default; - overflow: auto; -} - -#audit-result-view.visible { - display: block; -} - -#audit-result-view > .pane img.score { - float: left; - margin-top: 2px; - position: relative; - height: 16px; - width: 16px; - z-index: 100; -} - -#audit-result-view > .pane img.score.red { - content: url(Images/errorRedDot.png); -} - -#audit-result-view > .pane img.score.green { - content: url(Images/successGreenDot.png); -} - -#audit-result-view > .pane.expanded:nth-last-of-type(1) { - border-bottom: 1px solid rgb(189, 189, 189) !important; -} - -#audit-result-view .pane.expanded:nth-last-of-type(1) { - border-bottom: 0px transparent none; -} - -#audit-result-view > .pane > .body > .pane > .title { - padding-left: 16px; - background-image: none; - border-bottom: none; -} - -#audit-result-view > .pane > .body > .pane > .body { - background-color: transparent; -} - -#audit-result-view > .pane > .body > .pane .section { - margin-left: 16px; -} - -#audit-result-view .section .header { - border: 0; - background-image: none; - background-color: transparent; -} - -#audit-result-view .section .header > .title { - color: rgb(0, 0, 0); -} - -#audit-result-view .section .section-content { - width: 100%; - padding-left: 18px; - display: none; -} - -#audit-result-view .section.expanded .section-content { - display: block; -} - -#audit-result-view .section.expanded .section-content > p:nth-of-type(1) { - margin-top: 0; -} - -#audit-result-view .section.expanded .section-content > p:nth-of-type(1) > *:nth-child(1) { - margin-top: 0; -} - -#audit-result-view .section .header::before { - content: url(Images/treeRightTriangleBlack.png); -} - -#audit-result-view .section.expanded .header::before { - content: url(Images/treeDownTriangleBlack.png); -} - -div.panel.audits .sidebar > ol.sidebar-tree > li:nth-child(1) { - height: 0px; - padding-top: 0; - padding-bottom: 0; -} - .audit-launcher-view { z-index: 1000; position: absolute; @@ -174,6 +76,8 @@ div.panel.audits .sidebar > ol.sidebar-tree > li:nth-child(1) { bottom: 0; padding: 0 0 0 16px; white-space: nowrap; + display: -webkit-box; + -webkit-box-orient: vertical; } .audit-launcher-view h1 { @@ -192,10 +96,13 @@ div.panel.audits .sidebar > ol.sidebar-tree > li:nth-child(1) { } .audit-launcher-view div.button-container { - position: absolute; + display: -webkit-box; + -webkit-box-orient: vertical; width: 100%; - bottom: 16px; - padding-top: 16px; + padding: 16px 0; +} +.audit-launcher-view .flexible-space { + -webkit-box-flex: 1; } .audit-launcher-view div.audit-categories-container { @@ -270,3 +177,109 @@ body.inactive .audit-launcher-view button, .audit-launcher-view button:disabled background: url(Images/radioDot.png) center no-repeat, -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223))); } + +.audit-launcher-view .resource-progress > img { + content: url(Images/spinner.gif); + vertical-align: text-top; + margin: 0 4px 0 8px; +} + +.audit-result-view { + overflow: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: none; +} + +.audit-result-view.visible { + display: block; +} + +.audit-result-view .severity-severe { + content: url(Images/errorRedDot.png); +} + +.audit-result-view .severity-warning { + content: url(Images/warningOrangeDot.png); +} + +.audit-result-view .severity-info { + content: url(Images/successGreenDot.png); +} + +.audit-result-tree li.parent::before { + content: url(Images/treeRightTriangleBlack.png); + float: left; + width: 8px; + height: 8px; + margin-top: 1px; + padding-right: 2px; +} + +.audit-result-tree { + font-size: 11px; + line-height: 14px; + -webkit-user-select: text; +} + +.audit-result-tree > ol { + position: relative; + padding: 2px 6px !important; + margin: 0; + color: rgb(84, 84, 84); + cursor: default; + min-width: 100%; +} + +.audit-result-tree, .audit-result-tree ol { + list-style-type: none; + -webkit-padding-start: 12px; + margin: 0; +} + +.audit-result-tree li { + padding: 0 0 0 14px; + margin-top: 1px; + margin-bottom: 1px; + word-wrap: break-word; + text-indent: -2px; +} + +.audit-result-tree li.parent { + text-indent: -12px +} + +.audit-result-tree li.parent::before { + content: url(Images/treeRightTriangleBlack.png); + float: left; + width: 8px; + height: 8px; + margin-top: 0; + padding-right: 2px; +} + +.audit-result-tree li.parent.expanded::before { + content: url(Images/treeDownTriangleBlack.png); +} + +.audit-result-tree ol.children { + display: none; +} + +.audit-result-tree ol.children.expanded { + display: block; +} + +.audit-result { + font-weight: bold; + color: black; +} + +.audit-result img { + float: left; + margin-left: -40px; + margin-top: -1px; +} diff --git a/WebCore/inspector/front-end/inspector.css b/WebCore/inspector/front-end/inspector.css index 76b31fc..581c75a 100644 --- a/WebCore/inspector/front-end/inspector.css +++ b/WebCore/inspector/front-end/inspector.css @@ -219,6 +219,10 @@ body.attached #search-results-matches { background-image: url(Images/profilesIcon.png); } +.toolbar-item.audits .toolbar-icon { + background-image: url(Images/auditsIcon.png); +} + .toolbar-item.console .toolbar-icon { background-image: url(Images/consoleIcon.png); } @@ -520,6 +524,11 @@ body.platform-windows .monospace, body.platform-windows .source-code { font-family: Consolas, Lucida Console, monospace; } +body.platform-linux .monospace, body.platform-linux .source-code { + font-size: 11px; + font-family: dejavu sans mono, monospace; +} + #console-messages { position: absolute; z-index: 0; @@ -877,6 +886,7 @@ body.platform-windows .monospace, body.platform-windows .source-code { right: 0; left: 0; bottom: 0; + overflow: auto; } .resource-view.headers-visible .resource-view-content { @@ -969,7 +979,7 @@ body.platform-windows .monospace, body.platform-windows .source-code { position: absolute; top: 0; left: 0; - right: 225px; + right: 300px; bottom: 0; } @@ -978,7 +988,7 @@ body.platform-windows .monospace, body.platform-windows .source-code { top: 0; right: 0; bottom: 0; - width: 225px; + width: 300px; background-color: rgb(245, 245, 245); border-left: 1px solid rgb(64%, 64%, 64%); cursor: default; @@ -1323,15 +1333,6 @@ body.inactive .placard.selected { display: inline; } -.section .header input[type=checkbox] { - height: 10px; - width: 10px; - margin-left: 0; - margin-top: 0; - margin-bottom: 0; - vertical-align: 2px; -} - .section .header .subtitle, .event-bar .header .subtitle { float: right; font-size: 10px; @@ -1496,6 +1497,10 @@ body.inactive .placard.selected { text-decoration: none !important; } +.editing br { + display: none; +} + .elements-tree-editor { -webkit-user-select: text; -webkit-user-modify: read-write-plaintext-only; @@ -1571,6 +1576,10 @@ li.editing .swatch, li.editing .enabled-button, li.editing-sub-part .delete-but display: block; } +.section .properties li.disabled .enabled-button { + display: block; +} + .section .properties .name, .event-properties .name { color: rgb(136, 19, 145); } @@ -1653,7 +1662,6 @@ li.editing .swatch, li.editing .enabled-button, li.editing-sub-part .delete-but } .pane > .title > select { - display: none; float: right; width: 23px; height: 17px; @@ -1668,10 +1676,6 @@ li.editing .swatch, li.editing .enabled-button, li.editing-sub-part .delete-but -webkit-appearance: none; } -.pane.expanded:hover > .title > select { - display: inline-block; -} - .pane > .title > select:hover { background-position: -23px 0px; } @@ -1716,6 +1720,21 @@ li.editing .swatch, li.editing .enabled-button, li.editing-sub-part .delete-but height: 5px; } +.sidebar-pane-subtitle { + float: right; + overflow: hidden; +} + +.sidebar-pane-subtitle input, .section .header input[type=checkbox] { + font-size: inherit; + hight: 1em; + width: 1em; + margin-left: 0; + margin-top: 0; + margin-bottom: 0.25em; + vertical-align: bottom; +} + .metrics { padding: 8px; font-size: 10px; @@ -2287,7 +2306,6 @@ button.show-all-nodes { .panel-enabler-view.welcome .instructions { display: inline-block; vertical-align: middle; - width: 330px; margin: 0; white-space: normal; line-height: 175%; @@ -2298,7 +2316,8 @@ button.show-all-nodes { } .panel-enabler-view.welcome button.status-bar-item { - vertical-align: middle; + background-image: none; + vertical-align: top; } .pane button { @@ -2410,6 +2429,15 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { content: url(Images/debuggerStepOut.png); } +.toggle-breakpoints .glyph { + -webkit-mask-image: url(Images/breakpointsActivateButtonGlyph.png); + background-color: rgb(96, 96, 96) !important; +} + +.toggle-breakpoints.toggled-on .glyph { + -webkit-mask-image: url(Images/breakpointsDeactivateButtonGlyph.png); +} + #scripts-debugger-status { position: absolute; line-height: 24px; @@ -2639,7 +2667,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { margin-right: 3px; } -#resources-dividers { +.resources-dividers { position: absolute; left: 0; right: 0; @@ -2648,7 +2676,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { z-index: -100; } -#resources-event-dividers { +.resources-event-dividers { position: absolute; left: 0; right: 0; @@ -2658,7 +2686,11 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { pointer-events: none; } -#resources-dividers-label-bar { +.timeline .resources-event-dividers { + height: 19px; +} + +.resources-dividers-label-bar { position: absolute; top: 0; left: 0px; @@ -2686,24 +2718,26 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { pointer-events: auto; } -.resources-onload-divider { +.resources-event-divider { position: absolute; width: 2px; top: 0; bottom: 0; z-index: 300; +} + +.resources-red-divider { background-color: rgba(255, 0, 0, 0.5); } -.resources-ondomcontent-divider { - position: absolute; - width: 2px; - top: 0; - bottom: 0; - z-index: 300; +.resources-blue-divider { background-color: rgba(0, 0, 255, 0.5); } +.resources-orange-divider { + background-color: rgba(255, 178, 23, 0.5); +} + .resources-divider.last { background-color: transparent; } @@ -2747,6 +2781,10 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { margin-right: 5px; } +.resources-graph-label.waiting-right { + margin-left: 5px; +} + .resources-graph-label.before { color: rgba(0, 0, 0, 0.7); text-shadow: none; @@ -2815,7 +2853,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { display: block; } -.resources-graph-bar.waiting { +.resources-graph-bar.waiting, .resources-graph-bar.waiting-right { opacity: 0.35; } @@ -3305,6 +3343,10 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches { height: 80px; } +#timeline-overview-panel .timeline-graph-bar { + pointer-events: none; +} + .timeline-sidebar-background { top: 90px; bottom: 0; @@ -3558,7 +3600,12 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches { min-width: 5px; opacity: 0.8; -webkit-border-image: url(Images/timelineBarGray.png) 4 4 5 4; - pointer-events: none; + z-index: 180; + pointer-events: visibleFill; +} + +.timeline-graph-bar.with-children { + opacity: 0.3; } .timeline-graph-side.even { @@ -3589,6 +3636,41 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches { background-position-y: 72px; } +.timeline-details { + -webkit-user-select: text; +} + +.timeline-details-row-title { + font-weight: bold; + text-align: right; + white-space: nowrap; +} + +.timeline-details-row-data { + white-space: nowrap; +} + +.timeline-details-title { + font-weight: bold; + white-space: nowrap; +} + +.timeline-filter-status-bar-item .glyph { + -webkit-mask-image: url(Images/largerResourcesButtonGlyph.png); +} + +.timeline-filter-status-bar-item.toggled-on .glyph { + background-color: rgb(66, 129, 235) !important; +} + +.timeline-records-counter { + font-size: 11px; + position: relative; + top: 5px; + margin-left: 5px; + text-shadow: white 0 1px 0; +} + /* Profiler Style */ #profile-views { @@ -3824,7 +3906,7 @@ ol.breakpoint-list { } .source-frame-popover-tree { - border-top: 1px solid rgb(190, 190, 190); + border-top: 1px solid rgb(194, 194, 147); overflow: auto; position: absolute; top: 15px; @@ -3832,3 +3914,58 @@ ol.breakpoint-list { left: 0; right: 0; } + +.source-frame-eval-expression { + border: 1px solid rgb(163, 41, 34); + margin: -1px; + background-color: rgb(255, 255, 194); +} + +.styles-sidebar-separator { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(243, 243, 243)), color-stop(0.05, rgb(243, 243, 243)), color-stop(0.05, rgb(230, 230, 230)), to(rgb(209, 209, 209))); + padding: 0 5px; + border-top: 1px solid rgb(189, 189, 189); + border-bottom: 1px solid rgb(189, 189, 189); + color: rgb(110, 110, 110); + text-shadow: white 0 1px 0; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.styles-selector { + cursor: text; + -webkit-user-select: text; +} + +.workers-list { + list-style: none; + margin: 0; + padding: 0; +} + +.workers-list > li { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-left: 1em; + font-size: 12px; +} + +a.worker-item { + color: rgb(33%, 33%, 33%); + cursor: pointer; + text-decoration: none; +} + +a.worker-item:hover { + color: rgb(15%, 15%, 15%); +} + +.resource-content-unavailable { + color: rgb(50%, 50%, 50%); + font-style: italic; + font-size: 14px; + text-align: center; + padding: 32px; +} diff --git a/WebCore/inspector/front-end/inspector.html b/WebCore/inspector/front-end/inspector.html index 85633b5..4fa8ddc 100644 --- a/WebCore/inspector/front-end/inspector.html +++ b/WebCore/inspector/front-end/inspector.html @@ -41,6 +41,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="InspectorFrontendHostStub.js"></script> <script type="text/javascript" src="Object.js"></script> <script type="text/javascript" src="Settings.js"></script> + <script type="text/javascript" src="Checkbox.js"></script> <script type="text/javascript" src="ContextMenu.js"></script> <script type="text/javascript" src="KeyboardShortcut.js"></script> <script type="text/javascript" src="TextPrompt.js"></script> @@ -74,6 +75,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="CallStackSidebarPane.js"></script> <script type="text/javascript" src="ScopeChainSidebarPane.js"></script> <script type="text/javascript" src="WatchExpressionsSidebarPane.js"></script> + <script type="text/javascript" src="WorkersSidebarPane.js"></script> <script type="text/javascript" src="MetricsSidebarPane.js"></script> <script type="text/javascript" src="PropertiesSidebarPane.js"></script> <script type="text/javascript" src="EventListenersSidebarPane.js"></script> @@ -85,6 +87,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="SummaryBar.js"></script> <script type="text/javascript" src="ElementsPanel.js"></script> <script type="text/javascript" src="ResourcesPanel.js"></script> + <script type="text/javascript" src="InjectedFakeWorker.js"></script> <script type="text/javascript" src="ScriptsPanel.js"></script> <script type="text/javascript" src="StoragePanel.js"></script> <script type="text/javascript" src="ProfilesPanel.js"></script> diff --git a/WebCore/inspector/front-end/inspector.js b/WebCore/inspector/front-end/inspector.js index 8bcdf63..c75dd9c 100644 --- a/WebCore/inspector/front-end/inspector.js +++ b/WebCore/inspector/front-end/inspector.js @@ -63,6 +63,7 @@ var WebInspector = { // 4 - ?path // 5 - ?fragment URLRegExp: /^(http[s]?|file):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i, + GenericURLRegExp: /^([^:]+):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i, get platform() { @@ -209,14 +210,10 @@ var WebInspector = { this.panels.profiles = new WebInspector.ProfilesPanel(); this.panels.profiles.registerProfileType(new WebInspector.CPUProfileType()); } - if (hiddenPanels.indexOf("storage") === -1 && hiddenPanels.indexOf("databases") === -1) this.panels.storage = new WebInspector.StoragePanel(); - - // FIXME: Uncomment when ready. - // if (hiddenPanels.indexOf("audits") === -1) - // this.panels.audits = new WebInspector.AuditsPanel(); - + if (Preferences.auditsPanelEnabled && hiddenPanels.indexOf("audits") === -1) + this.panels.audits = new WebInspector.AuditsPanel(); if (hiddenPanels.indexOf("console") === -1) this.panels.console = new WebInspector.ConsolePanel(); }, @@ -239,12 +236,10 @@ var WebInspector = { var body = document.body; if (x) { - InspectorFrontendHost.attach(); body.removeStyleClass("detached"); body.addStyleClass("attached"); dockToggleButton.title = WebInspector.UIString("Undock into separate window."); } else { - InspectorFrontendHost.detach(); body.removeStyleClass("attached"); body.addStyleClass("detached"); dockToggleButton.title = WebInspector.UIString("Dock to main window."); @@ -481,7 +476,6 @@ WebInspector.loaded = function() this.addMainEventListeners(document); - window.addEventListener("unload", this.windowUnload.bind(this), true); window.addEventListener("resize", this.windowResize.bind(this), true); document.addEventListener("focus", this.focusChanged.bind(this), true); @@ -565,11 +559,6 @@ WebInspector.dispatch = function() { setTimeout(delayDispatch, 0); } -WebInspector.windowUnload = function(event) -{ - InspectorFrontendHost.windowUnloading(); -} - WebInspector.windowResize = function(event) { if (this.currentPanel) @@ -607,9 +596,29 @@ WebInspector.setAttachedWindow = function(attached) WebInspector.close = function(event) { + if (this._isClosing) + return; + this._isClosing = true; InspectorFrontendHost.closeWindow(); } +WebInspector.inspectedPageDestroyed = function() +{ + WebInspector.close(); +} + +WebInspector.documentMouseOver = function(event) +{ + if (event.target.tagName !== "A") + return; + + const anchor = event.target; + if (!anchor.hasStyleClass("webkit-html-resource-link")) + return; + if (anchor.href && anchor.href.indexOf("/data:") != -1) + return; +} + WebInspector.documentClick = function(event) { var anchor = event.target.enclosingNodeOrSelfWithNodeName("a"); @@ -618,22 +627,38 @@ WebInspector.documentClick = function(event) // Prevent the link from navigating, since we don't do any navigation by following links normally. event.preventDefault(); + event.stopPropagation(); function followLink() { // FIXME: support webkit-html-external-link links here. - if (WebInspector.canShowSourceLineForURL(anchor.href, anchor.preferredPanel)) { + if (WebInspector.canShowSourceLine(anchor.href, anchor.lineNumber, anchor.preferredPanel)) { if (anchor.hasStyleClass("webkit-html-external-link")) { anchor.removeStyleClass("webkit-html-external-link"); anchor.addStyleClass("webkit-html-resource-link"); } - WebInspector.showSourceLineForURL(anchor.href, anchor.lineNumber, anchor.preferredPanel); - } else { - var profileString = WebInspector.ProfileType.URLRegExp.exec(anchor.href); - if (profileString) - WebInspector.showProfileForURL(anchor.href); + WebInspector.showSourceLine(anchor.href, anchor.lineNumber, anchor.preferredPanel); + return; } + + const profileMatch = WebInspector.ProfileType.URLRegExp.exec(anchor.href); + if (profileMatch) { + WebInspector.showProfileForURL(anchor.href); + 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); + if (WebInspector.panels[panel]) + WebInspector.currentPanel = WebInspector.panels[panel]; + } + return; + } + + WebInspector.showResourcesPanel(); } if (WebInspector.followLinkTimeout) @@ -652,6 +677,9 @@ WebInspector.documentClick = function(event) WebInspector.documentKeyDown = function(event) { + if (WebInspector.isEditingAnyField()) + return; + if (this.currentFocusElement && this.currentFocusElement.handleKeyEvent) { this.currentFocusElement.handleKeyEvent(event); if (event.handled) { @@ -689,7 +717,13 @@ WebInspector.documentKeyDown = function(event) WebInspector.focusSearchField(); event.preventDefault(); } + break; + case "F3": + if (!isMac) { + WebInspector.focusSearchField(); + event.preventDefault(); + } break; case "U+0047": // G key @@ -743,21 +777,15 @@ WebInspector.documentKeyDown = function(event) break; - case "U+0041": // A key - if (isMac) - var shouldShowAuditsPanel = event.metaKey && !event.shiftKey && !event.ctrlKey && event.altKey; - else - var shouldShowAuditsPanel = event.ctrlKey && !event.shiftKey && !event.metaKey && event.altKey; - - if (shouldShowAuditsPanel) { - if (!this.panels.audits) { - this.panels.audits = new WebInspector.AuditsPanel(); - var toolbarElement = document.getElementById("toolbar"); - WebInspector.addPanelToolbarIcon(toolbarElement, this.panels.audits, this.panels.console.toolbarItem); - } - this.currentPanel = this.panels.audits; + case "U+0052": // R key + if ((event.metaKey && isMac) || (event.ctrlKey && !isMac)) { + InspectorBackend.reloadPage(); + event.preventDefault(); } - + break; + case "F5": + if (!isMac) + InspectorBackend.reloadPage(); break; } } @@ -885,8 +913,10 @@ WebInspector.focusSearchField = function() WebInspector.toggleAttach = function() { - this.attached = !this.attached; - this.drawer.resize(); + if (!this.attached) + InspectorFrontendHost.requestAttachWindow(); + else + InspectorFrontendHost.requestDetachWindow(); } WebInspector.toolbarDragStart = function(event) @@ -1015,6 +1045,11 @@ WebInspector.showConsolePanel = function() this.currentPanel = this.panels.console; } +WebInspector.showAuditsPanel = function() +{ + this.currentPanel = this.panels.audits; +} + WebInspector.clearConsoleMessages = function() { WebInspector.console.clearMessages(); @@ -1041,6 +1076,8 @@ WebInspector.updateResource = function(identifier, payload) this.resourceURLMap[resource.url] = resource; if (this.panels.resources) this.panels.resources.addResource(resource); + if (this.panels.audits) + this.panels.audits.resourceStarted(resource); } if (payload.didRequestChange) { @@ -1057,11 +1094,10 @@ WebInspector.updateResource = function(identifier, payload) if (resource.mainResource) this.mainResource = resource; - var match = payload.documentURL.match(WebInspector.URLRegExp); + var match = payload.documentURL.match(WebInspector.GenericURLRegExp); if (match) { var protocol = match[1].toLowerCase(); - if (protocol.indexOf("http") === 0 || protocol === "file") - this._addCookieDomain(protocol === "file" ? "" : match[2]); + this._addCookieDomain(match[2]); } } @@ -1079,12 +1115,14 @@ WebInspector.updateResource = function(identifier, payload) } if (payload.didLengthChange) { - resource.contentLength = payload.contentLength; + resource.resourceSize = payload.resourceSize; } if (payload.didCompletionChange) { resource.failed = payload.failed; resource.finished = payload.finished; + if (this.panels.audits) + this.panels.audits.resourceFinished(resource); } if (payload.didTimingChange) { @@ -1182,6 +1220,17 @@ WebInspector.resourceTrackingWasDisabled = function() this.panels.resources.resourceTrackingWasDisabled(); } + +WebInspector.searchingForNodeWasEnabled = function() +{ + this.panels.elements.searchingForNodeWasEnabled(); +} + +WebInspector.searchingForNodeWasDisabled = function() +{ + this.panels.elements.searchingForNodeWasDisabled(); +} + WebInspector.attachDebuggerWhenShown = function() { this.panels.scripts.attachDebuggerWhenShown(); @@ -1192,6 +1241,11 @@ WebInspector.debuggerWasEnabled = function() this.panels.scripts.debuggerWasEnabled(); } +WebInspector.updatePauseOnExceptionsState = function(pauseOnExceptionsState) +{ + this.panels.scripts.updatePauseOnExceptionsState(pauseOnExceptionsState); +} + WebInspector.debuggerWasDisabled = function() { this.panels.scripts.debuggerWasDisabled(); @@ -1212,6 +1266,13 @@ WebInspector.parsedScriptSource = function(sourceID, sourceURL, source, starting this.panels.scripts.addScript(sourceID, sourceURL, source, startingLine); } +WebInspector.restoredBreakpoint = function(sourceID, sourceURL, line, enabled, condition) +{ + var breakpoint = new WebInspector.Breakpoint(sourceURL, line, sourceID, condition); + breakpoint.enabled = enabled; + this.panels.scripts.addBreakpoint(breakpoint); +} + WebInspector.failedToParseScriptSource = function(sourceURL, source, startingLine, errorLine, errorMessage) { this.panels.scripts.addScript(null, sourceURL, source, startingLine, errorLine, errorMessage); @@ -1257,6 +1318,16 @@ WebInspector.reset = function() this.console.clearMessages(); } +WebInspector.bringToFront = function() +{ + InspectorFrontendHost.bringToFront(); +} + +WebInspector.inspectedURLChanged = function(url) +{ + InspectorFrontendHost.inspectedURLChanged(url); +} + WebInspector.resourceURLChanged = function(resource, oldURL) { delete this.resourceURLMap[oldURL]; @@ -1294,7 +1365,7 @@ WebInspector.updateConsoleMessageRepeatCount = function(count) this.console.updateMessageRepeatCount(count); } -WebInspector.log = function(message) +WebInspector.log = function(message, messageLevel) { // remember 'this' for setInterval() callback var self = this; @@ -1348,7 +1419,7 @@ WebInspector.log = function(message) var msg = new WebInspector.ConsoleMessage( WebInspector.ConsoleMessage.MessageSource.Other, WebInspector.ConsoleMessage.MessageType.Log, - WebInspector.ConsoleMessage.MessageLevel.Debug, + messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug, -1, null, null, @@ -1437,7 +1508,19 @@ WebInspector.displayNameForURL = function(url) var resource = this.resourceURLMap[url]; if (resource) return resource.displayName; - return url.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : ""); + + if (!WebInspector.mainResource) + return url.trimURL(""); + + var lastPathComponent = WebInspector.mainResource.lastPathComponent; + var index = WebInspector.mainResource.url.indexOf(lastPathComponent); + if (index !== -1 && index + lastPathComponent.length === WebInspector.mainResource.url.length) { + var baseURL = WebInspector.mainResource.url.substring(0, index); + if (url.indexOf(baseURL) === 0) + return url.substring(index); + } + + return url.trimURL(WebInspector.mainResource.domain); } WebInspector.resourceForURL = function(url) @@ -1455,27 +1538,27 @@ WebInspector.resourceForURL = function(url) return null; } -WebInspector._choosePanelToShowSourceLineForURL = function(url, preferredPanel) +WebInspector._choosePanelToShowSourceLine = function(url, line, preferredPanel) { preferredPanel = preferredPanel || "resources"; var panel = this.panels[preferredPanel]; - if (panel && panel.canShowSourceLineForURL(url)) + if (panel && panel.canShowSourceLine(url, line)) return panel; panel = this.panels.resources; - return panel.canShowSourceLineForURL(url) ? panel : null; + return panel.canShowSourceLine(url, line) ? panel : null; } -WebInspector.canShowSourceLineForURL = function(url, preferredPanel) +WebInspector.canShowSourceLine = function(url, line, preferredPanel) { - return !!this._choosePanelToShowSourceLineForURL(url, preferredPanel); + return !!this._choosePanelToShowSourceLine(url, line, preferredPanel); } -WebInspector.showSourceLineForURL = function(url, line, preferredPanel) +WebInspector.showSourceLine = function(url, line, preferredPanel) { - this.currentPanel = this._choosePanelToShowSourceLineForURL(url, preferredPanel); + this.currentPanel = this._choosePanelToShowSourceLine(url, line, preferredPanel); if (!this.currentPanel) return false; - this.currentPanel.showSourceLineForURL(url, line); + this.currentPanel.showSourceLine(url, line); return true; } @@ -1540,6 +1623,17 @@ WebInspector.linkifyURL = function(url, linkText, classes, isExternal, tooltipTe return WebInspector.linkifyURLAsNode(url, linkText, classes, isExternal, tooltipText).outerHTML; } +WebInspector.linkifyResourceAsNode = function(url, preferredPanel, lineNumber, classes, tooltipText) +{ + var linkText = WebInspector.displayNameForURL(url); + if (lineNumber) + linkText += ":" + lineNumber; + var node = WebInspector.linkifyURLAsNode(url, linkText, classes, false, tooltipText); + node.lineNumber = lineNumber; + node.preferredPanel = preferredPanel; + return node; +} + WebInspector.completeURL = function(baseURL, href) { var match = baseURL.match(WebInspector.URLRegExp); @@ -1559,6 +1653,7 @@ WebInspector.addMainEventListeners = function(doc) doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), false); doc.defaultView.addEventListener("blur", this.windowBlurred.bind(this), false); doc.addEventListener("click", this.documentClick.bind(this), true); + doc.addEventListener("mouseover", this.documentMouseOver.bind(this), true); } WebInspector._searchFieldManualFocus = function(event) @@ -1721,11 +1816,17 @@ WebInspector.isBeingEdited = function(element) return element.__editing; } +WebInspector.isEditingAnyField = function() +{ + return this.__editing; +} + WebInspector.startEditing = function(element, committedCallback, cancelledCallback, context, multiline) { if (element.__editing) return; element.__editing = true; + WebInspector.__editing = true; var oldText = getContent(element); var moveDirection = ""; @@ -1749,6 +1850,7 @@ WebInspector.startEditing = function(element, committedCallback, cancelledCallba function cleanUpAfterEditing() { delete this.__editing; + delete WebInspector.__editing; this.removeStyleClass("editing"); this.tabIndex = oldTabIndex; @@ -1801,6 +1903,10 @@ WebInspector.startEditing = function(element, committedCallback, cancelledCallba element.addEventListener("keydown", keyDownEventListener, true); WebInspector.currentFocusElement = element; + return { + cancel: editingCancelled.bind(element), + commit: editingCommitted.bind(element) + }; } WebInspector._toolbarItemClicked = function(event) diff --git a/WebCore/inspector/front-end/textViewer.css b/WebCore/inspector/front-end/textViewer.css index 1447dfd..b69545f 100644 --- a/WebCore/inspector/front-end/textViewer.css +++ b/WebCore/inspector/front-end/textViewer.css @@ -10,7 +10,6 @@ .text-editor-lines { border: 0; - width: 100%; -webkit-border-horizontal-spacing: 0; -webkit-border-vertical-spacing: 0; -webkit-user-select: text; @@ -61,66 +60,81 @@ .webkit-line-number { color: rgb(128, 128, 128); + background-color: rgb(240, 240, 240); + border-right: 1px solid rgb(187, 187, 187); text-align: right; word-break: normal; -webkit-user-select: none; - background-color: rgb(240, 240, 240); - border-right: 1px solid rgb(187, 187, 187) !important; - padding-left: 2px; - padding-right: 2px; - background-repeat: no-repeat; - background-position: right 1px; + padding-right: 4px; + padding-left: 6px; +} + +.webkit-line-number-outer { + margin-right: -4px; + margin-left: -4px; + border-color: transparent; + border-style: solid; + border-width: 0 0 0px 2px; vertical-align: top; } +.webkit-line-number-inner { + margin-right: 4px; +} + +.webkit-breakpoint .webkit-line-number-inner, .webkit-breakpoint-conditional .webkit-line-number-inner, .webkit-execution-line .webkit-line-number-inner { + margin-right: -10px; +} + .webkit-line-content { + width: 100%; padding-left: 2px; vertical-align: top; } -.webkit-execution-line .webkit-line-number { - color: transparent; - background-image: -webkit-canvas(program-counter); -} - -.webkit-breakpoint .webkit-line-number { +.webkit-breakpoint .webkit-line-number-outer { color: white; - background-image: -webkit-canvas(breakpoint); + border-width: 0 14px 0px 2px; + -webkit-border-image: url(Images/breakpointBorder.png) 0 14 0 2; } -.webkit-breakpoint-disabled .webkit-line-number { +.webkit-breakpoint-conditional .webkit-line-number-outer { color: white; - background-image: -webkit-canvas(breakpoint-disabled); + border-width: 0 14px 0px 2px; + -webkit-border-image: url(Images/breakpointConditionalBorder.png) 0 14 0 2; } -.webkit-breakpoint.webkit-execution-line .webkit-line-number { +.webkit-execution-line .webkit-line-number-outer { color: transparent; - background-image: -webkit-canvas(breakpoint-program-counter); + border-width: 0 14px 0px 2px; + -webkit-border-image: url(Images/programCounterBorder.png) 0 14 0 2; +} + +.webkit-breakpoint.webkit-execution-line .webkit-line-number-outer { + color: white; + -webkit-border-image: url(Images/breakpointCounterBorder.png) 0 14 0 2; } -.webkit-breakpoint-disabled.webkit-execution-line .webkit-line-number { +.webkit-breakpoint.webkit-execution-line .webkit-line-number-outer { color: transparent; - background-image: -webkit-canvas(breakpoint-disabled-program-counter); + -webkit-border-image: url(Images/breakpointCounterBorder.png) 0 14 0 2; } -.webkit-breakpoint.webkit-breakpoint-conditional .webkit-line-number { - color: white; - background-image: -webkit-canvas(breakpoint-conditional); +.webkit-breakpoint-conditional.webkit-execution-line .webkit-line-number-outer { + color: transparent; + -webkit-border-image: url(Images/breakpointConditionalCounterBorder.png) 0 14 0 2; } -.webkit-breakpoint-disabled.webkit-breakpoint-conditional .webkit-line-number { - color: white; - background-image: -webkit-canvas(breakpoint-disabled-conditional); +.webkit-breakpoint-disabled .webkit-line-number-outer { + opacity: 0.5; } -.webkit-breakpoint.webkit-breakpoint-conditional.webkit-execution-line .webkit-line-number { - color: transparent; - background-image: -webkit-canvas(breakpoint-conditional-program-counter); +.breakpoints-deactivated .webkit-breakpoint .webkit-line-number-outer { + opacity: 0.5; } -.webkit-breakpoint-disabled.webkit-breakpoint-conditional.webkit-execution-line .webkit-line-number { - color: transparent; - background-image: -webkit-canvas(breakpoint-disabled-conditional-program-counter); +.breakpoints-deactivated .webkit-breakpoint-disabled .webkit-line-number-outer { + opacity: 0.3; } .webkit-execution-line .webkit-line-content { diff --git a/WebCore/inspector/front-end/treeoutline.js b/WebCore/inspector/front-end/treeoutline.js index b6e35bb..799d8af 100644 --- a/WebCore/inspector/front-end/treeoutline.js +++ b/WebCore/inspector/front-end/treeoutline.js @@ -287,7 +287,7 @@ TreeOutline.prototype.findTreeElement = function(representedObject, isAncestor, if (cachedElement) return cachedElement; - // The representedObject isn't know, so we start at the top of the tree and work down to find the first + // The representedObject isn't known, so we start at the top of the tree and work down to find the first // tree element that represents representedObject or one of its ancestors. var item; var found = false; @@ -567,7 +567,7 @@ TreeElement.prototype._attach = function() if (this.selected) this._listItemNode.addStyleClass("selected"); - this._listItemNode.addEventListener("mousedown", TreeElement.treeElementSelected, false); + this._listItemNode.addEventListener("mousedown", TreeElement.treeElementMouseDown, false); this._listItemNode.addEventListener("click", TreeElement.treeElementToggled, false); this._listItemNode.addEventListener("dblclick", TreeElement.treeElementDoubleClicked, false); @@ -595,7 +595,7 @@ TreeElement.prototype._detach = function() this._childrenListNode.parentNode.removeChild(this._childrenListNode); } -TreeElement.treeElementSelected = function(event) +TreeElement.treeElementMouseDown = function(event) { var element = event.currentTarget; if (!element || !element.treeElement || !element.treeElement.selectable) @@ -604,7 +604,7 @@ TreeElement.treeElementSelected = function(event) if (element.treeElement.isEventWithinDisclosureTriangle(event)) return; - element.treeElement.select(); + element.treeElement.selectOnMouseDown(event); } TreeElement.treeElementToggled = function(event) @@ -767,6 +767,11 @@ TreeElement.prototype.revealed = function() return true; } +TreeElement.prototype.selectOnMouseDown = function(event) +{ + this.select(); +} + TreeElement.prototype.select = function(supressOnSelect) { if (!this.treeOutline || !this.selectable || this.selected) diff --git a/WebCore/inspector/front-end/utilities.js b/WebCore/inspector/front-end/utilities.js index f30ab9f..98f34b9 100644 --- a/WebCore/inspector/front-end/utilities.js +++ b/WebCore/inspector/front-end/utilities.js @@ -219,6 +219,17 @@ Element.prototype.positionAt = function(x, y) this.style.top = y + "px"; } +Element.prototype.pruneEmptyTextNodes = function() +{ + var sibling = this.firstChild; + while (sibling) { + var nextSibling = sibling.nextSibling; + if (sibling.nodeType === this.TEXT_NODE && sibling.nodeValue === "") + this.removeChild(sibling); + sibling = nextSibling; + } +} + Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = function(nameArray) { for (var node = this; node && node !== this.ownerDocument; node = node.parentNode) @@ -359,7 +370,7 @@ String.prototype.collapseWhitespace = function() String.prototype.trimURL = function(baseURLDomain) { - var result = this.replace(/^https?:\/\//i, ""); + var result = this.replace(/^(https|http|file):\/\//i, ""); if (baseURLDomain) result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp(), "i"), ""); return result; |