diff options
author | Ben Murdoch <benm@google.com> | 2010-06-15 19:36:43 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2010-06-16 14:52:28 +0100 |
commit | 545e470e52f0ac6a3a072bf559c796b42c6066b6 (patch) | |
tree | c0c14763654d84d37577dde512c3d3b4699a9e86 /WebCore/inspector | |
parent | 719298a66237d38ea5c05f1547123ad8aacbc237 (diff) | |
download | external_webkit-545e470e52f0ac6a3a072bf559c796b42c6066b6.zip external_webkit-545e470e52f0ac6a3a072bf559c796b42c6066b6.tar.gz external_webkit-545e470e52f0ac6a3a072bf559c796b42c6066b6.tar.bz2 |
Merge webkit.org at r61121: Initial merge by git.
Change-Id: Icd6db395c62285be384d137164d95d7466c98760
Diffstat (limited to 'WebCore/inspector')
49 files changed, 1701 insertions, 532 deletions
diff --git a/WebCore/inspector/InjectedScriptHost.cpp b/WebCore/inspector/InjectedScriptHost.cpp index c58073d..4c3c7ae 100644 --- a/WebCore/inspector/InjectedScriptHost.cpp +++ b/WebCore/inspector/InjectedScriptHost.cpp @@ -110,12 +110,6 @@ long InjectedScriptHost::pushNodePathToFrontend(Node* node, bool withChildren, b return id; } -void InjectedScriptHost::addNodesToSearchResult(const String& nodeIds) -{ - if (InspectorFrontend* frontend = inspectorFrontend()) - frontend->addNodesToSearchResult(nodeIds); -} - long InjectedScriptHost::pushNodeByPathToFrontend(const String& path) { InspectorDOMAgent* domAgent = inspectorDOMAgent(); diff --git a/WebCore/inspector/InjectedScriptHost.h b/WebCore/inspector/InjectedScriptHost.h index beffc9d..66cf41e 100644 --- a/WebCore/inspector/InjectedScriptHost.h +++ b/WebCore/inspector/InjectedScriptHost.h @@ -69,7 +69,6 @@ public: Node* nodeForId(long nodeId); long pushNodePathToFrontend(Node* node, bool withChildren, bool selectInUI); - void addNodesToSearchResult(const String& nodeIds); long pushNodeByPathToFrontend(const String& path); #if ENABLE(DATABASE) diff --git a/WebCore/inspector/InjectedScriptHost.idl b/WebCore/inspector/InjectedScriptHost.idl index 8b24157..5a4ce19 100644 --- a/WebCore/inspector/InjectedScriptHost.idl +++ b/WebCore/inspector/InjectedScriptHost.idl @@ -38,7 +38,6 @@ module core { [Custom] DOMObject nodeForId(in long nodeId); [Custom] int pushNodePathToFrontend(in DOMObject node, in boolean withChildren, in boolean selectInUI); - void addNodesToSearchResult(in DOMString nodeIds); long pushNodeByPathToFrontend(in DOMString path); #if defined(ENABLE_JAVASCRIPT_DEBUGGER) && ENABLE_JAVASCRIPT_DEBUGGER diff --git a/WebCore/inspector/InspectorBackend.cpp b/WebCore/inspector/InspectorBackend.cpp index 36f41b8..1eedda1 100644 --- a/WebCore/inspector/InspectorBackend.cpp +++ b/WebCore/inspector/InspectorBackend.cpp @@ -79,12 +79,18 @@ InspectorBackend::~InspectorBackend() { } -void InspectorBackend::saveFrontendSettings(const String& settings) +void InspectorBackend::saveApplicationSettings(const String& settings) { if (m_inspectorController) m_inspectorController->setSetting(InspectorController::frontendSettingsSettingName(), settings); } +void InspectorBackend::saveSessionSettings(const String& settings) +{ + if (m_inspectorController) + m_inspectorController->setSessionSettings(settings); +} + void InspectorBackend::storeLastActivePanel(const String& panelName) { if (m_inspectorController) @@ -215,9 +221,17 @@ void InspectorBackend::setPauseOnExceptionsState(long pauseState) frontend->updatePauseOnExceptionsState(ScriptDebugServer::shared().pauseOnExceptionsState()); } -#endif +void InspectorBackend::editScriptSource(long callId, const String& sourceID, const String& newContent) +{ + if (m_inspectorController) + m_inspectorController->editScriptSource(callId, sourceID, newContent); +} -#if ENABLE(JAVASCRIPT_DEBUGGER) +void InspectorBackend::getScriptSource(long callId, const String& sourceID) +{ + if (m_inspectorController) + m_inspectorController->getScriptSource(callId, sourceID); +} void InspectorBackend::enableProfiler(bool always) { @@ -351,6 +365,18 @@ void InspectorBackend::changeTagName(long callId, long nodeId, const AtomicStrin domAgent->changeTagName(callId, nodeId, tagName, expanded); } +void InspectorBackend::performSearch(const String& query, bool runSynchronously) +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->performSearch(query, runSynchronously); +} + +void InspectorBackend::searchCanceled() +{ + if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) + domAgent->searchCanceled(); +} + void InspectorBackend::getStyles(long callId, long nodeId, bool authorOnly) { if (InspectorDOMAgent* domAgent = inspectorDOMAgent()) diff --git a/WebCore/inspector/InspectorBackend.h b/WebCore/inspector/InspectorBackend.h index 0eb8e4c..61d73b8 100644 --- a/WebCore/inspector/InspectorBackend.h +++ b/WebCore/inspector/InspectorBackend.h @@ -57,7 +57,8 @@ public: InspectorController* inspectorController() { return m_inspectorController; } void disconnectController() { m_inspectorController = 0; } - void saveFrontendSettings(const String&); + void saveApplicationSettings(const String&); + void saveSessionSettings(const String&); void storeLastActivePanel(const String& panelName); @@ -84,12 +85,15 @@ public: void pauseInDebugger(); void resumeDebugger(); - void setPauseOnExceptionsState(long pauseState); - void stepOverStatementInDebugger(); void stepIntoStatementInDebugger(); void stepOutOfFunctionInDebugger(); + void setPauseOnExceptionsState(long pauseState); + + void editScriptSource(long callId, const String& sourceID, const String& newContent); + void getScriptSource(long callId, const String& sourceID); + void enableProfiler(bool always); void disableProfiler(bool always); @@ -116,6 +120,8 @@ public: void copyNode(long nodeId); void removeNode(long callId, long nodeId); void changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded); + void performSearch(const String& query, bool runSynchronously); + void searchCanceled(); void getStyles(long callId, long nodeId, bool authOnly); void getAllStyles(long callId); diff --git a/WebCore/inspector/InspectorBackend.idl b/WebCore/inspector/InspectorBackend.idl index e8cfdb9..c0078ee 100644 --- a/WebCore/inspector/InspectorBackend.idl +++ b/WebCore/inspector/InspectorBackend.idl @@ -34,7 +34,8 @@ module core { interface [Conditional=INSPECTOR] InspectorBackend { void storeLastActivePanel(in DOMString panelName); - void saveFrontendSettings(in DOMString settings); + void saveApplicationSettings(in DOMString settings); + void saveSessionSettings(in DOMString settings); void enableSearchingForNode(); void disableSearchingForNode(); @@ -65,6 +66,9 @@ module core { void setPauseOnExceptionsState(in long pauseOnExceptionsState); + void editScriptSource(in long callId, in DOMString sourceID, in DOMString newContent); + void getScriptSource(in long callId, in DOMString sourceID); + void enableProfiler(in boolean always); void disableProfiler(in boolean always); @@ -91,6 +95,9 @@ module core { 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 performSearch(in DOMString query, in boolean runSynchronously); + void searchCanceled(); + void highlightDOMNode(in long nodeId); void hideDOMNodeHighlight(); diff --git a/WebCore/inspector/InspectorClient.h b/WebCore/inspector/InspectorClient.h index 841c15e..2ce3a09 100644 --- a/WebCore/inspector/InspectorClient.h +++ b/WebCore/inspector/InspectorClient.h @@ -48,6 +48,8 @@ public: virtual void populateSetting(const String& key, String* value) = 0; virtual void storeSetting(const String& key, const String& value) = 0; + + virtual bool sendMessageToFrontend(const String& message) = 0; }; } // namespace WebCore diff --git a/WebCore/inspector/InspectorController.cpp b/WebCore/inspector/InspectorController.cpp index f38f8d0..7916cd0 100644 --- a/WebCore/inspector/InspectorController.cpp +++ b/WebCore/inspector/InspectorController.cpp @@ -24,7 +24,7 @@ * 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. + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -148,6 +148,7 @@ InspectorController::InspectorController(Page* page, InspectorClient* client) , m_cssStore(new InspectorCSSStore(this)) , m_expiredConsoleMessageCount(0) , m_showAfterVisible(CurrentPanel) + , m_sessionSettings(InspectorObject::create()) , m_groupLevel(0) , m_searchingForNode(false) , m_previousMessage(0) @@ -158,8 +159,7 @@ InspectorController::InspectorController(Page* page, InspectorClient* client) #if ENABLE(JAVASCRIPT_DEBUGGER) , m_debuggerEnabled(false) , m_attachDebuggerWhenShown(false) -#endif -#if ENABLE(JAVASCRIPT_DEBUGGER) + , m_pausedScriptState(0) , m_profilerEnabled(!WTF_USE_JSC) , m_recordingUserInitiatedProfile(false) , m_currentUserInitiatedProfileNumber(-1) @@ -230,6 +230,11 @@ void InspectorController::setSetting(const String& key, const String& value) m_client->storeSetting(key, value); } +void InspectorController::setSessionSettings(const String& settingsJSON) +{ + m_sessionSettings = InspectorValue::readJSON(settingsJSON); +} + void InspectorController::inspect(Node* node) { if (!enabled()) @@ -426,22 +431,27 @@ void InspectorController::setSearchingForNode(bool enabled) } } -void InspectorController::setFrontend(PassOwnPtr<InspectorFrontend> frontend) +void InspectorController::connectFrontend(const ScriptObject& webInspector) { - ASSERT(frontend); m_openingFrontend = false; - m_frontend = frontend; + m_frontend = new InspectorFrontend(webInspector, m_client); releaseDOMAgent(); m_domAgent = InspectorDOMAgent::create(m_cssStore.get(), 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") - enableDebugger(); - String profilerEnabled = setting(profilerEnabledSettingName); - if (profilerEnabled == "true") - enableProfiler(); +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (ScriptDebugServer::shared().isDebuggerAlwaysEnabled()) { + // FIXME (40364): This will force pushing script sources to frontend even if script + // panel is inactive. + enableDebuggerFromFrontend(false); + } else { + String debuggerEnabled = setting(debuggerEnabledSettingName); + if (debuggerEnabled == "true") + enableDebugger(); + String profilerEnabled = setting(profilerEnabledSettingName); + if (profilerEnabled == "true") + enableProfiler(); + } #endif // Initialize Web Inspector title. @@ -550,7 +560,7 @@ void InspectorController::populateScriptObjects() if (!m_frontend) return; - m_frontend->populateFrontendSettings(setting(frontendSettingsSettingName())); + m_frontend->populateApplicationSettings(setting(frontendSettingsSettingName())); if (m_resourceTrackingEnabled) m_frontend->resourceTrackingWasEnabled(); @@ -597,6 +607,7 @@ void InspectorController::populateScriptObjects() m_frontend->didCreateWorker(*it->second); #endif + m_frontend->populateSessionSettings(m_sessionSettings->toJSONString()); m_frontend->populateInterface(); // Dispatch pending frontend commands @@ -662,6 +673,7 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) m_counts.clear(); #if ENABLE(JAVASCRIPT_DEBUGGER) m_sourceIDToURL.clear(); + m_scriptIDToContent.clear(); #endif #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) m_profiles.clear(); @@ -673,6 +685,7 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) unbindAllResources(); m_cssStore->reset(); + m_sessionSettings = InspectorObject::create(); if (m_frontend) { m_frontend->reset(); m_domAgent->reset(); @@ -875,6 +888,9 @@ bool InspectorController::isMainResourceLoader(DocumentLoader* loader, const KUR void InspectorController::willSendRequest(unsigned long identifier, const ResourceRequest& request, const ResourceResponse& redirectResponse) { + if (!enabled()) + return; + bool isMainResource = (m_mainResource && m_mainResource->identifier() == identifier); if (m_timelineAgent) m_timelineAgent->willSendResourceRequest(identifier, isMainResource, request); @@ -884,20 +900,24 @@ void InspectorController::willSendRequest(unsigned long identifier, const Resour return; if (!redirectResponse.isNull()) { - resource->markResponseReceivedTime(); - resource->endTiming(); - resource->updateResponse(redirectResponse); - - // We always store last redirect by the original id key. Rest of the redirects are stored within the last one. - unsigned long id = m_inspectedPage->progress()->createUniqueIdentifier(); - RefPtr<InspectorResource> withRedirect = resource->appendRedirect(id, request.url()); - removeResource(resource.get()); - addResource(withRedirect.get()); - if (isMainResource) { - m_mainResource = withRedirect; - withRedirect->markMainResource(); + // Redirect may have empty URL and we'd like to not crash with invalid HashMap entry. + // See http/tests/misc/will-send-request-returns-null-on-redirect.html + if (!request.url().isEmpty()) { + resource->markResponseReceivedTime(); + resource->endTiming(); + resource->updateResponse(redirectResponse); + + // We always store last redirect by the original id key. Rest of the redirects are stored within the last one. + unsigned long id = m_inspectedPage->progress()->createUniqueIdentifier(); + RefPtr<InspectorResource> withRedirect = resource->appendRedirect(id, request.url()); + removeResource(resource.get()); + addResource(withRedirect.get()); + if (isMainResource) { + m_mainResource = withRedirect; + withRedirect->markMainResource(); + } + resource = withRedirect; } - resource = withRedirect; } resource->startTiming(); @@ -909,6 +929,9 @@ void InspectorController::willSendRequest(unsigned long identifier, const Resour void InspectorController::didReceiveResponse(unsigned long identifier, const ResourceResponse& response) { + if (!enabled()) + return; + if (RefPtr<InspectorResource> resource = getTrackedResource(identifier)) { resource->updateResponse(response); resource->markResponseReceivedTime(); @@ -926,6 +949,9 @@ void InspectorController::didReceiveResponse(unsigned long identifier, const Res void InspectorController::didReceiveContentLength(unsigned long identifier, int lengthReceived) { + if (!enabled()) + return; + RefPtr<InspectorResource> resource = getTrackedResource(identifier); if (!resource) return; @@ -938,6 +964,9 @@ void InspectorController::didReceiveContentLength(unsigned long identifier, int void InspectorController::didFinishLoading(unsigned long identifier) { + if (!enabled()) + return; + if (m_timelineAgent) m_timelineAgent->didFinishLoadingResource(identifier, false); @@ -954,6 +983,9 @@ void InspectorController::didFinishLoading(unsigned long identifier) void InspectorController::didFailLoading(unsigned long identifier, const ResourceError& error) { + if (!enabled()) + return; + if (m_timelineAgent) m_timelineAgent->didFinishLoadingResource(identifier, true); @@ -1498,6 +1530,8 @@ void InspectorController::stopUserInitiatedProfiling() #if USE(JSC) JSC::ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec(); #else + // Use null script state to avoid filtering by context security token. + // All functions from all iframes should be visible from Inspector UI. ScriptState* scriptState = 0; #endif RefPtr<ScriptProfile> profile = ScriptProfiler::stop(scriptState, title); @@ -1593,11 +1627,30 @@ void InspectorController::disableDebugger(bool always) m_debuggerEnabled = false; m_attachDebuggerWhenShown = false; + m_pausedScriptState = 0; if (m_frontend) m_frontend->debuggerWasDisabled(); } +void InspectorController::editScriptSource(long callId, const String& sourceID, const String& newContent) +{ + String result; + bool success = ScriptDebugServer::shared().editScriptSource(sourceID, newContent, result); + RefPtr<SerializedScriptValue> callFrames; + if (success) + callFrames = currentCallFrames(); + m_frontend->didEditScriptSource(callId, success, result, callFrames.get()); +} + +void InspectorController::getScriptSource(long callId, const String& sourceID) +{ + if (!m_frontend) + return; + String scriptSource = m_scriptIDToContent.get(sourceID); + m_frontend->didGetScriptSource(callId, scriptSource); +} + void InspectorController::resumeDebugger() { if (!m_debuggerEnabled) @@ -1605,6 +1658,18 @@ void InspectorController::resumeDebugger() ScriptDebugServer::shared().continueProgram(); } +PassRefPtr<SerializedScriptValue> InspectorController::currentCallFrames() +{ + if (!m_pausedScriptState) + return 0; + InjectedScript injectedScript = m_injectedScriptHost->injectedScriptFor(m_pausedScriptState); + if (injectedScript.hasNoValue()) { + ASSERT_NOT_REACHED(); + return 0; + } + return injectedScript.callFrames(); +} + void InspectorController::setBreakpoint(const String& sourceID, unsigned lineNumber, bool enabled, const String& condition) { ScriptBreakpoint breakpoint(enabled, condition); @@ -1634,9 +1699,10 @@ void InspectorController::removeBreakpoint(const String& sourceID, unsigned line // JavaScriptDebugListener functions -void InspectorController::didParseSource(const String& sourceID, const String& url, const String& data, int firstLine) +void InspectorController::didParseSource(const String& sourceID, const String& url, const String& data, int firstLine, ScriptWorldType worldType) { - m_frontend->parsedScriptSource(sourceID, url, data, firstLine); + // Don't send script content to the front end until it's really needed. + m_frontend->parsedScriptSource(sourceID, url, "", firstLine, worldType); if (url.isEmpty()) return; @@ -1652,6 +1718,7 @@ void InspectorController::didParseSource(const String& sourceID, const String& u } m_sourceIDToURL.set(sourceID, url); + m_scriptIDToContent.set(sourceID, data); } void InspectorController::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) @@ -1661,14 +1728,15 @@ void InspectorController::failedToParseSource(const String& url, const String& d void InspectorController::didPause(ScriptState* scriptState) { - ASSERT(scriptState); - InjectedScript injectedScript = m_injectedScriptHost->injectedScriptFor(scriptState); - RefPtr<SerializedScriptValue> callFrames = injectedScript.callFrames(); + ASSERT(scriptState && !m_pausedScriptState); + m_pausedScriptState = scriptState; + RefPtr<SerializedScriptValue> callFrames = currentCallFrames(); m_frontend->pausedScript(callFrames.get()); } void InspectorController::didContinue() { + m_pausedScriptState = 0; m_frontend->resumedScript(); } diff --git a/WebCore/inspector/InspectorController.h b/WebCore/inspector/InspectorController.h index a2c8a77..af36383 100644 --- a/WebCore/inspector/InspectorController.h +++ b/WebCore/inspector/InspectorController.h @@ -32,6 +32,7 @@ #include "Console.h" #include "Cookie.h" #include "InspectorDOMAgent.h" +#include "InspectorValues.h" #include "PlatformString.h" #include "ScriptArray.h" #include "ScriptBreakpoint.h" @@ -126,6 +127,7 @@ public: String setting(const String& key) const; void setSetting(const String& key, const String& value); + void setSessionSettings(const String&); void inspect(Node*); void highlight(Node*); @@ -134,6 +136,11 @@ public: void show(); void showPanel(SpecialPanels); void close(); + + // We are in transition from JS transport via webInspector to native + // transport via InspectorClient. After migration, webInspector parameter should + // be removed. + void connectFrontend(const ScriptObject& webInspector); void disconnectFrontend(); void addMessageToConsole(MessageSource, MessageType, MessageLevel, ScriptCallStack*); @@ -151,7 +158,6 @@ public: void inspectedWindowScriptObjectCleared(Frame*); bool windowVisible(); - void setFrontend(PassOwnPtr<InspectorFrontend>); void didCommitLoad(DocumentLoader*); void frameDetachedFromParent(Frame*); @@ -241,9 +247,13 @@ public: void disableDebugger(bool always = false); bool debuggerEnabled() const { return m_debuggerEnabled; } + void editScriptSource(long callId, const String& sourceID, const String& newContent); + void getScriptSource(long callId, const String& sourceID); + void resumeDebugger(); + PassRefPtr<SerializedScriptValue> currentCallFrames(); - virtual void didParseSource(const String& sourceID, const String& url, const String& data, int firstLine); + virtual void didParseSource(const String& sourceID, const String& url, const String& data, int firstLine, ScriptWorldType); virtual void failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage); virtual void didPause(ScriptState*); virtual void didContinue(); @@ -340,6 +350,7 @@ private: #endif SpecialPanels m_showAfterVisible; RefPtr<Node> m_highlightedNode; + RefPtr<InspectorValue> m_sessionSettings; unsigned m_groupLevel; bool m_searchingForNode; ConsoleMessage* m_previousMessage; @@ -356,7 +367,9 @@ private: #if ENABLE(JAVASCRIPT_DEBUGGER) bool m_debuggerEnabled; bool m_attachDebuggerWhenShown; + ScriptState* m_pausedScriptState; HashMap<String, String> m_sourceIDToURL; + HashMap<String, String> m_scriptIDToContent; HashMap<String, SourceBreakpoints> m_stickyBreakpoints; bool m_profilerEnabled; diff --git a/WebCore/inspector/InspectorDOMAgent.cpp b/WebCore/inspector/InspectorDOMAgent.cpp index 338dc33..b152dc3 100644 --- a/WebCore/inspector/InspectorDOMAgent.cpp +++ b/WebCore/inspector/InspectorDOMAgent.cpp @@ -51,6 +51,8 @@ #include "EventListener.h" #include "EventNames.h" #include "EventTarget.h" +#include "Frame.h" +#include "FrameTree.h" #include "HTMLFrameOwnerElement.h" #include "InspectorFrontend.h" #include "markup.h" @@ -65,18 +67,141 @@ #include "StyleSheetList.h" #include "Text.h" +#if ENABLE(XPATH) +#include "XPathResult.h" +#endif + #include <wtf/text/CString.h> #include <wtf/HashSet.h> +#include <wtf/ListHashSet.h> #include <wtf/OwnPtr.h> #include <wtf/Vector.h> namespace WebCore { +class MatchJob { +public: + virtual void match(ListHashSet<Node*>& resultCollector) = 0; + virtual ~MatchJob() { } + +protected: + MatchJob(Document* document, const String& query) + : m_document(document) + , m_query(query) { } + + void addNodesToResults(PassRefPtr<NodeList> nodes, ListHashSet<Node*>& resultCollector) + { + for (unsigned i = 0; nodes && i < nodes->length(); ++i) + resultCollector.add(nodes->item(i)); + } + + RefPtr<Document> m_document; + String m_query; +}; + +namespace { + +class MatchExactIdJob : public WebCore::MatchJob { +public: + MatchExactIdJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } + virtual ~MatchExactIdJob() { } + +protected: + virtual void match(ListHashSet<Node*>& resultCollector) + { + if (m_query.isEmpty()) + return; + + Element* element = m_document->getElementById(m_query); + if (element) + resultCollector.add(element); + } +}; + +class MatchExactClassNamesJob : public WebCore::MatchJob { +public: + MatchExactClassNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } + virtual ~MatchExactClassNamesJob() { } + + virtual void match(ListHashSet<Node*>& resultCollector) + { + if (!m_query.isEmpty()) + addNodesToResults(m_document->getElementsByClassName(m_query), resultCollector); + } +}; + +class MatchExactTagNamesJob : public WebCore::MatchJob { +public: + MatchExactTagNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } + virtual ~MatchExactTagNamesJob() { } + + virtual void match(ListHashSet<Node*>& resultCollector) + { + if (!m_query.isEmpty()) + addNodesToResults(m_document->getElementsByName(m_query), resultCollector); + } +}; + +class MatchQuerySelectorAllJob : public WebCore::MatchJob { +public: + MatchQuerySelectorAllJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } + virtual ~MatchQuerySelectorAllJob() { } + + virtual void match(ListHashSet<Node*>& resultCollector) + { + if (m_query.isEmpty()) + return; + + ExceptionCode ec = 0; + RefPtr<NodeList> list = m_document->querySelectorAll(m_query, ec); + if (!ec) + addNodesToResults(list, resultCollector); + } +}; + +class MatchXPathJob : public WebCore::MatchJob { +public: + MatchXPathJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } + virtual ~MatchXPathJob() { } + + virtual void match(ListHashSet<Node*>& resultCollector) + { +#if ENABLE(XPATH) + if (m_query.isEmpty()) + return; + + ExceptionCode ec = 0; + RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document.get(), 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec); + if (ec || !result) + return; + + unsigned long size = result->snapshotLength(ec); + for (unsigned long i = 0; !ec && i < size; ++i) { + Node* node = result->snapshotItem(i, ec); + if (!ec) + resultCollector.add(node); + } +#endif + } +}; + +class MatchPlainTextJob : public MatchXPathJob { +public: + MatchPlainTextJob(Document* document, const String& query) : MatchXPathJob(document, query) + { + m_query = "//text()[contains(., '" + m_query + "')] | //comment()[contains(., '" + m_query + "')]"; + } + virtual ~MatchPlainTextJob() { } +}; + +} + InspectorDOMAgent::InspectorDOMAgent(InspectorCSSStore* cssStore, InspectorFrontend* frontend) : EventListener(InspectorDOMAgentType) , m_cssStore(cssStore) , m_frontend(frontend) , m_lastNodeId(1) + , m_matchJobsTimer(this, &InspectorDOMAgent::onMatchJobsTimer) { } @@ -87,6 +212,7 @@ InspectorDOMAgent::~InspectorDOMAgent() void InspectorDOMAgent::reset() { + searchCanceled(); discardBindings(); ListHashSet<RefPtr<Document> > copy = m_documents; @@ -506,6 +632,103 @@ void InspectorDOMAgent::getEventListenersForNode(long callId, long nodeId) m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray); } +void InspectorDOMAgent::performSearch(const String& whitespaceTrimmedQuery, bool runSynchronously) +{ + // FIXME: Few things are missing here: + // 1) Search works with node granularity - number of matches within node is not calculated. + // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result + // is sufficient. + + int queryLength = whitespaceTrimmedQuery.length(); + bool startTagFound = !whitespaceTrimmedQuery.find('<'); + bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength; + + String tagNameQuery = whitespaceTrimmedQuery; + if (startTagFound || endTagFound) + tagNameQuery = tagNameQuery.substring(startTagFound ? 1 : 0, endTagFound ? queryLength - 1 : queryLength); + if (!Document::isValidName(tagNameQuery)) + tagNameQuery = ""; + + String attributeNameQuery = whitespaceTrimmedQuery; + if (!Document::isValidName(attributeNameQuery)) + attributeNameQuery = ""; + + String escapedQuery = whitespaceTrimmedQuery; + escapedQuery.replace("'", "\\'"); + String escapedTagNameQuery = tagNameQuery; + escapedTagNameQuery.replace("'", "\\'"); + + // Clear pending jobs. + searchCanceled(); + + // Find all frames, iframes and object elements to search their documents. + for (Frame* frame = mainFrameDocument()->frame(); frame; frame = frame->tree()->traverseNext()) { + Document* document = frame->document(); + if (!document) + continue; + + if (!tagNameQuery.isEmpty() && startTagFound && endTagFound) { + m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery)); + m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); + continue; + } + + if (!tagNameQuery.isEmpty() && startTagFound) { + m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]")); + m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); + continue; + } + + if (!tagNameQuery.isEmpty() && endTagFound) { + // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound. + // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains(). + m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]")); + m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); + continue; + } + + bool matchesEveryNode = whitespaceTrimmedQuery == "//*" || whitespaceTrimmedQuery == "*"; + if (matchesEveryNode) { + // These queries will match every node. Matching everything isn't useful and can be slow for large pages, + // so limit the search functions list to plain text and attribute matching for these. + m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]")); + m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); + continue; + } + + m_pendingMatchJobs.append(new MatchExactIdJob(document, whitespaceTrimmedQuery)); + m_pendingMatchJobs.append(new MatchExactClassNamesJob(document, whitespaceTrimmedQuery)); + m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery)); + m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, "[" + attributeNameQuery + "]")); + m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, whitespaceTrimmedQuery)); + m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]")); + if (!tagNameQuery.isEmpty()) + m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]")); + m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); + m_pendingMatchJobs.append(new MatchXPathJob(document, whitespaceTrimmedQuery)); + } + + if (runSynchronously) { + // For tests. + ListHashSet<Node*> resultCollector; + for (Deque<MatchJob*>::iterator it = m_pendingMatchJobs.begin(); it != m_pendingMatchJobs.end(); ++it) + (*it)->match(resultCollector); + reportNodesAsSearchResults(resultCollector); + searchCanceled(); + return; + } + m_matchJobsTimer.startOneShot(0); +} + +void InspectorDOMAgent::searchCanceled() +{ + if (m_matchJobsTimer.isActive()) + m_matchJobsTimer.stop(); + deleteAllValues(m_pendingMatchJobs); + m_pendingMatchJobs.clear(); + m_searchResults.clear(); +} + String InspectorDOMAgent::documentURLString(Document* document) const { if (!document || document->url().isNull()) @@ -1330,6 +1553,36 @@ CSSStyleSheet* InspectorDOMAgent::getParentStyleSheet(CSSStyleDeclaration* style return parentStyleSheet; } +void InspectorDOMAgent::onMatchJobsTimer(Timer<InspectorDOMAgent>*) +{ + if (!m_pendingMatchJobs.size()) { + searchCanceled(); + return; + } + + ListHashSet<Node*> resultCollector; + MatchJob* job = m_pendingMatchJobs.takeFirst(); + job->match(resultCollector); + delete job; + + reportNodesAsSearchResults(resultCollector); + + m_matchJobsTimer.startOneShot(0.025); +} + +void InspectorDOMAgent::reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector) +{ + ScriptArray nodeIds = m_frontend->newScriptArray(); + int index = 0; + for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it) { + if (m_searchResults.contains(*it)) + continue; + m_searchResults.add(*it); + nodeIds.set(index++, static_cast<long long>(pushNodePathToFrontend(*it))); + } + m_frontend->addNodesToSearchResult(nodeIds); +} + } // namespace WebCore #endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/InspectorDOMAgent.h b/WebCore/inspector/InspectorDOMAgent.h index f8925b5..a962569 100644 --- a/WebCore/inspector/InspectorDOMAgent.h +++ b/WebCore/inspector/InspectorDOMAgent.h @@ -31,13 +31,17 @@ #define InspectorDOMAgent_h #include "AtomicString.h" +#include "Document.h" #include "EventListener.h" #include "EventTarget.h" #include "InspectorCSSStore.h" +#include "NodeList.h" #include "ScriptArray.h" #include "ScriptObject.h" #include "ScriptState.h" +#include "Timer.h" +#include <wtf/Deque.h> #include <wtf/ListHashSet.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> @@ -53,8 +57,8 @@ namespace WebCore { class CSSStyleSheet; class Element; class Event; - class Document; class InspectorFrontend; + class MatchJob; class NameNodeMap; class Node; class Page; @@ -101,6 +105,8 @@ namespace WebCore { 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); + void performSearch(const String& whitespaceTrimmedQuery, bool runSynchronously); + void searchCanceled(); // Methods called from the frontend for CSS styles inspection. void getStyles(long callId, long nodeId, bool authorOnly); @@ -165,6 +171,9 @@ namespace WebCore { String documentURLString(Document* document) const; InspectorCSSStore* cssStore() { return m_cssStore; } + void onMatchJobsTimer(Timer<InspectorDOMAgent>*); + void reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector); + ScriptObject buildObjectForStyle(CSSStyleDeclaration*, bool bind); void populateObjectWithStyleProperties(CSSStyleDeclaration*, ScriptObject& result); ScriptArray buildArrayForDisabledStyleProperties(DisabledStyleDeclaration*); @@ -188,9 +197,11 @@ namespace WebCore { HashSet<long> m_childrenRequested; long m_lastNodeId; ListHashSet<RefPtr<Document> > m_documents; + Deque<MatchJob*> m_pendingMatchJobs; + Timer<InspectorDOMAgent> m_matchJobsTimer; + HashSet<RefPtr<Node> > m_searchResults; }; - } // namespace WebCore #endif // !defined(InspectorDOMAgent_h) diff --git a/WebCore/inspector/InspectorFrontend.cpp b/WebCore/inspector/InspectorFrontend.cpp index fb66cad..c9aa730 100644 --- a/WebCore/inspector/InspectorFrontend.cpp +++ b/WebCore/inspector/InspectorFrontend.cpp @@ -36,6 +36,7 @@ #include "Frame.h" #include "InjectedScript.h" #include "InjectedScriptHost.h" +#include "InspectorClient.h" #include "InspectorController.h" #include "InspectorWorkerResource.h" #include "Node.h" @@ -49,8 +50,9 @@ namespace WebCore { -InspectorFrontend::InspectorFrontend(ScriptObject webInspector) +InspectorFrontend::InspectorFrontend(ScriptObject webInspector, InspectorClient* inspectorClient) : m_webInspector(webInspector) + , m_inspectorClient(inspectorClient) { } @@ -86,10 +88,18 @@ void InspectorFrontend::didCommitLoad() callSimpleFunction("didCommitLoad"); } -void InspectorFrontend::populateFrontendSettings(const String& settings) +void InspectorFrontend::populateApplicationSettings(const String& settings) { ScriptFunctionCall function(m_webInspector, "dispatch"); - function.appendArgument("populateFrontendSettings"); + function.appendArgument("populateApplicationSettings"); + function.appendArgument(settings); + function.call(); +} + +void InspectorFrontend::populateSessionSettings(const String& settings) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("populateSessionSettings"); function.appendArgument(settings); function.call(); } @@ -305,7 +315,7 @@ void InspectorFrontend::debuggerWasDisabled() callSimpleFunction("debuggerWasDisabled"); } -void InspectorFrontend::parsedScriptSource(const String& sourceID, const String& url, const String& data, int firstLine) +void InspectorFrontend::parsedScriptSource(const String& sourceID, const String& url, const String& data, int firstLine, int scriptWorldType) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("parsedScriptSource"); @@ -313,6 +323,7 @@ void InspectorFrontend::parsedScriptSource(const String& sourceID, const String& function.appendArgument(url); function.appendArgument(data); function.appendArgument(firstLine); + function.appendArgument(scriptWorldType); function.call(); } @@ -354,6 +365,30 @@ void InspectorFrontend::resumedScript() callSimpleFunction("resumedScript"); } +void InspectorFrontend::didEditScriptSource(long callId, bool success, const String& result, SerializedScriptValue* newCallFrames) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didEditScriptSource"); + function.appendArgument(callId); + function.appendArgument(success); + function.appendArgument(result); + if (success && newCallFrames) { + ScriptValue newCallFramesValue = ScriptValue::deserialize(scriptState(), newCallFrames); + ASSERT(!newCallFramesValue .hasNoValue()); + function.appendArgument(newCallFramesValue); + } + function.call(); +} + +void InspectorFrontend::didGetScriptSource(long callId, const String& result) +{ + ScriptFunctionCall function(m_webInspector, "dispatch"); + function.appendArgument("didGetScriptSource"); + function.appendArgument(callId); + function.appendArgument(result); + function.call(); +} + void InspectorFrontend::profilerWasEnabled() { callSimpleFunction("profilerWasEnabled"); @@ -738,7 +773,7 @@ void InspectorFrontend::updateDOMStorage(long storageId) } #endif -void InspectorFrontend::addNodesToSearchResult(const String& nodeIds) +void InspectorFrontend::addNodesToSearchResult(const ScriptArray& nodeIds) { ScriptFunctionCall function(m_webInspector, "dispatch"); function.appendArgument("addNodesToSearchResult"); diff --git a/WebCore/inspector/InspectorFrontend.h b/WebCore/inspector/InspectorFrontend.h index fa752aa..1762014 100644 --- a/WebCore/inspector/InspectorFrontend.h +++ b/WebCore/inspector/InspectorFrontend.h @@ -30,6 +30,7 @@ #ifndef InspectorFrontend_h #define InspectorFrontend_h +#include "InspectorValues.h" #include "ScriptArray.h" #include "ScriptObject.h" #include "ScriptState.h" @@ -39,16 +40,20 @@ namespace WebCore { class ConsoleMessage; class Database; class Frame; + class InspectorClient; class InspectorResource; + class InspectorWorkerResource; class Node; class ScriptString; class SerializedScriptValue; class Storage; - class InspectorWorkerResource; class InspectorFrontend : public Noncopyable { public: - InspectorFrontend(ScriptObject webInspector); + // We are in transition from JS transport via webInspector to native + // transport via inspectorClient. After migration, webInspector parameter should + // be removed. + InspectorFrontend(ScriptObject webInspector, InspectorClient* inspectorClient); ~InspectorFrontend(); void close(); @@ -59,7 +64,8 @@ namespace WebCore { void didCommitLoad(); - void populateFrontendSettings(const String& settings); + void populateApplicationSettings(const String& settings); + void populateSessionSettings(const String& settings); void updateConsoleMessageExpiredCount(unsigned count); void addConsoleMessage(const ScriptObject& messageObj, const Vector<ScriptString>& frames, const Vector<RefPtr<SerializedScriptValue> >& arguments, const String& message); @@ -92,12 +98,15 @@ namespace WebCore { void debuggerWasEnabled(); void debuggerWasDisabled(); - void parsedScriptSource(const String& sourceID, const String& url, const String& data, int firstLine); + void parsedScriptSource(const String& sourceID, const String& url, const String& data, int firstLine, int scriptWorldType); 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(); + void didEditScriptSource(long callId, bool success, const String& result, SerializedScriptValue* newCallFrames); + void didGetScriptSource(long callId, const String& result); + void profilerWasEnabled(); void profilerWasDisabled(); void addProfileHeader(const ScriptValue& profile); @@ -158,7 +167,7 @@ namespace WebCore { void didGetCookies(long callId, const ScriptArray& cookies, const String& cookiesString); void didDispatchOnInjectedScript(long callId, SerializedScriptValue* result, bool isException); - void addNodesToSearchResult(const String& nodeIds); + void addNodesToSearchResult(const ScriptArray& nodeIds); void contextMenuItemSelected(int itemId); void contextMenuCleared(); @@ -169,6 +178,7 @@ namespace WebCore { private: void callSimpleFunction(const String& functionName); ScriptObject m_webInspector; + InspectorClient* m_inspectorClient; }; } // namespace WebCore diff --git a/WebCore/inspector/InspectorFrontendClientLocal.cpp b/WebCore/inspector/InspectorFrontendClientLocal.cpp index 18c52da..188566f 100644 --- a/WebCore/inspector/InspectorFrontendClientLocal.cpp +++ b/WebCore/inspector/InspectorFrontendClientLocal.cpp @@ -89,7 +89,7 @@ void InspectorFrontendClientLocal::frontendLoaded() ASSERT_NOT_REACHED(); return; } - m_inspectorController->setFrontend(new InspectorFrontend(webInspectorObj)); + m_inspectorController->connectFrontend(webInspectorObj); } void InspectorFrontendClientLocal::requestAttachWindow() diff --git a/WebCore/inspector/InspectorResource.cpp b/WebCore/inspector/InspectorResource.cpp index 999b8d6..f0e391d 100644 --- a/WebCore/inspector/InspectorResource.cpp +++ b/WebCore/inspector/InspectorResource.cpp @@ -169,7 +169,6 @@ void InspectorResource::updateScriptObject(InspectorFrontend* frontend) jsonObject.set("expectedContentLength", m_expectedContentLength); jsonObject.set("statusCode", m_responseStatusCode); jsonObject.set("statusText", m_responseStatusText); - jsonObject.set("suggestedFilename", m_suggestedFilename); ScriptObject responseHeaders = frontend->newScriptObject(); populateHeadersObject(&responseHeaders, m_responseHeaderFields); jsonObject.set("responseHeaders", responseHeaders); diff --git a/WebCore/inspector/InspectorTimelineAgent.h b/WebCore/inspector/InspectorTimelineAgent.h index 4c5b939..fe334be 100644 --- a/WebCore/inspector/InspectorTimelineAgent.h +++ b/WebCore/inspector/InspectorTimelineAgent.h @@ -97,9 +97,11 @@ public: void willPaint(const IntRect&); void didPaint(); + // FIXME: |length| should be passed in didWrite instead willWrite + // as the parser can not know how much it will process until it tries. 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); diff --git a/WebCore/inspector/InspectorValues.cpp b/WebCore/inspector/InspectorValues.cpp index f59e900..5c7d0c8 100644 --- a/WebCore/inspector/InspectorValues.cpp +++ b/WebCore/inspector/InspectorValues.cpp @@ -35,6 +35,407 @@ namespace WebCore { +namespace { + +static const int stackLimit = 1000; + +enum Token { + OBJECT_BEGIN, + OBJECT_END, + ARRAY_BEGIN, + ARRAY_END, + STRING, + NUMBER, + BOOL_TRUE, + BOOL_FALSE, + NULL_TOKEN, + LIST_SEPARATOR, + OBJECT_PAIR_SEPARATOR, + INVALID_TOKEN, +}; + +const char* const nullString = "null"; +const char* const trueString = "true"; +const char* const falseString = "false"; + +bool parseConstToken(const UChar* start, const UChar* end, const UChar** tokenEnd, const char* token) +{ + while (start < end && *token != '\0' && *start++ == *token++) { } + if (*token != '\0') + return false; + *tokenEnd = start; + return true; +} + +bool readInt(const UChar* start, const UChar* end, const UChar** tokenEnd, bool canHaveLeadingZeros) +{ + if (start == end) + return false; + bool haveLeadingZero = '0' == *start; + int length = 0; + while (start < end && '0' <= *start && *start <= '9') { + ++start; + ++length; + } + if (!length) + return false; + if (!canHaveLeadingZeros && length > 1 && haveLeadingZero) + return false; + *tokenEnd = start; + return true; +} + +bool parseNumberToken(const UChar* start, const UChar* end, const UChar** tokenEnd) +{ + // We just grab the number here. We validate the size in DecodeNumber. + // According to RFC4627, a valid number is: [minus] int [frac] [exp] + if (start == end) + return false; + UChar c = *start; + if ('-' == c) + ++start; + + if (!readInt(start, end, &start, false)) + return false; + if (start == end) { + *tokenEnd = start; + return true; + } + + // Optional fraction part + c = *start; + if ('.' == c) { + ++start; + if (!readInt(start, end, &start, true)) + return false; + if (start == end) { + *tokenEnd = start; + return true; + } + c = *start; + } + + // Optional exponent part + if ('e' == c || 'E' == c) { + ++start; + if (start == end) + return false; + c = *start; + if ('-' == c || '+' == c) { + ++start; + if (start == end) + return false; + } + if (!readInt(start, end, &start, true)) + return false; + } + + *tokenEnd = start; + return true; +} + +bool readHexDigits(const UChar* start, const UChar* end, const UChar** tokenEnd, int digits) +{ + if (end - start < digits) + return false; + for (int i = 0; i < digits; ++i) { + UChar c = *start++; + if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'))) + return false; + } + *tokenEnd = start; + return true; +} + +bool parseStringToken(const UChar* start, const UChar* end, const UChar** tokenEnd) +{ + while (start < end) { + UChar c = *start++; + if ('\\' == c) { + c = *start++; + // Make sure the escaped char is valid. + switch (c) { + case 'x': + if (!readHexDigits(start, end, &start, 2)) + return false; + break; + case 'u': + if (!readHexDigits(start, end, &start, 4)) + return false; + break; + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '"': + break; + default: + return false; + } + } else if ('"' == c) { + *tokenEnd = start; + return true; + } + } + return false; +} + +Token parseToken(const UChar* start, const UChar* end, const UChar** tokenEnd) +{ + if (start == end) + return INVALID_TOKEN; + + switch (*start) { + case 'n': + if (parseConstToken(start, end, tokenEnd, nullString)) + return NULL_TOKEN; + break; + case 't': + if (parseConstToken(start, end, tokenEnd, trueString)) + return BOOL_TRUE; + break; + case 'f': + if (parseConstToken(start, end, tokenEnd, falseString)) + return BOOL_FALSE; + break; + case '[': + *tokenEnd = start + 1; + return ARRAY_BEGIN; + case ']': + *tokenEnd = start + 1; + return ARRAY_END; + case ',': + *tokenEnd = start + 1; + return LIST_SEPARATOR; + case '{': + *tokenEnd = start + 1; + return OBJECT_BEGIN; + case '}': + *tokenEnd = start + 1; + return OBJECT_END; + case ':': + *tokenEnd = start + 1; + return OBJECT_PAIR_SEPARATOR; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + if (parseNumberToken(start, end, tokenEnd)) + return NUMBER; + break; + case '"': + if (parseStringToken(start + 1, end, tokenEnd)) + return STRING; + break; + } + return INVALID_TOKEN; +} + +inline int hexToInt(UChar c) +{ + if ('0' <= c && c <= '9') + return c - '0'; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + ASSERT_NOT_REACHED(); + return 0; +} + +bool decodeString(const UChar* start, const UChar* end, Vector<UChar>* output) +{ + while (start < end) { + UChar c = *start++; + if ('\\' != c) { + output->append(c); + continue; + } + c = *start++; + switch (c) { + case '"': + case '/': + case '\\': + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'x': + c = (hexToInt(*start) << 4) + + hexToInt(*(start + 1)); + start += 2; + break; + case 'u': + c = (hexToInt(*start) << 12) + + (hexToInt(*(start + 1)) << 8) + + (hexToInt(*(start + 2)) << 4) + + hexToInt(*(start + 3)); + start += 4; + break; + default: + return false; + } + output->append(c); + } + return true; +} + +bool decodeString(const UChar* start, const UChar* end, String* output) +{ + if (start == end) { + *output = ""; + return true; + } + if (start > end) + return false; + Vector<UChar> buffer; + buffer.reserveCapacity(end - start); + if (!decodeString(start, end, &buffer)) + return false; + *output = String(buffer.data(), buffer.size()); + return true; +} + +PassRefPtr<InspectorValue> buildValue(const UChar* start, const UChar* end, const UChar** valueTokenEnd, int depth) +{ + if (depth > stackLimit) + return 0; + + RefPtr<InspectorValue> result; + const UChar* tokenEnd; + Token token = parseToken(start, end, &tokenEnd); + switch (token) { + case INVALID_TOKEN: + return 0; + case NULL_TOKEN: + result = InspectorValue::null(); + break; + case BOOL_TRUE: + result = InspectorBasicValue::create(true); + break; + case BOOL_FALSE: + result = InspectorBasicValue::create(false); + break; + case NUMBER: { + bool ok; + double value = charactersToDouble(start, tokenEnd - start, &ok); + if (!ok) + return 0; + result = InspectorBasicValue::create(value); + break; + } + case STRING: { + String value; + bool ok = decodeString(start + 1, tokenEnd - 1, &value); + if (!ok) + return 0; + result = InspectorString::create(value); + break; + } + case ARRAY_BEGIN: { + RefPtr<InspectorArray> array = InspectorArray::create(); + start = tokenEnd; + token = parseToken(start, end, &tokenEnd); + while (token != ARRAY_END) { + RefPtr<InspectorValue> arrayNode = buildValue(start, end, &tokenEnd, depth + 1); + if (!arrayNode) + return 0; + array->push(arrayNode); + + // After a list value, we expect a comma or the end of the list. + start = tokenEnd; + token = parseToken(start, end, &tokenEnd); + if (token == LIST_SEPARATOR) { + start = tokenEnd; + token = parseToken(start, end, &tokenEnd); + if (token == ARRAY_END) + return 0; + } else if (token != ARRAY_END) { + // Unexpected value after list value. Bail out. + return 0; + } + } + if (token != ARRAY_END) + return 0; + result = array.release(); + break; + } + case OBJECT_BEGIN: { + RefPtr<InspectorObject> object = InspectorObject::create(); + start = tokenEnd; + token = parseToken(start, end, &tokenEnd); + while (token != OBJECT_END) { + if (token != STRING) + return 0; + String key; + if (!decodeString(start + 1, tokenEnd - 1, &key)) + return 0; + start = tokenEnd; + + token = parseToken(start, end, &tokenEnd); + if (token != OBJECT_PAIR_SEPARATOR) + return 0; + start = tokenEnd; + + RefPtr<InspectorValue> value = buildValue(start, end, &tokenEnd, depth + 1); + if (!value) + return 0; + object->set(key, value); + start = tokenEnd; + + // After a key/value pair, we expect a comma or the end of the + // object. + token = parseToken(start, end, &tokenEnd); + if (token == LIST_SEPARATOR) { + start = tokenEnd; + token = parseToken(start, end, &tokenEnd); + if (token == OBJECT_END) + return 0; + } else if (token != OBJECT_END) { + // Unexpected value after last object value. Bail out. + return 0; + } + } + if (token != OBJECT_END) + return 0; + result = object.release(); + break; + } + + default: + // We got a token that's not a value. + return 0; + } + *valueTokenEnd = tokenEnd; + return result.release(); +} + inline bool escapeChar(UChar c, Vector<UChar>* dst) { switch (c) { @@ -71,6 +472,44 @@ inline void doubleQuoteString(const String& str, Vector<UChar>* dst) dst->append('"'); } +} // anonymous namespace + +bool InspectorValue::asBool(bool*) const +{ + return false; +} + +bool InspectorValue::asNumber(double*) const +{ + return false; +} + +bool InspectorValue::asString(String*) const +{ + return false; +} + +PassRefPtr<InspectorObject> InspectorValue::asObject() +{ + return 0; +} + +PassRefPtr<InspectorArray> InspectorValue::asArray() +{ + return 0; +} + +PassRefPtr<InspectorValue> InspectorValue::readJSON(const String& json) +{ + const UChar* start = json.characters(); + const UChar* end = json.characters() + json.length(); + const UChar *tokenEnd; + RefPtr<InspectorValue> value = buildValue(start, end, &tokenEnd, 0); + if (!value || tokenEnd != end) + return 0; + return value.release(); +} + String InspectorValue::toJSONString() const { Vector<UChar> result; @@ -82,7 +521,23 @@ String InspectorValue::toJSONString() const void InspectorValue::writeJSON(Vector<UChar>* output) const { ASSERT(m_type == TypeNull); - output->append("null", 4); + output->append(nullString, 4); +} + +bool InspectorBasicValue::asBool(bool* output) const +{ + if (type() != TypeBoolean) + return false; + *output = m_boolValue; + return true; +} + +bool InspectorBasicValue::asNumber(double* output) const +{ + if (type() != TypeDouble) + return false; + *output = m_doubleValue; + return true; } void InspectorBasicValue::writeJSON(Vector<UChar>* output) const @@ -90,26 +545,87 @@ void InspectorBasicValue::writeJSON(Vector<UChar>* output) const ASSERT(type() == TypeBoolean || type() == TypeDouble); if (type() == TypeBoolean) { if (m_boolValue) - output->append("true", 4); + output->append(trueString, 4); else - output->append("false", 5); + output->append(falseString, 5); } else if (type() == TypeDouble) { String value = String::format("%f", m_doubleValue); output->append(value.characters(), value.length()); } } +bool InspectorString::asString(String* output) const +{ + *output = m_stringValue; + return true; +} + void InspectorString::writeJSON(Vector<UChar>* output) const { ASSERT(type() == TypeString); doubleQuoteString(m_stringValue, output); } +PassRefPtr<InspectorObject> InspectorObject::asObject() +{ + return this; +} + +bool InspectorObject::getBool(const String& name, bool* output) const +{ + RefPtr<InspectorValue> value = get(name); + if (!value) + return false; + return value->asBool(output); +} + +bool InspectorObject::getNumber(const String& name, double* output) const +{ + RefPtr<InspectorValue> value = get(name); + if (!value) + return false; + return value->asNumber(output); +} + +bool InspectorObject::getString(const String& name, String* output) const +{ + RefPtr<InspectorValue> value = get(name); + if (!value) + return false; + return value->asString(output); +} + +PassRefPtr<InspectorObject> InspectorObject::getObject(const String& name) const +{ + PassRefPtr<InspectorValue> value = get(name); + if (!value) + return false; + return value->asObject(); +} + +PassRefPtr<InspectorArray> InspectorObject::getArray(const String& name) const +{ + PassRefPtr<InspectorValue> value = get(name); + if (!value) + return false; + return value->asArray(); +} + +PassRefPtr<InspectorValue> InspectorObject::get(const String& name) const +{ + Dictionary::const_iterator it = m_data.find(name); + if (it == m_data.end()) + return 0; + return it->second; +} + void InspectorObject::writeJSON(Vector<UChar>* output) const { output->append('{'); - for (Dictionary::const_iterator it = m_data.begin(); it != m_data.end(); ++it) { - if (it != m_data.begin()) + for (size_t i = 0; i < m_order.size(); ++i) { + Dictionary::const_iterator it = m_data.find(m_order[i]); + ASSERT(it != m_data.end()); + if (i) output->append(','); doubleQuoteString(it->first, output); output->append(':'); @@ -118,6 +634,11 @@ void InspectorObject::writeJSON(Vector<UChar>* output) const output->append('}'); } +PassRefPtr<InspectorArray> InspectorArray::asArray() +{ + return this; +} + void InspectorArray::writeJSON(Vector<UChar>* output) const { output->append('['); diff --git a/WebCore/inspector/InspectorValues.h b/WebCore/inspector/InspectorValues.h index a60bb2c..b9920c4 100644 --- a/WebCore/inspector/InspectorValues.h +++ b/WebCore/inspector/InspectorValues.h @@ -42,6 +42,8 @@ namespace WebCore { +class InspectorArray; +class InspectorObject; class String; class InspectorValue : public RefCounted<InspectorValue> { @@ -65,6 +67,14 @@ public: Type type() const { return m_type; } + virtual bool asBool(bool* output) const; + virtual bool asNumber(double* output) const; + virtual bool asString(String* output) const; + virtual PassRefPtr<InspectorObject> asObject(); + virtual PassRefPtr<InspectorArray> asArray(); + + static PassRefPtr<InspectorValue> readJSON(const String& json); + String toJSONString() const; virtual void writeJSON(Vector<UChar>* output) const; @@ -93,6 +103,9 @@ public: return adoptRef(new InspectorBasicValue(value)); } + virtual bool asBool(bool* output) const; + virtual bool asNumber(double* output) const; + virtual void writeJSON(Vector<UChar>* output) const; private: @@ -117,6 +130,9 @@ public: { return adoptRef(new InspectorString(value)); } + + virtual bool asString(String* output) const; + virtual void writeJSON(Vector<UChar>* output) const; private: @@ -127,6 +143,13 @@ private: }; class InspectorObject : public InspectorValue { +private: + typedef HashMap<String, RefPtr<InspectorValue> > Dictionary; + +public: + typedef Dictionary::iterator iterator; + typedef Dictionary::const_iterator const_iterator; + public: static PassRefPtr<InspectorObject> create() { @@ -134,17 +157,31 @@ public: } ~InspectorObject() { } + virtual PassRefPtr<InspectorObject> asObject(); + void setBool(const String& name, bool); void setNumber(const String& name, double); void setString(const String& name, const String&); void set(const String& name, PassRefPtr<InspectorValue>); + bool getBool(const String& name, bool* output) const; + bool getNumber(const String& name, double* output) const; + bool getString(const String& name, String* output) const; + PassRefPtr<InspectorObject> getObject(const String& name) const; + PassRefPtr<InspectorArray> getArray(const String& name) const; + PassRefPtr<InspectorValue> get(const String& name) const; + virtual void writeJSON(Vector<UChar>* output) const; + iterator begin() { return m_data.begin(); } + iterator end() { return m_data.end(); } + const_iterator begin() const { return m_data.begin(); } + const_iterator end() const { return m_data.end(); } + private: InspectorObject() : InspectorValue(TypeObject) { } - typedef HashMap<String, RefPtr<InspectorValue> > Dictionary; Dictionary m_data; + Vector<String> m_order; }; class InspectorArray : public InspectorValue { @@ -155,6 +192,8 @@ public: } ~InspectorArray() { } + virtual PassRefPtr<InspectorArray> asArray(); + void pushBool(bool); void pushNumber(double); void pushString(const String&); @@ -170,22 +209,23 @@ private: inline void InspectorObject::setBool(const String& name, bool value) { - m_data.set(name, InspectorBasicValue::create(value)); + set(name, InspectorBasicValue::create(value)); } inline void InspectorObject::setNumber(const String& name, double value) { - m_data.set(name, InspectorBasicValue::create(value)); + set(name, InspectorBasicValue::create(value)); } inline void InspectorObject::setString(const String& name, const String& value) { - m_data.set(name, InspectorString::create(value)); + set(name, InspectorString::create(value)); } inline void InspectorObject::set(const String& name, PassRefPtr<InspectorValue> value) { - m_data.set(name, value); + if (m_data.set(name, value).second) + m_order.append(name); } inline void InspectorArray::pushBool(bool value) @@ -212,4 +252,3 @@ inline void InspectorArray::push(PassRefPtr<InspectorValue> value) #endif // ENABLE(INSPECTOR) #endif // !defined(InspectorValues_h) - diff --git a/WebCore/inspector/ScriptDebugListener.h b/WebCore/inspector/ScriptDebugListener.h index c669f5e..539b000 100644 --- a/WebCore/inspector/ScriptDebugListener.h +++ b/WebCore/inspector/ScriptDebugListener.h @@ -38,11 +38,16 @@ namespace WebCore { class String; +enum ScriptWorldType { +MAIN_WORLD = 0, +EXTENSIONS_WORLD +}; + class ScriptDebugListener { public: virtual ~ScriptDebugListener() { } - virtual void didParseSource(const String& sourceID, const String& url, const String& data, int firstLine) = 0; + virtual void didParseSource(const String& sourceID, const String& url, const String& data, int firstLine, ScriptWorldType) = 0; virtual void failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) = 0; virtual void didPause(ScriptState*) = 0; virtual void didContinue() = 0; diff --git a/WebCore/inspector/front-end/Breakpoint.js b/WebCore/inspector/front-end/Breakpoint.js deleted file mode 100644 index e69de29..0000000 --- a/WebCore/inspector/front-end/Breakpoint.js +++ /dev/null diff --git a/WebCore/inspector/front-end/BreakpointManager.js b/WebCore/inspector/front-end/BreakpointManager.js index c277fb1..3ccccac 100644 --- a/WebCore/inspector/front-end/BreakpointManager.js +++ b/WebCore/inspector/front-end/BreakpointManager.js @@ -30,11 +30,32 @@ WebInspector.BreakpointManager = function() } WebInspector.BreakpointManager.prototype = { + setOneTimeBreakpoint: function(sourceID, line) + { + var breakpoint = new WebInspector.Breakpoint(this, sourceID, undefined, line, true, undefined); + if (this._breakpoints[breakpoint.id]) + return; + if (this._oneTimeBreakpoint) + this._removeBreakpointFromBackend(this._oneTimeBreakpoint); + this._oneTimeBreakpoint = breakpoint; + this._saveBreakpointOnBackend(breakpoint); + }, + + removeOneTimeBreakpoint: function() + { + if (this._oneTimeBreakpoint) { + this._removeBreakpointFromBackend(this._oneTimeBreakpoint); + delete this._oneTimeBreakpoint; + } + }, + addBreakpoint: function(sourceID, sourceURL, line, enabled, condition) { var breakpoint = new WebInspector.Breakpoint(this, sourceID, sourceURL, line, enabled, condition); if (this._breakpoints[breakpoint.id]) return; + if (this._oneTimeBreakpoint && (this._oneTimeBreakpoint.id == breakpoint.id)) + delete this._oneTimeBreakpoint; this._breakpoints[breakpoint.id] = breakpoint; this._saveBreakpointOnBackend(breakpoint); this.dispatchEventToListeners("breakpoint-added", breakpoint); @@ -72,6 +93,7 @@ WebInspector.BreakpointManager.prototype = { reset: function() { this._breakpoints = {}; + delete this._oneTimeBreakpoint; }, _saveBreakpointOnBackend: function(breakpoint) diff --git a/WebCore/inspector/front-end/CSSStyleModel.js b/WebCore/inspector/front-end/CSSStyleModel.js new file mode 100644 index 0000000..66a20ce --- /dev/null +++ b/WebCore/inspector/front-end/CSSStyleModel.js @@ -0,0 +1,111 @@ +/* + * 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.CSSStyleModel = function() +{ +} + +WebInspector.CSSStyleModel.prototype = { + getStylesAsync: function(nodeId, authOnly, userCallback) + { + InspectorBackend.getStyles(WebInspector.Callback.wrap(userCallback), nodeId, authOnly); + }, + + getComputedStyleAsync: function(nodeId, userCallback) + { + InspectorBackend.getComputedStyle(WebInspector.Callback.wrap(userCallback), nodeId); + }, + + setRuleSelector: function(ruleId, newContent, nodeId, successCallback, failureCallback) + { + function callback(newRulePayload, doesAffectSelectedNode) + { + if (!newRulePayload) + failureCallback(); + else + successCallback(WebInspector.CSSStyleDeclaration.parseRule(newRulePayload), doesAffectSelectedNode); + } + + InspectorBackend.setRuleSelector(WebInspector.Callback.wrap(callback), ruleId, newContent, nodeId); + }, + + addRule: function(nodeId, newContent, successCallback, failureCallback) + { + function callback(rule, doesAffectSelectedNode) + { + if (!rule) { + // Invalid syntax for a selector + failureCallback(); + } else { + var styleRule = WebInspector.CSSStyleDeclaration.parseRule(rule); + styleRule.rule = rule; + successCallback(styleRule, doesAffectSelectedNode); + } + } + + InspectorBackend.addRule(WebInspector.Callback.wrap(callback), newContent, nodeId); + }, + + toggleStyleEnabled: function(styleId, propertyName, disabled, userCallback) + { + function callback(newPayload) + { + if (!newPayload) { + userCallback(null); + return; + } + + var newStyle = WebInspector.CSSStyleDeclaration.parseStyle(newPayload); + userCallback(newStyle); + } + + InspectorBackend.toggleStyleEnabled(WebInspector.Callback.wrap(callback), styleId, propertyName, disabled); + }, + + setCSSText: function(styleId, cssText) + { + InspectorBackend.setStyleText(WebInspector.Callback.wrap(null), styleId, cssText); + }, + + applyStyleText: function(styleId, styleText, propertyName, successCallback, failureCallback) + { + function callback(success, newPayload, changedProperties) + { + if (!success) + failureCallback(); + else { + var newStyle = newPayload ? WebInspector.CSSStyleDeclaration.parseStyle(newPayload) : null; + successCallback(newStyle, changedProperties); + } + } + + InspectorBackend.applyStyleText(WebInspector.Callback.wrap(callback), styleId, styleText, propertyName); + } +} diff --git a/WebCore/inspector/front-end/ConsoleView.js b/WebCore/inspector/front-end/ConsoleView.js index 8521cb3..d1f347b 100644 --- a/WebCore/inspector/front-end/ConsoleView.js +++ b/WebCore/inspector/front-end/ConsoleView.js @@ -48,7 +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); + WebInspector.applicationSettings.addEventListener("loaded", this._settingsLoaded, this); this.topGroup = new WebInspector.ConsoleGroup(null, 0); this.messagesElement.insertBefore(this.topGroup.element, this.promptElement); @@ -104,7 +104,7 @@ WebInspector.ConsoleView = function(drawer) WebInspector.ConsoleView.prototype = { _settingsLoaded: function() { - this.prompt.history = WebInspector.settings.consoleHistory; + this.prompt.history = WebInspector.applicationSettings.consoleHistory; }, _updateFilter: function(e) @@ -536,7 +536,7 @@ WebInspector.ConsoleView.prototype = { self.prompt.historyOffset = 0; self.prompt.text = ""; - WebInspector.settings.consoleHistory = self.prompt.history.slice(-30); + WebInspector.applicationSettings.consoleHistory = self.prompt.history.slice(-30); self.addMessage(new WebInspector.ConsoleCommandResult(result, exception, commandMessage)); } diff --git a/WebCore/inspector/front-end/ElementsPanel.js b/WebCore/inspector/front-end/ElementsPanel.js index c853440..55ba82d 100644 --- a/WebCore/inspector/front-end/ElementsPanel.js +++ b/WebCore/inspector/front-end/ElementsPanel.js @@ -240,7 +240,7 @@ WebInspector.ElementsPanel.prototype = { this._currentSearchResultIndex = 0; this._searchResults = []; - InjectedScriptAccess.getDefault().searchCanceled(function() {}); + InspectorBackend.searchCanceled(); }, performSearch: function(query) @@ -256,7 +256,7 @@ WebInspector.ElementsPanel.prototype = { this._matchesCountUpdateTimeout = null; this._searchQuery = query; - InjectedScriptAccess.getDefault().performSearch(whitespaceTrimmedQuery, false, function() {}); + InspectorBackend.performSearch(whitespaceTrimmedQuery); }, searchingForNodeWasEnabled: function() @@ -288,12 +288,11 @@ WebInspector.ElementsPanel.prototype = { addNodesToSearchResult: function(nodeIds) { - if (!nodeIds) + if (!nodeIds.length) return; - var nodeIdsArray = nodeIds.split(","); - for (var i = 0; i < nodeIdsArray.length; ++i) { - var nodeId = nodeIdsArray[i]; + for (var i = 0; i < nodeIds.length; ++i) { + var nodeId = nodeIds[i]; var node = WebInspector.domAgent.nodeForId(nodeId); if (!node) continue; diff --git a/WebCore/inspector/front-end/EventListenersSidebarPane.js b/WebCore/inspector/front-end/EventListenersSidebarPane.js index 045d29b..6798845 100644 --- a/WebCore/inspector/front-end/EventListenersSidebarPane.js +++ b/WebCore/inspector/front-end/EventListenersSidebarPane.js @@ -46,7 +46,7 @@ WebInspector.EventListenersSidebarPane = function() option.label = WebInspector.UIString("Selected Node Only"); this.settingsSelectElement.appendChild(option); - WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this); + WebInspector.applicationSettings.addEventListener("loaded", this._settingsLoaded, this); this.settingsSelectElement.addEventListener("click", function(event) { event.stopPropagation() }, false); this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false); @@ -56,7 +56,7 @@ WebInspector.EventListenersSidebarPane = function() WebInspector.EventListenersSidebarPane.prototype = { _settingsLoaded: function() { - var filter = WebInspector.settings.eventListenersFilter; + var filter = WebInspector.applicationSettings.eventListenersFilter; if (filter === "all") this.settingsSelectElement[0].selected = true; if (filter === "selected") @@ -112,7 +112,7 @@ WebInspector.EventListenersSidebarPane.prototype = { _changeSetting: function(event) { var selectedOption = this.settingsSelectElement[this.settingsSelectElement.selectedIndex]; - WebInspector.settings.eventListenersFilter = selectedOption.value; + WebInspector.applicationSettings.eventListenersFilter = selectedOption.value; for (var i = 0; i < this.sections.length; ++i) this.sections[i].update(); @@ -142,7 +142,7 @@ WebInspector.EventListenersSection.prototype = { { // A Filtered Array simplifies when to create connectors var filteredEventListeners = this.eventListeners; - if (WebInspector.settings.eventListenersFilter === "selected") { + if (WebInspector.applicationSettings.eventListenersFilter === "selected") { filteredEventListeners = []; for (var i = 0; i < this.eventListeners.length; ++i) { var eventListener = this.eventListeners[i]; diff --git a/WebCore/inspector/front-end/HAREntry.js b/WebCore/inspector/front-end/HAREntry.js new file mode 100644 index 0000000..c06e64f --- /dev/null +++ b/WebCore/inspector/front-end/HAREntry.js @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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. + */ + +// See http://groups.google.com/group/http-archive-specification/web/har-1-2-spec +// for HAR specification. + +WebInspector.HAREntry = function(resource) +{ + this._resource = resource; +} + +WebInspector.HAREntry.prototype = { + build: function() + { + return { + pageref: this._resource.documentURL, + startedDateTime: new Date(this._resource.startTime * 1000), + time: this._toMilliseconds(this._resource.duration), + request: this._buildRequest(), + response: this._buildResponse(), + // cache: {...}, -- Not supproted yet. + timings: this._buildTimings() + }; + }, + + _buildRequest: function() + { + var res = { + method: this._resource.requestMethod, + url: this._resource.url, + // httpVersion: "HTTP/1.1" -- Not available. + // cookies: [] -- Not available. + headers: this._buildHeaders(this._resource.requestHeaders), + headersSize: -1, // Not available. + bodySize: -1 // Not available. + }; + if (this._resource.queryParameters) + res.queryString = this._buildParameters(this._resource.queryParameters); + if (this._resource.requestFormData) + res.postData = this._buildPostData(); + return res; + }, + + _buildResponse: function() + { + return { + status: this._resource.statusCode, + statusText: this._resource.statusText, + // "httpVersion": "HTTP/1.1" -- Not available. + // "cookies": [], -- Not available. + headers: this._buildHeaders(this._resource.responseHeaders), + content: this._buildContent(), + redirectURL: this._resource.responseHeaderValue("Location") || "", + headersSize: -1, // Not available. + bodySize: this._resource.resourceSize + }; + }, + + _buildContent: function() + { + return { + size: this._resource.resourceSize, + // compression: 0, -- Not available. + mimeType: this._resource.mimeType, + // text: -- Not available. + }; + }, + + _buildTimings: function() + { + return { + blocked: -1, // Not available. + dns: -1, // Not available. + connect: -1, // Not available. + send: -1, // Not available. + wait: this._toMilliseconds(this._resource.latency), + receive: this._toMilliseconds(this._resource.receiveDuration), + ssl: -1 // Not available. + }; + }, + + _buildHeaders: function(headers) + { + var result = []; + for (var name in headers) + result.push({ name: name, value: headers[name] }); + return result; + }, + + _buildPostData: function() + { + return { + mimeType: this._resource.requestHeaderValue("Content-Type"), + params: this._buildParameters(this._resource.formParameters), + text: this._resource.requestFormData + }; + }, + + _buildParameters: function(parameters) + { + return parameters.slice(); + }, + + _toMilliseconds: function(time) + { + return time === -1 ? -1 : Math.round(time * 1000); + } +}; diff --git a/WebCore/inspector/front-end/InjectedScript.js b/WebCore/inspector/front-end/InjectedScript.js index e3be6a3..e62a916 100644 --- a/WebCore/inspector/front-end/InjectedScript.js +++ b/WebCore/inspector/front-end/InjectedScript.js @@ -83,8 +83,6 @@ InjectedScript.releaseWrapperObjectGroup = function(objectGroupName) { // Called from within InspectorController on the 'inspected page' side. InjectedScript.reset = function() { - InjectedScript._searchResults = []; - InjectedScript._includedInSearchResultsPropertyName = "__includedInInspectorSearchResults"; } InjectedScript.reset(); @@ -211,7 +209,7 @@ InjectedScript.setPropertyValue = function(objectProxy, propertyName, expression return true; } catch(e) { try { - var result = inspectedWindow.eval("\"" + InjectedScript._escapeCharacters(expression, "\"") + "\""); + var result = inspectedWindow.eval("\"" + expression.replace(/"/g, "\\\"") + "\""); object[propertyName] = result; return true; } catch(e) { @@ -355,247 +353,6 @@ 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. - // 2) Search does not work outside main documents' domain - we need to use specific InjectedScript instances - // for other domains. - // 3) There is no need to push all search results to the front-end at a time, pushing next / previous result - // is sufficient. - var tagNameQuery = whitespaceTrimmedQuery; - var attributeNameQuery = whitespaceTrimmedQuery; - var startTagFound = (tagNameQuery.indexOf("<") === 0); - var endTagFound = (tagNameQuery.lastIndexOf(">") === (tagNameQuery.length - 1)); - - if (startTagFound || endTagFound) { - var tagNameQueryLength = tagNameQuery.length; - tagNameQuery = tagNameQuery.substring((startTagFound ? 1 : 0), (endTagFound ? (tagNameQueryLength - 1) : tagNameQueryLength)); - } - - // Check the tagNameQuery is it is a possibly valid tag name. - if (!/^[a-zA-Z0-9\-_:]+$/.test(tagNameQuery)) - tagNameQuery = null; - - // Check the attributeNameQuery is it is a possibly valid tag name. - if (!/^[a-zA-Z0-9\-_:]+$/.test(attributeNameQuery)) - attributeNameQuery = null; - - const escapedQuery = InjectedScript._escapeCharacters(whitespaceTrimmedQuery, "'"); - const escapedTagNameQuery = (tagNameQuery ? InjectedScript._escapeCharacters(tagNameQuery, "'") : null); - const escapedWhitespaceTrimmedQuery = InjectedScript._escapeCharacters(whitespaceTrimmedQuery, "'"); - const searchResultsProperty = InjectedScript._includedInSearchResultsPropertyName; - - function addNodesToResults(nodes, length, getItem) - { - if (!length) - return; - - var nodeIds = []; - for (var i = 0; i < length; ++i) { - var node = getItem.call(nodes, i); - // Skip this node if it already has the property. - if (searchResultsProperty in node) - continue; - - if (!InjectedScript._searchResults.length) { - InjectedScript._currentSearchResultIndex = 0; - } - - node[searchResultsProperty] = true; - InjectedScript._searchResults.push(node); - var nodeId = InjectedScriptHost.pushNodePathToFrontend(node, false, false); - nodeIds.push(nodeId); - } - InjectedScriptHost.addNodesToSearchResult(nodeIds.join(",")); - } - - function matchExactItems(doc) - { - matchExactId.call(this, doc); - matchExactClassNames.call(this, doc); - matchExactTagNames.call(this, doc); - matchExactAttributeNames.call(this, doc); - } - - function matchExactId(doc) - { - const result = doc.__proto__.getElementById.call(doc, whitespaceTrimmedQuery); - addNodesToResults.call(this, result, (result ? 1 : 0), function() { return this }); - } - - function matchExactClassNames(doc) - { - const result = doc.__proto__.getElementsByClassName.call(doc, whitespaceTrimmedQuery); - addNodesToResults.call(this, result, result.length, result.item); - } - - function matchExactTagNames(doc) - { - if (!tagNameQuery) - return; - const result = doc.__proto__.getElementsByTagName.call(doc, tagNameQuery); - addNodesToResults.call(this, result, result.length, result.item); - } - - function matchExactAttributeNames(doc) - { - if (!attributeNameQuery) - return; - const result = doc.__proto__.querySelectorAll.call(doc, "[" + attributeNameQuery + "]"); - addNodesToResults.call(this, result, result.length, result.item); - } - - function matchPartialTagNames(doc) - { - if (!tagNameQuery) - return; - const result = doc.__proto__.evaluate.call(doc, "//*[contains(name(), '" + escapedTagNameQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem); - } - - function matchStartOfTagNames(doc) - { - if (!tagNameQuery) - return; - const result = doc.__proto__.evaluate.call(doc, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem); - } - - function matchPartialTagNamesAndAttributeValues(doc) - { - if (!tagNameQuery) { - matchPartialAttributeValues.call(this, doc); - return; - } - - const result = doc.__proto__.evaluate.call(doc, "//*[contains(name(), '" + escapedTagNameQuery + "') or contains(@*, '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem); - } - - function matchPartialAttributeValues(doc) - { - const result = doc.__proto__.evaluate.call(doc, "//*[contains(@*, '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem); - } - - function matchStyleSelector(doc) - { - const result = doc.__proto__.querySelectorAll.call(doc, whitespaceTrimmedQuery); - addNodesToResults.call(this, result, result.length, result.item); - } - - function matchPlainText(doc) - { - const result = doc.__proto__.evaluate.call(doc, "//text()[contains(., '" + escapedQuery + "')] | //comment()[contains(., '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem); - } - - function matchXPathQuery(doc) - { - const result = doc.__proto__.evaluate.call(doc, whitespaceTrimmedQuery, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem); - } - - function finishedSearching() - { - // Remove the searchResultsProperty now that the search is finished. - for (var i = 0; i < InjectedScript._searchResults.length; ++i) - delete InjectedScript._searchResults[i][searchResultsProperty]; - } - - const mainFrameDocument = inspectedWindow.document; - const searchDocuments = [mainFrameDocument]; - var searchFunctions; - if (tagNameQuery && startTagFound && endTagFound) - searchFunctions = [matchExactTagNames, matchPlainText]; - else if (tagNameQuery && startTagFound) - searchFunctions = [matchStartOfTagNames, matchPlainText]; - else if (tagNameQuery && endTagFound) { - // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound. - // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains(). - searchFunctions = [matchPartialTagNames, matchPlainText]; - } else if (whitespaceTrimmedQuery === "//*" || whitespaceTrimmedQuery === "*") { - // These queries will match every node. Matching everything isn't useful and can be slow for large pages, - // so limit the search functions list to plain text and attribute matching. - searchFunctions = [matchPartialAttributeValues, matchPlainText]; - } else - searchFunctions = [matchExactItems, matchStyleSelector, matchPartialTagNamesAndAttributeValues, matchPlainText, matchXPathQuery]; - - // Find all frames, iframes and object elements to search their documents. - const subdocumentResult = mainFrameDocument.querySelectorAll("iframe, frame, object"); - - for (var i = 0; i < subdocumentResult.length; ++i) { - var element = subdocumentResult.item(i); - if (element.contentDocument) - searchDocuments.push(element.contentDocument); - } - - const panel = InjectedScript; - var documentIndex = 0; - var searchFunctionIndex = 0; - var chunkIntervalIdentifier = null; - - // Split up the work into chunks so we don't block the UI thread while processing. - - function processChunk() - { - var searchDocument = searchDocuments[documentIndex]; - var searchFunction = searchFunctions[searchFunctionIndex]; - - if (++searchFunctionIndex > searchFunctions.length) { - searchFunction = searchFunctions[0]; - searchFunctionIndex = 0; - - if (++documentIndex > searchDocuments.length) { - if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier) - delete panel._currentSearchChunkIntervalIdentifier; - clearInterval(chunkIntervalIdentifier); - finishedSearching.call(panel); - return false; - } - - searchDocument = searchDocuments[documentIndex]; - } - - try { - searchFunction.call(panel, searchDocument); - } catch(err) { - // ignore any exceptions. the query might be malformed, but we allow that. - } - return true; - } - - if (runSynchronously) - while (processChunk()) {} - else { - processChunk(); - chunkIntervalIdentifier = setInterval(processChunk, 25); - InjectedScript._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier; - } - return true; -} - -InjectedScript.searchCanceled = function() -{ - if (InjectedScript._searchResults) { - const searchResultsProperty = InjectedScript._includedInSearchResultsPropertyName; - for (var i = 0; i < this._searchResults.length; ++i) { - var node = this._searchResults[i]; - - // Remove the searchResultsProperty since there might be an unfinished search. - delete node[searchResultsProperty]; - } - } - - if (InjectedScript._currentSearchChunkIntervalIdentifier) { - clearInterval(InjectedScript._currentSearchChunkIntervalIdentifier); - delete InjectedScript._currentSearchChunkIntervalIdentifier; - } - InjectedScript._searchResults = []; - return true; -} - InjectedScript.openInInspectedWindow = function(url) { // Don't call window.open on wrapper - popup blocker mutes it. @@ -1017,38 +774,15 @@ InjectedScript._className = function(obj) // Both of the methods below result in "Object" names on the foreign engine bindings. // I gave up and am using a check below to distinguish between the egine bingings. - if (typeof Document === "object") { - // JSC + if (jsEngine == "JSC") { var str = inspectedWindow.Object ? inspectedWindow.Object.prototype.toString.call(obj) : InjectedScript._toString(obj); return str.replace(/^\[object (.*)\]$/i, "$1"); + } else { + // V8 + if (typeof obj !== "object") + return "null"; + return obj.constructor.name || "Object"; } - // V8 - if (typeof obj !== "object") - return "null"; - return obj.constructor.name; -} - -InjectedScript._escapeCharacters = function(str, chars) -{ - var foundChar = false; - for (var i = 0; i < chars.length; ++i) { - if (str.indexOf(chars.charAt(i)) !== -1) { - foundChar = true; - break; - } - } - - if (!foundChar) - return str; - - var result = ""; - for (var i = 0; i < str.length; ++i) { - if (chars.indexOf(str.charAt(i)) !== -1) - result += "\\"; - result += str.charAt(i); - } - - return result; } return InjectedScript; diff --git a/WebCore/inspector/front-end/InjectedScriptAccess.js b/WebCore/inspector/front-end/InjectedScriptAccess.js index 2558267..c388213 100644 --- a/WebCore/inspector/front-end/InjectedScriptAccess.js +++ b/WebCore/inspector/front-end/InjectedScriptAccess.js @@ -71,29 +71,18 @@ InjectedScriptAccess._installHandler = function(methodName, async) // - Make sure last parameter of all the InjectedSriptAccess.* calls is a callback function. // We keep these sorted. InjectedScriptAccess._installHandler("addInspectedNode"); -InjectedScriptAccess._installHandler("addStyleSelector"); -InjectedScriptAccess._installHandler("applyStyleRuleText"); -InjectedScriptAccess._installHandler("applyStyleText"); InjectedScriptAccess._installHandler("clearConsoleMessages"); InjectedScriptAccess._installHandler("evaluate"); InjectedScriptAccess._installHandler("evaluateInCallFrame"); InjectedScriptAccess._installHandler("getCompletions"); -InjectedScriptAccess._installHandler("getComputedStyle"); -InjectedScriptAccess._installHandler("getInlineStyle"); InjectedScriptAccess._installHandler("getNodePropertyValue"); InjectedScriptAccess._installHandler("getProperties"); InjectedScriptAccess._installHandler("getPrototypes"); -InjectedScriptAccess._installHandler("getStyles"); InjectedScriptAccess._installHandler("openInInspectedWindow"); -InjectedScriptAccess._installHandler("performSearch"); InjectedScriptAccess._installHandler("pushNodeToFrontend"); InjectedScriptAccess._installHandler("nodeByPath"); -InjectedScriptAccess._installHandler("searchCanceled"); InjectedScriptAccess._installHandler("setOuterHTML"); InjectedScriptAccess._installHandler("setPropertyValue"); -InjectedScriptAccess._installHandler("setStyleProperty"); -InjectedScriptAccess._installHandler("setStyleText"); -InjectedScriptAccess._installHandler("toggleStyleEnabled"); InjectedScriptAccess._installHandler("evaluateOnSelf"); // Some methods can't run synchronously even on the injected script side (such as DB transactions). diff --git a/WebCore/inspector/front-end/InspectorBackendStub.js b/WebCore/inspector/front-end/InspectorBackendStub.js index 4670af1..492bf87 100644 --- a/WebCore/inspector/front-end/InspectorBackendStub.js +++ b/WebCore/inspector/front-end/InspectorBackendStub.js @@ -185,6 +185,16 @@ WebInspector.InspectorBackendStub.prototype = { WebInspector.updatePauseOnExceptionsState(value); }, + editScriptSource: function() + { + WebInspector.didEditScriptSource(callId, false); + }, + + getScriptSource: function(callId, sourceID) + { + WebInspector.didGetScriptSource(callId, null); + }, + resumeDebugger: function() { }, @@ -237,7 +247,11 @@ WebInspector.InspectorBackendStub.prototype = { { }, - saveFrontendSettings: function() + saveApplicationSettings: function() + { + }, + + saveSessionSettings: function() { }, @@ -259,6 +273,14 @@ WebInspector.InspectorBackendStub.prototype = { removeAllScriptsToEvaluateOnLoad: function() { + }, + + performSearch: function() + { + }, + + searchCanceled: function() + { } } diff --git a/WebCore/inspector/front-end/KeyboardShortcut.js b/WebCore/inspector/front-end/KeyboardShortcut.js index 4e7c88a..2bcf3d1 100644 --- a/WebCore/inspector/front-end/KeyboardShortcut.js +++ b/WebCore/inspector/front-end/KeyboardShortcut.js @@ -50,14 +50,14 @@ WebInspector.KeyboardShortcut.Modifiers = { WebInspector.KeyboardShortcut.Keys = { Backspace: { code: 8, name: "\u21a4" }, - Tab: { code: 9, name: "<Tab>" }, - Enter: { code: 13, name: "<Enter>" }, - Esc: { code: 27, name: "<Esc>" }, + Tab: { code: 9, name: { mac: "\u21e5", other: "<Tab>" } }, + Enter: { code: 13, name: { mac: "\u21a9", other: "<Enter>" } }, + Esc: { code: 27, name: { mac: "\u238b", other: "<Esc>" } }, Space: { code: 32, name: "<Space>" }, - PageUp: { code: 33, name: "<PageUp>" }, // also NUM_NORTH_EAST - PageDown: { code: 34, name: "<PageDown>" }, // also NUM_SOUTH_EAST - End: { code: 35, name: "<End>" }, // also NUM_SOUTH_WEST - Home: { code: 36, name: "<Home>" }, // also NUM_NORTH_WEST + PageUp: { code: 33, name: { mac: "\u21de", other: "<PageUp>" } }, // also NUM_NORTH_EAST + PageDown: { code: 34, name: { mac: "\u21df", other: "<PageDown>" } }, // also NUM_SOUTH_EAST + End: { code: 35, name: { mac: "\u2197", other: "<End>" } }, // also NUM_SOUTH_WEST + Home: { code: 36, name: { mac: "\u2196", other: "<Home>" } }, // also NUM_NORTH_WEST Left: { code: 37, name: "\u2190" }, // also NUM_WEST Up: { code: 38, name: "\u2191" }, // also NUM_NORTH Right: { code: 39, name: "\u2192" }, // also NUM_EAST @@ -130,7 +130,16 @@ WebInspector.KeyboardShortcut.makeDescriptor = function(key, optModifiers) WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers) { - return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + (typeof key === "string" ? key.toUpperCase() : key.name); + return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key); +} + +WebInspector.KeyboardShortcut._keyName = function(key) +{ + if (typeof key === "string") + return key.toUpperCase(); + if (typeof key.name === "string") + return key.name; + return key.name[WebInspector.platform] || key.name.other; } WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers) @@ -143,11 +152,12 @@ WebInspector.KeyboardShortcut._modifiersToString = function(modifiers) const cmdKey = "\u2318"; const optKey = "\u2325"; const shiftKey = "\u21e7"; + const ctrlKey = "\u2303"; var isMac = WebInspector.isMac(); var res = ""; if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Ctrl) - res += "<Ctrl> + "; + res += isMac ? ctrlKey : "<Ctrl> + "; if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Alt) res += isMac ? optKey : "<Alt> + "; if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Shift) diff --git a/WebCore/inspector/front-end/Resource.js b/WebCore/inspector/front-end/Resource.js index 279e3af..2ae23a0 100644 --- a/WebCore/inspector/front-end/Resource.js +++ b/WebCore/inspector/front-end/Resource.js @@ -33,7 +33,6 @@ WebInspector.Resource = function(identifier, url) this._startTime = -1; this._endTime = -1; this._requestMethod = ""; - this._requestFormData = ""; this._category = WebInspector.resourceCategories.other; } @@ -88,7 +87,7 @@ WebInspector.Resource.prototype = { var oldURL = this._url; this._url = x; - + delete this._parsedQueryParameters; // FIXME: We should make the WebInspector object listen for the "url changed" event. // Then resourceURLChanged can be removed. WebInspector.resourceURLChanged(this, oldURL); @@ -215,6 +214,13 @@ WebInspector.Resource.prototype = { return this._responseReceivedTime - this._startTime; }, + get receiveDuration() + { + if (this._endTime === -1 || this._responseReceivedTime === -1) + return -1; + return this._endTime - this._responseReceivedTime; + }, + get resourceSize() { return this._resourceSize || 0; @@ -384,6 +390,22 @@ WebInspector.Resource.prototype = { return this._sortedRequestHeaders; }, + requestHeaderValue: function(headerName) + { + return this._headerValue(this.requestHeaders, headerName); + }, + + get requestFormData() + { + return this._requestFormData; + }, + + set requestFormData(x) + { + this._requestFormData = x; + delete this._parsedFormParameters; + }, + get responseHeaders() { if (this._responseHeaders === undefined) @@ -415,6 +437,61 @@ WebInspector.Resource.prototype = { return this._sortedResponseHeaders; }, + responseHeaderValue: function(headerName) + { + return this._headerValue(this.responseHeaders, headerName); + }, + + get queryParameters() + { + if (this._parsedQueryParameters) + return this._parsedQueryParameters; + var queryString = this.url.split("?", 2)[1]; + if (!queryString) + return; + this._parsedQueryParameters = this._parseParameters(queryString); + return this._parsedQueryParameters; + }, + + get formParameters() + { + if (this._parsedFormParameters) + return this._parsedFormParameters; + if (!this.requestFormData) + return; + var requestContentType = this.requestHeaderValue("Content-Type"); + if (!requestContentType || !requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) + return; + this._parsedFormParameters = this._parseParameters(this.requestFormData); + return this._parsedFormParameters; + }, + + _parseParameters: function(queryString) + { + function parseNameValue(pair) + { + var parameter = {}; + var splitPair = pair.split("=", 2); + + parameter.name = splitPair[0]; + if (splitPair.length === 1) + parameter.value = ""; + else + parameter.value = splitPair[1]; + return parameter; + } + return queryString.split("&").map(parseNameValue); + }, + + _headerValue: function(headers, headerName) + { + headerName = headerName.toLowerCase(); + for (var header in headers) { + if (header.toLowerCase() === headerName) + return headers[header]; + } + }, + get scripts() { if (!("_scripts" in this)) diff --git a/WebCore/inspector/front-end/ResourceView.js b/WebCore/inspector/front-end/ResourceView.js index ffcb9b9..bfd1576 100644 --- a/WebCore/inspector/front-end/ResourceView.js +++ b/WebCore/inspector/front-end/ResourceView.js @@ -143,7 +143,7 @@ WebInspector.ResourceView.prototype = { _selectTab: function() { if (this._headersVisible) { - if (!this.hasContentTab() || WebInspector.settings.resourceViewTab === "headers") + if (!this.hasContentTab() || WebInspector.applicationSettings.resourceViewTab === "headers") this._selectHeadersTab(); else this.selectContentTab(); @@ -154,14 +154,14 @@ WebInspector.ResourceView.prototype = { _selectHeadersTab: function(updatePrefs) { if (updatePrefs) - WebInspector.settings.resourceViewTab = "headers"; + WebInspector.applicationSettings.resourceViewTab = "headers"; this.tabbedPane.selectTabById("headers"); }, selectContentTab: function(updatePrefs) { if (updatePrefs) - WebInspector.settings.resourceViewTab = "content"; + WebInspector.applicationSettings.resourceViewTab = "content"; this._innerSelectContentTab(); }, @@ -187,17 +187,10 @@ WebInspector.ResourceView.prototype = { _refreshQueryString: function() { - var url = this.resource.url; - var hasQueryString = url.indexOf("?") >= 0; - - if (!hasQueryString) { - this.queryStringTreeElement.hidden = true; - return; - } - - this.queryStringTreeElement.hidden = false; - var parmString = url.split("?", 2)[1]; - this._refreshParms(WebInspector.UIString("Query String Parameters"), parmString, this.queryStringTreeElement); + var queryParameters = this.resource.queryParameters; + this.queryStringTreeElement.hidden = !queryParameters; + if (queryParameters) + this._refreshParms(WebInspector.UIString("Query String Parameters"), queryParameters, this.queryStringTreeElement); }, _refreshFormData: function() @@ -205,21 +198,17 @@ WebInspector.ResourceView.prototype = { this.formDataTreeElement.hidden = true; this.requestPayloadTreeElement.hidden = true; - var isFormData = this.resource.requestFormData; - if (!isFormData) + var formData = this.resource.requestFormData; + if (!formData) return; - var isFormEncoded = false; - var requestContentType = this._getHeaderValue(this.resource.requestHeaders, "Content-Type"); - if (requestContentType && requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) - isFormEncoded = true; - - if (isFormEncoded) { + var formParameters = this.resource.formParameters; + if (formParameters) { this.formDataTreeElement.hidden = false; - this._refreshParms(WebInspector.UIString("Form Data"), this.resource.requestFormData, this.formDataTreeElement); + this._refreshParms(WebInspector.UIString("Form Data"), formParameters, this.formDataTreeElement); } else { this.requestPayloadTreeElement.hidden = false; - this._refreshRequestPayload(this.resource.requestFormData); + this._refreshRequestPayload(formData); } }, @@ -233,24 +222,15 @@ WebInspector.ResourceView.prototype = { this.requestPayloadTreeElement.appendChild(parmTreeElement); }, - _refreshParms: function(title, parmString, parmsTreeElement) + _refreshParms: function(title, parms, parmsTreeElement) { - var parms = parmString.split("&"); - for (var i = 0; i < parms.length; ++i) { - var parm = parms[i]; - parm = parm.split("=", 2); - if (parm.length == 1) - parm.push(""); - parms[i] = parm; - } - parmsTreeElement.removeChildren(); parmsTreeElement.title = title + "<span class=\"header-count\">" + WebInspector.UIString(" (%d)", parms.length) + "</span>"; for (var i = 0; i < parms.length; ++i) { - var key = parms[i][0]; - var value = parms[i][1]; + var name = parms[i].name; + var value = parms[i].value; var errorDecoding = false; if (this._decodeRequestParameters) { @@ -269,7 +249,7 @@ WebInspector.ResourceView.prototype = { if (errorDecoding) valueEscaped += " <span class=\"error-message\">" + WebInspector.UIString("(unable to decode value)").escapeHTML() + "</span>"; - var title = "<div class=\"header-name\">" + key.escapeHTML() + ":</div>"; + var title = "<div class=\"header-name\">" + name.escapeHTML() + ":</div>"; title += "<div class=\"header-value source-code\">" + valueEscaped + "</div>"; var parmTreeElement = new TreeElement(title, null, false); diff --git a/WebCore/inspector/front-end/ResourcesPanel.js b/WebCore/inspector/front-end/ResourcesPanel.js index b2dfa15..ef6aa26 100644 --- a/WebCore/inspector/front-end/ResourcesPanel.js +++ b/WebCore/inspector/front-end/ResourcesPanel.js @@ -151,7 +151,7 @@ WebInspector.ResourcesPanel.prototype = { { this.largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "resources-larger-resources-status-bar-item"); - WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this); + WebInspector.applicationSettings.addEventListener("loaded", this._settingsLoaded, this); this.largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false); this.sortingSelectElement = document.createElement("select"); this.sortingSelectElement.className = "status-bar-item"; @@ -160,9 +160,9 @@ WebInspector.ResourcesPanel.prototype = { _settingsLoaded: function() { - this.largerResourcesButton.toggled = WebInspector.settings.resourcesLargeRows; - if (!WebInspector.settings.resourcesLargeRows) - this._setLargerResources(WebInspector.settings.resourcesLargeRows); + this.largerResourcesButton.toggled = WebInspector.applicationSettings.resourcesLargeRows; + if (!WebInspector.applicationSettings.resourcesLargeRows) + this._setLargerResources(WebInspector.applicationSettings.resourcesLargeRows); }, get mainResourceLoadTime() @@ -637,7 +637,7 @@ WebInspector.ResourcesPanel.prototype = { if (!this.itemsTreeElement._childrenListNode) return; - WebInspector.settings.resourcesLargeRows = !WebInspector.settings.resourcesLargeRows; + WebInspector.applicationSettings.resourcesLargeRows = !WebInspector.applicationSettings.resourcesLargeRows; this._setLargerResources(this.itemsTreeElement.smallChildren); }, diff --git a/WebCore/inspector/front-end/Script.js b/WebCore/inspector/front-end/Script.js index 79004f3..42d6850 100644 --- a/WebCore/inspector/front-end/Script.js +++ b/WebCore/inspector/front-end/Script.js @@ -23,7 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.Script = function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage) +WebInspector.Script = function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage, worldType) { this.sourceID = sourceID; this.sourceURL = sourceURL; @@ -31,6 +31,7 @@ WebInspector.Script = function(sourceID, sourceURL, source, startingLine, errorL this.startingLine = startingLine; this.errorLine = errorLine; this.errorMessage = errorMessage; + this.worldType = worldType; // if no URL, look for "//@ sourceURL=" decorator // note that this sourceURL comment decorator is behavior that FireBug added @@ -46,6 +47,16 @@ WebInspector.Script = function(sourceID, sourceURL, source, startingLine, errorL } } +WebInspector.Script.WorldType = { + MAIN_WORLD: 0, + EXTENSIONS_WORLD: 1 +} + +WebInspector.Script.WorldType = { + MAIN_WORLD: 0, + EXTENSIONS_WORLD: 1 +} + WebInspector.Script.prototype = { get linesCount() { diff --git a/WebCore/inspector/front-end/ScriptView.js b/WebCore/inspector/front-end/ScriptView.js index 1a24211..fb0bb4b 100644 --- a/WebCore/inspector/front-end/ScriptView.js +++ b/WebCore/inspector/front-end/ScriptView.js @@ -34,7 +34,7 @@ WebInspector.ScriptView = function(script) this._frameNeedsSetup = true; this._sourceFrameSetup = false; 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); + this.sourceFrame = new WebInspector.SourceFrame(this.element, this._addBreakpoint.bind(this), this._removeBreakpoint.bind(this), canEditScripts ? this._editLine.bind(this) : null, this._continueToLine.bind(this)); } WebInspector.ScriptView.prototype = { @@ -50,12 +50,28 @@ WebInspector.ScriptView.prototype = { { if (!this._frameNeedsSetup) return; + delete this._frameNeedsSetup; this.attach(); + if (this.script.source) + this._sourceFrameSetupFinished(); + else { + var callbackId = WebInspector.Callback.wrap(this._didGetScriptSource.bind(this)) + InspectorBackend.getScriptSource(callbackId, this.script.sourceID); + } + }, + + _didGetScriptSource: function(source) + { + this.script.source = source || WebInspector.UIString("<source is not available>"); + this._sourceFrameSetupFinished(); + }, + + _sourceFrameSetupFinished: function() + { this.sourceFrame.setContent("text/javascript", this._prependWhitespace(this.script.source)); this._sourceFrameSetup = true; - delete this._frameNeedsSetup; }, _prependWhitespace: function(content) { @@ -71,6 +87,13 @@ WebInspector.ScriptView.prototype = { document.getElementById("script-resource-views").appendChild(this.element); }, + _continueToLine: function(line) + { + var scriptsPanel = WebInspector.panels.scripts; + if (scriptsPanel) + scriptsPanel.continueToLine(this.script.sourceID, line); + }, + _addBreakpoint: function(line) { WebInspector.breakpointManager.addBreakpoint(this.script.sourceID, this.script.sourceURL, line, true, ""); @@ -99,10 +122,11 @@ WebInspector.ScriptView.prototype = { showingFirstSearchResult: WebInspector.SourceView.prototype.showingFirstSearchResult, showingLastSearchResult: WebInspector.SourceView.prototype.showingLastSearchResult, _jumpToSearchResult: WebInspector.SourceView.prototype._jumpToSearchResult, - _sourceFrameSetupFinished: WebInspector.SourceView.prototype._sourceFrameSetupFinished, _removeBreakpoint: WebInspector.SourceView.prototype._removeBreakpoint, _editLine: WebInspector.SourceView.prototype._editLine, resize: WebInspector.SourceView.prototype.resize } WebInspector.ScriptView.prototype.__proto__ = WebInspector.View.prototype; + +WebInspector.didGetScriptSource = WebInspector.Callback.processCallback; diff --git a/WebCore/inspector/front-end/ScriptsPanel.js b/WebCore/inspector/front-end/ScriptsPanel.js index 755daff..4504d57 100644 --- a/WebCore/inspector/front-end/ScriptsPanel.js +++ b/WebCore/inspector/front-end/ScriptsPanel.js @@ -241,9 +241,9 @@ WebInspector.ScriptsPanel.prototype = { return this.toggleBreakpointsButton.toggled; }, - addScript: function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage) + addScript: function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage, scriptWorldType) { - var script = new WebInspector.Script(sourceID, sourceURL, source, startingLine, errorLine, errorMessage); + var script = new WebInspector.Script(sourceID, sourceURL, source, startingLine, errorLine, errorMessage, scriptWorldType); this._sourceIDMap[sourceID] = script; var resource = WebInspector.resourceURLMap[sourceURL]; @@ -264,6 +264,13 @@ WebInspector.ScriptsPanel.prototype = { this._addScriptToFilesMenu(script); }, + continueToLine: function(sourceID, line) + { + WebInspector.breakpointManager.setOneTimeBreakpoint(sourceID, line); + if (this.paused) + this._togglePause(); + }, + _resourceLoadingFinished: function(e) { var resource = e.target; @@ -334,10 +341,10 @@ WebInspector.ScriptsPanel.prototype = { canEditScripts: function() { - return !!InspectorBackend.editScriptSource; + return Preferences.canEditScriptSource; }, - editScriptSource: function(sourceID, newContent, line, linesCountToShift, callback) + editScriptSource: function(sourceID, newContent, line, linesCountToShift, commitEditingCallback, cancelEditingCallback) { if (!this.canEditScripts()) return; @@ -347,12 +354,19 @@ WebInspector.ScriptsPanel.prototype = { for (var i = 0; i < breakpoints.length; ++i) WebInspector.breakpointManager.removeBreakpoint(breakpoints[i]); - function mycallback(newBody) + function mycallback(success, newBodyOrErrorMessage, callFrames) { - callback(newBody); + if (success) { + commitEditingCallback(newBodyOrErrorMessage); + if (callFrames && callFrames.length) + this.debuggerPaused(callFrames); + } else { + cancelEditingCallback(); + WebInspector.log(newBodyOrErrorMessage, WebInspector.ConsoleMessage.MessageLevel.Warning); + } for (var i = 0; i < breakpoints.length; ++i) { var breakpoint = breakpoints[i]; - if (breakpoint.line >= line) + if (success && breakpoint.line >= line) breakpoint.line += linesCountToShift; WebInspector.breakpointManager.addBreakpoint(breakpoint); } @@ -400,6 +414,7 @@ WebInspector.ScriptsPanel.prototype = { debuggerPaused: function(callFrames) { + WebInspector.breakpointManager.removeOneTimeBreakpoint(); this._paused = true; this._waitingToPause = false; this._stepping = false; @@ -624,7 +639,7 @@ WebInspector.ScriptsPanel.prototype = { var url = scriptOrResource.url || scriptOrResource.sourceURL; if (url && !options.initialLoad) - WebInspector.settings.lastViewedScriptFile = url; + WebInspector.applicationSettings.lastViewedScriptFile = url; if (!options.fromBackForwardAction) { var oldIndex = this._currentBackForwardIndex; @@ -725,10 +740,13 @@ WebInspector.ScriptsPanel.prototype = { else { // if not first item, check to see if this was the last viewed var url = option.representedObject.url || option.representedObject.sourceURL; - var lastURL = WebInspector.settings.lastViewedScriptFile; + var lastURL = WebInspector.applicationSettings.lastViewedScriptFile; if (url && url === lastURL) this._showScriptOrResource(option.representedObject, {initialLoad: true}); } + + if (script.worldType === WebInspector.Script.WorldType.EXTENSIONS_WORLD) + script.filesSelectOption.addStyleClass("extension-script"); }, _clearCurrentExecutionLine: function() diff --git a/WebCore/inspector/front-end/Settings.js b/WebCore/inspector/front-end/Settings.js index b9b5f75..c7fc5a9 100644 --- a/WebCore/inspector/front-end/Settings.js +++ b/WebCore/inspector/front-end/Settings.js @@ -30,6 +30,7 @@ var Preferences = { + canEditScriptSource: false, maxInlineTextChildLength: 80, minConsoleHeight: 75, minSidebarWidth: 100, @@ -44,16 +45,39 @@ var Preferences = { auditsPanelEnabled: true } -WebInspector.populateFrontendSettings = function(settingsString) +WebInspector.populateApplicationSettings = function(settingsString) { - WebInspector.settings._load(settingsString); + WebInspector.applicationSettings._load(settingsString); + WebInspector.applicationSettings._installSetting("eventListenersFilter", "event-listeners-filter", "all"); + WebInspector.applicationSettings._installSetting("colorFormat", "color-format", "hex"); + WebInspector.applicationSettings._installSetting("resourcesLargeRows", "resources-large-rows", true); + WebInspector.applicationSettings._installSetting("watchExpressions", "watch-expressions", []); + WebInspector.applicationSettings._installSetting("lastViewedScriptFile", "last-viewed-script-file"); + WebInspector.applicationSettings._installSetting("showInheritedComputedStyleProperties", "show-inherited-computed-style-properties", false); + WebInspector.applicationSettings._installSetting("showUserAgentStyles", "show-user-agent-styles", true); + WebInspector.applicationSettings._installSetting("resourceViewTab", "resource-view-tab", "content"); + WebInspector.applicationSettings._installSetting("consoleHistory", "console-history", []); + WebInspector.applicationSettings.dispatchEventToListeners("loaded"); } -WebInspector.Settings = function() +WebInspector.populateSessionSettings = function(settingsString) { + WebInspector.sessionSettings._load(settingsString); + WebInspector.sessionSettings.dispatchEventToListeners("loaded"); +} + +WebInspector.Settings = function(sessionScope) +{ + this._sessionScope = sessionScope; } WebInspector.Settings.prototype = { + reset: function() + { + this._store = {}; + this.dispatchEventToListeners("loaded"); + }, + _load: function(settingsString) { try { @@ -62,17 +86,6 @@ WebInspector.Settings.prototype = { // May fail; this._store = {}; } - - this._installSetting("eventListenersFilter", "event-listeners-filter", "all"); - this._installSetting("colorFormat", "color-format", "hex"); - this._installSetting("resourcesLargeRows", "resources-large-rows", true); - this._installSetting("watchExpressions", "watch-expressions", []); - this._installSetting("lastViewedScriptFile", "last-viewed-script-file"); - 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"); }, _installSetting: function(name, propertyName, defaultValue) @@ -93,7 +106,11 @@ WebInspector.Settings.prototype = { { this._store[propertyName] = newValue; try { - InspectorBackend.saveFrontendSettings(JSON.stringify(this._store)); + var store = JSON.stringify(this._store); + if (this._sessionScope) + InspectorBackend.saveSessionSettings(store); + else + InspectorBackend.saveApplicationSettings(store); } catch (e) { // May fail; } diff --git a/WebCore/inspector/front-end/SourceFrame.js b/WebCore/inspector/front-end/SourceFrame.js index 5e529ae..f221086 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, editDelegate) +WebInspector.SourceFrame = function(parentElement, addBreakpointDelegate, removeBreakpointDelegate, editDelegate, continueToHereDelegate) { this._parentElement = parentElement; @@ -42,6 +42,7 @@ WebInspector.SourceFrame = function(parentElement, addBreakpointDelegate, remove this._loaded = false; + this._continueToHereDelegate = continueToHereDelegate; this._addBreakpointDelegate = addBreakpointDelegate; this._removeBreakpointDelegate = removeBreakpointDelegate; this._editDelegate = editDelegate; @@ -409,9 +410,14 @@ WebInspector.SourceFrame.prototype = { return; var row = target.parentElement; + if (!WebInspector.panels.scripts) + return; + var lineNumber = row.lineNumber; var contextMenu = new WebInspector.ContextMenu(); + contextMenu.appendItem(WebInspector.UIString("Continue to Here"), this._continueToHereDelegate.bind(this, lineNumber + 1)); + var breakpoint = this._textModel.getAttribute(lineNumber, "breakpoint"); if (!breakpoint) { // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint. diff --git a/WebCore/inspector/front-end/SourceView.js b/WebCore/inspector/front-end/SourceView.js index f01c241..e4d7fed 100644 --- a/WebCore/inspector/front-end/SourceView.js +++ b/WebCore/inspector/front-end/SourceView.js @@ -32,8 +32,8 @@ WebInspector.SourceView = function(resource) this.element.addStyleClass("source"); - var canEditScripts = WebInspector.panels.scripts.canEditScripts() && resource.type === WebInspector.Resource.Type.Script; - this.sourceFrame = new WebInspector.SourceFrame(this.contentElement, this._addBreakpoint.bind(this), this._removeBreakpoint.bind(this), canEditScripts ? this._editLine.bind(this) : null); + var canEditScripts = WebInspector.panels.scripts && WebInspector.panels.scripts.canEditScripts() && resource.type === WebInspector.Resource.Type.Script; + this.sourceFrame = new WebInspector.SourceFrame(this.contentElement, this._addBreakpoint.bind(this), this._removeBreakpoint.bind(this), canEditScripts ? this._editLine.bind(this) : null, this._continueToLine.bind(this)); resource.addEventListener("finished", this._resourceLoadingFinished, this); this._frameNeedsSetup = true; } @@ -115,6 +115,15 @@ WebInspector.SourceView.prototype = { this.resource.removeEventListener("finished", this._resourceLoadingFinished, this); }, + _continueToLine: function(line) + { + var scriptsPanel = WebInspector.panels.scripts; + if (scriptsPanel) { + var sourceID = this._sourceIDForLine(line); + scriptsPanel.continueToLine(sourceID, line); + } + }, + _addBreakpoint: function(line) { var sourceID = this._sourceIDForLine(line); @@ -126,7 +135,7 @@ WebInspector.SourceView.prototype = { WebInspector.breakpointManager.removeBreakpoint(breakpoint); }, - _editLine: function(line, newContent) + _editLine: function(line, newContent, cancelEditingCallback) { var lines = []; var textModel = this.sourceFrame.textModel; @@ -138,7 +147,7 @@ WebInspector.SourceView.prototype = { } var linesCountToShift = newContent.split("\n").length - 1; - WebInspector.panels.scripts.editScriptSource(this._sourceIDForLine(line), lines.join("\n"), line, linesCountToShift, this._editLineComplete.bind(this)); + WebInspector.panels.scripts.editScriptSource(this._sourceIDForLine(line), lines.join("\n"), line, linesCountToShift, this._editLineComplete.bind(this), cancelEditingCallback); }, _editLineComplete: function(newBody) @@ -202,7 +211,7 @@ WebInspector.SourceView.prototype = { this.localContentElement = document.createElement("div"); this.localContentElement.className = "resource-view-content"; this.tabbedPane.appendTab("local", WebInspector.UIString("Local"), this.localContentElement, this.selectLocalContentTab.bind(this)); - this.localSourceFrame = new WebInspector.SourceFrame(this.localContentElement, this._addBreakpoint.bind(this), this._removeBreakpoint.bind(this)); + this.localSourceFrame = new WebInspector.SourceFrame(this.localContentElement, this._addBreakpoint.bind(this), this._removeBreakpoint.bind(this), null, this._continueToLine.bind(this)); } this.localSourceFrame.setContent(mimeType, content, ""); }, diff --git a/WebCore/inspector/front-end/StylesSidebarPane.js b/WebCore/inspector/front-end/StylesSidebarPane.js index 35d25d4..18b7f0f 100644 --- a/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/WebCore/inspector/front-end/StylesSidebarPane.js @@ -60,7 +60,7 @@ WebInspector.StylesSidebarPane = function(computedStylePane) this.settingsSelectElement.addEventListener("click", function(event) { event.stopPropagation() }, false); this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false); - WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this); + WebInspector.applicationSettings.addEventListener("loaded", this._settingsLoaded, this); this.titleElement.appendChild(this.settingsSelectElement); this._computedStylePane = computedStylePane; @@ -96,7 +96,7 @@ WebInspector.StylesSidebarPane.PseudoIdNames = [ WebInspector.StylesSidebarPane.prototype = { _settingsLoaded: function() { - var format = WebInspector.settings.colorFormat; + var format = WebInspector.applicationSettings.colorFormat; if (format === "hex") this.settingsSelectElement[0].selected = true; if (format === "rgb") @@ -133,22 +133,22 @@ WebInspector.StylesSidebarPane.prototype = { return; } - function getStylesCallback(styles) + function stylesCallback(styles) { if (styles) this._rebuildUpdate(node, styles); } - function getComputedStyleCallback(computedStyle) + function computedStyleCallback(computedStyle) { if (computedStyle) this._refreshUpdate(node, computedStyle, editedSection); }; if (refresh) - InspectorBackend.getComputedStyle(WebInspector.Callback.wrap(getComputedStyleCallback.bind(this)), node.id); + WebInspector.cssModel.getComputedStyleAsync(node.id, computedStyleCallback.bind(this)); else - InspectorBackend.getStyles(WebInspector.Callback.wrap(getStylesCallback.bind(this)), node.id, !WebInspector.settings.showUserAgentStyles); + WebInspector.cssModel.getStylesAsync(node.id, !WebInspector.applicationSettings.showUserAgentStyles, stylesCallback.bind(this)); }, _refreshUpdate: function(node, computedStyle, editedSection) @@ -466,7 +466,7 @@ WebInspector.StylesSidebarPane.prototype = { { for (var i = 0; i < properties.length; ++i) { var property = properties[i]; - // Does this style contain non-overriden inherited property? + // Does this style contain non-overridden inherited property? if (property.name in WebInspector.StylesSidebarPane.InheritedProperties) return true; } @@ -482,7 +482,7 @@ WebInspector.StylesSidebarPane.prototype = { // Select the correct color format setting again, since it needs to be selected. var selectedIndex = 0; for (var i = 0; i < options.length; ++i) { - if (options[i].value === WebInspector.settings.colorFormat) { + if (options[i].value === WebInspector.applicationSettings.colorFormat) { selectedIndex = i; break; } @@ -494,7 +494,7 @@ WebInspector.StylesSidebarPane.prototype = { _changeColorFormat: function(event) { var selectedOption = this.settingsSelectElement[this.settingsSelectElement.selectedIndex]; - WebInspector.settings.colorFormat = selectedOption.value; + WebInspector.applicationSettings.colorFormat = selectedOption.value; for (var pseudoId in this.sections) { var sections = this.sections[pseudoId]; @@ -581,18 +581,18 @@ WebInspector.ComputedStyleSidebarPane = function() function settingsLoaded() { - if (WebInspector.settings.showInheritedComputedStyleProperties) { + if (WebInspector.applicationSettings.showInheritedComputedStyleProperties) { this.bodyElement.addStyleClass("show-inherited"); showInheritedCheckbox.checked = true; } } - WebInspector.settings.addEventListener("loaded", settingsLoaded.bind(this)); + WebInspector.applicationSettings.addEventListener("loaded", settingsLoaded.bind(this)); function showInheritedToggleFunction(event) { - WebInspector.settings.showInheritedComputedStyleProperties = showInheritedCheckbox.checked; - if (WebInspector.settings.showInheritedComputedStyleProperties) + WebInspector.applicationSettings.showInheritedComputedStyleProperties = showInheritedCheckbox.checked; + if (WebInspector.applicationSettings.showInheritedComputedStyleProperties) this.bodyElement.addStyleClass("show-inherited"); else this.bodyElement.removeStyleClass("show-inherited"); @@ -600,7 +600,7 @@ WebInspector.ComputedStyleSidebarPane = function() showInheritedCheckbox.addEventListener(showInheritedToggleFunction.bind(this)); } - + WebInspector.ComputedStyleSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyle, usedProperties, editable, isInherited, isFirstSection) @@ -882,14 +882,9 @@ WebInspector.StylePropertiesSection.prototype = { return moveToNextIfNeeded.call(this); var self = this; - function callback(newRulePayload, doesAffectSelectedNode) - { - if (!newRulePayload) { - // Invalid Syntax for a Selector - moveToNextIfNeeded.call(self); - return; - } + function successCallback(newRule, doesAffectSelectedNode) + { if (!doesAffectSelectedNode) { self.noAffect = true; self.element.addStyleClass("no-affect"); @@ -898,7 +893,6 @@ WebInspector.StylePropertiesSection.prototype = { self.element.removeStyleClass("no-affect"); } - var newRule = WebInspector.CSSStyleDeclaration.parseRule(newRulePayload); self.rule = newRule; self.styleRule = { section: self, style: newRule.style, selectorText: newRule.selectorText, parentStyleSheet: newRule.parentStyleSheet, rule: newRule }; @@ -912,7 +906,7 @@ WebInspector.StylePropertiesSection.prototype = { moveToNextIfNeeded.call(self); } - InspectorBackend.setRuleSelector(WebInspector.Callback.wrap(callback), this.rule.id, newContent, this.pane.node.id); + WebInspector.cssModel.setRuleSelector(this.rule.id, newContent, this.pane.node.id, successCallback, moveToNextIfNeeded.bind(this)); }, editingSelectorCancelled: function() @@ -939,17 +933,8 @@ WebInspector.BlankStylePropertiesSection.prototype = { editingSelectorCommitted: function(element, newContent, oldContent, context) { var self = this; - function callback(rule, doesSelectorAffectSelectedNode) + function successCallback(styleRule, doesSelectorAffectSelectedNode) { - if (!rule) { - // Invalid Syntax for a Selector - self.editingSelectorCancelled(); - return; - } - - var styleRule = WebInspector.CSSStyleDeclaration.parseRule(rule); - styleRule.rule = rule; - self.makeNormal(styleRule); if (!doesSelectorAffectSelectedNode) { @@ -963,7 +948,7 @@ WebInspector.BlankStylePropertiesSection.prototype = { self.addNewBlankProperty().startEditing(); } - InspectorBackend.addRule(WebInspector.Callback.wrap(callback), newContent, this.pane.node.id); + WebInspector.cssModel.addRule(this.pane.node.id, newContent, successCallback, this.editingSelectorCancelled.bind(this)); }, editingSelectorCancelled: function() @@ -1137,9 +1122,9 @@ WebInspector.StylePropertyTreeElement.prototype = { var format; if (Preferences.showColorNicknames && color.nickname) format = "nickname"; - else if (WebInspector.settings.colorFormat === "rgb") + else if (WebInspector.applicationSettings.colorFormat === "rgb") format = (color.simple ? "rgb" : "rgba"); - else if (WebInspector.settings.colorFormat === "hsl") + else if (WebInspector.applicationSettings.colorFormat === "hsl") format = (color.simple ? "hsl" : "hsla"); else if (color.simple) format = (color.hasShortHex() ? "shorthex" : "hex"); @@ -1220,6 +1205,9 @@ WebInspector.StylePropertyTreeElement.prototype = { this.listItemElement.removeChildren(); + if (!this.treeOutline) + return; + // Append the checkbox for root elements of an editable section. if (this.treeOutline.section && this.treeOutline.section.editable && this.parent.root) this.listItemElement.appendChild(enabledCheckboxElement); @@ -1254,12 +1242,12 @@ WebInspector.StylePropertyTreeElement.prototype = { var disabled = !event.target.checked; var self = this; - function callback(newPayload) + function callback(newStyle) { - if (!newPayload) + if (!newStyle) return; - self.style = WebInspector.CSSStyleDeclaration.parseStyle(newPayload); + self.style = newStyle; self._styleRule.style = self.style; // Set the disabled property here, since the code above replies on it not changing @@ -1272,7 +1260,7 @@ WebInspector.StylePropertyTreeElement.prototype = { self.updateAll(true); } - InspectorBackend.toggleStyleEnabled(WebInspector.Callback.wrap(callback), this.style.id, this.name, disabled); + WebInspector.cssModel.toggleStyleEnabled(this.style.id, this.name, disabled, callback); }, updateState: function() @@ -1434,7 +1422,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. - InspectorBackend.setStyleText(WebInspector.Callback.wrap(null), this.style.id, this.originalCSSText); + WebInspector.cssModel.setCSSText(this.style.id, this.originalCSSText); } this.applyStyleText(this.listItemElement.textContent); @@ -1454,7 +1442,7 @@ WebInspector.StylePropertyTreeElement.prototype = { if (this._newProperty) this.treeOutline.removeChild(this); else if (this.originalCSSText) { - InspectorBackend.setStyleText(WebInspector.Callback.wrap(null), this.style.id, this.originalCSSText); + WebInspector.cssModel.setCSSText(this.style.id, this.originalCSSText); if (this.treeOutline.section && this.treeOutline.section.pane) this.treeOutline.section.pane.dispatchEventToListeners("style edited"); @@ -1541,28 +1529,29 @@ WebInspector.StylePropertyTreeElement.prototype = { } var self = this; - function callback(success, newPayload, changedProperties) + + function failureCallback() { - 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. - if (self._newProperty) { - self.parent.removeChild(self); - return; - } - if (updateInterface) - self.updateTitle(); + // 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. + if (self._newProperty) { + self.parent.removeChild(self); return; } + if (updateInterface) + self.updateTitle(); + } + function successCallback(newStyle, changedProperties) + { elementsPanel.removeStyleChange(section.identifier, self.style, self.name); if (!styleTextLength) { // Do remove ourselves from UI when the property removal is confirmed. self.parent.removeChild(self); } else { - self.style = WebInspector.CSSStyleDeclaration.parseStyle(newPayload); + self.style = newStyle; for (var i = 0; i < changedProperties.length; ++i) elementsPanel.addStyleChange(section.identifier, self.style, changedProperties[i]); self._styleRule.style = self.style; @@ -1574,7 +1563,8 @@ WebInspector.StylePropertyTreeElement.prototype = { if (updateInterface) self.updateAll(true); } - InspectorBackend.applyStyleText(WebInspector.Callback.wrap(callback), this.style.id, styleText, this.name); + + WebInspector.cssModel.applyStyleText(this.style.id, styleText, this.name, successCallback, failureCallback); } } diff --git a/WebCore/inspector/front-end/TextPrompt.js b/WebCore/inspector/front-end/TextPrompt.js index 2bbe075..b6bcd52 100644 --- a/WebCore/inspector/front-end/TextPrompt.js +++ b/WebCore/inspector/front-end/TextPrompt.js @@ -242,7 +242,7 @@ WebInspector.TextPrompt.prototype = { wordPrefixLength = commonPrefix.length; if (selection.isCollapsed) - var completionText = completions[1]; + var completionText = completions[0]; else { var currentText = fullWordRange.toString(); diff --git a/WebCore/inspector/front-end/TextViewer.js b/WebCore/inspector/front-end/TextViewer.js index cd06072..4709a59 100644 --- a/WebCore/inspector/front-end/TextViewer.js +++ b/WebCore/inspector/front-end/TextViewer.js @@ -260,12 +260,14 @@ WebInspector.TextViewer.prototype = { return; // Do not trigger editing from line numbers. 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); + var cancelEditingCallback = this._cancelEditingLine.bind(this, lineRow.lastChild, oldContent); + var commitEditingCallback = this._commitEditingLine.bind(this, lineRow.lineNumber, lineRow.lastChild, cancelEditingCallback); + this._editingLine = WebInspector.startEditing(lineRow.lastChild, commitEditingCallback, cancelEditingCallback, null, true); }, - _commitEditingLine: function(lineNumber, element) + _commitEditingLine: function(lineNumber, element, cancelEditingCallback) { - this._editCallback(lineNumber, element.textContent) + this._editCallback(lineNumber, element.textContent, cancelEditingCallback); delete this._editingLine; }, diff --git a/WebCore/inspector/front-end/TimelinePanel.js b/WebCore/inspector/front-end/TimelinePanel.js index b9f0814..29efd79 100644 --- a/WebCore/inspector/front-end/TimelinePanel.js +++ b/WebCore/inspector/front-end/TimelinePanel.js @@ -940,7 +940,7 @@ WebInspector.TimelinePanel.FormattedRecord.prototype = { case recordTypes.TimerRemove: contentHelper._appendTextRow(WebInspector.UIString("Timer ID"), this.data.timerId); if (typeof this.timeout === "number") { - contentHelper._appendTextRow(WebInspector.UIString("Timeout"), this.timeout); + contentHelper._appendTextRow(WebInspector.UIString("Timeout"), Number.secondsToString(this.timeout / 1000, WebInspector.UIString)); contentHelper._appendTextRow(WebInspector.UIString("Repeats"), !this.singleShot); } if (typeof this.callSiteScriptLine === "number") diff --git a/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js b/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js index 3655d68..b530ab6 100644 --- a/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js +++ b/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js @@ -31,7 +31,7 @@ WebInspector.WatchExpressionsSidebarPane = function() { WebInspector.SidebarPane.call(this, WebInspector.UIString("Watch Expressions")); - WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this); + WebInspector.applicationSettings.addEventListener("loaded", this._settingsLoaded, this); } WebInspector.WatchExpressionsSidebarPane.prototype = { @@ -39,7 +39,7 @@ WebInspector.WatchExpressionsSidebarPane.prototype = { { this.bodyElement.removeChildren(); - this.expanded = WebInspector.settings.watchExpressions.length > 0; + this.expanded = WebInspector.applicationSettings.watchExpressions.length > 0; this.section = new WebInspector.WatchExpressionsSection(); this.bodyElement.appendChild(this.section.element); @@ -77,7 +77,7 @@ WebInspector.WatchExpressionsSection = function() WebInspector.ObjectPropertiesSection.call(this); - this.watchExpressions = WebInspector.settings.watchExpressions; + this.watchExpressions = WebInspector.applicationSettings.watchExpressions; this.headerElement.className = "hidden"; this.editable = true; @@ -195,7 +195,7 @@ WebInspector.WatchExpressionsSection.prototype = { if (this.watchExpressions[i]) toSave.push(this.watchExpressions[i]); - WebInspector.settings.watchExpressions = toSave; + WebInspector.applicationSettings.watchExpressions = toSave; return toSave.length; } } diff --git a/WebCore/inspector/front-end/WebKit.qrc b/WebCore/inspector/front-end/WebKit.qrc index e93bf2c..fd84586 100644 --- a/WebCore/inspector/front-end/WebKit.qrc +++ b/WebCore/inspector/front-end/WebKit.qrc @@ -19,6 +19,7 @@ <file>ConsoleView.js</file> <file>ContextMenu.js</file> <file>CookieItemsView.js</file> + <file>CSSStyleModel.js</file> <file>Database.js</file> <file>DatabaseQueryView.js</file> <file>DatabaseTableView.js</file> @@ -32,6 +33,7 @@ <file>ElementsTreeOutline.js</file> <file>EventListenersSidebarPane.js</file> <file>FontView.js</file> + <file>HAREntry.js</file> <file>HelpScreen.js</file> <file>ImageView.js</file> <file>InjectedFakeWorker.js</file> diff --git a/WebCore/inspector/front-end/inspector.css b/WebCore/inspector/front-end/inspector.css index ef34f2c..52b85bc 100644 --- a/WebCore/inspector/front-end/inspector.css +++ b/WebCore/inspector/front-end/inspector.css @@ -2351,6 +2351,10 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { max-width: 250px; } +#scripts-files option.extension-script { + color: rgb(70, 134, 240); +} + #scripts-functions { max-width: 150px; } @@ -3976,7 +3980,6 @@ ol.breakpoint-list { .styles-selector { cursor: text; - -webkit-user-select: text; } .workers-list { @@ -4004,6 +4007,7 @@ a.worker-item { white-space: nowrap; -webkit-background-origin: padding; -webkit-background-clip: padding; + -webkit-user-select: text; } .styles-section:not(.first-styles-section) { @@ -4058,7 +4062,6 @@ a.worker-item { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; - -webkit-user-select: text; cursor: auto; } diff --git a/WebCore/inspector/front-end/inspector.html b/WebCore/inspector/front-end/inspector.html index 75b31eb..4c51634 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="CSSStyleModel.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> @@ -127,6 +128,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="TestController.js"></script> <script type="text/javascript" src="HelpScreen.js"></script> <script type="text/javascript" src="ShortcutsHelp.js"></script> + <script type="text/javascript" src="HAREntry.js"></script> </head> <body class="detached"> <div id="toolbar"> diff --git a/WebCore/inspector/front-end/inspector.js b/WebCore/inspector/front-end/inspector.js index 8c25be5..e8108af 100644 --- a/WebCore/inspector/front-end/inspector.js +++ b/WebCore/inspector/front-end/inspector.js @@ -440,7 +440,8 @@ WebInspector.loaded = function() var port = WebInspector.port; document.body.addStyleClass("port-" + port); - this.settings = new WebInspector.Settings(); + this.applicationSettings = new WebInspector.Settings(false); + this.sessionSettings = new WebInspector.Settings(true); this._registerShortcuts(); // set order of some sections explicitly @@ -466,6 +467,7 @@ WebInspector.loaded = function() }; this.breakpointManager = new WebInspector.BreakpointManager(); + this.cssModel = new WebInspector.CSSStyleModel(); this.panels = {}; this._createPanels(); @@ -571,6 +573,13 @@ WebInspector.dispatch = function() { setTimeout(delayDispatch, 0); } +WebInspector.dispatchMessageFromBackend = function(arguments) +{ + var methodName = arguments.shift(); + WebInspector[methodName].apply(this, arguments); +} + + WebInspector.windowResize = function(event) { if (this.currentPanel) @@ -696,7 +705,7 @@ WebInspector._registerShortcuts = function() shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta) ]; section.addRelatedKeys(keys, WebInspector.UIString("Next/previous panel")); - section.addKey(shortcut.Keys.Esc.name, WebInspector.UIString("Toggle console")); + section.addKey(shortcut.shortcutToString(shortcut.Keys.Esc), WebInspector.UIString("Toggle console")); section.addKey(shortcut.shortcutToString("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search")); keys = [ shortcut.shortcutToString("g", shortcut.Modifiers.CtrlOrMeta), @@ -1318,9 +1327,9 @@ WebInspector.profilerWasDisabled = function() this.panels.profiles.profilerWasDisabled(); } -WebInspector.parsedScriptSource = function(sourceID, sourceURL, source, startingLine) +WebInspector.parsedScriptSource = function(sourceID, sourceURL, source, startingLine, scriptWorldType) { - this.panels.scripts.addScript(sourceID, sourceURL, source, startingLine); + this.panels.scripts.addScript(sourceID, sourceURL, source, startingLine, undefined, undefined, scriptWorldType); } WebInspector.restoredBreakpoint = function(sourceID, sourceURL, line, enabled, condition) @@ -1360,6 +1369,7 @@ WebInspector.reset = function() panel.reset(); } + this.sessionSettings.reset(); this.breakpointManager.reset(); for (var category in this.resourceCategories) diff --git a/WebCore/inspector/front-end/utilities.js b/WebCore/inspector/front-end/utilities.js index 9ad1c10..bb19dbd 100644 --- a/WebCore/inspector/front-end/utilities.js +++ b/WebCore/inspector/front-end/utilities.js @@ -55,7 +55,14 @@ Function.prototype.bind = function(thisObject) { var func = this; var args = Array.prototype.slice.call(arguments, 1); - return function() { return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))) }; + function bound() + { + return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))); + } + bound.toString = function() { + return "bound: " + func; + }; + return bound; } Node.prototype.rangeOfWord = function(offset, stopCharacters, stayWithinNode, direction) |