diff options
Diffstat (limited to 'WebCore/inspector')
65 files changed, 3601 insertions, 1763 deletions
diff --git a/WebCore/inspector/ConsoleMessage.cpp b/WebCore/inspector/ConsoleMessage.cpp index 6cfb75e..eb8d49a 100644 --- a/WebCore/inspector/ConsoleMessage.cpp +++ b/WebCore/inspector/ConsoleMessage.cpp @@ -90,7 +90,12 @@ void ConsoleMessage::addToConsole(InspectorFrontend* frontend) jsonObj.set("url", m_url); jsonObj.set("groupLevel", static_cast<int>(m_groupLevel)); jsonObj.set("repeatCount", static_cast<int>(m_repeatCount)); - frontend->addMessageToConsole(jsonObj, m_frames, m_wrappedArguments, m_message); + frontend->addConsoleMessage(jsonObj, m_frames, m_wrappedArguments, m_message); +} + +void ConsoleMessage::updateRepeatCountInConsole(InspectorFrontend* frontend) +{ + frontend->updateConsoleMessageRepeatCount(m_repeatCount); } #endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/ConsoleMessage.h b/WebCore/inspector/ConsoleMessage.h index d03f2b7..8ed6660 100644 --- a/WebCore/inspector/ConsoleMessage.h +++ b/WebCore/inspector/ConsoleMessage.h @@ -49,6 +49,7 @@ namespace WebCore { #if ENABLE(INSPECTOR) void addToConsole(InspectorFrontend* frontend); + void updateRepeatCountInConsole(InspectorFrontend* frontend); #endif void incrementCount() { ++m_repeatCount; }; bool isEqual(ScriptState*, ConsoleMessage* msg) const; diff --git a/WebCore/inspector/DOMDispatchTimelineItem.cpp b/WebCore/inspector/DOMDispatchTimelineItem.cpp deleted file mode 100644 index acff513..0000000 --- a/WebCore/inspector/DOMDispatchTimelineItem.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* -* Copyright (C) 2009 Google Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are -* met: -* -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above -* copyright notice, this list of conditions and the following disclaimer -* in the documentation and/or other materials provided with the -* distribution. -* * Neither the name of Google Inc. nor the names of its -* contributors may be used to endorse or promote products derived from -* this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "config.h" -#include "DOMDispatchTimelineItem.h" - -#if ENABLE(INSPECTOR) - -#include "Event.h" -#include "InspectorFrontend.h" - -namespace WebCore { - -DOMDispatchTimelineItem::DOMDispatchTimelineItem(PassOwnPtr<TimelineItem> previous, double startTime, const Event& event) - : TimelineItem(previous, startTime, DOMDispatchTimelineItemType) - , m_eventType(event.type().string()) -{ -} - -ScriptObject DOMDispatchTimelineItem::convertToScriptObject(InspectorFrontend* frontend) -{ - ScriptObject selfObj = TimelineItem::convertToScriptObject(frontend); - ScriptObject dataObj = frontend->newScriptObject(); - dataObj.set("type", m_eventType); - selfObj.set("data", dataObj); - return selfObj; -} - -} // namespace WebCore - -#endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/DOMDispatchTimelineItem.h b/WebCore/inspector/DOMDispatchTimelineItem.h deleted file mode 100644 index 384ce25..0000000 --- a/WebCore/inspector/DOMDispatchTimelineItem.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -* Copyright (C) 2009 Google Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are -* met: -* -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above -* copyright notice, this list of conditions and the following disclaimer -* in the documentation and/or other materials provided with the -* distribution. -* * Neither the name of Google Inc. nor the names of its -* contributors may be used to endorse or promote products derived from -* this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef DOMDispatchTimelineItem_h -#define DOMDispatchTimelineItem_h - -#include "TimelineItem.h" -#include "PlatformString.h" - -namespace WebCore { - - class Event; - class InspectorFrontend; - - class DOMDispatchTimelineItem : public TimelineItem { - public: - DOMDispatchTimelineItem(PassOwnPtr<TimelineItem> previous, double startTime, const Event&); - - virtual ~DOMDispatchTimelineItem() { } - - protected: - virtual ScriptObject convertToScriptObject(InspectorFrontend*); - - private: - String m_eventType; - }; - -} // namespace WebCore - -#endif // !defined(DOMDispatchTimelineItem_h) - diff --git a/WebCore/inspector/InspectorBackend.cpp b/WebCore/inspector/InspectorBackend.cpp index 1273512..83a9719 100644 --- a/WebCore/inspector/InspectorBackend.cpp +++ b/WebCore/inspector/InspectorBackend.cpp @@ -258,33 +258,26 @@ const String& InspectorBackend::platform() const return platform; } -void InspectorBackend::enableTimeline(bool always) +void InspectorBackend::startTimelineProfiler() { if (m_inspectorController) - m_inspectorController->enableTimeline(always); + m_inspectorController->startTimelineProfiler(); } -void InspectorBackend::disableTimeline(bool always) +void InspectorBackend::stopTimelineProfiler() { if (m_inspectorController) - m_inspectorController->disableTimeline(always); + m_inspectorController->stopTimelineProfiler(); } -bool InspectorBackend::timelineEnabled() const +bool InspectorBackend::timelineProfilerEnabled() const { if (m_inspectorController) - return m_inspectorController->timelineEnabled(); + return m_inspectorController->timelineProfilerEnabled(); return false; } #if ENABLE(JAVASCRIPT_DEBUGGER) -const ProfilesArray& InspectorBackend::profiles() const -{ - if (m_inspectorController) - return m_inspectorController->profiles(); - return m_emptyProfiles; -} - void InspectorBackend::startProfiling() { if (m_inspectorController) @@ -316,6 +309,18 @@ bool InspectorBackend::profilerEnabled() return false; } +void InspectorBackend::getProfileHeaders(long callId) +{ + if (m_inspectorController) + m_inspectorController->getProfileHeaders(callId); +} + +void InspectorBackend::getProfile(long callId, unsigned uid) +{ + if (m_inspectorController) + m_inspectorController->getProfile(callId, uid); +} + void InspectorBackend::enableDebugger(bool always) { if (m_inspectorController) @@ -455,6 +460,35 @@ void InspectorBackend::copyNode(long nodeId) String markup = createMarkup(node); Pasteboard::generalPasteboard()->writePlainText(markup); } + +void InspectorBackend::removeNode(long callId, long nodeId) +{ + InspectorFrontend* frontend = inspectorFrontend(); + if (!frontend) + return; + + Node* node = nodeForId(nodeId); + if (!node) { + // Use -1 to denote an error condition. + frontend->didRemoveNode(callId, -1); + return; + } + + Node* parentNode = node->parentNode(); + if (!parentNode) { + frontend->didRemoveNode(callId, -1); + return; + } + + ExceptionCode code; + parentNode->removeChild(node, code); + if (code) { + frontend->didRemoveNode(callId, -1); + return; + } + + frontend->didRemoveNode(callId, nodeId); +} void InspectorBackend::getCookies(long callId, const String& domain) { diff --git a/WebCore/inspector/InspectorBackend.h b/WebCore/inspector/InspectorBackend.h index d7eea8c..9d75e2f 100644 --- a/WebCore/inspector/InspectorBackend.h +++ b/WebCore/inspector/InspectorBackend.h @@ -95,13 +95,11 @@ public: const String& platform() const; - void enableTimeline(bool always); - void disableTimeline(bool always); - bool timelineEnabled() const; + void startTimelineProfiler(); + void stopTimelineProfiler(); + bool timelineProfilerEnabled() const; #if ENABLE(JAVASCRIPT_DEBUGGER) - const ProfilesArray& profiles() const; - void startProfiling(); void stopProfiling(); @@ -109,6 +107,9 @@ public: void disableProfiler(bool always); bool profilerEnabled(); + void getProfileHeaders(long callId); + void getProfile(long callId, unsigned uid); + void enableDebugger(bool always); void disableDebugger(bool always); bool debuggerEnabled() const; @@ -137,6 +138,7 @@ public: void setTextNodeValue(long callId, long nodeId, const String& value); void getEventListenersForNode(long callId, long nodeId); void copyNode(long nodeId); + void removeNode(long callId, long nodeId); void getCookies(long callId, const String& domain); void deleteCookie(const String& cookieName, const String& domain); @@ -170,9 +172,6 @@ private: InspectorController* m_inspectorController; InspectorClient* m_client; -#if ENABLE(JAVASCRIPT_DEBUGGER) - ProfilesArray m_emptyProfiles; -#endif }; } // namespace WebCore diff --git a/WebCore/inspector/InspectorBackend.idl b/WebCore/inspector/InspectorBackend.idl index 8803765..7a00c6a 100644 --- a/WebCore/inspector/InspectorBackend.idl +++ b/WebCore/inspector/InspectorBackend.idl @@ -61,9 +61,9 @@ module core { DOMString localizedStringsURL(); DOMString hiddenPanels(); DOMString platform(); - void enableTimeline(in boolean always); - void disableTimeline(in boolean always); - boolean timelineEnabled(); + void startTimelineProfiler(); + void stopTimelineProfiler(); + boolean timelineProfilerEnabled(); [ImplementationFunction=moveWindowBy] void moveByUnrestricted(in float x, in float y); void setAttachedWindowHeight(in unsigned long height); [Custom] DOMObject wrapCallback(in DOMObject callback); @@ -100,7 +100,8 @@ module core { void startProfiling(); void stopProfiling(); - [Custom] Array profiles(); + void getProfileHeaders(in long callId); + void getProfile(in long callId, in unsigned long uid); #endif void dispatchOnInjectedScript(in long callId, in DOMString methodName, in DOMString arguments, in boolean async); void getChildNodes(in long callId, in long nodeId); @@ -109,6 +110,7 @@ module core { void setTextNodeValue(in long callId, in long nodeId, in DOMString value); void getEventListenersForNode(in long callId, in long nodeId); void copyNode(in long nodeId); + void removeNode(in long callId, in long nodeId); void getCookies(in long callId, in DOMString domain); void deleteCookie(in DOMString cookieName, in DOMString domain); diff --git a/WebCore/inspector/InspectorController.cpp b/WebCore/inspector/InspectorController.cpp index 0922d7d..a6628cd 100644 --- a/WebCore/inspector/InspectorController.cpp +++ b/WebCore/inspector/InspectorController.cpp @@ -107,12 +107,12 @@ using namespace std; namespace WebCore { static const char* const UserInitiatedProfileName = "org.webkit.profiles.user-initiated"; +static const char* const CPUProfileType = "CPU"; static const char* const resourceTrackingEnabledSettingName = "resourceTrackingEnabled"; static const char* const debuggerEnabledSettingName = "debuggerEnabled"; static const char* const profilerEnabledSettingName = "profilerEnabled"; static const char* const inspectorAttachedHeightName = "inspectorAttachedHeight"; static const char* const lastActivePanelSettingName = "lastActivePanel"; -static const char* const timelineEnabledSettingName = "timelineEnabled"; static const unsigned defaultAttachedHeight = 300; static const float minimumAttachedHeight = 250.0f; @@ -171,6 +171,8 @@ InspectorController::~InspectorController() s_settingCache = 0; } + releaseDOMAgent(); + m_inspectorBackend->disconnectController(); } @@ -372,13 +374,14 @@ void InspectorController::addConsoleMessage(ScriptState* scriptState, ConsoleMes if (m_previousMessage && m_previousMessage->isEqual(scriptState, consoleMessage)) { m_previousMessage->incrementCount(); delete consoleMessage; + if (windowVisible()) + m_previousMessage->updateRepeatCountInConsole(m_frontend.get()); } else { m_previousMessage = consoleMessage; m_consoleMessages.append(consoleMessage); + if (windowVisible()) + m_previousMessage->addToConsole(m_frontend.get()); } - - if (m_frontend) - m_previousMessage->addToConsole(m_frontend.get()); } void InspectorController::clearConsoleMessages(bool clearUI) @@ -552,11 +555,10 @@ void InspectorController::setFrontendProxyObject(ScriptState* scriptState, Scrip m_scriptState = scriptState; m_injectedScriptObj = injectedScriptObj; m_frontend.set(new InspectorFrontend(this, scriptState, webInspectorObj)); - m_domAgent = new InspectorDOMAgent(m_frontend.get()); - - Setting timelineEnabled = setting(timelineEnabledSettingName); - if (m_timelineAgent.get() || (timelineEnabled.type() == Setting::BooleanType && timelineEnabled.booleanValue())) - m_timelineAgent = new InspectorTimelineAgent(m_frontend.get()); + releaseDOMAgent(); + m_domAgent = InspectorDOMAgent::create(m_frontend.get()); + if (m_timelineAgent) + m_timelineAgent->resetFrontendProxyObject(m_frontend.get()); } void InspectorController::show() @@ -611,11 +613,7 @@ void InspectorController::close() m_frontend.set(0); m_injectedScriptObj = ScriptObject(); - // m_domAgent is RefPtr. Remove DOM listeners first to ensure that there are - // no references to the DOM agent from the DOM tree. - if (m_domAgent) - m_domAgent->setDocument(0); - m_domAgent = 0; + releaseDOMAgent(); m_timelineAgent = 0; m_scriptState = 0; if (m_page) @@ -645,6 +643,15 @@ void InspectorController::closeWindow() m_client->closeWindow(); } +void InspectorController::releaseDOMAgent() +{ + // m_domAgent is RefPtr. Remove DOM listeners first to ensure that there are + // no references to the DOM agent from the DOM tree. + if (m_domAgent) + m_domAgent->setDocument(0); + m_domAgent = 0; +} + void InspectorController::populateScriptObjects() { ASSERT(m_frontend); @@ -656,9 +663,11 @@ void InspectorController::populateScriptObjects() ResourcesMap::iterator resourcesEnd = m_resources.end(); for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) { it->second->createScriptObject(m_frontend.get()); - m_frontend->addCookieDomain(it->second->frame()->document()->url().host()); + KURL resourceURL = it->second->frame()->document()->url(); + if (resourceURL.protocolInHTTPFamily() || resourceURL.protocolIs("file")) + m_frontend->addCookieDomain(resourceURL.host()); } - + unsigned messageCount = m_consoleMessages.size(); for (unsigned i = 0; i < messageCount; ++i) m_consoleMessages[i]->addToConsole(m_frontend.get()); @@ -724,7 +733,7 @@ void InspectorController::pruneResources(ResourcesMap* resourceMap, DocumentLoad if (!loaderToKeep || !resource->isSameLoader(loaderToKeep)) { removeResource(resource); - if (m_frontend) + if (windowVisible()) resource->releaseScriptObject(m_frontend.get(), true); } } @@ -765,7 +774,8 @@ void InspectorController::didCommitLoad(DocumentLoader* loader) // We don't add the main resource until its load is committed. This is // needed to keep the load for a user-entered URL from showing up in the // list of resources for the page they are navigating away from. - m_mainResource->createScriptObject(m_frontend.get()); + if (windowVisible()) + m_mainResource->createScriptObject(m_frontend.get()); } else { // Pages loaded from the page cache are committed before // m_mainResource is the right resource for this load, so we @@ -859,7 +869,7 @@ void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, ensureResourceTrackingSettingsLoaded(); if (!isMainResource && !m_resourceTrackingEnabled) return; - + RefPtr<InspectorResource> resource = InspectorResource::createCached(m_nextIdentifier--, loader, cachedResource); if (isMainResource) { @@ -869,7 +879,7 @@ void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, addResource(resource.get()); - if (m_frontend) + if (windowVisible()) resource->createScriptObject(m_frontend.get()); } @@ -895,7 +905,7 @@ void InspectorController::identifierForInitialRequest(unsigned long identifier, addResource(resource.get()); - if (m_frontend && loader->frameLoader()->isLoadingFromCachedPage() && resource == m_mainResource) + if (windowVisible() && loader->frameLoader()->isLoadingFromCachedPage() && resource == m_mainResource) resource->createScriptObject(m_frontend.get()); } @@ -904,8 +914,11 @@ void InspectorController::mainResourceFiredDOMContentEvent(DocumentLoader* loade if (!enabled() || !isMainResourceLoader(loader, url)) return; - if (m_mainResource) + if (m_mainResource) { m_mainResource->markDOMContentEventTime(); + if (windowVisible()) + m_mainResource->updateScriptObject(m_frontend.get()); + } } void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, const KURL& url) @@ -913,8 +926,11 @@ void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, con if (!enabled() || !isMainResourceLoader(loader, url)) return; - if (m_mainResource) + if (m_mainResource) { m_mainResource->markLoadEventTime(); + if (windowVisible()) + m_mainResource->updateScriptObject(m_frontend.get()); + } } bool InspectorController::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl) @@ -935,7 +951,7 @@ void InspectorController::willSendRequest(DocumentLoader*, unsigned long identif resource->updateResponse(redirectResponse); } - if (resource != m_mainResource && m_frontend) + if (resource != m_mainResource && windowVisible()) resource->createScriptObject(m_frontend.get()); } @@ -948,7 +964,7 @@ void InspectorController::didReceiveResponse(DocumentLoader*, unsigned long iden resource->updateResponse(response); resource->markResponseReceivedTime(); - if (m_frontend) + if (windowVisible()) resource->updateScriptObject(m_frontend.get()); } @@ -960,7 +976,7 @@ void InspectorController::didReceiveContentLength(DocumentLoader*, unsigned long resource->addLength(lengthReceived); - if (m_frontend) + if (windowVisible()) resource->updateScriptObject(m_frontend.get()); } @@ -976,9 +992,11 @@ void InspectorController::didFinishLoading(DocumentLoader*, unsigned long identi addResource(resource.get()); - if (m_frontend) { + if (windowVisible()) { resource->updateScriptObject(m_frontend.get()); - m_frontend->addCookieDomain(resource->frame()->document()->url().host()); + KURL resourceURL = resource->frame()->document()->url(); + if (resourceURL.protocolInHTTPFamily() || resourceURL.protocolIs("file")) + m_frontend->addCookieDomain(resourceURL.host()); } } @@ -995,7 +1013,7 @@ void InspectorController::didFailLoading(DocumentLoader*, unsigned long identifi addResource(resource.get()); - if (m_frontend) + if (windowVisible()) resource->updateScriptObject(m_frontend.get()); } @@ -1010,7 +1028,7 @@ void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identi resource->setXMLHttpResponseText(sourceString); - if (m_frontend) + if (windowVisible()) resource->updateScriptObject(m_frontend.get()); } @@ -1027,11 +1045,11 @@ void InspectorController::scriptImported(unsigned long identifier, const String& // thing by the Inspector. They should be made into distinct types. resource->setXMLHttpResponseText(ScriptString(sourceString)); - if (m_frontend) + if (windowVisible()) resource->updateScriptObject(m_frontend.get()); } -void InspectorController::enableResourceTracking(bool always) +void InspectorController::enableResourceTracking(bool always, bool reload) { if (!enabled()) return; @@ -1047,7 +1065,8 @@ void InspectorController::enableResourceTracking(bool always) if (m_frontend) m_frontend->resourceTrackingWasEnabled(); - m_inspectedPage->mainFrame()->loader()->reload(); + if (reload) + m_inspectedPage->mainFrame()->loader()->reload(); } void InspectorController::disableResourceTracking(bool always) @@ -1075,44 +1094,38 @@ void InspectorController::ensureResourceTrackingSettingsLoaded() m_resourceTrackingEnabled = true; } -void InspectorController::enableTimeline(bool always) +void InspectorController::startTimelineProfiler() { if (!enabled()) return; - if (always) - setSetting(timelineEnabledSettingName, Setting(true)); - - if (m_timelineAgent.get()) + if (m_timelineAgent) return; m_timelineAgent = new InspectorTimelineAgent(m_frontend.get()); if (m_frontend) - m_frontend->timelineWasEnabled(); + m_frontend->timelineProfilerWasStarted(); } -void InspectorController::disableTimeline(bool always) +void InspectorController::stopTimelineProfiler() { if (!enabled()) return; - if (always) - setSetting(timelineEnabledSettingName, Setting(false)); - - if (!m_timelineAgent.get()) + if (!m_timelineAgent) return; - m_timelineAgent.set(0); + m_timelineAgent = 0; if (m_frontend) - m_frontend->timelineWasDisabled(); + m_frontend->timelineProfilerWasStopped(); } -bool InspectorController::timelineEnabled() const +bool InspectorController::timelineProfilerEnabled() const { if (!enabled()) return false; - return m_timelineAgent.get(); + return m_timelineAgent; } #if ENABLE(DATABASE) @@ -1147,7 +1160,7 @@ void InspectorController::didOpenDatabase(Database* database, const String& doma m_databaseResources.set(resource->id(), resource); // Resources are only bound while visible. - if (m_frontend && windowVisible()) + if (windowVisible()) resource->bind(m_frontend.get()); } #endif @@ -1238,7 +1251,7 @@ void InspectorController::didUseDOMStorage(StorageArea* storageArea, bool isLoca m_domStorageResources.set(resource->id(), resource); // Resources are only bound while visible. - if (m_frontend && windowVisible()) + if (windowVisible()) resource->bind(m_frontend.get()); } @@ -1339,11 +1352,11 @@ void InspectorController::addProfile(PassRefPtr<Profile> prpProfile, unsigned li return; RefPtr<Profile> profile = prpProfile; - m_profiles.append(profile); + m_profiles.add(profile->uid(), profile); if (m_frontend) { JSLock lock(SilenceAssertionsOnly); - m_frontend->addProfile(toJS(m_scriptState, profile.get())); + m_frontend->addProfileHeader(createProfileHeader(*profile)); } addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL); @@ -1354,8 +1367,10 @@ void InspectorController::addProfileFinishedMessageToConsole(PassRefPtr<Profile> RefPtr<Profile> profile = prpProfile; UString message = "Profile \"webkit-profile://"; - message += encodeWithURLEscapeSequences(profile->title()); + message += encodeWithURLEscapeSequences(CPUProfileType); message += "/"; + message += encodeWithURLEscapeSequences(profile->title()); + message += "#"; message += UString::from(profile->uid()); message += "\" finished."; addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL); @@ -1364,11 +1379,42 @@ void InspectorController::addProfileFinishedMessageToConsole(PassRefPtr<Profile> void InspectorController::addStartProfilingMessageToConsole(const UString& title, unsigned lineNumber, const UString& sourceURL) { UString message = "Profile \"webkit-profile://"; + message += encodeWithURLEscapeSequences(CPUProfileType); + message += "/"; message += encodeWithURLEscapeSequences(title); - message += "/0\" started."; + message += "#0\" started."; addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL); } +void InspectorController::getProfileHeaders(long callId) +{ + if (!m_frontend) + return; + ScriptArray result = m_frontend->newScriptArray(); + ProfilesMap::iterator profilesEnd = m_profiles.end(); + int i = 0; + for (ProfilesMap::iterator it = m_profiles.begin(); it != profilesEnd; ++it) + result.set(i++, createProfileHeader(*it->second)); + m_frontend->didGetProfileHeaders(callId, result); +} + +void InspectorController::getProfile(long callId, unsigned uid) +{ + if (!m_frontend) + return; + ProfilesMap::iterator it = m_profiles.find(uid); + if (it != m_profiles.end()) + m_frontend->didGetProfile(callId, toJS(m_scriptState, it->second.get())); +} + +ScriptObject InspectorController::createProfileHeader(const JSC::Profile& profile) +{ + ScriptObject header = m_frontend->newScriptObject(); + header.set("title", profile.title()); + header.set("uid", profile.uid()); + return header; +} + UString InspectorController::getCurrentUserInitiatedProfileName(bool incrementProfileNumber = false) { if (incrementProfileNumber) @@ -1400,7 +1446,7 @@ void InspectorController::startUserInitiatedProfiling(Timer<InspectorController> UString title = getCurrentUserInitiatedProfileName(true); - ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame())->globalExec(); + ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec(); Profiler::profiler()->startProfiling(scriptState, title); addStartProfilingMessageToConsole(title, 0, UString()); @@ -1417,7 +1463,7 @@ void InspectorController::stopUserInitiatedProfiling() UString title = getCurrentUserInitiatedProfileName(); - ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame())->globalExec(); + ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec(); RefPtr<Profile> profile = Profiler::profiler()->stopProfiling(scriptState, title); if (profile) addProfile(profile, 0, UString()); @@ -1549,7 +1595,7 @@ void InspectorController::didContinue() void InspectorController::evaluateForTestInFrontend(long callId, const String& script) { - if (m_frontend && windowVisible()) + if (m_frontend) m_frontend->evaluateForTestInFrontend(callId, script); else m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script)); diff --git a/WebCore/inspector/InspectorController.h b/WebCore/inspector/InspectorController.h index 06fd250..58458bc 100644 --- a/WebCore/inspector/InspectorController.h +++ b/WebCore/inspector/InspectorController.h @@ -227,14 +227,14 @@ public: void resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString); void scriptImported(unsigned long identifier, const String& sourceString); - void enableResourceTracking(bool always = false); + void enableResourceTracking(bool always = false, bool reload = true); void disableResourceTracking(bool always = false); bool resourceTrackingEnabled() const { return m_resourceTrackingEnabled; } void ensureResourceTrackingSettingsLoaded(); - void enableTimeline(bool always = false); - void disableTimeline(bool always = false); - bool timelineEnabled() const; + void startTimelineProfiler(); + void stopTimelineProfiler(); + bool timelineProfilerEnabled() const; InspectorTimelineAgent* timelineAgent() { return m_timelineAgent.get(); } void mainResourceFiredLoadEvent(DocumentLoader*, const KURL&); @@ -269,7 +269,6 @@ public: void addProfile(PassRefPtr<JSC::Profile>, unsigned lineNumber, const JSC::UString& sourceURL); void addProfileFinishedMessageToConsole(PassRefPtr<JSC::Profile>, unsigned lineNumber, const JSC::UString& sourceURL); void addStartProfilingMessageToConsole(const JSC::UString& title, unsigned lineNumber, const JSC::UString& sourceURL); - const ProfilesArray& profiles() const { return m_profiles; } bool isRecordingUserInitiatedProfile() const { return m_recordingUserInitiatedProfile; } @@ -307,6 +306,7 @@ private: void storeLastActivePanel(const String& panelName); void closeWindow(); InspectorDOMAgent* domAgent() { return m_domAgent.get(); } + void releaseDOMAgent(); friend class InspectorFrontend; // Following are used from InspectorFrontend only. We don't want to expose them to the @@ -321,9 +321,14 @@ private: void deleteCookie(const String& cookieName, const String& domain); #if ENABLE(JAVASCRIPT_DEBUGGER) + typedef HashMap<unsigned int, RefPtr<JSC::Profile> > ProfilesMap; + void startUserInitiatedProfilingSoon(); void toggleRecordButton(bool); void enableDebuggerFromFrontend(bool always); + void getProfileHeaders(long callId); + void getProfile(long callId, unsigned uid); + ScriptObject createProfileHeader(const JSC::Profile& profile); #endif #if ENABLE(DATABASE) void selectDatabase(Database* database); @@ -403,7 +408,7 @@ private: int m_currentUserInitiatedProfileNumber; unsigned m_nextUserInitiatedProfileNumber; Timer<InspectorController> m_startProfiling; - ProfilesArray m_profiles; + ProfilesMap m_profiles; #endif }; diff --git a/WebCore/inspector/InspectorDOMAgent.h b/WebCore/inspector/InspectorDOMAgent.h index b9bdb6b..3f736f7 100644 --- a/WebCore/inspector/InspectorDOMAgent.h +++ b/WebCore/inspector/InspectorDOMAgent.h @@ -68,6 +68,11 @@ namespace WebCore { class InspectorDOMAgent : public EventListener { public: + static PassRefPtr<InspectorDOMAgent> create(InspectorFrontend* frontend) + { + return adoptRef(new InspectorDOMAgent(frontend)); + } + static const InspectorDOMAgent* cast(const EventListener* listener) { return listener->type() == InspectorDOMAgentType diff --git a/WebCore/inspector/InspectorDOMStorageResource.cpp b/WebCore/inspector/InspectorDOMStorageResource.cpp index 37818e7..c93e987 100644 --- a/WebCore/inspector/InspectorDOMStorageResource.cpp +++ b/WebCore/inspector/InspectorDOMStorageResource.cpp @@ -104,7 +104,7 @@ void InspectorDOMStorageResource::handleEvent(ScriptExecutionContext*, Event* ev ASSERT(eventNames().storageEvent == event->type()); StorageEvent* storageEvent = static_cast<StorageEvent*>(event); Storage* storage = storageEvent->storageArea(); - bool isLocalStorage = storageEvent->source()->localStorage() == storage; + bool isLocalStorage = storage->frame()->domWindow()->localStorage() == storage; if (isSameHostAndType(storage->frame(), isLocalStorage)) m_frontend->updateDOMStorage(m_id); } diff --git a/WebCore/inspector/InspectorFrontend.cpp b/WebCore/inspector/InspectorFrontend.cpp index ad935a8..2c422ac 100644 --- a/WebCore/inspector/InspectorFrontend.cpp +++ b/WebCore/inspector/InspectorFrontend.cpp @@ -78,9 +78,9 @@ void InspectorFrontend::didCommitLoad() callSimpleFunction("didCommitLoad"); } -void InspectorFrontend::addMessageToConsole(const ScriptObject& messageObj, const Vector<ScriptString>& frames, const Vector<ScriptValue> wrappedArguments, const String& message) +void InspectorFrontend::addConsoleMessage(const ScriptObject& messageObj, const Vector<ScriptString>& frames, const Vector<ScriptValue> wrappedArguments, const String& message) { - OwnPtr<ScriptFunctionCall> function(newFunctionCall("addMessageToConsole")); + OwnPtr<ScriptFunctionCall> function(newFunctionCall("addConsoleMessage")); function->appendArgument(messageObj); if (!frames.isEmpty()) { for (unsigned i = 0; i < frames.size(); ++i) @@ -93,6 +93,13 @@ void InspectorFrontend::addMessageToConsole(const ScriptObject& messageObj, cons function->call(); } +void InspectorFrontend::updateConsoleMessageRepeatCount(const int count) +{ + OwnPtr<ScriptFunctionCall> function(newFunctionCall("updateConsoleMessageRepeatCount")); + function->appendArgument(count); + function->call(); +} + void InspectorFrontend::clearConsoleMessages() { callSimpleFunction("clearConsoleMessages"); @@ -197,20 +204,20 @@ void InspectorFrontend::resourceTrackingWasDisabled() callSimpleFunction("resourceTrackingWasDisabled"); } -void InspectorFrontend::timelineWasEnabled() +void InspectorFrontend::timelineProfilerWasStarted() { - callSimpleFunction("timelineWasEnabled"); + callSimpleFunction("timelineProfilerWasStarted"); } -void InspectorFrontend::timelineWasDisabled() +void InspectorFrontend::timelineProfilerWasStopped() { - callSimpleFunction("timelineWasDisabled"); + callSimpleFunction("timelineProfilerWasStopped"); } -void InspectorFrontend::addItemToTimeline(const ScriptObject& itemObj) +void InspectorFrontend::addRecordToTimeline(const ScriptObject& record) { - OwnPtr<ScriptFunctionCall> function(newFunctionCall("addItemToTimeline")); - function->appendArgument(itemObj); + OwnPtr<ScriptFunctionCall> function(newFunctionCall("addRecordToTimeline")); + function->appendArgument(record); function->call(); } @@ -261,9 +268,9 @@ void InspectorFrontend::failedToParseScriptSource(const JSC::SourceCode& source, function->call(); } -void InspectorFrontend::addProfile(const JSC::JSValue& profile) +void InspectorFrontend::addProfileHeader(const ScriptValue& profile) { - OwnPtr<ScriptFunctionCall> function(newFunctionCall("addProfile")); + OwnPtr<ScriptFunctionCall> function(newFunctionCall("addProfileHeader")); function->appendArgument(profile); function->call(); } @@ -275,6 +282,22 @@ void InspectorFrontend::setRecordingProfile(bool isProfiling) function->call(); } +void InspectorFrontend::didGetProfileHeaders(int callId, const ScriptArray& headers) +{ + OwnPtr<ScriptFunctionCall> function(newFunctionCall("didGetProfileHeaders")); + function->appendArgument(callId); + function->appendArgument(headers); + function->call(); +} + +void InspectorFrontend::didGetProfile(int callId, const ScriptValue& profile) +{ + OwnPtr<ScriptFunctionCall> function(newFunctionCall("didGetProfile")); + function->appendArgument(callId); + function->appendArgument(profile); + function->call(); +} + void InspectorFrontend::pausedScript(const ScriptValue& callFrames) { OwnPtr<ScriptFunctionCall> function(newFunctionCall("pausedScript")); @@ -343,6 +366,14 @@ void InspectorFrontend::attributesUpdated(int id, const ScriptArray& attributes) function->call(); } +void InspectorFrontend::didRemoveNode(int callId, int nodeId) +{ + OwnPtr<ScriptFunctionCall> function(newFunctionCall("didRemoveNode")); + function->appendArgument(callId); + function->appendArgument(nodeId); + function->call(); +} + void InspectorFrontend::didGetChildNodes(int callId) { OwnPtr<ScriptFunctionCall> function(newFunctionCall("didGetChildNodes")); diff --git a/WebCore/inspector/InspectorFrontend.h b/WebCore/inspector/InspectorFrontend.h index f7055bd..3cb9b8c 100644 --- a/WebCore/inspector/InspectorFrontend.h +++ b/WebCore/inspector/InspectorFrontend.h @@ -63,7 +63,8 @@ namespace WebCore { ScriptObject newScriptObject(); void didCommitLoad(); - void addMessageToConsole(const ScriptObject& messageObj, const Vector<ScriptString>& frames, const Vector<ScriptValue> wrappedArguments, const String& message); + void addConsoleMessage(const ScriptObject& messageObj, const Vector<ScriptString>& frames, const Vector<ScriptValue> wrappedArguments, const String& message); + void updateConsoleMessageRepeatCount(const int count); void clearConsoleMessages(); bool addResource(long long identifier, const ScriptObject& resourceObj); @@ -89,8 +90,10 @@ namespace WebCore { void profilerWasDisabled(); void parsedScriptSource(const JSC::SourceCode&); void failedToParseScriptSource(const JSC::SourceCode&, int errorLine, const JSC::UString& errorMessage); - void addProfile(const JSC::JSValue& profile); + void addProfileHeader(const ScriptValue& profile); void setRecordingProfile(bool isProfiling); + void didGetProfileHeaders(int callId, const ScriptArray& headers); + void didGetProfile(int callId, const ScriptValue& profile); void pausedScript(const ScriptValue& callFrames); void resumedScript(); #endif @@ -120,10 +123,11 @@ namespace WebCore { void didGetChildNodes(int callId); void didApplyDomChange(int callId, bool success); void didGetEventListenersForNode(int callId, int nodeId, ScriptArray& listenersArray); + void didRemoveNode(int callId, int nodeId); - void timelineWasEnabled(); - void timelineWasDisabled(); - void addItemToTimeline(const ScriptObject& itemObj); + void timelineProfilerWasStarted(); + void timelineProfilerWasStopped(); + void addRecordToTimeline(const ScriptObject&); void didGetCookies(int callId, const ScriptArray& cookies, const String& cookiesString); void didDispatchOnInjectedScript(int callId, const String& result, bool isException); diff --git a/WebCore/inspector/InspectorResource.cpp b/WebCore/inspector/InspectorResource.cpp index 69ab39b..b8bb22b 100644 --- a/WebCore/inspector/InspectorResource.cpp +++ b/WebCore/inspector/InspectorResource.cpp @@ -33,6 +33,7 @@ #if ENABLE(INSPECTOR) +#include "Cache.h" #include "CachedResource.h" #include "DocLoader.h" #include "DocumentLoader.h" @@ -227,6 +228,18 @@ void InspectorResource::releaseScriptObject(InspectorFrontend* frontend, bool ca frontend->removeResource(m_identifier); } +CachedResource* InspectorResource::cachedResource() const +{ + // Try hard to find a corresponding CachedResource. During preloading, DocLoader may not have the resource in document resources set yet, + // but Inspector will already try to fetch data that is only available via CachedResource (and it won't update once the resource is added, + // because m_changes will not have the appropriate bits set). + const String& url = requestURL(); + CachedResource* cachedResource = m_frame->document()->docLoader()->cachedResource(url); + if (!cachedResource) + cachedResource = cache()->resourceForURL(url); + return cachedResource; +} + InspectorResource::Type InspectorResource::type() const { if (!m_xmlHttpResponseText.isNull()) @@ -238,7 +251,7 @@ InspectorResource::Type InspectorResource::type() const if (m_loader->frameLoader() && m_requestURL == m_loader->frameLoader()->iconURL()) return Image; - CachedResource* cachedResource = m_frame->document()->docLoader()->cachedResource(requestURL()); + CachedResource* cachedResource = this->cachedResource(); if (!cachedResource) return Other; @@ -281,13 +294,14 @@ String InspectorResource::sourceString() const return encoding.decode(buffer->data(), buffer->size()); } -PassRefPtr<SharedBuffer> InspectorResource::resourceData(String* textEncodingName) const { +PassRefPtr<SharedBuffer> InspectorResource::resourceData(String* textEncodingName) const +{ if (m_requestURL == m_loader->requestURL()) { *textEncodingName = m_frame->document()->inputEncoding(); return m_loader->mainResourceData(); } - CachedResource* cachedResource = m_frame->document()->docLoader()->cachedResource(requestURL()); + CachedResource* cachedResource = this->cachedResource(); if (!cachedResource) return 0; diff --git a/WebCore/inspector/InspectorResource.h b/WebCore/inspector/InspectorResource.h index 880eab7..0335586 100644 --- a/WebCore/inspector/InspectorResource.h +++ b/WebCore/inspector/InspectorResource.h @@ -145,6 +145,8 @@ namespace WebCore { InspectorResource(long long identifier, DocumentLoader*); Type type() const; + CachedResource* cachedResource() const; + long long m_identifier; RefPtr<DocumentLoader> m_loader; RefPtr<Frame> m_frame; diff --git a/WebCore/inspector/InspectorTimelineAgent.cpp b/WebCore/inspector/InspectorTimelineAgent.cpp index c3ad075..4f7b736 100644 --- a/WebCore/inspector/InspectorTimelineAgent.cpp +++ b/WebCore/inspector/InspectorTimelineAgent.cpp @@ -33,19 +33,16 @@ #if ENABLE(INSPECTOR) -#include "DOMDispatchTimelineItem.h" #include "Event.h" #include "InspectorFrontend.h" -#include "TimelineItem.h" +#include "TimelineRecordFactory.h" #include <wtf/CurrentTime.h> namespace WebCore { InspectorTimelineAgent::InspectorTimelineAgent(InspectorFrontend* frontend) - : m_sessionStartTime(currentTimeInMilliseconds()) - , m_frontend(frontend) - , m_currentTimelineItem(0) + : m_frontend(frontend) { ASSERT(m_frontend); } @@ -56,75 +53,140 @@ InspectorTimelineAgent::~InspectorTimelineAgent() void InspectorTimelineAgent::willDispatchDOMEvent(const Event& event) { - m_currentTimelineItem = new DOMDispatchTimelineItem(m_currentTimelineItem.release(), sessionTimeInMilliseconds(), event); + pushCurrentRecord(TimelineRecordFactory::createDOMDispatchRecord(m_frontend, currentTimeInMilliseconds(), event), DOMDispatchTimelineRecordType); } void InspectorTimelineAgent::didDispatchDOMEvent() { - ASSERT(m_currentTimelineItem->type() == DOMDispatchTimelineItemType); - didCompleteCurrentRecord(); + didCompleteCurrentRecord(DOMDispatchTimelineRecordType); } void InspectorTimelineAgent::willLayout() { - m_currentTimelineItem = new TimelineItem(m_currentTimelineItem.release(), sessionTimeInMilliseconds(), LayoutTimelineItemType); + pushCurrentRecord(TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()), LayoutTimelineRecordType); } void InspectorTimelineAgent::didLayout() { - ASSERT(m_currentTimelineItem->type() == LayoutTimelineItemType); - didCompleteCurrentRecord(); + didCompleteCurrentRecord(LayoutTimelineRecordType); } void InspectorTimelineAgent::willRecalculateStyle() { - m_currentTimelineItem = new TimelineItem(m_currentTimelineItem.release(), sessionTimeInMilliseconds(), RecalculateStylesTimelineItemType); + pushCurrentRecord(TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()), RecalculateStylesTimelineRecordType); } void InspectorTimelineAgent::didRecalculateStyle() { - ASSERT(m_currentTimelineItem->type() == RecalculateStylesTimelineItemType); - didCompleteCurrentRecord(); + didCompleteCurrentRecord(RecalculateStylesTimelineRecordType); } void InspectorTimelineAgent::willPaint() { - m_currentTimelineItem = new TimelineItem(m_currentTimelineItem.release(), sessionTimeInMilliseconds(), PaintTimelineItemType); + pushCurrentRecord(TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()), PaintTimelineRecordType); } void InspectorTimelineAgent::didPaint() { - ASSERT(m_currentTimelineItem->type() == PaintTimelineItemType); - didCompleteCurrentRecord(); + didCompleteCurrentRecord(PaintTimelineRecordType); } void InspectorTimelineAgent::willWriteHTML() { - m_currentTimelineItem = new TimelineItem(m_currentTimelineItem.release(), sessionTimeInMilliseconds(), ParseHTMLTimelineItemType); + pushCurrentRecord(TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()), ParseHTMLTimelineRecordType); } void InspectorTimelineAgent::didWriteHTML() { - ASSERT(m_currentTimelineItem->type() == ParseHTMLTimelineItemType); - didCompleteCurrentRecord(); + didCompleteCurrentRecord(ParseHTMLTimelineRecordType); +} + +void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot) +{ + addRecordToTimeline(TimelineRecordFactory::createTimerInstallRecord(m_frontend, currentTimeInMilliseconds(), timerId, + timeout, singleShot), TimerInstallTimelineRecordType); +} + +void InspectorTimelineAgent::didRemoveTimer(int timerId) +{ + addRecordToTimeline(TimelineRecordFactory::createGenericTimerRecord(m_frontend, currentTimeInMilliseconds(), timerId), + TimerRemoveTimelineRecordType); +} + +void InspectorTimelineAgent::willFireTimer(int timerId) +{ + pushCurrentRecord(TimelineRecordFactory::createGenericTimerRecord(m_frontend, currentTimeInMilliseconds(), timerId), + TimerFireTimelineRecordType); +} + +void InspectorTimelineAgent::didFireTimer() +{ + didCompleteCurrentRecord(TimerFireTimelineRecordType); +} + +void InspectorTimelineAgent::willChangeXHRReadyState(const String& url, int readyState) +{ + pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeTimelineRecord(m_frontend, currentTimeInMilliseconds(), url, readyState), + XHRReadyStateChangeRecordType); +} + +void InspectorTimelineAgent::didChangeXHRReadyState() +{ + didCompleteCurrentRecord(XHRReadyStateChangeRecordType); +} + +void InspectorTimelineAgent::willLoadXHR(const String& url) +{ + pushCurrentRecord(TimelineRecordFactory::createXHRLoadTimelineRecord(m_frontend, currentTimeInMilliseconds(), url), XHRLoadRecordType); +} + +void InspectorTimelineAgent::didLoadXHR() +{ + didCompleteCurrentRecord(XHRLoadRecordType); +} + +void InspectorTimelineAgent::willEvaluateScriptTag(const String& url, int lineNumber) +{ + pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptTagTimelineRecord(m_frontend, currentTimeInMilliseconds(), url, lineNumber), EvaluateScriptTagTimelineRecordType); +} + +void InspectorTimelineAgent::didEvaluateScriptTag() +{ + didCompleteCurrentRecord(EvaluateScriptTagTimelineRecordType); } void InspectorTimelineAgent::reset() { - m_sessionStartTime = currentTimeInMilliseconds(); - m_currentTimelineItem.set(0); + m_recordStack.clear(); +} + +void InspectorTimelineAgent::resetFrontendProxyObject(InspectorFrontend* frontend) +{ + ASSERT(frontend); + reset(); + m_frontend = frontend; } -void InspectorTimelineAgent::didCompleteCurrentRecord() +void InspectorTimelineAgent::addRecordToTimeline(ScriptObject record, TimelineRecordType type) { - OwnPtr<TimelineItem> item(m_currentTimelineItem.release()); - m_currentTimelineItem = item->releasePrevious(); + record.set("type", type); + if (m_recordStack.isEmpty()) + m_frontend->addRecordToTimeline(record); + else { + TimelineRecordEntry parent = m_recordStack.last(); + parent.children.set(parent.children.length(), record); + } +} - item->setEndTime(sessionTimeInMilliseconds()); - if (m_currentTimelineItem.get()) - m_currentTimelineItem->addChildItem(item.release()); - else - item->addToTimeline(m_frontend); +void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type) +{ + ASSERT(!m_recordStack.isEmpty()); + TimelineRecordEntry entry = m_recordStack.last(); + m_recordStack.removeLast(); + ASSERT(entry.type == type); + entry.record.set("children", entry.children); + entry.record.set("endTime", currentTimeInMilliseconds()); + addRecordToTimeline(entry.record, type); } double InspectorTimelineAgent::currentTimeInMilliseconds() @@ -132,9 +194,9 @@ double InspectorTimelineAgent::currentTimeInMilliseconds() return currentTime() * 1000.0; } -double InspectorTimelineAgent::sessionTimeInMilliseconds() +void InspectorTimelineAgent::pushCurrentRecord(ScriptObject record, TimelineRecordType type) { - return currentTimeInMilliseconds() - m_sessionStartTime; + m_recordStack.append(TimelineRecordEntry(record, m_frontend->newScriptArray(), type)); } } // namespace WebCore diff --git a/WebCore/inspector/InspectorTimelineAgent.h b/WebCore/inspector/InspectorTimelineAgent.h index 4095fed..0401977 100644 --- a/WebCore/inspector/InspectorTimelineAgent.h +++ b/WebCore/inspector/InspectorTimelineAgent.h @@ -31,16 +31,30 @@ #ifndef InspectorTimelineAgent_h #define InspectorTimelineAgent_h +#include "Document.h" +#include "ScriptExecutionContext.h" #include "ScriptObject.h" #include "ScriptArray.h" - -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> namespace WebCore { class Event; class InspectorFrontend; - class TimelineItem; + + // Must be kept in sync with TimelineAgent.js + enum TimelineRecordType { + DOMDispatchTimelineRecordType = 0, + LayoutTimelineRecordType = 1, + RecalculateStylesTimelineRecordType = 2, + PaintTimelineRecordType = 3, + ParseHTMLTimelineRecordType = 4, + TimerInstallTimelineRecordType = 5, + TimerRemoveTimelineRecordType = 6, + TimerFireTimelineRecordType = 7, + XHRReadyStateChangeRecordType = 8, + XHRLoadRecordType = 9, + EvaluateScriptTagTimelineRecordType = 10, + }; class InspectorTimelineAgent { public: @@ -48,30 +62,66 @@ namespace WebCore { ~InspectorTimelineAgent(); void reset(); + void resetFrontendProxyObject(InspectorFrontend*); // Methods called from WebCore. void willDispatchDOMEvent(const Event&); void didDispatchDOMEvent(); + void willLayout(); void didLayout(); + void willRecalculateStyle(); void didRecalculateStyle(); + void willPaint(); void didPaint(); - void didWriteHTML(); + void willWriteHTML(); - private: - double sessionTimeInMilliseconds(); + void didWriteHTML(); + + void didInstallTimer(int timerId, int timeout, bool singleShot); + void didRemoveTimer(int timerId); + void willFireTimer(int timerId); + void didFireTimer(); + + void willChangeXHRReadyState(const String&, int); + void didChangeXHRReadyState(); + void willLoadXHR(const String&); + void didLoadXHR(); + void willEvaluateScriptTag(const String&, int); + void didEvaluateScriptTag(); + + static InspectorTimelineAgent* retrieve(ScriptExecutionContext*); + private: + struct TimelineRecordEntry { + TimelineRecordEntry(ScriptObject record, ScriptArray children, TimelineRecordType type) : record(record), children(children), type(type) { } + ScriptObject record; + ScriptArray children; + TimelineRecordType type; + }; + + void pushCurrentRecord(ScriptObject, TimelineRecordType); + static double currentTimeInMilliseconds(); - void didCompleteCurrentRecord(); + void didCompleteCurrentRecord(TimelineRecordType); + + void addRecordToTimeline(ScriptObject, TimelineRecordType); - double m_sessionStartTime; InspectorFrontend* m_frontend; - OwnPtr<TimelineItem> m_currentTimelineItem; + + Vector< TimelineRecordEntry > m_recordStack; }; +inline InspectorTimelineAgent* InspectorTimelineAgent::retrieve(ScriptExecutionContext* context) +{ + if (context->isDocument()) + return static_cast<Document*>(context)->inspectorTimelineAgent(); + return 0; +} + } // namespace WebCore #endif // !defined(InspectorTimelineAgent_h) diff --git a/WebCore/inspector/JavaScriptCallFrame.cpp b/WebCore/inspector/JavaScriptCallFrame.cpp index 9225a03..1559d82 100644 --- a/WebCore/inspector/JavaScriptCallFrame.cpp +++ b/WebCore/inspector/JavaScriptCallFrame.cpp @@ -25,6 +25,7 @@ #include "config.h" #include "JavaScriptCallFrame.h" +#include "JSDOMBinding.h" #if ENABLE(JAVASCRIPT_DEBUGGER) @@ -105,7 +106,7 @@ JSValue JavaScriptCallFrame::evaluate(const UString& script, JSValue& exception) return jsNull(); JSLock lock(SilenceAssertionsOnly); - return m_debuggerCallFrame.evaluate(script, exception); + return DebuggerCallFrame_evaluateInWorld(m_debuggerCallFrame, script, exception); } } // namespace WebCore diff --git a/WebCore/inspector/JavaScriptCallFrame.h b/WebCore/inspector/JavaScriptCallFrame.h index e86b0eb..47cdac2 100644 --- a/WebCore/inspector/JavaScriptCallFrame.h +++ b/WebCore/inspector/JavaScriptCallFrame.h @@ -44,7 +44,12 @@ namespace WebCore { return adoptRef(new JavaScriptCallFrame(debuggerCallFrame, caller, sourceID, line)); } - void invalidate() { m_isValid = false; } + void invalidate() + { + m_isValid = false; + m_debuggerCallFrame = 0; + } + bool isValid() const { return m_isValid; } JavaScriptCallFrame* caller(); @@ -56,6 +61,7 @@ namespace WebCore { m_debuggerCallFrame = debuggerCallFrame; m_line = line; m_sourceID = sourceID; + m_isValid = true; } String functionName() const; diff --git a/WebCore/inspector/JavaScriptProfileNode.cpp b/WebCore/inspector/JavaScriptProfileNode.cpp index 3c3e279..2d462f6 100644 --- a/WebCore/inspector/JavaScriptProfileNode.cpp +++ b/WebCore/inspector/JavaScriptProfileNode.cpp @@ -104,28 +104,6 @@ static JSValueRef getSelfTime(JSContextRef ctx, JSObjectRef thisObject, JSString return JSValueMakeNumber(ctx, profileNode->selfTime()); } -static JSValueRef getTotalPercent(JSContextRef ctx, JSObjectRef thisObject, JSStringRef, JSValueRef*) -{ - JSC::JSLock lock(SilenceAssertionsOnly); - - if (!JSValueIsObjectOfClass(ctx, thisObject, ProfileNodeClass())) - return JSValueMakeUndefined(ctx); - - ProfileNode* profileNode = static_cast<ProfileNode*>(JSObjectGetPrivate(thisObject)); - return JSValueMakeNumber(ctx, profileNode->totalPercent()); -} - -static JSValueRef getSelfPercent(JSContextRef ctx, JSObjectRef thisObject, JSStringRef, JSValueRef*) -{ - JSC::JSLock lock(SilenceAssertionsOnly); - - if (!JSValueIsObjectOfClass(ctx, thisObject, ProfileNodeClass())) - return JSValueMakeUndefined(ctx); - - ProfileNode* profileNode = static_cast<ProfileNode*>(JSObjectGetPrivate(thisObject)); - return JSValueMakeNumber(ctx, profileNode->selfPercent()); -} - static JSValueRef getNumberOfCalls(JSContextRef ctx, JSObjectRef thisObject, JSStringRef, JSValueRef*) { JSC::JSLock lock(SilenceAssertionsOnly); @@ -184,30 +162,6 @@ static JSValueRef getChildren(JSContextRef ctx, JSObjectRef thisObject, JSString return result; } -static JSValueRef getParent(JSContextRef ctx, JSObjectRef thisObject, JSStringRef, JSValueRef*) -{ - JSC::JSLock lock(SilenceAssertionsOnly); - - if (!JSValueIsObjectOfClass(ctx, thisObject, ProfileNodeClass())) - return JSValueMakeUndefined(ctx); - - ProfileNode* profileNode = static_cast<ProfileNode*>(JSObjectGetPrivate(thisObject)); - ExecState* exec = toJS(ctx); - return toRef(exec, toJS(exec, profileNode->parent())); -} - -static JSValueRef getHead(JSContextRef ctx, JSObjectRef thisObject, JSStringRef, JSValueRef*) -{ - JSC::JSLock lock(SilenceAssertionsOnly); - - if (!JSValueIsObjectOfClass(ctx, thisObject, ProfileNodeClass())) - return JSValueMakeUndefined(ctx); - - ProfileNode* profileNode = static_cast<ProfileNode*>(JSObjectGetPrivate(thisObject)); - ExecState* exec = toJS(ctx); - return toRef(exec, toJS(exec, profileNode->head())); -} - static JSValueRef getVisible(JSContextRef ctx, JSObjectRef thisObject, JSStringRef, JSValueRef*) { JSC::JSLock lock(SilenceAssertionsOnly); @@ -245,12 +199,8 @@ JSClassRef ProfileNodeClass() { "lineNumber", getLineNumber, 0, kJSPropertyAttributeNone }, { "totalTime", getTotalTime, 0, kJSPropertyAttributeNone }, { "selfTime", getSelfTime, 0, kJSPropertyAttributeNone }, - { "totalPercent", getTotalPercent, 0, kJSPropertyAttributeNone }, - { "selfPercent", getSelfPercent, 0, kJSPropertyAttributeNone }, { "numberOfCalls", getNumberOfCalls, 0, kJSPropertyAttributeNone }, { "children", getChildren, 0, kJSPropertyAttributeNone }, - { "parent", getParent, 0, kJSPropertyAttributeNone }, - { "head", getHead, 0, kJSClassAttributeNone }, { "visible", getVisible, 0, kJSPropertyAttributeNone }, { "callUID", getCallUID, 0, kJSPropertyAttributeNone }, { 0, 0, 0, 0 } diff --git a/WebCore/inspector/TimelineItem.cpp b/WebCore/inspector/TimelineItem.cpp deleted file mode 100644 index b368c49..0000000 --- a/WebCore/inspector/TimelineItem.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* -* Copyright (C) 2009 Google Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are -* met: -* -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above -* copyright notice, this list of conditions and the following disclaimer -* in the documentation and/or other materials provided with the -* distribution. -* * Neither the name of Google Inc. nor the names of its -* contributors may be used to endorse or promote products derived from -* this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "config.h" -#include "TimelineItem.h" - -#if ENABLE(INSPECTOR) - -#include "InspectorFrontend.h" -#include "ScriptArray.h" -#include "ScriptObject.h" - -namespace WebCore { - -TimelineItem::TimelineItem(PassOwnPtr<TimelineItem> previous, double startTime, TimelineItemType itemType) - : m_startTime(startTime) - , m_endTime(0.0) - , m_itemType(itemType) - , m_previous(previous) -{ -} - -void TimelineItem::addToTimeline(InspectorFrontend* frontend) -{ - frontend->addItemToTimeline(convertToScriptObject(frontend)); -} - -ScriptObject TimelineItem::convertToScriptObject(InspectorFrontend* frontend) -{ - ScriptObject selfObj = frontend->newScriptObject(); - selfObj.set("time", m_startTime); - selfObj.set("type", static_cast<int>(m_itemType)); - selfObj.set("duration", m_endTime - m_startTime); - ScriptArray children = convertChildrenToScriptArray(frontend); - selfObj.set("children", children); - return selfObj; -} - -ScriptArray TimelineItem::convertChildrenToScriptArray(InspectorFrontend* frontend) -{ - ScriptArray children = frontend->newScriptArray(); - for (unsigned i = 0; i < m_children.size(); ++i) - children.set(i, m_children[i]->convertToScriptObject(frontend)); - return children; -} - -void TimelineItem::addChildItem(PassOwnPtr<TimelineItem> timelineItem) -{ - m_children.append(timelineItem); -} - -} // namespace WebCore - -#endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/TimelineItem.h b/WebCore/inspector/TimelineItem.h deleted file mode 100644 index 1909230..0000000 --- a/WebCore/inspector/TimelineItem.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -* Copyright (C) 2009 Google Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are -* met: -* -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above -* copyright notice, this list of conditions and the following disclaimer -* in the documentation and/or other materials provided with the -* distribution. -* * Neither the name of Google Inc. nor the names of its -* contributors may be used to endorse or promote products derived from -* this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef TimelineItem_h -#define TimelineItem_h - -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - - class InspectorFrontend; - class TimelineItem; - class ScriptArray; - class ScriptObject; - - typedef Vector<OwnPtr<TimelineItem> > TimelineItems; - - // Must be kept in sync with TimelineAgent.js - enum TimelineItemType { - DOMDispatchTimelineItemType = 0, - LayoutTimelineItemType = 1, - RecalculateStylesTimelineItemType = 2, - PaintTimelineItemType = 3, - ParseHTMLTimelineItemType = 4, - }; - - class TimelineItem { - public: - TimelineItem(PassOwnPtr<TimelineItem>, double startTime, TimelineItemType); - - virtual ~TimelineItem() { } - - TimelineItem* previous() const { return m_previous.get(); } - - PassOwnPtr<TimelineItem*> releasePrevious() { return m_previous.release(); } - - void addToTimeline(InspectorFrontend*); - - void addChildItem(PassOwnPtr<TimelineItem>); - - void setEndTime(double endTime) { m_endTime = endTime; } - - TimelineItemType type() { return m_itemType; } - protected: - virtual ScriptObject convertToScriptObject(InspectorFrontend*); - - ScriptArray convertChildrenToScriptArray(InspectorFrontend*); - - private: - double m_startTime; - double m_endTime; - TimelineItemType m_itemType; - OwnPtr<TimelineItem> m_previous; - TimelineItems m_children; - }; - -} // namespace WebCore - -#endif // !defined(TimelineItem_h) diff --git a/WebCore/inspector/TimelineRecordFactory.cpp b/WebCore/inspector/TimelineRecordFactory.cpp new file mode 100644 index 0000000..085bcd9 --- /dev/null +++ b/WebCore/inspector/TimelineRecordFactory.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TimelineRecordFactory.h" + +#if ENABLE(INSPECTOR) + +#include "Event.h" +#include "InspectorFrontend.h" +#include "ScriptArray.h" +#include "ScriptObject.h" +namespace WebCore { + +// static +ScriptObject TimelineRecordFactory::createGenericRecord(InspectorFrontend* frontend, double startTime) +{ + ScriptObject record = frontend->newScriptObject(); + record.set("startTime", startTime); + return record; +} + +// static +ScriptObject TimelineRecordFactory::createDOMDispatchRecord(InspectorFrontend* frontend, double startTime, const Event& event) +{ + ScriptObject record = createGenericRecord(frontend, startTime); + ScriptObject data = frontend->newScriptObject(); + data.set("type", event.type().string()); + record.set("data", data); + return record; +} + +// static +ScriptObject TimelineRecordFactory::createGenericTimerRecord(InspectorFrontend* frontend, double startTime, int timerId) +{ + ScriptObject record = createGenericRecord(frontend, startTime); + ScriptObject data = frontend->newScriptObject(); + data.set("timerId", timerId); + record.set("data", data); + return record; +} + +// static +ScriptObject TimelineRecordFactory::createTimerInstallRecord(InspectorFrontend* frontend, double startTime, int timerId, int timeout, bool singleShot) +{ + ScriptObject record = createGenericRecord(frontend, startTime); + ScriptObject data = frontend->newScriptObject(); + data.set("timerId", timerId); + data.set("timeout", timeout); + data.set("singleShot", singleShot); + record.set("data", data); + return record; +} + +// static +ScriptObject TimelineRecordFactory::createXHRReadyStateChangeTimelineRecord(InspectorFrontend* frontend, double startTime, const String& url, int readyState) +{ + ScriptObject record = createGenericRecord(frontend, startTime); + ScriptObject data = frontend->newScriptObject(); + data.set("url", url); + data.set("readyState", readyState); + record.set("data", data); + return record; +} + +// static +ScriptObject TimelineRecordFactory::createXHRLoadTimelineRecord(InspectorFrontend* frontend, double startTime, const String& url) +{ + ScriptObject record = createGenericRecord(frontend, startTime); + ScriptObject data = frontend->newScriptObject(); + data.set("url", url); + record.set("data", data); + return record; +} + +// static +ScriptObject TimelineRecordFactory::createEvaluateScriptTagTimelineRecord(InspectorFrontend* frontend, double startTime, const String& url, double lineNumber) +{ + ScriptObject item = createGenericRecord(frontend, startTime); + ScriptObject data = frontend->newScriptObject(); + data.set("url", url); + data.set("lineNumber", lineNumber); + item.set("data", data); + return item; +} + +} // namespace WebCore + +#endif // ENABLE(INSPECTOR) diff --git a/WebCore/inspector/TimelineRecordFactory.h b/WebCore/inspector/TimelineRecordFactory.h new file mode 100644 index 0000000..3d36649 --- /dev/null +++ b/WebCore/inspector/TimelineRecordFactory.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TimelineRecordFactory_h +#define TimelineRecordFactory_h + +#include "PlatformString.h" + +namespace WebCore { + + class Event; + class InspectorFrontend; + class ScriptObject; + + class TimelineRecordFactory { + public: + static ScriptObject createGenericRecord(InspectorFrontend*, double startTime); + + static ScriptObject createDOMDispatchRecord(InspectorFrontend*, double startTime, const Event&); + + static ScriptObject createGenericTimerRecord(InspectorFrontend*, double startTime, int timerId); + + static ScriptObject createTimerInstallRecord(InspectorFrontend*, double startTime, int timerId, int timeout, bool singleShot); + + static ScriptObject createXHRReadyStateChangeTimelineRecord(InspectorFrontend*, double startTime, const String& url, int readyState); + static ScriptObject createXHRLoadTimelineRecord(InspectorFrontend*, double startTime, const String& url); + + static ScriptObject createEvaluateScriptTagTimelineRecord(InspectorFrontend*, double startTime, const String&, double lineNumber); + + private: + TimelineRecordFactory() { } + }; + +} // namespace WebCore + +#endif // !defined(TimelineRecordFactory_h) diff --git a/WebCore/inspector/front-end/AbstractTimelinePanel.js b/WebCore/inspector/front-end/AbstractTimelinePanel.js new file mode 100644 index 0000000..75e4062 --- /dev/null +++ b/WebCore/inspector/front-end/AbstractTimelinePanel.js @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org> + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.AbstractTimelinePanel = function() +{ + WebInspector.Panel.call(this); + this._items = []; + this._staleItems = []; +} + +WebInspector.AbstractTimelinePanel.prototype = { + get categories() + { + // Should be implemented by the concrete subclasses. + return {}; + }, + + populateSidebar: function() + { + // Should be implemented by the concrete subclasses. + }, + + createItemTreeElement: function(item) + { + // Should be implemented by the concrete subclasses. + }, + + createItemGraph: function(item) + { + // Should be implemented by the concrete subclasses. + }, + + createInterface: function() + { + this._createFilterPanel(); + + this.containerElement = document.createElement("div"); + this.containerElement.id = "resources-container"; + this.containerElement.addEventListener("scroll", this._updateDividersLabelBarPosition.bind(this), false); + this.element.appendChild(this.containerElement); + + this.createSidebar(this.containerElement, this.element); + this.sidebarElement.id = "resources-sidebar"; + this.populateSidebar(); + + this._createGraph(); + }, + + _createFilterPanel: function() + { + this.filterBarElement = document.createElement("div"); + this.filterBarElement.id = "resources-filter"; + this.filterBarElement.className = "scope-bar"; + this.element.appendChild(this.filterBarElement); + + function createFilterElement(category) + { + if (category === "all") + var label = WebInspector.UIString("All"); + else if (this.categories[category]) + var label = this.categories[category].title; + + var categoryElement = document.createElement("li"); + categoryElement.category = category; + categoryElement.addStyleClass(category); + categoryElement.appendChild(document.createTextNode(label)); + categoryElement.addEventListener("click", this._updateFilter.bind(this), false); + this.filterBarElement.appendChild(categoryElement); + + return categoryElement; + } + + this.filterAllElement = createFilterElement.call(this, "all"); + + // Add a divider + var dividerElement = document.createElement("div"); + dividerElement.addStyleClass("divider"); + this.filterBarElement.appendChild(dividerElement); + + for (var category in this.categories) + createFilterElement.call(this, category); + }, + + _showCategory: function(category) + { + var filterClass = "filter-" + category.toLowerCase(); + this.itemsGraphsElement.addStyleClass(filterClass); + this.itemsTreeElement.childrenListElement.addStyleClass(filterClass); + }, + + _hideCategory: function(category) + { + var filterClass = "filter-" + category.toLowerCase(); + this.itemsGraphsElement.removeStyleClass(filterClass); + this.itemsTreeElement.childrenListElement.removeStyleClass(filterClass); + }, + + filter: function(target, selectMultiple) + { + function unselectAll() + { + for (var i = 0; i < this.filterBarElement.childNodes.length; ++i) { + var child = this.filterBarElement.childNodes[i]; + if (!child.category) + continue; + + child.removeStyleClass("selected"); + this._hideCategory(child.category); + } + } + + if (target === this.filterAllElement) { + if (target.hasStyleClass("selected")) { + // We can't unselect All, so we break early here + return; + } + + // If All wasn't selected, and now is, unselect everything else. + unselectAll.call(this); + } else { + // Something other than All is being selected, so we want to unselect All. + if (this.filterAllElement.hasStyleClass("selected")) { + this.filterAllElement.removeStyleClass("selected"); + this._hideCategory("all"); + } + } + + if (!selectMultiple) { + // If multiple selection is off, we want to unselect everything else + // and just select ourselves. + unselectAll.call(this); + + target.addStyleClass("selected"); + this._showCategory(target.category); + return; + } + + if (target.hasStyleClass("selected")) { + // If selectMultiple is turned on, and we were selected, we just + // want to unselect ourselves. + target.removeStyleClass("selected"); + this._hideCategory(target.category); + } else { + // If selectMultiple is turned on, and we weren't selected, we just + // want to select ourselves. + target.addStyleClass("selected"); + this._showCategory(target.category); + } + }, + + _updateFilter: function(e) + { + var isMac = InspectorController.platform().indexOf("mac-") === 0; + var selectMultiple = false; + if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey) + selectMultiple = true; + if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey) + selectMultiple = true; + + this.filter(e.target, selectMultiple); + + // When we are updating our filtering, scroll to the top so we don't end up + // in blank graph under all the resources. + this.containerElement.scrollTop = 0; + }, + + _createGraph: function() + { + this._containerContentElement = document.createElement("div"); + this._containerContentElement.id = "resources-container-content"; + this.containerElement.appendChild(this._containerContentElement); + + this.summaryBar = new WebInspector.SummaryBar(this.categories); + this.summaryBar.element.id = "resources-summary"; + this._containerContentElement.appendChild(this.summaryBar.element); + + this.itemsGraphsElement = document.createElement("div"); + this.itemsGraphsElement.id = "resources-graphs"; + this._containerContentElement.appendChild(this.itemsGraphsElement); + + this.dividersElement = document.createElement("div"); + this.dividersElement.id = "resources-dividers"; + this._containerContentElement.appendChild(this.dividersElement); + + this.eventDividersElement = document.createElement("div"); + this.eventDividersElement.id = "resources-event-dividers"; + this._containerContentElement.appendChild(this.eventDividersElement); + + this.dividersLabelBarElement = document.createElement("div"); + this.dividersLabelBarElement.id = "resources-dividers-label-bar"; + this._containerContentElement.appendChild(this.dividersLabelBarElement); + }, + + updateGraphDividersIfNeeded: function(force) + { + if (!this.visible) { + this.needsRefresh = true; + return false; + } + + if (document.body.offsetWidth <= 0) { + // The stylesheet hasn't loaded yet or the window is closed, + // so we can't calculate what is need. Return early. + return false; + } + + var dividerCount = Math.round(this.dividersElement.offsetWidth / 64); + var slice = this.calculator.boundarySpan / dividerCount; + if (!force && this._currentDividerSlice === slice) + return false; + + this._currentDividerSlice = slice; + + this.dividersElement.removeChildren(); + this.eventDividersElement.removeChildren(); + this.dividersLabelBarElement.removeChildren(); + + for (var i = 1; i <= dividerCount; ++i) { + var divider = document.createElement("div"); + divider.className = "resources-divider"; + if (i === dividerCount) + divider.addStyleClass("last"); + divider.style.left = ((i / dividerCount) * 100) + "%"; + + this.dividersElement.appendChild(divider.cloneNode()); + + var label = document.createElement("div"); + label.className = "resources-divider-label"; + if (!isNaN(slice)) + label.textContent = this.calculator.formatValue(slice * i); + divider.appendChild(label); + + this.dividersLabelBarElement.appendChild(divider); + } + }, + + _updateDividersLabelBarPosition: function() + { + var scrollTop = this.containerElement.scrollTop; + var dividersTop = (scrollTop < this.summaryBar.element.offsetHeight ? this.summaryBar.element.offsetHeight : scrollTop); + this.dividersElement.style.top = scrollTop + "px"; + this.eventDividersElement.style.top = scrollTop + "px"; + this.dividersLabelBarElement.style.top = dividersTop + "px"; + }, + + get needsRefresh() + { + return this._needsRefresh; + }, + + set needsRefresh(x) + { + if (this._needsRefresh === x) + return; + + this._needsRefresh = x; + + if (x) { + if (this.visible && !("_refreshTimeout" in this)) + this._refreshTimeout = setTimeout(this.refresh.bind(this), 500); + } else { + if ("_refreshTimeout" in this) { + clearTimeout(this._refreshTimeout); + delete this._refreshTimeout; + } + } + }, + + refreshIfNeeded: function() + { + if (this.needsRefresh) + this.refresh(); + }, + + show: function() + { + WebInspector.Panel.prototype.show.call(this); + + this._updateDividersLabelBarPosition(); + this.refreshIfNeeded(); + }, + + resize: function() + { + this.updateGraphDividersIfNeeded(); + }, + + updateMainViewWidth: function(width) + { + this._containerContentElement.style.left = width + "px"; + this.updateGraphDividersIfNeeded(); + }, + + refresh: function() + { + this.needsRefresh = false; + + var staleItemsLength = this._staleItems.length; + var boundariesChanged = false; + + for (var i = 0; i < staleItemsLength; ++i) { + var item = this._staleItems[i]; + if (!item._itemTreeElement) { + // Create the timeline tree element and graph. + item._itemTreeElement = this.createItemTreeElement(item); + item._itemTreeElement._itemGraph = this.createItemGraph(item); + + this.itemsTreeElement.appendChild(item._itemTreeElement); + this.itemsGraphsElement.appendChild(item._itemTreeElement._itemGraph.graphElement); + } + + if (item._itemTreeElement.refresh) + item._itemTreeElement.refresh(); + + if (this.calculator.updateBoundaries(item)) + boundariesChanged = true; + } + + if (boundariesChanged) { + // The boundaries changed, so all item graphs are stale. + this._staleItems = this._items; + staleItemsLength = this._staleItems.length; + } + + for (var i = 0; i < staleItemsLength; ++i) + this._staleItems[i]._itemTreeElement._itemGraph.refresh(this.calculator); + + this._staleItems = []; + + this.updateGraphDividersIfNeeded(); + }, + + reset: function() + { + this.containerElement.scrollTop = 0; + + if (this._calculator) + this._calculator.reset(); + + if (this._items) { + var itemsLength = this._items.length; + for (var i = 0; i < itemsLength; ++i) { + var item = this._items[i]; + delete item._itemsTreeElement; + } + } + + this._items = []; + this._staleItems = []; + + this.itemsTreeElement.removeChildren(); + this.itemsGraphsElement.removeChildren(); + + this.updateGraphDividersIfNeeded(true); + }, + + get calculator() + { + return this._calculator; + }, + + set calculator(x) + { + if (!x || this._calculator === x) + return; + + this._calculator = x; + this._calculator.reset(); + + this._staleItems = this._items; + this.refresh(); + }, + + addItem: function(item) + { + this._items.push(item); + this.refreshItem(item); + }, + + removeItem: function(item) + { + this._items.remove(item, true); + + if (item._itemTreeElement) { + this.itemsTreeElement.removeChild(resource._itemTreeElement); + this.itemsGraphsElement.removeChild(resource._itemTreeElement._itemGraph.graphElement); + } + + delete item._itemTreeElement; + this.adjustScrollPosition(); + }, + + refreshItem: function(item) + { + this._staleItems.push(item); + this.needsRefresh = true; + }, + + revealAndSelectItem: function(item) + { + if (item._itemsTreeElement) { + item._itemsTreeElement.reveal(); + item._itemsTreeElement.select(true); + } + }, + + sortItems: function(sortingFunction) + { + var sortedElements = [].concat(this.itemsTreeElement.children); + sortedElements.sort(sortingFunction); + + var sortedElementsLength = sortedElements.length; + for (var i = 0; i < sortedElementsLength; ++i) { + var treeElement = sortedElements[i]; + if (treeElement === this.itemsTreeElement.children[i]) + continue; + + var wasSelected = treeElement.selected; + this.itemsTreeElement.removeChild(treeElement); + this.itemsTreeElement.insertChild(treeElement, i); + if (wasSelected) + treeElement.select(true); + + var graphElement = treeElement._itemGraph.graphElement; + this.itemsGraphsElement.insertBefore(graphElement, this.itemsGraphsElement.children[i]); + } + }, + + adjustScrollPosition: function() + { + // Prevent the container from being scrolled off the end. + if ((this.containerElement.scrollTop + this.containerElement.offsetHeight) > this.sidebarElement.offsetHeight) + this.containerElement.scrollTop = (this.sidebarElement.offsetHeight - this.containerElement.offsetHeight); + } +} + +WebInspector.AbstractTimelinePanel.prototype.__proto__ = WebInspector.Panel.prototype; + +WebInspector.AbstractTimelineCalculator = function() +{ +} + +WebInspector.AbstractTimelineCalculator.prototype = { + computeSummaryValues: function(items) + { + var total = 0; + var categoryValues = {}; + + var itemsLength = items.length; + for (var i = 0; i < itemsLength; ++i) { + var item = items[i]; + var value = this._value(item); + if (typeof value === "undefined") + continue; + if (!(item.category.name in categoryValues)) + categoryValues[item.category.name] = 0; + categoryValues[item.category.name] += value; + total += value; + } + + return {categoryValues: categoryValues, total: total}; + }, + + computeBarGraphPercentages: function(item) + { + return {start: 0, middle: 0, end: (this._value(item) / this.boundarySpan) * 100}; + }, + + computeBarGraphLabels: function(item) + { + const label = this.formatValue(this._value(item)); + return {left: label, right: label, tooltip: label}; + }, + + get boundarySpan() + { + return this.maximumBoundary - this.minimumBoundary; + }, + + updateBoundaries: function(item) + { + this.minimumBoundary = 0; + + var value = this._value(item); + if (typeof this.maximumBoundary === "undefined" || value > this.maximumBoundary) { + this.maximumBoundary = value; + return true; + } + return false; + }, + + reset: function() + { + delete this.minimumBoundary; + delete this.maximumBoundary; + }, + + _value: function(item) + { + return 0; + }, + + formatValue: function(value) + { + return value.toString(); + } +} + +WebInspector.AbstractTimelineCategory = function(name, title, color) +{ + this.name = name; + this.title = title; + this.color = color; +} + +WebInspector.AbstractTimelineCategory.prototype = { + toString: function() + { + return this.title; + } +} diff --git a/WebCore/inspector/front-end/BottomUpProfileDataGridTree.js b/WebCore/inspector/front-end/BottomUpProfileDataGridTree.js index 89b4ddc..41a8a3a 100644 --- a/WebCore/inspector/front-end/BottomUpProfileDataGridTree.js +++ b/WebCore/inspector/front-end/BottomUpProfileDataGridTree.js @@ -29,6 +29,110 @@ // each child still represent the root node. We have to be particularly careful of recursion with this mode // because a root node can represent itself AND an ancestor. +WebInspector.BottomUpProfileDataGridNode = function(/*ProfileView*/ profileView, /*ProfileNode*/ profileNode, /*BottomUpProfileDataGridTree*/ owningTree) +{ + // In bottom up mode, our parents are our children since we display an inverted tree. + // However, we don't want to show the very top parent since it is redundant. + var hasChildren = !!(profileNode.parent && profileNode.parent.parent); + + WebInspector.ProfileDataGridNode.call(this, profileView, profileNode, owningTree, hasChildren); + + this._remainingNodeInfos = []; +} + +WebInspector.BottomUpProfileDataGridNode.prototype = { + _takePropertiesFromProfileDataGridNode: function(/*ProfileDataGridNode*/ profileDataGridNode) + { + this._save(); + + this.selfTime = profileDataGridNode.selfTime; + this.totalTime = profileDataGridNode.totalTime; + this.numberOfCalls = profileDataGridNode.numberOfCalls; + }, + + // When focusing, we keep just the members of the callstack. + _keepOnlyChild: function(/*ProfileDataGridNode*/ child) + { + this._save(); + + this.removeChildren(); + this.appendChild(child); + }, + + _exclude: function(aCallUID) + { + if (this._remainingNodeInfos) + this._populate(); + + this._save(); + + var children = this.children; + var index = this.children.length; + + while (index--) + children[index]._exclude(aCallUID); + + var child = this.childrenByCallUID[aCallUID]; + + if (child) + this._merge(child, true); + }, + + _merge: function(/*ProfileDataGridNode*/ child, /*Boolean*/ shouldAbsorb) + { + this.selfTime -= child.selfTime; + + WebInspector.ProfileDataGridNode.prototype._merge.call(this, child, shouldAbsorb); + }, + + _sharedPopulate: function() + { + var remainingNodeInfos = this._remainingNodeInfos; + var count = remainingNodeInfos.length; + + for (var index = 0; index < count; ++index) { + var nodeInfo = remainingNodeInfos[index]; + var ancestor = nodeInfo.ancestor; + var focusNode = nodeInfo.focusNode; + var child = this.findChild(ancestor); + + // If we already have this child, then merge the data together. + if (child) { + var totalTimeAccountedFor = nodeInfo.totalTimeAccountedFor; + + child.selfTime += focusNode.selfTime; + child.numberOfCalls += focusNode.numberOfCalls; + + if (!totalTimeAccountedFor) + child.totalTime += focusNode.totalTime; + } else { + // If not, add it as a true ancestor. + // In heavy mode, we take our visual identity from ancestor node... + var child = new WebInspector.BottomUpProfileDataGridNode(this.profileView, ancestor, this.tree); + + if (ancestor !== focusNode) { + // but the actual statistics from the "root" node (bottom of the callstack). + child.selfTime = focusNode.selfTime; + child.totalTime = focusNode.totalTime; + child.numberOfCalls = focusNode.numberOfCalls; + } + + this.appendChild(child); + } + + var parent = ancestor.parent; + if (parent && parent.parent) { + nodeInfo.ancestor = parent; + child._remainingNodeInfos.push(nodeInfo); + } + } + + delete this._remainingNodeInfos; + } +} + +WebInspector.BottomUpProfileDataGridNode.prototype.__proto__ = WebInspector.ProfileDataGridNode.prototype; + WebInspector.BottomUpProfileDataGridTree = function(/*ProfileView*/ aProfileView, /*ProfileNode*/ aProfileNode) { WebInspector.ProfileDataGridTree.call(this, aProfileView, aProfileNode); @@ -139,114 +243,10 @@ WebInspector.BottomUpProfileDataGridTree.prototype = { if (this.lastComparator) this.sort(this.lastComparator, true); - } -} - -WebInspector.BottomUpProfileDataGridTree.prototype.__proto__ = WebInspector.ProfileDataGridTree.prototype; - -WebInspector.BottomUpProfileDataGridNode = function(/*ProfileView*/ profileView, /*ProfileNode*/ profileNode, /*BottomUpProfileDataGridTree*/ owningTree) -{ - // In bottom up mode, our parents are our children since we display an inverted tree. - // However, we don't want to show the very top parent since it is redundant. - var hasChildren = !!(profileNode.parent && profileNode.parent.parent); - - WebInspector.ProfileDataGridNode.call(this, profileView, profileNode, owningTree, hasChildren); - - this._remainingNodeInfos = []; -} - -WebInspector.BottomUpProfileDataGridNode.prototype = { - _takePropertiesFromProfileDataGridNode: function(/*ProfileDataGridNode*/ profileDataGridNode) - { - this._save(); - - this.selfTime = profileDataGridNode.selfTime; - this.totalTime = profileDataGridNode.totalTime; - this.numberOfCalls = profileDataGridNode.numberOfCalls; - }, - - // When focusing, we keep just the members of the callstack. - _keepOnlyChild: function(/*ProfileDataGridNode*/ child) - { - this._save(); - - this.removeChildren(); - this.appendChild(child); - }, - - _exclude: function(aCallUID) - { - if (this._remainingNodeInfos) - this._populate(); - - this._save(); - - var children = this.children; - var index = this.children.length; - - while (index--) - children[index]._exclude(aCallUID); - - var child = this.childrenByCallUID[aCallUID]; - - if (child) - this._merge(child, true); - }, - - _merge: function(/*ProfileDataGridNode*/ child, /*Boolean*/ shouldAbsorb) - { - this.selfTime -= child.selfTime; - - WebInspector.ProfileDataGridNode.prototype._merge.call(this, child, shouldAbsorb); }, - _populate: function(event) - { - var remainingNodeInfos = this._remainingNodeInfos; - var count = remainingNodeInfos.length; - - for (var index = 0; index < count; ++index) { - var nodeInfo = remainingNodeInfos[index]; - var ancestor = nodeInfo.ancestor; - var focusNode = nodeInfo.focusNode; - var child = this.findChild(ancestor); - - // If we already have this child, then merge the data together. - if (child) { - var totalTimeAccountedFor = nodeInfo.totalTimeAccountedFor; - - child.selfTime += focusNode.selfTime; - child.numberOfCalls += focusNode.numberOfCalls; - - if (!totalTimeAccountedFor) - child.totalTime += focusNode.totalTime; - } else { - // If not, add it as a true ancestor. - // In heavy mode, we take our visual identity from ancestor node... - var child = new WebInspector.BottomUpProfileDataGridNode(this.profileView, ancestor, this.tree); - - if (ancestor !== focusNode) { - // but the actual statistics from the "root" node (bottom of the callstack). - child.selfTime = focusNode.selfTime; - child.totalTime = focusNode.totalTime; - child.numberOfCalls = focusNode.numberOfCalls; - } - - this.appendChild(child); - } - - var parent = ancestor.parent; - if (parent && parent.parent) { - nodeInfo.ancestor = parent; - child._remainingNodeInfos.push(nodeInfo); - } - } - - delete this._remainingNodeInfos; - - if (this.removeEventListener) - this.removeEventListener("populate", this._populate, this); - } + _sharedPopulate: WebInspector.BottomUpProfileDataGridNode.prototype._sharedPopulate } -WebInspector.BottomUpProfileDataGridNode.prototype.__proto__ = WebInspector.ProfileDataGridNode.prototype; +WebInspector.BottomUpProfileDataGridTree.prototype.__proto__ = WebInspector.ProfileDataGridTree.prototype; + diff --git a/WebCore/inspector/front-end/ConsoleView.js b/WebCore/inspector/front-end/ConsoleView.js index 3782d32..9317824 100644 --- a/WebCore/inspector/front-end/ConsoleView.js +++ b/WebCore/inspector/front-end/ConsoleView.js @@ -27,6 +27,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +const ExpressionStopCharacters = " =:[({;,!+-*/&|^<>"; + WebInspector.ConsoleView = function(drawer) { WebInspector.View.call(this, document.getElementById("console-view")); @@ -44,7 +46,7 @@ WebInspector.ConsoleView = function(drawer) this.promptElement = document.getElementById("console-prompt"); this.promptElement.handleKeyEvent = this._promptKeyDown.bind(this); - this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), " .=:[({;"); + this.prompt = new WebInspector.TextPrompt(this.promptElement, this.completions.bind(this), ExpressionStopCharacters + "."); this.topGroup = new WebInspector.ConsoleGroup(null, 0); this.messagesElement.insertBefore(this.topGroup.element, this.promptElement); @@ -92,49 +94,76 @@ WebInspector.ConsoleView = function(drawer) this.warningElement = createFilterElement.call(this, "Warnings"); this.logElement = createFilterElement.call(this, "Logs"); - this.filter(this.allElement); + this.filter(this.allElement, false); } WebInspector.ConsoleView.prototype = { _updateFilter: function(e) { - this.filter(e.target); + var isMac = InspectorController.platform().indexOf("mac-") === 0; + var selectMultiple = false; + if (isMac && e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey) + selectMultiple = true; + if (!isMac && e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey) + selectMultiple = true; + + this.filter(e.target, selectMultiple); }, - filter: function(target) + filter: function(target, selectMultiple) { + function unselectAll() + { + this.allElement.removeStyleClass("selected"); + this.errorElement.removeStyleClass("selected"); + this.warningElement.removeStyleClass("selected"); + this.logElement.removeStyleClass("selected"); + + this.messagesElement.removeStyleClass("filter-all"); + this.messagesElement.removeStyleClass("filter-errors"); + this.messagesElement.removeStyleClass("filter-warnings"); + this.messagesElement.removeStyleClass("filter-logs"); + } + + var targetFilterClass = "filter-" + target.category.toLowerCase(); + if (target.category == "All") { if (target.hasStyleClass("selected")) { // We can't unselect all, so we break early here return; } - - this.errorElement.removeStyleClass("selected"); - this.warningElement.removeStyleClass("selected"); - this.logElement.removeStyleClass("selected"); - - document.getElementById("console-messages").removeStyleClass("filter-errors"); - document.getElementById("console-messages").removeStyleClass("filter-warnings"); - document.getElementById("console-messages").removeStyleClass("filter-logs"); + + unselectAll.call(this); } else { // Something other than all is being selected, so we want to unselect all if (this.allElement.hasStyleClass("selected")) { this.allElement.removeStyleClass("selected"); - document.getElementById("console-messages").removeStyleClass("filter-all"); + this.messagesElement.removeStyleClass("filter-all"); } } + if (!selectMultiple) { + // If multiple selection is off, we want to unselect everything else + // and just select ourselves. + unselectAll.call(this); + + target.addStyleClass("selected"); + this.messagesElement.addStyleClass(targetFilterClass); + + return; + } + if (target.hasStyleClass("selected")) { + // If selectMultiple is turned on, and we were selected, we just + // want to unselect ourselves. target.removeStyleClass("selected"); - var newClass = "filter-" + target.category.toLowerCase(); - var filterElement = document.getElementById("console-messages"); - filterElement.removeStyleClass(newClass); + this.messagesElement.removeStyleClass(targetFilterClass); } else { + // If selectMultiple is turned on, and we weren't selected, we just + // want to select ourselves. target.addStyleClass("selected"); - var newClass = "filter-" + target.category.toLowerCase(); - var filterElement = document.getElementById("console-messages"); - filterElement.addStyleClass(newClass); + this.messagesElement.addStyleClass(targetFilterClass); } }, @@ -172,40 +201,7 @@ WebInspector.ConsoleView.prototype = { addMessage: function(msg) { if (msg instanceof WebInspector.ConsoleMessage && !(msg instanceof WebInspector.ConsoleCommandResult)) { - msg.totalRepeatCount = msg.repeatCount; - msg.repeatDelta = msg.repeatCount; - - var messageRepeated = false; - - if (msg.isEqual && msg.isEqual(this.previousMessage)) { - // Because sometimes we get a large number of repeated messages and sometimes - // we get them one at a time, we need to know the difference between how many - // repeats we used to have and how many we have now. - msg.repeatDelta -= this.previousMessage.totalRepeatCount; - - if (!isNaN(this.repeatCountBeforeCommand)) - msg.repeatCount -= this.repeatCountBeforeCommand; - - if (!this.commandSincePreviousMessage) { - // Recreate the previous message element to reset the repeat count. - var messagesElement = this.currentGroup.messagesElement; - messagesElement.removeChild(messagesElement.lastChild); - messagesElement.appendChild(msg.toMessageElement()); - - messageRepeated = true; - } - } else - delete this.repeatCountBeforeCommand; - - // Increment the error or warning count - switch (msg.level) { - case WebInspector.ConsoleMessage.MessageLevel.Warning: - WebInspector.warnings += msg.repeatDelta; - break; - case WebInspector.ConsoleMessage.MessageLevel.Error: - WebInspector.errors += msg.repeatDelta; - break; - } + this._incrementErrorWarningCount(msg); // Add message to the resource panel if (msg.url in WebInspector.resourceURLMap) { @@ -216,13 +212,9 @@ WebInspector.ConsoleView.prototype = { this.commandSincePreviousMessage = false; this.previousMessage = msg; - - if (messageRepeated) - return; } else if (msg instanceof WebInspector.ConsoleCommand) { if (this.previousMessage) { this.commandSincePreviousMessage = true; - this.repeatCountBeforeCommand = this.previousMessage.totalRepeatCount; } } @@ -250,6 +242,35 @@ WebInspector.ConsoleView.prototype = { this.promptElement.scrollIntoView(false); }, + updateMessageRepeatCount: function(count) { + var msg = this.previousMessage; + var prevRepeatCount = msg.totalRepeatCount; + + if (!this.commandSincePreviousMessage) { + msg.repeatDelta = count - prevRepeatCount; + msg.repeatCount = msg.repeatCount + msg.repeatDelta; + msg.totalRepeatCount = count; + msg._updateRepeatCount(); + this._incrementErrorWarningCount(msg); + } else { + msgCopy = new WebInspector.ConsoleMessage(msg.source, msg.type, msg.level, msg.line, msg.url, msg.groupLevel, count - prevRepeatCount); + msgCopy.totalRepeatCount = count; + msgCopy.setMessageBody(msg.args); + this.addMessage(msgCopy); + } + }, + + _incrementErrorWarningCount: function(msg) { + switch (msg.level) { + case WebInspector.ConsoleMessage.MessageLevel.Warning: + WebInspector.warnings += msg.repeatDelta; + break; + case WebInspector.ConsoleMessage.MessageLevel.Error: + WebInspector.errors += msg.repeatDelta; + break; + } + }, + clearMessages: function(clearInspectorController) { if (clearInspectorController) @@ -267,15 +288,13 @@ WebInspector.ConsoleView.prototype = { WebInspector.warnings = 0; delete this.commandSincePreviousMessage; - delete this.repeatCountBeforeCommand; delete this.previousMessage; }, completions: function(wordRange, bestMatchOnly, completionsReadyCallback) { // Pass less stop characters to rangeOfWord so the range will be a more complete expression. - const expressionStopCharacters = " =:{;"; - var expressionRange = wordRange.startContainer.rangeOfWord(wordRange.startOffset, expressionStopCharacters, this.promptElement, "backward"); + var expressionRange = wordRange.startContainer.rangeOfWord(wordRange.startOffset, ExpressionStopCharacters, this.promptElement, "backward"); var expressionString = expressionRange.toString(); var lastIndex = expressionString.length - 1; @@ -292,16 +311,6 @@ WebInspector.ConsoleView.prototype = { var reportCompletions = this._reportCompletions.bind(this, bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix); // Collect comma separated object properties for the completion. - if (!expressionString) { - if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused) { - // Evaluate into properties in scope of the selected call frame. - reportCompletions(WebInspector.panels.scripts.variablesInSelectedCallFrame()); - return; - } else { - expressionString = "this"; - } - } - var includeInspectorCommandLineAPI = (!dotNotation && !bracketNotation); if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused) var callFrameId = WebInspector.panels.scripts.selectedCallFrameId(); @@ -343,7 +352,7 @@ WebInspector.ConsoleView.prototype = { if (bestMatchOnly) break; } - setTimeout(completionsReadyCallback, 0, results); + completionsReadyCallback(results); }, _clearButtonClicked: function() @@ -382,10 +391,9 @@ WebInspector.ConsoleView.prototype = { _promptKeyDown: function(event) { - switch (event.keyIdentifier) { - case "Enter": - this._enterKeyPressed(event); - return; + if (isEnterKey(event)) { + this._enterKeyPressed(event); + return; } this.prompt.handleKeyEvent(event); @@ -514,21 +522,31 @@ WebInspector.ConsoleView.prototype = { _formatarray: function(arr, elem) { - var self = this; - function printResult(properties) - { - if (!properties) - return; - elem.appendChild(document.createTextNode("[")); - for (var i = 0; i < properties.length; ++i) { - var property = properties[i].value; - elem.appendChild(self._format(property)); - if (i < properties.length - 1) - elem.appendChild(document.createTextNode(", ")); - } - elem.appendChild(document.createTextNode("]")); + InjectedScriptAccess.getProperties(arr, false, this._printArray.bind(this, elem)); + }, + + _printArray: function(elem, properties) + { + if (!properties) + return; + var elements = []; + for (var i = 0; i < properties.length; ++i) { + var name = properties[i].name; + if (name == parseInt(name)) + elements[name] = this._format(properties[i].value); } - InjectedScriptAccess.getProperties(arr, false, printResult); + + elem.appendChild(document.createTextNode("[")); + for (var i = 0; i < elements.length; ++i) { + var element = elements[i]; + if (element) + elem.appendChild(element); + else + elem.appendChild(document.createTextNode("undefined")) + if (i < elements.length - 1) + elem.appendChild(document.createTextNode(", ")); + } + elem.appendChild(document.createTextNode("]")); }, _formatnode: function(object, elem) @@ -538,6 +556,7 @@ WebInspector.ConsoleView.prototype = { if (!nodeId) return; var treeOutline = new WebInspector.ElementsTreeOutline(); + treeOutline.showInElementsPanelEnabled = true; treeOutline.rootDOMNode = WebInspector.domAgent.nodeForId(nodeId); treeOutline.element.addStyleClass("outline-disclosure"); if (!treeOutline.children[0].hasChildren) @@ -589,6 +608,8 @@ WebInspector.ConsoleMessage = function(source, type, level, line, url, groupLeve this.url = url; this.groupLevel = groupLevel; this.repeatCount = repeatCount; + this.repeatDelta = repeatCount; + this.totalRepeatCount = repeatCount; if (arguments.length > 7) this.setMessageBody(Array.prototype.slice.call(arguments, 7)); } @@ -596,6 +617,7 @@ WebInspector.ConsoleMessage = function(source, type, level, line, url, groupLeve WebInspector.ConsoleMessage.prototype = { setMessageBody: function(args) { + this.args = args; switch (this.type) { case WebInspector.ConsoleMessage.MessageType.Trace: var span = document.createElement("span"); @@ -691,13 +713,15 @@ WebInspector.ConsoleMessage.prototype = { toMessageElement: function() { - if (this.propertiesSection) - return this.propertiesSection.element; + if (this._element) + return this._element; var element = document.createElement("div"); element.message = this; element.className = "console-message"; + this._element = element; + switch (this.source) { case WebInspector.ConsoleMessage.MessageSource.HTML: element.addStyleClass("console-html-source"); @@ -737,9 +761,8 @@ WebInspector.ConsoleMessage.prototype = { break; } - if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup) { + if (this.type === WebInspector.ConsoleMessage.MessageType.StartGroup) element.addStyleClass("console-group-title"); - } if (this.elementsTreeOutline) { element.addStyleClass("outline-disclosure"); @@ -747,15 +770,6 @@ WebInspector.ConsoleMessage.prototype = { return element; } - if (this.repeatCount > 1) { - var messageRepeatCountElement = document.createElement("span"); - messageRepeatCountElement.className = "bubble"; - messageRepeatCountElement.textContent = this.repeatCount; - - element.appendChild(messageRepeatCountElement); - element.addStyleClass("repeated-message"); - } - if (this.url && this.url !== "undefined") { var urlElement = document.createElement("a"); urlElement.className = "console-message-url webkit-html-resource-link"; @@ -775,12 +789,28 @@ WebInspector.ConsoleMessage.prototype = { var messageTextElement = document.createElement("span"); messageTextElement.className = "console-message-text"; + if (this.type === WebInspector.ConsoleMessage.MessageType.Assert) + messageTextElement.appendChild(document.createTextNode(WebInspector.UIString("Assertion failed: "))); messageTextElement.appendChild(this.formattedMessage); element.appendChild(messageTextElement); + if (this.repeatCount > 1) + this._updateRepeatCount(); + return element; }, + _updateRepeatCount: function() { + if (!this.repeatCountElement) { + this.repeatCountElement = document.createElement("span"); + this.repeatCountElement.className = "bubble"; + + this._element.insertBefore(this.repeatCountElement, this._element.firstChild); + this._element.addStyleClass("repeated-message"); + } + this.repeatCountElement.textContent = this.repeatCount; + }, + toString: function() { var sourceString; @@ -822,6 +852,9 @@ WebInspector.ConsoleMessage.prototype = { case WebInspector.ConsoleMessage.MessageType.EndGroup: typeString = "End Group"; break; + case WebInspector.ConsoleMessage.MessageType.Assert: + typeString = "Assert"; + break; } var levelString; @@ -877,7 +910,8 @@ WebInspector.ConsoleMessage.MessageType = { Object: 1, Trace: 2, StartGroup: 3, - EndGroup: 4 + EndGroup: 4, + Assert: 5 } WebInspector.ConsoleMessage.MessageLevel = { diff --git a/WebCore/inspector/front-end/CookieItemsView.js b/WebCore/inspector/front-end/CookieItemsView.js index 9397e7c..9f9845c 100644 --- a/WebCore/inspector/front-end/CookieItemsView.js +++ b/WebCore/inspector/front-end/CookieItemsView.js @@ -252,7 +252,7 @@ WebInspector.CookieItemsView.prototype = { resize: function() { - if (this._datagrid) + if (this._dataGrid) this._dataGrid.updateWidths(); }, diff --git a/WebCore/inspector/front-end/DatabaseQueryView.js b/WebCore/inspector/front-end/DatabaseQueryView.js index 6c5fa02..e85af66 100644 --- a/WebCore/inspector/front-end/DatabaseQueryView.js +++ b/WebCore/inspector/front-end/DatabaseQueryView.js @@ -94,10 +94,9 @@ WebInspector.DatabaseQueryView.prototype = { _promptKeyDown: function(event) { - switch (event.keyIdentifier) { - case "Enter": - this._enterKeyPressed(event); - return; + if (isEnterKey(event)) { + this._enterKeyPressed(event); + return; } this.prompt.handleKeyEvent(event); diff --git a/WebCore/inspector/front-end/ElementsPanel.js b/WebCore/inspector/front-end/ElementsPanel.js index fd0874a..aa6319c 100644 --- a/WebCore/inspector/front-end/ElementsPanel.js +++ b/WebCore/inspector/front-end/ElementsPanel.js @@ -268,7 +268,12 @@ WebInspector.ElementsPanel.prototype = { if (!this._searchResults.length) { this._currentSearchResultIndex = 0; - this.focusedDOMNode = node; + + // Only change the focusedDOMNode if the search was manually performed, because + // the search may have been performed programmatically and we wouldn't want to + // change the current focusedDOMNode. + if (WebInspector.currentFocusElement === document.getElementById("search")) + this.focusedDOMNode = node; } this._searchResults.push(node); @@ -479,7 +484,7 @@ WebInspector.ElementsPanel.prototype = { updatedParentTreeElements.push(parentNodeItem); } - if (!updateBreadcrumbs && (this.focusedDOMNode === parent || isAncestor(this.focusedDOMNode, parent))) + if (!updateBreadcrumbs && (this.focusedDOMNode === parent || isAncestorNode(this.focusedDOMNode, parent))) updateBreadcrumbs = true; } diff --git a/WebCore/inspector/front-end/ElementsTreeOutline.js b/WebCore/inspector/front-end/ElementsTreeOutline.js index 9e13773..be01647 100644 --- a/WebCore/inspector/front-end/ElementsTreeOutline.js +++ b/WebCore/inspector/front-end/ElementsTreeOutline.js @@ -38,6 +38,7 @@ WebInspector.ElementsTreeOutline = function() { this.includeRootDOMNode = true; this.selectEnabled = false; + this.showInElementsPanelEnabled = false; this.rootDOMNode = null; this.focusedDOMNode = null; } @@ -184,6 +185,34 @@ WebInspector.ElementsTreeOutline.prototype = { return element; }, + + handleKeyEvent: function(event) + { + var selectedElement = this.selectedTreeElement; + if (!selectedElement) + return; + + // Delete or backspace pressed, delete the node. + if (event.keyCode === 8 || event.keyCode === 46) { + selectedElement.remove(); + return; + } + + // On Enter or Return start editing the first attribute + // or create a new attribute on the selected element. + if (event.keyIdentifier === "Enter") { + if (this._editing) + return; + + selectedElement._startEditing(); + + // prevent a newline from being immediately inserted + event.preventDefault(); + return; + } + + TreeOutline.prototype.handleKeyEvent.call(this, event); + }, _onmousedown: function(event) { @@ -197,12 +226,15 @@ WebInspector.ElementsTreeOutline.prototype = { _onmousemove: function(event) { + var element = this._treeElementFromEvent(event); + if (element && this._previousHoveredElement === element) + return; + if (this._previousHoveredElement) { this._previousHoveredElement.hovered = false; delete this._previousHoveredElement; } - var element = this._treeElementFromEvent(event); if (element && !element.elementCloseTag) { element.hovered = true; this._previousHoveredElement = element; @@ -276,38 +308,43 @@ WebInspector.ElementsTreeElement.prototype = { if (x) { this.updateSelection(); this.listItemElement.addStyleClass("hovered"); - } else + if (this._canAddAttributes) + this._pendingToggleNewAttribute = setTimeout(this.toggleNewAttributeButton.bind(this, true), 500); + } else { this.listItemElement.removeStyleClass("hovered"); - if (this._canAddAttributes) - this.toggleNewAttributeButton(); + if (this._pendingToggleNewAttribute) { + clearTimeout(this._pendingToggleNewAttribute); + delete this._pendingToggleNewAttribute; + } + this.toggleNewAttributeButton(false); + } } }, - toggleNewAttributeButton: function() + toggleNewAttributeButton: function(visible) { - function removeWhenEditing(event) + function removeAddAttributeSpan() { if (this._addAttributeElement && this._addAttributeElement.parentNode) this._addAttributeElement.parentNode.removeChild(this._addAttributeElement); delete this._addAttributeElement; + + this.updateSelection(); } - if (!this._addAttributeElement && this._hovered && !this._editing) { + if (!this._addAttributeElement && visible && !this._editing) { var span = document.createElement("span"); - span.className = "add-attribute"; - span.textContent = "\u2026"; - span.addEventListener("dblclick", removeWhenEditing.bind(this), false); + span.className = "add-attribute webkit-html-attribute-name"; + span.textContent = " ?=\"\""; + span.addEventListener("dblclick", removeAddAttributeSpan.bind(this), false); this._addAttributeElement = span; var tag = this.listItemElement.getElementsByClassName("webkit-html-tag")[0]; this._insertInLastAttributePosition(tag, span); - } else if (!this._hovered && this._addAttributeElement) { - if (this._addAttributeElement.parentNode) - this._addAttributeElement.parentNode.removeChild(this._addAttributeElement); - delete this._addAttributeElement; - } + } else if (!visible && this._addAttributeElement) + removeAddAttributeSpan.call(this); }, - + updateSelection: function() { var listItemElement = this.listItemElement; @@ -475,6 +512,14 @@ WebInspector.ElementsTreeElement.prototype = { if (this._editing) return; + if (this.isEventWithinDisclosureTriangle(event)) + return; + + if (this.treeOutline.showInElementsPanelEnabled) { + WebInspector.showElementsPanel(); + WebInspector.panels.elements.focusedDOMNode = this.representedObject; + } + // Prevent selecting the nearest word on double click. if (event.detail >= 2) event.preventDefault(); @@ -485,14 +530,9 @@ WebInspector.ElementsTreeElement.prototype = { if (this._editing) return; - if (this._startEditing(event, treeElement)) + if (this._startEditingFromEvent(event, treeElement)) return; - if (this.treeOutline.panel) { - this.treeOutline.rootDOMNode = this.representedObject.parentNode; - this.treeOutline.focusedDOMNode = this.representedObject; - } - if (this.hasChildren && !this.expanded) this.expand(); }, @@ -508,9 +548,11 @@ WebInspector.ElementsTreeElement.prototype = { tag.appendChild(node); tag.appendChild(document.createTextNode('>')); } + + this.updateSelection(); }, - _startEditing: function(event, treeElement) + _startEditingFromEvent: function(event, treeElement) { if (this.treeOutline.focusedDOMNode != this.representedObject) return; @@ -533,6 +575,30 @@ WebInspector.ElementsTreeElement.prototype = { return false; }, + _startEditing: function() + { + if (this.treeOutline.focusedDOMNode !== this.representedObject) + return; + + var listItem = this._listItemNode; + + if (this._canAddAttributes) { + this.toggleNewAttributeButton(false); + var attribute = listItem.getElementsByClassName("webkit-html-attribute")[0]; + if (attribute) + return this._startEditingAttribute(attribute, attribute.getElementsByClassName("webkit-html-attribute-value")[0]); + + return this._addNewAttribute(listItem); + } + + if (this.representedObject.nodeType === Node.TEXT_NODE) { + var textNode = listItem.getElementsByClassName("webkit-html-text-node")[0]; + if (textNode) + return this._startEditingTextNode(textNode); + return; + } + }, + _addNewAttribute: function(listItemElement) { var attr = document.createElement("span"); @@ -640,7 +706,7 @@ WebInspector.ElementsTreeElement.prototype = { } } - if (!found && moveDirection === "backward") + if (!found && moveDirection === "backward" && attributes.length > 0) moveToAttribute = attributes[attributes.length - 1].name; else if (!found && moveDirection === "forward" && !/^\s*$/.test(newText)) newAttribute = true; @@ -772,9 +838,9 @@ WebInspector.ElementsTreeElement.prototype = { if (node.parentNode && node.parentNode.nodeName.toLowerCase() == "script") { var newNode = document.createElement("span"); newNode.textContent = node.textContent; - + var javascriptSyntaxHighlighter = new WebInspector.JavaScriptSourceSyntaxHighlighter(null, null); - javascriptSyntaxHighlighter.syntaxHighlightLine(newNode, null); + javascriptSyntaxHighlighter.syntaxHighlightNode(newNode); info.title = "<span class=\"webkit-html-text-node webkit-html-js-node\">" + newNode.innerHTML.replace(/^[\n\r]*/, "").replace(/\s*$/, "") + "</span>"; } else if (node.parentNode && node.parentNode.nodeName.toLowerCase() == "style") { @@ -822,7 +888,30 @@ WebInspector.ElementsTreeElement.prototype = { return true; } return false; + }, + + remove: function() + { + var parentElement = this.parent; + if (!parentElement) + return; + + var self = this; + function removeNodeCallback(removedNodeId) + { + // -1 is an error code, which means removing the node from the DOM failed, + // so we shouldn't remove it from the tree. + if (removedNodeId === -1) + return; + + parentElement.removeChild(self); + } + + var callId = WebInspector.Callback.wrap(removeNodeCallback); + InspectorController.removeNode(callId, this.representedObject.id); } } WebInspector.ElementsTreeElement.prototype.__proto__ = TreeElement.prototype; + +WebInspector.didRemoveNode = WebInspector.Callback.processCallback; diff --git a/WebCore/inspector/front-end/EventListenersSidebarPane.js b/WebCore/inspector/front-end/EventListenersSidebarPane.js index 55b8e55..2938196 100644 --- a/WebCore/inspector/front-end/EventListenersSidebarPane.js +++ b/WebCore/inspector/front-end/EventListenersSidebarPane.js @@ -71,6 +71,8 @@ WebInspector.EventListenersSidebarPane.prototype = { var eventListener = eventListeners[i]; eventListener.node = WebInspector.domAgent.nodeForId(eventListener.nodeId); delete eventListener.nodeId; // no longer needed + if (/^function _inspectorCommandLineAPI_logEvent\(/.test(eventListener.listener.toString())) + continue; // ignore event listeners generated by monitorEvent var type = eventListener.type; var section = sectionMap[type]; if (!section) { @@ -201,18 +203,13 @@ WebInspector.EventListenerBar.prototype = { if (node.nodeType === Node.DOCUMENT_NODE) return "document"; - + return appropriateSelectorForNode(node); }, _getFunctionDisplayName: function() { - // TODO: v8 does not yet provide the raw function, this handles such a case with a placeholder - // I didn't make this a UIString because it should be implemented eventually. - if (!this.eventListener.listener) - return "(listener)"; - - // Requires that Function.toString() return at least the function's signature + // Requires that Function.toString() return at least the function's signature. var match = this.eventListener.listener.toString().match(/function ([^\(]+?)\(/); return (match ? match[1] : WebInspector.UIString("(anonymous function)")); } diff --git a/WebCore/inspector/front-end/Images/timelineBarBlue.png b/WebCore/inspector/front-end/Images/timelineBarBlue.png Binary files differnew file mode 100644 index 0000000..22641b5 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineBarBlue.png diff --git a/WebCore/inspector/front-end/Images/timelineBarGray.png b/WebCore/inspector/front-end/Images/timelineBarGray.png Binary files differnew file mode 100644 index 0000000..f66cf43 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineBarGray.png diff --git a/WebCore/inspector/front-end/Images/timelineBarGreen.png b/WebCore/inspector/front-end/Images/timelineBarGreen.png Binary files differnew file mode 100644 index 0000000..cc59082 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineBarGreen.png diff --git a/WebCore/inspector/front-end/Images/timelineBarOrange.png b/WebCore/inspector/front-end/Images/timelineBarOrange.png Binary files differnew file mode 100644 index 0000000..e5ae6f5 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineBarOrange.png diff --git a/WebCore/inspector/front-end/Images/timelineBarPurple.png b/WebCore/inspector/front-end/Images/timelineBarPurple.png Binary files differnew file mode 100644 index 0000000..f891252 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineBarPurple.png diff --git a/WebCore/inspector/front-end/Images/timelineBarRed.png b/WebCore/inspector/front-end/Images/timelineBarRed.png Binary files differnew file mode 100644 index 0000000..b850037 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineBarRed.png diff --git a/WebCore/inspector/front-end/Images/timelineBarYellow.png b/WebCore/inspector/front-end/Images/timelineBarYellow.png Binary files differnew file mode 100644 index 0000000..2b3e9a7 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineBarYellow.png diff --git a/WebCore/inspector/front-end/Images/timelineCheckmarks.png b/WebCore/inspector/front-end/Images/timelineCheckmarks.png Binary files differnew file mode 100644 index 0000000..9afa9bb --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineCheckmarks.png diff --git a/WebCore/inspector/front-end/Images/timelineDots.png b/WebCore/inspector/front-end/Images/timelineDots.png Binary files differnew file mode 100644 index 0000000..e9ba4d3 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineDots.png diff --git a/WebCore/inspector/front-end/Images/timelineIcon.png b/WebCore/inspector/front-end/Images/timelineIcon.png Binary files differnew file mode 100644 index 0000000..09bcf30 --- /dev/null +++ b/WebCore/inspector/front-end/Images/timelineIcon.png diff --git a/WebCore/inspector/front-end/InjectedScript.js b/WebCore/inspector/front-end/InjectedScript.js index 75c8ced..3bed8da 100644 --- a/WebCore/inspector/front-end/InjectedScript.js +++ b/WebCore/inspector/front-end/InjectedScript.js @@ -517,14 +517,30 @@ InjectedScript.getCompletions = function(expression, includeInspectorCommandLine var callFrame = InjectedScript._callFrameForId(callFrameId); if (!callFrame) return props; - expressionResult = InjectedScript._evaluateOn(callFrame.evaluate, callFrame, expression); + if (expression) + expressionResult = InjectedScript._evaluateOn(callFrame.evaluate, callFrame, expression); + else { + // Evaluate into properties in scope of the selected call frame. + var scopeChain = callFrame.scopeChain; + for (var i = 0; i < scopeChain.length; ++i) { + var scopeObject = scopeChain[i]; + try { + for (var propertyName in scopeObject) + props[propertyName] = true; + } catch (e) { + } + } + } } else { + if (!expression) + expression = "this"; expressionResult = InjectedScript._evaluateOn(InjectedScript._window().eval, InjectedScript._window(), expression); } - for (var prop in expressionResult) - props[prop] = true; + if (expressionResult) + for (var prop in expressionResult) + props[prop] = true; if (includeInspectorCommandLineAPI) - for (var prop in InjectedScript._window()._inspectorCommandLineAPI) + for (var prop in InjectedScript._window().console._inspectorCommandLineAPI) if (prop.charAt(0) !== '_') props[prop] = true; } catch(e) { @@ -559,7 +575,7 @@ InjectedScript._evaluateOn = function(evalFunction, object, expression) InjectedScript._ensureCommandLineAPIInstalled(evalFunction, object); // Surround the expression in with statements to inject our command line API so that // the window object properties still take more precedent than our API functions. - expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }"; + expression = "with (window.console._inspectorCommandLineAPI) { with (window) { " + expression + " } }"; var value = evalFunction.call(object, expression); // When evaluating on call frame error is not thrown, but returned as a value. @@ -576,7 +592,7 @@ InjectedScript.addInspectedNode = function(nodeId) return false; InjectedScript._ensureCommandLineAPIInstalled(InjectedScript._window().eval, InjectedScript._window()); - var inspectedNodes = InjectedScript._window()._inspectorCommandLineAPI._inspectedNodes; + var inspectedNodes = InjectedScript._window().console._inspectorCommandLineAPI._inspectedNodes; inspectedNodes.unshift(node); if (inspectedNodes.length >= 5) inspectedNodes.pop(); @@ -885,9 +901,9 @@ InjectedScript._inspectObject = function(o) InjectedScript._ensureCommandLineAPIInstalled = function(evalFunction, evalObject) { - if (evalFunction.call(evalObject, "window._inspectorCommandLineAPI")) + if (evalFunction.call(evalObject, "window.console._inspectorCommandLineAPI")) return; - var inspectorCommandLineAPI = evalFunction.call(evalObject, "window._inspectorCommandLineAPI = { \ + var inspectorCommandLineAPI = evalFunction.call(evalObject, "window.console._inspectorCommandLineAPI = { \ $: function() { return document.getElementById.apply(document, arguments) }, \ $$: function() { return document.querySelectorAll.apply(document, arguments) }, \ $x: function(xpath, context) { \ @@ -906,12 +922,50 @@ InjectedScript._ensureCommandLineAPIInstalled = function(evalFunction, evalObjec values: function(o) { var a = []; for (var k in o) a.push(o[k]); return a; }, \ profile: function() { return console.profile.apply(console, arguments) }, \ profileEnd: function() { return console.profileEnd.apply(console, arguments) }, \ + _logEvent: function _inspectorCommandLineAPI_logEvent(e) { console.log(e.type, e); }, \ + _allEventTypes: [\"mouse\", \"key\", \"load\", \"unload\", \"abort\", \"error\", \ + \"select\", \"change\", \"submit\", \"reset\", \"focus\", \"blur\", \ + \"resize\", \"scroll\"], \ + _normalizeEventTypes: function(t) { \ + if (typeof t === \"undefined\") \ + t = _inspectorCommandLineAPI._allEventTypes; \ + else if (typeof t === \"string\") \ + t = [t]; \ + var i, te = []; \ + for (i = 0; i < t.length; i++) { \ + if (t[i] === \"mouse\") \ + te.splice(0, 0, \"mousedown\", \"mouseup\", \"click\", \"dblclick\", \ + \"mousemove\", \"mouseover\", \"mouseout\"); \ + else if (t[i] === \"key\") \ + te.splice(0, 0, \"keydown\", \"keyup\", \"keypress\"); \ + else \ + te.push(t[i]); \ + } \ + return te; \ + }, \ + monitorEvent: function(o, t) { \ + if (!o || !o.addEventListener || !o.removeEventListener) \ + return; \ + t = _inspectorCommandLineAPI._normalizeEventTypes(t); \ + for (i = 0; i < t.length; i++) { \ + o.removeEventListener(t[i], _inspectorCommandLineAPI._logEvent, false); \ + o.addEventListener(t[i], _inspectorCommandLineAPI._logEvent, false); \ + } \ + }, \ + unmonitorEvent: function(o, t) { \ + if (!o || !o.removeEventListener) \ + return; \ + t = _inspectorCommandLineAPI._normalizeEventTypes(t); \ + for (i = 0; i < t.length; i++) { \ + o.removeEventListener(t[i], _inspectorCommandLineAPI._logEvent, false); \ + } \ + }, \ _inspectedNodes: [], \ - get $0() { return _inspectorCommandLineAPI._inspectedNodes[0] }, \ - get $1() { return _inspectorCommandLineAPI._inspectedNodes[1] }, \ - get $2() { return _inspectorCommandLineAPI._inspectedNodes[2] }, \ - get $3() { return _inspectorCommandLineAPI._inspectedNodes[3] }, \ - get $4() { return _inspectorCommandLineAPI._inspectedNodes[4] } \ + get $0() { return console._inspectorCommandLineAPI._inspectedNodes[0] }, \ + get $1() { return console._inspectorCommandLineAPI._inspectedNodes[1] }, \ + get $2() { return console._inspectorCommandLineAPI._inspectedNodes[2] }, \ + get $3() { return console._inspectorCommandLineAPI._inspectedNodes[3] }, \ + get $4() { return console._inspectorCommandLineAPI._inspectedNodes[4] } \ };"); inspectorCommandLineAPI.clear = InspectorController.wrapCallback(InjectedScript._clearConsoleMessages); @@ -1032,12 +1086,6 @@ InjectedScript.CallFrameProxy.prototype = { scopeObjectProxy.isDocument = true; else if (!foundLocalScope) scopeObjectProxy.isWithBlock = true; - scopeObjectProxy.properties = []; - try { - for (var propertyName in scopeObject) - scopeObjectProxy.properties.push(propertyName); - } catch (e) { - } scopeChainProxy.push(scopeObjectProxy); } return scopeChainProxy; @@ -1110,6 +1158,8 @@ Object.type = function(obj) return "regexp"; if (obj instanceof win.NodeList) return "array"; + if (obj instanceof win.HTMLCollection || obj instanceof win.HTMLAllCollection) + return "array"; if (obj instanceof win.Error) return "error"; return type; @@ -1132,9 +1182,8 @@ Object.describe = function(obj, abbreviated) switch (type1) { case "object": case "node": - return type2; case "array": - return "[" + obj.toString() + "]"; + return type2; case "string": if (!abbreviated) return obj; diff --git a/WebCore/inspector/front-end/InspectorControllerStub.js b/WebCore/inspector/front-end/InspectorControllerStub.js new file mode 100644 index 0000000..6fb5a1b --- /dev/null +++ b/WebCore/inspector/front-end/InspectorControllerStub.js @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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. + */ + +if (!window.InspectorController) { + +WebInspector.InspectorControllerStub = function() +{ + this._searchingForNode = false; + this._windowVisible = true; + this._attachedWindowHeight = 0; + this._debuggerEnabled = true; + this._profilerEnabled = true; + this._resourceTrackingEnabled = false; + this._timelineEnabled = false; + this._settings = {}; +} + +WebInspector.InspectorControllerStub.prototype = { + wrapCallback: function(func) + { + return func; + }, + + isWindowVisible: function() + { + return this._windowVisible; + }, + + platform: function() + { + return "mac-leopard"; + }, + + + closeWindow: function() + { + this._windowVisible = false; + }, + + attach: function() + { + }, + + detach: function() + { + }, + + storeLastActivePanel: function(panel) + { + }, + + clearMessages: function() + { + }, + + searchingForNode: function() + { + return this._searchingForNode; + }, + + search: function(sourceRow, query) + { + }, + + toggleNodeSearch: function() + { + this._searchingForNode = !this._searchingForNode; + }, + + setAttachedWindowHeight: function(height) + { + }, + + moveByUnrestricted: function(x, y) + { + }, + + + addResourceSourceToFrame: function(identifier, element) + { + }, + + addSourceToFrame: function(mimeType, source, element) + { + return false; + }, + + getResourceDocumentNode: function(identifier) + { + return undefined; + }, + + highlightDOMNode: function(node) + { + }, + + hideDOMNodeHighlight: function() + { + }, + + inspectedWindow: function() + { + return window; + }, + + loaded: function() + { + }, + + localizedStringsURL: function() + { + return undefined; + }, + + windowUnloading: function() + { + return false; + }, + + hiddenPanels: function() + { + return ""; + }, + + debuggerEnabled: function() + { + return this._debuggerEnabled; + }, + + enableResourceTracking: function() + { + this._resourceTrackingEnabled = true; + WebInspector.resourceTrackingWasEnabled(); + }, + + disableResourceTracking: function() + { + this._resourceTrackingEnabled = false; + WebInspector.resourceTrackingWasDisabled(); + }, + + resourceTrackingEnabled: function() + { + return this._resourceTrackingEnabled; + }, + + enableDebugger: function() + { + this._debuggerEnabled = true; + }, + + disableDebugger: function() + { + this._debuggerEnabled = false; + }, + + addBreakpoint: function(sourceID, line, condition) + { + }, + + removeBreakpoint: function(sourceID, line) + { + }, + + updateBreakpoint: function(sourceID, line, condition) + { + }, + + pauseInDebugger: function() + { + }, + + pauseOnExceptions: function() + { + return false; + }, + + setPauseOnExceptions: function(value) + { + }, + + resumeDebugger: function() + { + }, + + profilerEnabled: function() + { + return true; + }, + + enableProfiler: function() + { + this._profilerEnabled = true; + }, + + disableProfiler: function() + { + this._profilerEnabled = false; + }, + + startProfiling: function() + { + }, + + stopProfiling: function() + { + }, + + getProfileHeaders: function(callId) + { + WebInspector.didGetProfileHeaders(callId, []); + }, + + getProfile: function(callId, uid) + { + if (WebInspector.__fullProfiles && (uid in WebInspector.__fullProfiles)) + { + WebInspector.didGetProfile(callId, WebInspector.__fullProfiles[uid]); + } + }, + + takeHeapSnapshot: function() + { + }, + + databaseTableNames: function(database) + { + return []; + }, + + stepIntoStatementInDebugger: function() + { + }, + + stepOutOfFunctionInDebugger: function() + { + }, + + stepOverStatementInDebugger: function() + { + }, + + setSetting: function(setting, value) + { + this._settings[setting] = value; + }, + + dispatchOnInjectedScript: function() + { + }, + + releaseWrapperObjectGroup: function() + { + }, + + setting: function(setting) + { + return this._settings[setting]; + } +} + +window.InspectorController = new WebInspector.InspectorControllerStub(); + +} diff --git a/WebCore/inspector/front-end/Panel.js b/WebCore/inspector/front-end/Panel.js index 5046f6b..6cd200b 100644 --- a/WebCore/inspector/front-end/Panel.js +++ b/WebCore/inspector/front-end/Panel.js @@ -82,6 +82,8 @@ WebInspector.Panel.prototype = { this._toolbarItem.addStyleClass("toggled-on"); WebInspector.currentFocusElement = document.getElementById("main-panels"); + + this.updateSidebarWidth(); }, hide: function() @@ -267,6 +269,103 @@ WebInspector.Panel.prototype = { currentView.jumpToLastSearchResult(); else currentView.jumpToPreviousSearchResult(); + }, + + handleKeyEvent: function(event) + { + this.handleSidebarKeyEvent(event); + }, + + handleSidebarKeyEvent: function(event) + { + if (this.hasSidebar && this.sidebarTree) + this.sidebarTree.handleKeyEvent(event); + }, + + createSidebar: function(parentElement, resizerParentElement) + { + if (this.hasSidebar) + return; + + if (!parentElement) + parentElement = this.element; + + if (!resizerParentElement) + resizerParentElement = parentElement; + + this.hasSidebar = true; + + this.sidebarElement = document.createElement("div"); + this.sidebarElement.className = "sidebar"; + parentElement.appendChild(this.sidebarElement); + + this.sidebarResizeElement = document.createElement("div"); + this.sidebarResizeElement.className = "sidebar-resizer-vertical"; + this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false); + resizerParentElement.appendChild(this.sidebarResizeElement); + + this.sidebarTreeElement = document.createElement("ol"); + this.sidebarTreeElement.className = "sidebar-tree"; + this.sidebarElement.appendChild(this.sidebarTreeElement); + + this.sidebarTree = new TreeOutline(this.sidebarTreeElement); + }, + + _startSidebarDragging: function(event) + { + WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize"); + }, + + _sidebarDragging: function(event) + { + this.updateSidebarWidth(event.pageX); + + event.preventDefault(); + }, + + _endSidebarDragging: function(event) + { + WebInspector.elementDragEnd(event); + }, + + updateSidebarWidth: function(width) + { + if (!this.hasSidebar) + return; + + if (this.sidebarElement.offsetWidth <= 0) { + // The stylesheet hasn't loaded yet or the window is closed, + // so we can't calculate what is need. Return early. + return; + } + + if (!("_currentSidebarWidth" in this)) + this._currentSidebarWidth = this.sidebarElement.offsetWidth; + + if (typeof width === "undefined") + width = this._currentSidebarWidth; + + width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2); + + this._currentSidebarWidth = width; + this.setSidebarWidth(width); + + this.updateMainViewWidth(width); + + var visibleView = this.visibleView; + if (visibleView && "resize" in visibleView) + visibleView.resize(); + }, + + setSidebarWidth: function(width) + { + this.sidebarElement.style.width = width + "px"; + this.sidebarResizeElement.style.left = (width - 3) + "px"; + }, + + updateMainViewWidth: function(width) + { + // Should be implemented by ancestors. } } diff --git a/WebCore/inspector/front-end/ProfileDataGridTree.js b/WebCore/inspector/front-end/ProfileDataGridTree.js index 3fb0e00..9a7c741 100644 --- a/WebCore/inspector/front-end/ProfileDataGridTree.js +++ b/WebCore/inspector/front-end/ProfileDataGridTree.js @@ -126,20 +126,6 @@ WebInspector.ProfileDataGridNode.prototype = { this.profileView._dataGridNodeDeselected(this); }, - expand: function() - { - if (!this.parent) { - var currentComparator = this.parent.lastComparator; - - if (!currentComparator || (currentComparator === this.lastComparator)) - return; - - this.sort(currentComparator); - } - - WebInspector.DataGridNode.prototype.expand.call(this); - }, - sort: function(/*Function*/ comparator, /*Boolean*/ force) { var gridNodeGroups = [[this]]; @@ -153,7 +139,7 @@ WebInspector.ProfileDataGridNode.prototype = { // If the grid node is collapsed, then don't sort children (save operation for later). // If the grid node has the same sorting as previously, then there is no point in sorting it again. - if (!force && !gridNode.expanded || gridNode.lastComparator === comparator) { + if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) { if (gridNode.children.length) gridNode.shouldRefreshChildren = true; continue; @@ -224,6 +210,26 @@ WebInspector.ProfileDataGridNode.prototype = { return this.totalTime / this.tree.totalTime * 100.0; }, + get _parent() + { + return this.parent !== this.dataGrid ? this.parent : this.tree; + }, + + _populate: function(event) + { + this._sharedPopulate(); + + if (this._parent) { + var currentComparator = this._parent.lastComparator; + + if (currentComparator) + this.sort(currentComparator, true); + } + + if (this.removeEventListener) + this.removeEventListener("populate", this._populate, this); + }, + // When focusing and collapsing we modify lots of nodes in the tree. // This allows us to restore them all to their original state when we revert. _save: function() diff --git a/WebCore/inspector/front-end/ProfileView.js b/WebCore/inspector/front-end/ProfileView.js index 2b8c6ce..afced41 100644 --- a/WebCore/inspector/front-end/ProfileView.js +++ b/WebCore/inspector/front-end/ProfileView.js @@ -23,7 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.ProfileView = function(profile) +// FIXME: Rename the file. + +WebInspector.CPUProfileView = function(profile) { WebInspector.View.call(this); @@ -78,15 +80,25 @@ WebInspector.ProfileView = function(profile) this.profile = profile; - this.profileDataGridTree = this.bottomUpProfileDataGridTree; - this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator("selfTime", false)); - - this.refresh(); + var self = this; + function profileCallback(profile) + { + self.profile.representedObject = profile; + self._assignParentsInProfile(); + + self.profileDataGridTree = self.bottomUpProfileDataGridTree; + self.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator("selfTime", false)); + + self.refresh(); + + self._updatePercentButton(); + } - this._updatePercentButton(); + var callId = WebInspector.Callback.wrap(profileCallback); + InspectorController.getProfile(callId, this.profile.uid); } -WebInspector.ProfileView.prototype = { +WebInspector.CPUProfileView.prototype = { get statusBarItems() { return [this.viewSelectElement, this.percentButton.element, this.focusButton.element, this.excludeButton.element, this.resetButton.element]; @@ -158,7 +170,7 @@ WebInspector.ProfileView.prototype = { WebInspector.View.prototype.hide.call(this); this._currentSearchResultIndex = -1; }, - + resize: function() { if (this.dataGrid) @@ -493,7 +505,7 @@ WebInspector.ProfileView.prototype = { _sortData: function(event) { - this._sortProfile(this.profile); + this._sortProfile(this.profile.representedObject); }, _sortProfile: function() @@ -533,7 +545,97 @@ WebInspector.ProfileView.prototype = { event.preventDefault(); event.stopPropagation(); + }, + + _assignParentsInProfile: function() + { + var head = this.profile.head; + head.parent = null; + head.head = null; + var nodesToTraverse = [ { parent: head, children: head.children } ]; + while (nodesToTraverse.length > 0) { + var pair = nodesToTraverse.shift(); + var parent = pair.parent; + var children = pair.children; + var length = children.length; + for (var i = 0; i < length; ++i) { + children[i].head = head; + children[i].parent = parent; + if (children[i].children.length > 0) + nodesToTraverse.push({ parent: children[i], children: children[i].children }); + } + } + } +} + +WebInspector.CPUProfileView.prototype.__proto__ = WebInspector.View.prototype; + +WebInspector.CPUProfileType = function() +{ + WebInspector.ProfileType.call(this, WebInspector.CPUProfileType.TypeId, WebInspector.UIString("CPU PROFILES")); + this._recording = false; +} + +WebInspector.CPUProfileType.TypeId = "CPU"; + +WebInspector.CPUProfileType.prototype = { + get buttonTooltip() + { + return this._recording ? WebInspector.UIString("Stop profiling.") : WebInspector.UIString("Start profiling."); + }, + + get buttonStyle() + { + return this._recording ? "record-profile-status-bar-item status-bar-item toggled-on" : "record-profile-status-bar-item status-bar-item"; + }, + + buttonClicked: function() + { + this._recording = !this._recording; + + if (this._recording) + InspectorController.startProfiling(); + else + InspectorController.stopProfiling(); + }, + + setRecordingProfile: function(isProfiling) + { + this._recording = isProfiling; + }, + + createSidebarTreeElementForProfile: function(profile) + { + return new WebInspector.ProfileSidebarTreeElement(profile); + }, + + createView: function(profile) + { + return new WebInspector.CPUProfileView(profile); } } -WebInspector.ProfileView.prototype.__proto__ = WebInspector.View.prototype; +WebInspector.CPUProfileType.prototype.__proto__ = WebInspector.ProfileType.prototype; + +WebInspector.CPUProfile = function(profile) +{ + this.representedObject = profile; + this.typeId = WebInspector.CPUProfileType.TypeId; +} + +WebInspector.CPUProfile.prototype = { + get title() + { + return this.representedObject.title; + }, + + get uid() + { + return this.representedObject.uid; + }, + + get head() + { + return this.representedObject.head; + } +} diff --git a/WebCore/inspector/front-end/ProfilesPanel.js b/WebCore/inspector/front-end/ProfilesPanel.js index 3bd4464..74c2bb6 100644 --- a/WebCore/inspector/front-end/ProfilesPanel.js +++ b/WebCore/inspector/front-end/ProfilesPanel.js @@ -25,11 +25,73 @@ const UserInitiatedProfileName = "org.webkit.profiles.user-initiated"; +WebInspector.ProfileType = function(id, name) +{ + this._id = id; + this._name = name; +} + +WebInspector.ProfileType.URLRegExp = /webkit-profile:\/\/(.+)\/(.+)#([0-9]+)/; + +WebInspector.ProfileType.prototype = { + get buttonTooltip() + { + return ""; + }, + + get buttonStyle() + { + return undefined; + }, + + get buttonCaption() + { + return this.name; + }, + + get id() + { + return this._id; + }, + + get name() + { + return this._name; + }, + + buttonClicked: function() + { + }, + + viewForProfile: function(profile) + { + if (!profile._profileView) + profile._profileView = this.createView(profile); + return profile._profileView; + }, + + // Must be implemented by subclasses. + createView: function(profile) + { + throw new Error("Needs implemented."); + }, + + // Must be implemented by subclasses. + createSidebarTreeElementForProfile: function(profile) + { + throw new Error("Needs implemented."); + } +} + WebInspector.ProfilesPanel = function() { WebInspector.Panel.call(this); + this.createSidebar(); + this.element.addStyleClass("profiles"); + this._profileTypesByIdMap = {}; + this._profileTypeButtonsByIdMap = {}; var panelEnablerHeading = WebInspector.UIString("You need to enable profiling before you can use the Profiles panel."); var panelEnablerDisclaimer = WebInspector.UIString("Enabling profiling will make scripts run slower."); @@ -39,32 +101,6 @@ WebInspector.ProfilesPanel = function() this.element.appendChild(this.panelEnablerView.element); - this.sidebarElement = document.createElement("div"); - this.sidebarElement.id = "profiles-sidebar"; - this.sidebarElement.className = "sidebar"; - this.element.appendChild(this.sidebarElement); - - this.sidebarResizeElement = document.createElement("div"); - this.sidebarResizeElement.className = "sidebar-resizer-vertical"; - this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false); - this.element.appendChild(this.sidebarResizeElement); - - this.sidebarTreeElement = document.createElement("ol"); - this.sidebarTreeElement.className = "sidebar-tree"; - this.sidebarElement.appendChild(this.sidebarTreeElement); - - this.sidebarTree = new TreeOutline(this.sidebarTreeElement); - - this.profilesListTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("CPU PROFILES"), null, true); - this.sidebarTree.appendChild(this.profilesListTreeElement); - this.profilesListTreeElement.expand(); - - this.snapshotsListTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("HEAP SNAPSHOTS"), null, true); - if (Preferences.heapProfilerPresent) { - this.sidebarTree.appendChild(this.snapshotsListTreeElement); - this.snapshotsListTreeElement.expand(); - } - this.profileViews = document.createElement("div"); this.profileViews.id = "profile-views"; this.element.appendChild(this.profileViews); @@ -72,18 +108,10 @@ WebInspector.ProfilesPanel = function() this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item"); this.enableToggleButton.addEventListener("click", this._toggleProfiling.bind(this), false); - this.recordButton = new WebInspector.StatusBarButton(WebInspector.UIString("Start profiling."), "record-profile-status-bar-item"); - this.recordButton.addEventListener("click", this._recordClicked.bind(this), false); - - this.recording = false; - - this.snapshotButton = new WebInspector.StatusBarButton(WebInspector.UIString("Take heap snapshot."), "heap-snapshot-status-bar-item"); - this.snapshotButton.visible = Preferences.heapProfilerPresent; - this.snapshotButton.addEventListener("click", this._snapshotClicked.bind(this), false); - this.profileViewStatusBarItemsContainer = document.createElement("div"); this.profileViewStatusBarItemsContainer.id = "profile-view-status-bar-items"; + this._profiles = []; this.reset(); } @@ -97,13 +125,30 @@ WebInspector.ProfilesPanel.prototype = { get statusBarItems() { - return [this.enableToggleButton.element, this.recordButton.element, this.snapshotButton.element, this.profileViewStatusBarItemsContainer]; + function clickHandler(profileType, buttonElement) + { + profileType.buttonClicked.call(profileType); + this.updateProfileTypeButtons(); + } + + var items = [this.enableToggleButton.element]; + // FIXME: Generate a single "combo-button". + for (var typeId in this._profileTypesByIdMap) { + var profileType = this.getProfileType(typeId); + if (profileType.buttonStyle) { + var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption); + this._profileTypeButtonsByIdMap[typeId] = button.element; + button.element.addEventListener("click", clickHandler.bind(this, profileType, button.element), false); + items.push(button.element); + } + } + items.push(this.profileViewStatusBarItemsContainer); + return items; }, show: function() { WebInspector.Panel.prototype.show.call(this); - this._updateSidebarWidth(); if (this._shouldPopulateProfiles) this._populateProfiles(); }, @@ -129,13 +174,8 @@ WebInspector.ProfilesPanel.prototype = { reset: function() { - if (this._profiles) { - var profiledLength = this._profiles.length; - for (var i = 0; i < profiledLength; ++i) { - var profile = this._profiles[i]; - delete profile._profileView; - } - } + for (var i = 0; i < this._profiles.length; ++i) + delete this._profiles[i]._profileView; delete this.currentQuery; this.searchCanceled(); @@ -147,8 +187,9 @@ WebInspector.ProfilesPanel.prototype = { this.sidebarTreeElement.removeStyleClass("some-expandable"); - this.profilesListTreeElement.removeChildren(); - this.snapshotsListTreeElement.removeChildren(); + for (var typeId in this._profileTypesByIdMap) + this.getProfileType(typeId).treeElement.removeChildren(); + this.profileViews.removeChildren(); this.profileViewStatusBarItemsContainer.removeChildren(); @@ -156,25 +197,36 @@ WebInspector.ProfilesPanel.prototype = { this._updateInterface(); }, - handleKeyEvent: function(event) + registerProfileType: function(profileType) { - this.sidebarTree.handleKeyEvent(event); + this._profileTypesByIdMap[profileType.id] = profileType; + profileType.treeElement = new WebInspector.SidebarSectionTreeElement(profileType.name, null, true); + this.sidebarTree.appendChild(profileType.treeElement); + profileType.treeElement.expand(); }, - addProfile: function(profile) + _makeKey: function(text, profileTypeId) { - this._profiles.push(profile); - this._profilesIdMap[profile.uid] = profile; + return escape(text) + '/' + escape(profileTypeId); + }, - var sidebarParent = this.profilesListTreeElement; + addProfileHeader: function(typeId, profile) + { + var profileType = this.getProfileType(typeId); + var sidebarParent = profileType.treeElement; var small = false; var alternateTitle; + profile.__profilesPanelProfileType = profileType; + this._profiles.push(profile); + this._profilesIdMap[this._makeKey(profile.uid, typeId)] = profile; + if (profile.title.indexOf(UserInitiatedProfileName) !== 0) { - if (!(profile.title in this._profileGroups)) - this._profileGroups[profile.title] = []; + var profileTitleKey = this._makeKey(profile.title, typeId); + if (!(profileTitleKey in this._profileGroups)) + this._profileGroups[profileTitleKey] = []; - var group = this._profileGroups[profile.title]; + var group = this._profileGroups[profileTitleKey]; group.push(profile); if (group.length === 2) { @@ -182,12 +234,12 @@ WebInspector.ProfilesPanel.prototype = { group._profilesTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(profile.title); // Insert at the same index for the first profile of the group. - var index = this.sidebarTree.children.indexOf(group[0]._profilesTreeElement); - this.sidebarTree.insertChild(group._profilesTreeElement, index); + var index = sidebarParent.children.indexOf(group[0]._profilesTreeElement); + sidebarParent.insertChild(group._profilesTreeElement, index); // Move the first profile to the group. var selected = group[0]._profilesTreeElement.selected; - this.sidebarTree.removeChild(group[0]._profilesTreeElement); + sidebarParent.removeChild(group[0]._profilesTreeElement); group._profilesTreeElement.appendChild(group[0]._profilesTreeElement); if (selected) { group[0]._profilesTreeElement.select(); @@ -207,13 +259,15 @@ WebInspector.ProfilesPanel.prototype = { } } - var profileTreeElement = new WebInspector.ProfileSidebarTreeElement(profile); + var profileTreeElement = profileType.createSidebarTreeElementForProfile(profile); profileTreeElement.small = small; if (alternateTitle) profileTreeElement.mainTitle = alternateTitle; profile._profilesTreeElement = profileTreeElement; sidebarParent.appendChild(profileTreeElement); + if (!this.visibleView) + this.showProfile(profile); }, showProfile: function(profile) @@ -224,7 +278,7 @@ WebInspector.ProfilesPanel.prototype = { if (this.visibleView) this.visibleView.hide(); - var view = this.profileViewForProfile(profile); + var view = profile.__profilesPanelProfileType.viewForProfile(profile); view.show(this.profileViews); @@ -245,18 +299,28 @@ WebInspector.ProfilesPanel.prototype = { this.showProfile(view.profile); }, - profileViewForProfile: function(profile) + getProfileType: function(typeId) { - if (!profile) - return null; - if (!profile._profileView) - profile._profileView = new WebInspector.ProfileView(profile); - return profile._profileView; + return this._profileTypesByIdMap[typeId]; }, - showProfileById: function(uid) + showProfileForURL: function(url) { - this.showProfile(this._profilesIdMap[uid]); + var match = url.match(WebInspector.ProfileType.URLRegExp); + if (!match) + return; + this.showProfile(this._profilesIdMap[this._makeKey(match[3], match[1])]); + }, + + updateProfileTypeButtons: function() + { + for (var typeId in this._profileTypeButtonsByIdMap) { + var buttonElement = this._profileTypeButtonsByIdMap[typeId]; + var profileType = this.getProfileType(typeId); + buttonElement.className = profileType.buttonStyle; + buttonElement.title = profileType.buttonTooltip; + // FIXME: Apply profileType.buttonCaption once captions are added to button controls. + } }, closeVisibleView: function() @@ -266,16 +330,17 @@ WebInspector.ProfilesPanel.prototype = { delete this.visibleView; }, - displayTitleForProfileLink: function(title) + displayTitleForProfileLink: function(title, typeId) { title = unescape(title); if (title.indexOf(UserInitiatedProfileName) === 0) { title = WebInspector.UIString("Profile %d", title.substring(UserInitiatedProfileName.length + 1)); } else { - if (!(title in this._profileGroupsForLinks)) - this._profileGroupsForLinks[title] = 0; + var titleKey = this._makeKey(title, typeId); + if (!(titleKey in this._profileGroupsForLinks)) + this._profileGroupsForLinks[titleKey] = 0; - groupNumber = ++this._profileGroupsForLinks[title]; + groupNumber = ++this._profileGroupsForLinks[titleKey]; if (groupNumber > 2) // The title is used in the console message announcing that a profile has started so it gets @@ -296,7 +361,7 @@ WebInspector.ProfilesPanel.prototype = { var profilesLength = this._profiles.length; for (var i = 0; i < profilesLength; ++i) { - var view = this.profileViewForProfile(this._profiles[i]); + var view = this._profiles[i].viewForProfile(); if (!view.performSearch || view === visibleView) continue; views.push(view); @@ -323,19 +388,6 @@ WebInspector.ProfilesPanel.prototype = { } }, - setRecordingProfile: function(isProfiling) - { - this.recording = isProfiling; - - if (isProfiling) { - this.recordButton.toggled = true; - this.recordButton.title = WebInspector.UIString("Stop profiling."); - } else { - this.recordButton.toggled = false; - this.recordButton.title = WebInspector.UIString("Start profiling."); - } - }, - resize: function() { var visibleView = this.visibleView; @@ -345,39 +397,24 @@ WebInspector.ProfilesPanel.prototype = { _updateInterface: function() { + // FIXME: Replace ProfileType-specific button visibility changes by a single ProfileType-agnostic "combo-button" visibility change. if (InspectorController.profilerEnabled()) { this.enableToggleButton.title = WebInspector.UIString("Profiling enabled. Click to disable."); this.enableToggleButton.toggled = true; - this.recordButton.visible = true; - if (Preferences.heapProfilerPresent) - this.snapshotButton.visible = true; + for (var typeId in this._profileTypeButtonsByIdMap) + this._profileTypeButtonsByIdMap[typeId].removeStyleClass("hidden"); this.profileViewStatusBarItemsContainer.removeStyleClass("hidden"); this.panelEnablerView.visible = false; } else { this.enableToggleButton.title = WebInspector.UIString("Profiling disabled. Click to enable."); this.enableToggleButton.toggled = false; - this.recordButton.visible = false; - this.snapshotButton.visible = false; + for (var typeId in this._profileTypeButtonsByIdMap) + this._profileTypeButtonsByIdMap[typeId].addStyleClass("hidden"); this.profileViewStatusBarItemsContainer.addStyleClass("hidden"); this.panelEnablerView.visible = true; } }, - _recordClicked: function() - { - this.recording = !this.recording; - - if (this.recording) - InspectorController.startProfiling(); - else - InspectorController.stopProfiling(); - }, - - _snapshotClicked: function() - { - InspectorController.takeHeapSnapshot(); - }, - _enableProfiling: function() { if (InspectorController.profilerEnabled()) @@ -397,66 +434,27 @@ WebInspector.ProfilesPanel.prototype = { { // FIXME: This code needs to be adjusted when more profiling types are added. // Currently defaults to CPU profiles. - var cpuProfiles = this.sidebarTree.children[0]; + var cpuProfiles = this.getProfileType(WebInspector.CPUProfileType.TypeId).treeElement; if (cpuProfiles.children.length) return; - var profiles = InspectorController.profiles(); - var profilesLength = profiles.length; - for (var i = 0; i < profilesLength; ++i) { - var profile = profiles[i]; - this.addProfile(profile); + function populateCallback(profileHeaders) { + profileHeaders.sort(function(a, b) { return a.uid - b.uid; }); + var profileHeadersLength = profileHeaders.length; + for (var i = 0; i < profileHeadersLength; ++i) + WebInspector.addProfileHeader(profileHeaders[i]); } - if (cpuProfiles.children[0]) - cpuProfiles.children[0].select(); + var callId = WebInspector.Callback.wrap(populateCallback); + InspectorController.getProfileHeaders(callId); delete this._shouldPopulateProfiles; }, - _startSidebarDragging: function(event) - { - WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize"); - }, - - _sidebarDragging: function(event) - { - this._updateSidebarWidth(event.pageX); - - event.preventDefault(); - }, - - _endSidebarDragging: function(event) - { - WebInspector.elementDragEnd(event); - }, - - _updateSidebarWidth: function(width) + updateMainViewWidth: function(width) { - if (this.sidebarElement.offsetWidth <= 0) { - // The stylesheet hasn't loaded yet or the window is closed, - // so we can't calculate what is need. Return early. - return; - } - - if (!("_currentSidebarWidth" in this)) - this._currentSidebarWidth = this.sidebarElement.offsetWidth; - - if (typeof width === "undefined") - width = this._currentSidebarWidth; - - width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2); - - this._currentSidebarWidth = width; - - this.sidebarElement.style.width = width + "px"; this.profileViews.style.left = width + "px"; this.profileViewStatusBarItemsContainer.style.left = width + "px"; - this.sidebarResizeElement.style.left = (width - 3) + "px"; - - var visibleView = this.visibleView; - if (visibleView && "resize" in visibleView) - visibleView.resize(); } } @@ -535,3 +533,6 @@ WebInspector.ProfileGroupSidebarTreeElement.prototype = { } WebInspector.ProfileGroupSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype; + +WebInspector.didGetProfileHeaders = WebInspector.Callback.processCallback; +WebInspector.didGetProfile = WebInspector.Callback.processCallback; diff --git a/WebCore/inspector/front-end/ResourceCategory.js b/WebCore/inspector/front-end/ResourceCategory.js index fc508d0..6e94265 100644 --- a/WebCore/inspector/front-end/ResourceCategory.js +++ b/WebCore/inspector/front-end/ResourceCategory.js @@ -26,18 +26,13 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.ResourceCategory = function(title, name) +WebInspector.ResourceCategory = function(name, title, color) { - this.name = name; - this.title = title; + WebInspector.AbstractTimelineCategory.call(this, name, title, color); this.resources = []; } WebInspector.ResourceCategory.prototype = { - toString: function() - { - return this.title; - }, addResource: function(resource) { @@ -66,3 +61,5 @@ WebInspector.ResourceCategory.prototype = { this.resources = []; } } + +WebInspector.ResourceCategory.prototype.__proto__ = WebInspector.AbstractTimelineCategory.prototype; diff --git a/WebCore/inspector/front-end/ResourceView.js b/WebCore/inspector/front-end/ResourceView.js index 28586f6..2ea7fdb 100644 --- a/WebCore/inspector/front-end/ResourceView.js +++ b/WebCore/inspector/front-end/ResourceView.js @@ -178,7 +178,7 @@ WebInspector.ResourceView.prototype = { var isFormEncoded = false; var requestContentType = this._getHeaderValue(this.resource.requestHeaders, "Content-Type"); - if (requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) + if (requestContentType && requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) isFormEncoded = true; if (isFormEncoded) { diff --git a/WebCore/inspector/front-end/ResourcesPanel.js b/WebCore/inspector/front-end/ResourcesPanel.js index b575fc3..940570f 100644 --- a/WebCore/inspector/front-end/ResourcesPanel.js +++ b/WebCore/inspector/front-end/ResourcesPanel.js @@ -29,186 +29,129 @@ WebInspector.ResourcesPanel = function() { - WebInspector.Panel.call(this); + WebInspector.AbstractTimelinePanel.call(this); this.element.addStyleClass("resources"); - this.filterBarElement = document.createElement("div"); - this.filterBarElement.id = "resources-filter"; - this.element.appendChild(this.filterBarElement); + this._createPanelEnabler(); this.viewsContainerElement = document.createElement("div"); this.viewsContainerElement.id = "resource-views"; this.element.appendChild(this.viewsContainerElement); - this.containerElement = document.createElement("div"); - this.containerElement.id = "resources-container"; - this.containerElement.addEventListener("scroll", this._updateDividersLabelBarPosition.bind(this), false); - this.element.appendChild(this.containerElement); + this.createInterface(); - this.sidebarElement = document.createElement("div"); - this.sidebarElement.id = "resources-sidebar"; - this.sidebarElement.className = "sidebar"; - this.containerElement.appendChild(this.sidebarElement); + this._createStatusbarButtons(); - this.sidebarResizeElement = document.createElement("div"); - this.sidebarResizeElement.className = "sidebar-resizer-vertical"; - this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false); - this.element.appendChild(this.sidebarResizeElement); - - this.containerContentElement = document.createElement("div"); - this.containerContentElement.id = "resources-container-content"; - this.containerElement.appendChild(this.containerContentElement); - - this.summaryBar = new WebInspector.SummaryBar(this.categories); - this.summaryBar.element.id = "resources-summary"; - this.containerContentElement.appendChild(this.summaryBar.element); - - this.resourcesGraphsElement = document.createElement("div"); - this.resourcesGraphsElement.id = "resources-graphs"; - this.containerContentElement.appendChild(this.resourcesGraphsElement); - - this.dividersElement = document.createElement("div"); - this.dividersElement.id = "resources-dividers"; - this.containerContentElement.appendChild(this.dividersElement); - - this.dividersLabelBarElement = document.createElement("div"); - this.dividersLabelBarElement.id = "resources-dividers-label-bar"; - this.containerContentElement.appendChild(this.dividersLabelBarElement); - - this.sidebarTreeElement = document.createElement("ol"); - this.sidebarTreeElement.className = "sidebar-tree"; - this.sidebarElement.appendChild(this.sidebarTreeElement); + this.reset(); + this.filter(this.filterAllElement, false); + this.graphsTreeElement.children[0].select(); +} - this.sidebarTree = new TreeOutline(this.sidebarTreeElement); +WebInspector.ResourcesPanel.prototype = { + toolbarItemClass: "resources", - var timeGraphItem = new WebInspector.SidebarTreeElement("resources-time-graph-sidebar-item", WebInspector.UIString("Time")); - timeGraphItem.onselect = this._graphSelected.bind(this); + get toolbarItemLabel() + { + return WebInspector.UIString("Resources"); + }, - var transferTimeCalculator = new WebInspector.ResourceTransferTimeCalculator(); - var transferDurationCalculator = new WebInspector.ResourceTransferDurationCalculator(); + get statusBarItems() + { + return [this.enableToggleButton.element, this.largerResourcesButton.element, this.sortingSelectElement]; + }, - timeGraphItem.sortingOptions = [ - { name: WebInspector.UIString("Sort by Start Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime, calculator: transferTimeCalculator }, - { name: WebInspector.UIString("Sort by Response Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime, calculator: transferTimeCalculator }, - { name: WebInspector.UIString("Sort by End Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime, calculator: transferTimeCalculator }, - { name: WebInspector.UIString("Sort by Duration"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration, calculator: transferDurationCalculator }, - { name: WebInspector.UIString("Sort by Latency"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency, calculator: transferDurationCalculator }, - ]; + get categories() + { + return WebInspector.resourceCategories; + }, - timeGraphItem.selectedSortingOptionIndex = 1; + createItemTreeElement: function(item) + { + return new WebInspector.ResourceSidebarTreeElement(item); + }, - var sizeGraphItem = new WebInspector.SidebarTreeElement("resources-size-graph-sidebar-item", WebInspector.UIString("Size")); - sizeGraphItem.onselect = this._graphSelected.bind(this); + createItemGraph: function(item) + { + return new WebInspector.ResourceGraph(item); + }, - var transferSizeCalculator = new WebInspector.ResourceTransferSizeCalculator(); - sizeGraphItem.sortingOptions = [ - { name: WebInspector.UIString("Sort by Size"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize, calculator: transferSizeCalculator }, - ]; + isCategoryVisible: function(categoryName) + { + return (this.itemsGraphsElement.hasStyleClass("filter-all") || this.itemsGraphsElement.hasStyleClass("filter-" + categoryName.toLowerCase())); + }, - sizeGraphItem.selectedSortingOptionIndex = 0; + populateSidebar: function() + { + var timeGraphItem = new WebInspector.SidebarTreeElement("resources-time-graph-sidebar-item", WebInspector.UIString("Time")); + timeGraphItem.onselect = this._graphSelected.bind(this); - this.graphsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("GRAPHS"), {}, true); - this.sidebarTree.appendChild(this.graphsTreeElement); + var transferTimeCalculator = new WebInspector.ResourceTransferTimeCalculator(); + var transferDurationCalculator = new WebInspector.ResourceTransferDurationCalculator(); - this.graphsTreeElement.appendChild(timeGraphItem); - this.graphsTreeElement.appendChild(sizeGraphItem); - this.graphsTreeElement.expand(); + timeGraphItem.sortingOptions = [ + { name: WebInspector.UIString("Sort by Start Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingStartTime, calculator: transferTimeCalculator }, + { name: WebInspector.UIString("Sort by Response Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingResponseReceivedTime, calculator: transferTimeCalculator }, + { name: WebInspector.UIString("Sort by End Time"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByAscendingEndTime, calculator: transferTimeCalculator }, + { name: WebInspector.UIString("Sort by Duration"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingDuration, calculator: transferDurationCalculator }, + { name: WebInspector.UIString("Sort by Latency"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingLatency, calculator: transferDurationCalculator }, + ]; - this.resourcesTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RESOURCES"), {}, true); - this.sidebarTree.appendChild(this.resourcesTreeElement); + timeGraphItem.selectedSortingOptionIndex = 1; - this.resourcesTreeElement.expand(); + var sizeGraphItem = new WebInspector.SidebarTreeElement("resources-size-graph-sidebar-item", WebInspector.UIString("Size")); + sizeGraphItem.onselect = this._graphSelected.bind(this); - var panelEnablerHeading = WebInspector.UIString("You need to enable resource tracking to use this panel."); - var panelEnablerDisclaimer = WebInspector.UIString("Enabling resource tracking will reload the page and make page loading slower."); - var panelEnablerButton = WebInspector.UIString("Enable resource tracking"); + var transferSizeCalculator = new WebInspector.ResourceTransferSizeCalculator(); + sizeGraphItem.sortingOptions = [ + { name: WebInspector.UIString("Sort by Size"), sortingFunction: WebInspector.ResourceSidebarTreeElement.CompareByDescendingSize, calculator: transferSizeCalculator }, + ]; - this.panelEnablerView = new WebInspector.PanelEnablerView("resources", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton); - this.panelEnablerView.addEventListener("enable clicked", this._enableResourceTracking, this); + sizeGraphItem.selectedSortingOptionIndex = 0; - this.element.appendChild(this.panelEnablerView.element); + this.graphsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("GRAPHS"), {}, true); + this.sidebarTree.appendChild(this.graphsTreeElement); - this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item"); - this.enableToggleButton.addEventListener("click", this._toggleResourceTracking.bind(this), false); + this.graphsTreeElement.appendChild(timeGraphItem); + this.graphsTreeElement.appendChild(sizeGraphItem); + this.graphsTreeElement.expand(); - this.largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "resources-larger-resources-status-bar-item"); - this.largerResourcesButton.toggled = Preferences.resourcesLargeRows; - this.largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false); - if (!Preferences.resourcesLargeRows) { - Preferences.resourcesLargeRows = !Preferences.resourcesLargeRows; - this._toggleLargerResources(); // this will toggle the preference back to the original - } + this.itemsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RESOURCES"), {}, true); + this.sidebarTree.appendChild(this.itemsTreeElement); - this.sortingSelectElement = document.createElement("select"); - this.sortingSelectElement.className = "status-bar-item"; - this.sortingSelectElement.addEventListener("change", this._changeSortingFunction.bind(this), false); - - var createFilterElement = function (category) { - var categoryElement = document.createElement("li"); - categoryElement.category = category; - categoryElement.addStyleClass(category); - var label = WebInspector.UIString("All"); - if (WebInspector.resourceCategories[category]) - label = WebInspector.resourceCategories[category].title; - categoryElement.appendChild(document.createTextNode(label)); - categoryElement.addEventListener("click", this._updateFilter.bind(this), false); - this.filterBarElement.appendChild(categoryElement); - return categoryElement; - }; - - var allElement = createFilterElement.call(this, "all"); - this.filter(allElement.category); - for (var category in this.categories) - createFilterElement.call(this, category); + this.itemsTreeElement.expand(); + }, - this.reset(); + _createPanelEnabler: function() + { + var panelEnablerHeading = WebInspector.UIString("You need to enable resource tracking to use this panel."); + var panelEnablerDisclaimer = WebInspector.UIString("Enabling resource tracking will reload the page and make page loading slower."); + var panelEnablerButton = WebInspector.UIString("Enable resource tracking"); - timeGraphItem.select(); -} + this.panelEnablerView = new WebInspector.PanelEnablerView("resources", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton); + this.panelEnablerView.addEventListener("enable clicked", this._enableResourceTracking, this); -WebInspector.ResourcesPanel.prototype = { - toolbarItemClass: "resources", + this.element.appendChild(this.panelEnablerView.element); - get categories() - { - if (!this._categories) { - this._categories = {documents: {color: {r: 47, g: 102, b: 236}}, stylesheets: {color: {r: 157, g: 231, b: 119}}, images: {color: {r: 164, g: 60, b: 255}}, scripts: {color: {r: 255, g: 121, b: 0}}, xhr: {color: {r: 231, g: 231, b: 10}}, fonts: {color: {r: 255, g: 82, b: 62}}, other: {color: {r: 186, g: 186, b: 186}}}; - for (var category in this._categories) { - this._categories[category].title = WebInspector.resourceCategories[category].title; - } - } - return this._categories; + this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item"); + this.enableToggleButton.addEventListener("click", this._toggleResourceTracking.bind(this), false); }, - filter: function (category) { - if (this._filterCategory && this._filterCategory === category) - return; - - if (this._filterCategory) { - var filterElement = this.filterBarElement.getElementsByClassName(this._filterCategory)[0]; - filterElement.removeStyleClass("selected"); - var oldClass = "filter-" + this._filterCategory; - this.resourcesTreeElement.childrenListElement.removeStyleClass(oldClass); - this.resourcesGraphsElement.removeStyleClass(oldClass); + _createStatusbarButtons: function() + { + this.largerResourcesButton = new WebInspector.StatusBarButton(WebInspector.UIString("Use small resource rows."), "resources-larger-resources-status-bar-item"); + this.largerResourcesButton.toggled = Preferences.resourcesLargeRows; + this.largerResourcesButton.addEventListener("click", this._toggleLargerResources.bind(this), false); + if (!Preferences.resourcesLargeRows) { + Preferences.resourcesLargeRows = !Preferences.resourcesLargeRows; + this._toggleLargerResources(); // this will toggle the preference back to the original } - this._filterCategory = category; - var filterElement = this.filterBarElement.getElementsByClassName(this._filterCategory)[0]; - filterElement.addStyleClass("selected"); - var newClass = "filter-" + this._filterCategory; - this.resourcesTreeElement.childrenListElement.addStyleClass(newClass); - this.resourcesGraphsElement.addStyleClass(newClass); - }, - _updateFilter: function (e) { - this.filter(e.target.category); + this.sortingSelectElement = document.createElement("select"); + this.sortingSelectElement.className = "status-bar-item"; + this.sortingSelectElement.addEventListener("change", this._changeSortingFunction.bind(this), false); }, - get toolbarItemLabel() - { - return WebInspector.UIString("Resources"); - }, - get mainResourceLoadTime() { return this._mainResourceLoadTime || -1; @@ -222,7 +165,7 @@ WebInspector.ResourcesPanel.prototype = { this._mainResourceLoadTime = x; // Update the dividers to draw the new line - this._updateGraphDividersIfNeeded(true); + this.updateGraphDividersIfNeeded(true); }, get mainResourceDOMContentTime() @@ -237,21 +180,12 @@ WebInspector.ResourcesPanel.prototype = { this._mainResourceDOMContentTime = x; - this._updateGraphDividersIfNeeded(true); - }, - - get statusBarItems() - { - return [this.enableToggleButton.element, this.largerResourcesButton.element, this.sortingSelectElement]; + this.updateGraphDividersIfNeeded(true); }, show: function() { - WebInspector.Panel.prototype.show.call(this); - - this._updateDividersLabelBarPosition(); - this._updateSidebarWidth(); - this.refreshIfNeeded(); + WebInspector.AbstractTimelinePanel.prototype.show.call(this); var visibleView = this.visibleView; if (visibleView) { @@ -274,7 +208,7 @@ WebInspector.ResourcesPanel.prototype = { resize: function() { - this._updateGraphDividersIfNeeded(); + WebInspector.AbstractTimelinePanel.prototype.resize.call(this); var visibleView = this.visibleView; if (visibleView && "resize" in visibleView) @@ -292,7 +226,7 @@ WebInspector.ResourcesPanel.prototype = { var resourcesLength = this._resources.length; for (var i = 0; i < resourcesLength; ++i) { var resource = this._resources[i]; - if (!resource._resourcesTreeElement) + if (!resource._itemsTreeElement) continue; var resourceView = this.resourceViewForResource(resource); if (!resourceView.performSearch || resourceView === visibleView) @@ -309,7 +243,7 @@ WebInspector.ResourcesPanel.prototype = { function sortFuction(a, b) { - return resourceTreeElementSortFunction(a.resource._resourcesTreeElement, b.resource._resourcesTreeElement); + return resourceTreeElementSortFunction(a.resource._itemsTreeElement, b.resource._itemsTreeElement); } return sortFuction; @@ -317,7 +251,7 @@ WebInspector.ResourcesPanel.prototype = { searchMatchFound: function(view, matches) { - view.resource._resourcesTreeElement.searchMatches = matches; + view.resource._itemsTreeElement.searchMatches = matches; }, searchCanceled: function(startingNewSearch) @@ -329,8 +263,8 @@ WebInspector.ResourcesPanel.prototype = { for (var i = 0; i < this._resources.length; ++i) { var resource = this._resources[i]; - if (resource._resourcesTreeElement) - resource._resourcesTreeElement.updateErrorsAndWarnings(); + if (resource._itemsTreeElement) + resource._itemsTreeElement.updateErrorsAndWarnings(); } }, @@ -338,8 +272,8 @@ WebInspector.ResourcesPanel.prototype = { { for (var i = 0; i < this._resources.length; ++i) { var resource = this._resources[i]; - if (resource._resourcesTreeElement) - resource._resourcesTreeElement.resetBubble(); + if (resource._itemsTreeElement) + resource._itemsTreeElement.resetBubble(); } WebInspector.Panel.prototype.performSearch.call(this, query); @@ -352,23 +286,6 @@ WebInspector.ResourcesPanel.prototype = { return null; }, - get calculator() - { - return this._calculator; - }, - - set calculator(x) - { - if (!x || this._calculator === x) - return; - - this._calculator = x; - this._calculator.reset(); - - this._staleResources = this._resources; - this.refresh(); - }, - get sortingFunction() { return this._sortingFunction; @@ -380,75 +297,19 @@ WebInspector.ResourcesPanel.prototype = { this._sortResourcesIfNeeded(); }, - get needsRefresh() - { - return this._needsRefresh; - }, - - set needsRefresh(x) - { - if (this._needsRefresh === x) - return; - - this._needsRefresh = x; - - if (x) { - if (this.visible && !("_refreshTimeout" in this)) - this._refreshTimeout = setTimeout(this.refresh.bind(this), 500); - } else { - if ("_refreshTimeout" in this) { - clearTimeout(this._refreshTimeout); - delete this._refreshTimeout; - } - } - }, - - refreshIfNeeded: function() - { - if (this.needsRefresh) - this.refresh(); - }, - refresh: function() { - this.needsRefresh = false; - - var staleResourcesLength = this._staleResources.length; - var boundariesChanged = false; - - for (var i = 0; i < staleResourcesLength; ++i) { - var resource = this._staleResources[i]; - if (!resource._resourcesTreeElement) { - // Create the resource tree element and graph. - resource._resourcesTreeElement = new WebInspector.ResourceSidebarTreeElement(resource); - resource._resourcesTreeElement._resourceGraph = new WebInspector.ResourceGraph(resource); - - this.resourcesTreeElement.appendChild(resource._resourcesTreeElement); - this.resourcesGraphsElement.appendChild(resource._resourcesTreeElement._resourceGraph.graphElement); - } - - resource._resourcesTreeElement.refresh(); - - if (this.calculator.updateBoundaries(resource)) - boundariesChanged = true; - } + WebInspector.AbstractTimelinePanel.prototype.refresh.call(this); - if (boundariesChanged) { - // The boundaries changed, so all resource graphs are stale. - this._staleResources = this._resources; - staleResourcesLength = this._staleResources.length; - } - - for (var i = 0; i < staleResourcesLength; ++i) - this._staleResources[i]._resourcesTreeElement._resourceGraph.refresh(this.calculator); - - this._staleResources = []; - - this._updateGraphDividersIfNeeded(); this._sortResourcesIfNeeded(); this._updateSummaryGraph(); }, + _updateSummaryGraph: function() + { + this.summaryBar.update(this._resources); + }, + resourceTrackingWasEnabled: function() { this.reset(); @@ -463,14 +324,9 @@ WebInspector.ResourcesPanel.prototype = { { this.closeVisibleResource(); - this.containerElement.scrollTop = 0; - delete this.currentQuery; this.searchCanceled(); - if (this._calculator) - this._calculator.reset(); - if (this._resources) { var resourcesLength = this._resources.length; for (var i = 0; i < resourcesLength; ++i) { @@ -479,23 +335,18 @@ WebInspector.ResourcesPanel.prototype = { resource.warnings = 0; resource.errors = 0; - delete resource._resourcesTreeElement; delete resource._resourcesView; } } - this._resources = []; - this._staleResources = []; + WebInspector.AbstractTimelinePanel.prototype.reset.call(this); this.mainResourceLoadTime = -1; this.mainResourceDOMContentTime = -1; - - this.resourcesTreeElement.removeChildren(); + this.viewsContainerElement.removeChildren(); - this.resourcesGraphsElement.removeChildren(); - this.summaryBar.reset(); - this._updateGraphDividersIfNeeded(true); + this.summaryBar.reset(); if (InspectorController.resourceTrackingEnabled()) { this.enableToggleButton.title = WebInspector.UIString("Resource tracking enabled. Click to disable."); @@ -523,20 +374,12 @@ WebInspector.ResourcesPanel.prototype = { if (this.visibleView === resource._resourcesView) this.closeVisibleResource(); - this._resources.remove(resource, true); - - if (resource._resourcesTreeElement) { - this.resourcesTreeElement.removeChild(resource._resourcesTreeElement); - this.resourcesGraphsElement.removeChild(resource._resourcesTreeElement._resourceGraph.graphElement); - } + this.removeItem(resource); resource.warnings = 0; resource.errors = 0; - delete resource._resourcesTreeElement; delete resource._resourcesView; - - this._adjustScrollPosition(); }, addMessageToResource: function(resource, msg) @@ -553,8 +396,8 @@ WebInspector.ResourcesPanel.prototype = { break; } - if (!this.currentQuery && resource._resourcesTreeElement) - resource._resourcesTreeElement.updateErrorsAndWarnings(); + if (!this.currentQuery && resource._itemsTreeElement) + resource._itemsTreeElement.updateErrorsAndWarnings(); var view = this.resourceViewForResource(resource); if (view.addMessage) @@ -569,8 +412,8 @@ WebInspector.ResourcesPanel.prototype = { resource.warnings = 0; resource.errors = 0; - if (!this.currentQuery && resource._resourcesTreeElement) - resource._resourcesTreeElement.updateErrorsAndWarnings(); + if (!this.currentQuery && resource._itemsTreeElement) + resource._itemsTreeElement.updateErrorsAndWarnings(); var view = resource._resourcesView; if (!view || !view.clearMessages) @@ -581,8 +424,7 @@ WebInspector.ResourcesPanel.prototype = { refreshResource: function(resource) { - this._staleResources.push(resource); - this.needsRefresh = true; + this.refreshItem(resource); }, recreateViewForResourceIfNeeded: function(resource) @@ -597,8 +439,8 @@ WebInspector.ResourcesPanel.prototype = { resource.warnings = 0; resource.errors = 0; - if (!this.currentQuery && resource._resourcesTreeElement) - resource._resourcesTreeElement.updateErrorsAndWarnings(); + if (!this.currentQuery && resource._itemsTreeElement) + resource._itemsTreeElement.updateErrorsAndWarnings(); var oldView = resource._resourcesView; @@ -634,14 +476,11 @@ WebInspector.ResourcesPanel.prototype = { view.highlightLine(line); } - if (resource._resourcesTreeElement) { - resource._resourcesTreeElement.reveal(); - resource._resourcesTreeElement.select(true); - } + this.revealAndSelectItem(resource); this.visibleResource = resource; - this._updateSidebarWidth(); + this.updateSidebarWidth(); }, showView: function(view) @@ -663,7 +502,7 @@ WebInspector.ResourcesPanel.prototype = { if (this._lastSelectedGraphTreeElement) this._lastSelectedGraphTreeElement.select(true); - this._updateSidebarWidth(); + this.updateSidebarWidth(); }, resourceViewForResource: function(resource) @@ -692,113 +531,59 @@ WebInspector.ResourcesPanel.prototype = { return view.sourceFrame; }, - handleKeyEvent: function(event) - { - this.sidebarTree.handleKeyEvent(event); - }, - _sortResourcesIfNeeded: function() { - var sortedElements = [].concat(this.resourcesTreeElement.children); - sortedElements.sort(this.sortingFunction); - - var sortedElementsLength = sortedElements.length; - for (var i = 0; i < sortedElementsLength; ++i) { - var treeElement = sortedElements[i]; - if (treeElement === this.resourcesTreeElement.children[i]) - continue; - - var wasSelected = treeElement.selected; - this.resourcesTreeElement.removeChild(treeElement); - this.resourcesTreeElement.insertChild(treeElement, i); - if (wasSelected) - treeElement.select(true); - - var graphElement = treeElement._resourceGraph.graphElement; - this.resourcesGraphsElement.insertBefore(graphElement, this.resourcesGraphsElement.children[i]); - } + this.sortItems(this.sortingFunction); }, - _updateGraphDividersIfNeeded: function(force) + updateGraphDividersIfNeeded: function(force) { - if (!this.visible) { - this.needsRefresh = true; - return; - } - - if (document.body.offsetWidth <= 0) { - // The stylesheet hasn't loaded yet or the window is closed, - // so we can't calculate what is need. Return early. - return; - } - - var dividerCount = Math.round(this.dividersElement.offsetWidth / 64); - var slice = this.calculator.boundarySpan / dividerCount; - if (!force && this._currentDividerSlice === slice) + var proceed = WebInspector.AbstractTimelinePanel.prototype.updateGraphDividersIfNeeded.call(this, force); + + if (!proceed) return; - this._currentDividerSlice = slice; - - this.dividersElement.removeChildren(); - this.dividersLabelBarElement.removeChildren(); - - for (var i = 1; i <= dividerCount; ++i) { - var divider = document.createElement("div"); - divider.className = "resources-divider"; - if (i === dividerCount) - divider.addStyleClass("last"); - divider.style.left = ((i / dividerCount) * 100) + "%"; - - this.dividersElement.appendChild(divider.cloneNode()); - - var label = document.createElement("div"); - label.className = "resources-divider-label"; - if (!isNaN(slice)) - label.textContent = this.calculator.formatValue(slice * i); - divider.appendChild(label); - - this.dividersLabelBarElement.appendChild(divider); - } - - if (this.calculator.startAtZero) { + if (this.calculator.startAtZero || !this.calculator.computePercentageFromEventTime) { // If our current sorting method starts at zero, that means it shows all // resources starting at the same point, and so onLoad event and DOMContent // event lines really wouldn't make much sense here, so don't render them. + // Additionally, if the calculator doesn't have the computePercentageFromEventTime + // function defined, we are probably sorting by size, and event times aren't relevant + // in this case. return; } if (this.mainResourceLoadTime !== -1) { var percent = this.calculator.computePercentageFromEventTime(this.mainResourceLoadTime); + var loadDivider = document.createElement("div"); loadDivider.className = "resources-onload-divider"; - loadDivider.style.left = percent + "%"; - loadDivider.title = WebInspector.UIString("Load event fired"); - this.dividersElement.appendChild(loadDivider); + + var loadDividerPadding = document.createElement("div"); + loadDividerPadding.className = "resources-event-divider-padding"; + loadDividerPadding.style.left = percent + "%"; + loadDividerPadding.title = WebInspector.UIString("Load event fired"); + loadDividerPadding.appendChild(loadDivider); + + this.eventDividersElement.appendChild(loadDividerPadding); } if (this.mainResourceDOMContentTime !== -1) { var percent = this.calculator.computePercentageFromEventTime(this.mainResourceDOMContentTime); + var domContentDivider = document.createElement("div"); domContentDivider.className = "resources-ondomcontent-divider"; - domContentDivider.title = WebInspector.UIString("DOMContent event fired"); - domContentDivider.style.left = percent + "%"; - this.dividersElement.appendChild(domContentDivider); + + var domContentDividerPadding = document.createElement("div"); + domContentDividerPadding.className = "resources-event-divider-padding"; + domContentDividerPadding.style.left = percent + "%"; + domContentDividerPadding.title = WebInspector.UIString("DOMContent event fired"); + domContentDividerPadding.appendChild(domContentDivider); + + this.eventDividersElement.appendChild(domContentDividerPadding); } }, - _updateSummaryGraph: function() - { - this.summaryBar.update(this._resources); - }, - - _updateDividersLabelBarPosition: function() - { - var scrollTop = this.containerElement.scrollTop; - var dividersTop = (scrollTop < this.summaryBar.element.offsetHeight ? this.summaryBar.element.offsetHeight : scrollTop); - this.dividersElement.style.top = scrollTop + "px"; - this.dividersLabelBarElement.style.top = dividersTop + "px"; - }, - _graphSelected: function(treeElement) { if (this._lastSelectedGraphTreeElement) @@ -825,32 +610,25 @@ WebInspector.ResourcesPanel.prototype = { _toggleLargerResources: function() { - if (!this.resourcesTreeElement._childrenListNode) + if (!this.itemsTreeElement._childrenListNode) return; - this.resourcesTreeElement.smallChildren = !this.resourcesTreeElement.smallChildren; + this.itemsTreeElement.smallChildren = !this.itemsTreeElement.smallChildren; Preferences.resourcesLargeRows = !Preferences.resourcesLargeRows; InspectorController.setSetting("resources-large-rows", Preferences.resourcesLargeRows); - if (this.resourcesTreeElement.smallChildren) { - this.resourcesGraphsElement.addStyleClass("small"); + if (this.itemsTreeElement.smallChildren) { + this.itemsGraphsElement.addStyleClass("small"); this.largerResourcesButton.title = WebInspector.UIString("Use large resource rows."); this.largerResourcesButton.toggled = false; - this._adjustScrollPosition(); + this.adjustScrollPosition(); } else { - this.resourcesGraphsElement.removeStyleClass("small"); + this.itemsGraphsElement.removeStyleClass("small"); this.largerResourcesButton.title = WebInspector.UIString("Use small resource rows."); this.largerResourcesButton.toggled = true; } }, - _adjustScrollPosition: function() - { - // Prevent the container from being scrolled off the end. - if ((this.containerElement.scrollTop + this.containerElement.offsetHeight) > this.sidebarElement.offsetHeight) - this.containerElement.scrollTop = (this.sidebarElement.offsetHeight - this.containerElement.offsetHeight); - }, - _changeSortingFunction: function() { var selectedOption = this.sortingSelectElement[this.sortingSelectElement.selectedIndex]; @@ -875,41 +653,8 @@ WebInspector.ResourcesPanel.prototype = { } }, - _startSidebarDragging: function(event) - { - WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize"); - }, - - _sidebarDragging: function(event) + setSidebarWidth: function(width) { - this._updateSidebarWidth(event.pageX); - - event.preventDefault(); - }, - - _endSidebarDragging: function(event) - { - WebInspector.elementDragEnd(event); - }, - - _updateSidebarWidth: function(width) - { - if (this.sidebarElement.offsetWidth <= 0) { - // The stylesheet hasn't loaded yet or the window is closed, - // so we can't calculate what is need. Return early. - return; - } - - if (!("_currentSidebarWidth" in this)) - this._currentSidebarWidth = this.sidebarElement.offsetWidth; - - if (typeof width === "undefined") - width = this._currentSidebarWidth; - - width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2); - - this._currentSidebarWidth = width; - if (this.visibleResource) { this.containerElement.style.width = width + "px"; this.sidebarElement.style.removeProperty("width"); @@ -918,15 +663,13 @@ WebInspector.ResourcesPanel.prototype = { this.containerElement.style.removeProperty("width"); } - this.containerContentElement.style.left = width + "px"; - this.viewsContainerElement.style.left = width + "px"; this.sidebarResizeElement.style.left = (width - 3) + "px"; + }, - this._updateGraphDividersIfNeeded(); - - var visibleView = this.visibleView; - if (visibleView && "resize" in visibleView) - visibleView.resize(); + updateMainViewWidth: function(width) + { + WebInspector.AbstractTimelinePanel.prototype.updateMainViewWidth.call(this, width); + this.viewsContainerElement.style.left = width + "px"; }, _enableResourceTracking: function() @@ -947,88 +690,19 @@ WebInspector.ResourcesPanel.prototype = { this.sortingSelectElement.visible = true; InspectorController.enableResourceTracking(!!optionalAlways); } - } -} - -WebInspector.ResourcesPanel.prototype.__proto__ = WebInspector.Panel.prototype; - -WebInspector.ResourceCalculator = function() -{ -} - -WebInspector.ResourceCalculator.prototype = { - computeSummaryValues: function(resources) - { - var total = 0; - var categoryValues = {}; - - var resourcesLength = resources.length; - for (var i = 0; i < resourcesLength; ++i) { - var resource = resources[i]; - var value = this._value(resource); - if (typeof value === "undefined") - continue; - if (!(resource.category.name in categoryValues)) - categoryValues[resource.category.name] = 0; - categoryValues[resource.category.name] += value; - total += value; - } - - return {categoryValues: categoryValues, total: total}; - }, - - computeBarGraphPercentages: function(resource) - { - return {start: 0, middle: 0, end: (this._value(resource) / this.boundarySpan) * 100}; - }, - - computeBarGraphLabels: function(resource) - { - const label = this.formatValue(this._value(resource)); - var tooltip = label; - if (resource.cached) - tooltip = WebInspector.UIString("%s (from cache)", tooltip); - return {left: label, right: label, tooltip: tooltip}; - }, - - get boundarySpan() - { - return this.maximumBoundary - this.minimumBoundary; - }, - - updateBoundaries: function(resource) - { - this.minimumBoundary = 0; - - var value = this._value(resource); - if (typeof this.maximumBoundary === "undefined" || value > this.maximumBoundary) { - this.maximumBoundary = value; - return true; - } - - return false; }, - reset: function() + get _resources() { - delete this.minimumBoundary; - delete this.maximumBoundary; - }, - - _value: function(resource) - { - return 0; - }, - - formatValue: function(value) - { - return value.toString(); + return this._items; } } +WebInspector.ResourcesPanel.prototype.__proto__ = WebInspector.AbstractTimelinePanel.prototype; + WebInspector.ResourceTimeCalculator = function(startAtZero) { - WebInspector.ResourceCalculator.call(this); + WebInspector.AbstractTimelineCalculator.call(this); this.startAtZero = startAtZero; } @@ -1186,10 +860,10 @@ WebInspector.ResourceTimeCalculator.prototype = { _upperBound: function(resource) { return 0; - }, + } } -WebInspector.ResourceTimeCalculator.prototype.__proto__ = WebInspector.ResourceCalculator.prototype; +WebInspector.ResourceTimeCalculator.prototype.__proto__ = WebInspector.AbstractTimelineCalculator.prototype; WebInspector.ResourceTransferTimeCalculator = function() { @@ -1236,10 +910,19 @@ WebInspector.ResourceTransferDurationCalculator.prototype.__proto__ = WebInspect WebInspector.ResourceTransferSizeCalculator = function() { - WebInspector.ResourceCalculator.call(this); + WebInspector.AbstractTimelineCalculator.call(this); } WebInspector.ResourceTransferSizeCalculator.prototype = { + computeBarGraphLabels: function(resource) + { + const label = this.formatValue(this._value(resource)); + var tooltip = label; + if (resource.cached) + tooltip = WebInspector.UIString("%s (from cache)", tooltip); + return {left: label, right: label, tooltip: tooltip}; + }, + _value: function(resource) { return resource.contentLength; @@ -1251,7 +934,7 @@ WebInspector.ResourceTransferSizeCalculator.prototype = { } } -WebInspector.ResourceTransferSizeCalculator.prototype.__proto__ = WebInspector.ResourceCalculator.prototype; +WebInspector.ResourceTransferSizeCalculator.prototype.__proto__ = WebInspector.AbstractTimelineCalculator.prototype; WebInspector.ResourceSidebarTreeElement = function(resource) { @@ -1269,13 +952,12 @@ WebInspector.ResourceSidebarTreeElement.prototype = { { WebInspector.SidebarTreeElement.prototype.onattach.call(this); - var link = document.createElement("a"); - link.href = this.resource.url; - link.className = "invisible"; - while (this._listItemNode.firstChild) - link.appendChild(this._listItemNode.firstChild); - this._listItemNode.appendChild(link); this._listItemNode.addStyleClass("resources-category-" + this.resource.category.name); + this._listItemNode.draggable = true; + + // FIXME: should actually add handler to parent, to be resolved via + // https://bugs.webkit.org/show_bug.cgi?id=30227 + this._listItemNode.addEventListener("dragstart", this.ondragstart.bind(this), false); }, onselect: function() @@ -1288,6 +970,13 @@ WebInspector.ResourceSidebarTreeElement.prototype = { InjectedScriptAccess.openInInspectedWindow(this.resource.url, function() {}); }, + ondragstart: function(event) { + event.dataTransfer.setData("text/plain", this.resource.url); + event.dataTransfer.setData("text/uri-list", this.resource.url + "\r\n"); + event.dataTransfer.effectAllowed = "copy"; + return true; + }, + get mainTitle() { return this.resource.displayName; @@ -1318,7 +1007,7 @@ WebInspector.ResourceSidebarTreeElement.prototype = { get selectable() { - return WebInspector.panels.resources._filterCategory == "all" || WebInspector.panels.resources._filterCategory == this.resource.category.name; + return WebInspector.panels.resources.isCategoryVisible(this.resource.category.name); }, createIconElement: function() diff --git a/WebCore/inspector/front-end/ScriptsPanel.js b/WebCore/inspector/front-end/ScriptsPanel.js index 05ec197..4aa0ab2 100644 --- a/WebCore/inspector/front-end/ScriptsPanel.js +++ b/WebCore/inspector/front-end/ScriptsPanel.js @@ -388,22 +388,6 @@ WebInspector.ScriptsPanel.prototype = { InjectedScriptAccess.evaluateInCallFrame(callFrame.id, code, objectGroup, evalCallback); }, - variablesInSelectedCallFrame: function() - { - var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame; - if (!this._paused || !selectedCallFrame) - return {}; - - var result = {}; - var scopeChain = selectedCallFrame.scopeChain; - for (var i = 0; i < scopeChain.length; ++i) { - var scopeObjectProperties = scopeChain[i].properties; - for (var j = 0; j < scopeObjectProperties.length; ++j) - result[scopeObjectProperties[j]] = true; - } - return result; - }, - debuggerPaused: function(callFrames) { this._paused = true; diff --git a/WebCore/inspector/front-end/SourceFrame.js b/WebCore/inspector/front-end/SourceFrame.js index 790055a..1c11f76 100644 --- a/WebCore/inspector/front-end/SourceFrame.js +++ b/WebCore/inspector/front-end/SourceFrame.js @@ -425,7 +425,7 @@ WebInspector.SourceFrame.prototype = { return; var expression = selection.getRangeAt(0).toString().trimWhitespace(); - WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, function(result, exception) { + WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, "console", function(result, exception) { WebInspector.showConsole(); var commandMessage = new WebInspector.ConsoleCommand(expression); WebInspector.console.addMessage(commandMessage); @@ -980,132 +980,377 @@ WebInspector.CSSSourceSyntaxHighligher.prototype.__proto__ = WebInspector.Source WebInspector.JavaScriptSourceSyntaxHighlighter = function(table, sourceFrame) { WebInspector.SourceSyntaxHighligher.call(this, table, sourceFrame); - this.findNumber = this.generateFinder(/^(-?(\d+\.?\d*([eE][+-]\d+)?|0[xX]\h+|Infinity)|NaN)(?:\W|$)/, 1, "webkit-javascript-number"); - this.findKeyword = this.generateFinder(/^(null|true|false|break|case|catch|const|default|finally|for|instanceof|new|var|continue|function|return|void|delete|if|this|do|while|else|in|switch|throw|try|typeof|with|debugger|class|enum|export|extends|import|super|get|set)(?:\W|$)/, 1, "webkit-javascript-keyword"); - this.findSingleLineString = this.generateFinder(/^"(?:[^"\\]|\\.)*"|^'([^'\\]|\\.)*'/, 0, "webkit-javascript-string"); // " this quote keeps Xcode happy - this.findMultilineCommentStart = this.generateFinder(/^\/\*.*$/, 0, "webkit-javascript-comment"); - this.findMultilineCommentEnd = this.generateFinder(/^.*?\*\//, 0, "webkit-javascript-comment"); - this.findMultilineSingleQuoteStringStart = this.generateFinder(/^'(?:[^'\\]|\\.)*\\$/, 0, "webkit-javascript-string"); - this.findMultilineSingleQuoteStringEnd = this.generateFinder(/^(?:[^'\\]|\\.)*?'/, 0, "webkit-javascript-string"); - this.findMultilineDoubleQuoteStringStart = this.generateFinder(/^"(?:[^"\\]|\\.)*\\$/, 0, "webkit-javascript-string"); - this.findMultilineDoubleQuoteStringEnd = this.generateFinder(/^(?:[^"\\]|\\.)*?"/, 0, "webkit-javascript-string"); - this.findMultilineRegExpEnd = this.generateFinder(/^(?:[^\/\\]|\\.)*?\/([gim]{0,3})/, 0, "webkit-javascript-regexp"); - this.findSingleLineComment = this.generateFinder(/^\/\/.*|^\/\*.*?\*\//, 0, "webkit-javascript-comment"); -} - -WebInspector.JavaScriptSourceSyntaxHighlighter.prototype = { - deleteContinueFlags: function(cell) + this.LexState = { + Initial: 1, + DivisionAllowed: 2, + }; + this.ContinueState = { + None: 0, + Comment: 1, + SingleQuoteString: 2, + DoubleQuoteString: 3, + RegExp: 4 + }; + + this.nonToken = ""; + this.cursor = 0; + this.lineIndex = -1; + this.lineCode = ""; + this.lineFragment = null; + this.lexState = this.LexState.Initial; + this.continueState = this.ContinueState.None; + + this.rules = [{ + pattern: /^(?:\/\/.*)/, + action: singleLineCommentAction + }, { + pattern: /^(?:\/\*(?:[^\*]|\*[^\/])*\*+\/)/, + action: multiLineSingleLineCommentAction + }, { + pattern: /^(?:\/\*(?:[^\*]|\*[^\/])*)/, + action: multiLineCommentStartAction + }, { + pattern: /^(?:(?:[^\*]|\*[^\/])*\*+\/)/, + action: multiLineCommentEndAction, + continueStateCondition: this.ContinueState.Comment + }, { + pattern: /^.*/, + action: multiLineCommentMiddleAction, + continueStateCondition: this.ContinueState.Comment + }, { + pattern: /^(?:(?:0|[1-9]\d*)\.\d+?(?:[eE](?:\d+|\+\d+|-\d+))?|\.\d+(?:[eE](?:\d+|\+\d+|-\d+))?|(?:0|[1-9]\d*)(?:[eE](?:\d+|\+\d+|-\d+))?|0x[0-9a-fA-F]+|0X[0-9a-fA-F]+)/, + action: numericLiteralAction + }, { + pattern: /^(?:"(?:[^"\\]|\\(?:['"\bfnrtv]|[^'"\bfnrtv0-9xu]|0|x[0-9a-fA-F][0-9a-fA-F]|(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))*"|'(?:[^'\\]|\\(?:['"\bfnrtv]|[^'"\bfnrtv0-9xu]|0|x[0-9a-fA-F][0-9a-fA-F]|(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))*')/, + action: stringLiteralAction + }, { + pattern: /^(?:'(?:[^'\\]|\\(?:['"\bfnrtv]|[^'"\bfnrtv0-9xu]|0|x[0-9a-fA-F][0-9a-fA-F]|(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))*)\\$/, + action: singleQuoteStringStartAction + }, { + pattern: /^(?:(?:[^'\\]|\\(?:['"\bfnrtv]|[^'"\bfnrtv0-9xu]|0|x[0-9a-fA-F][0-9a-fA-F]|(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))*')/, + action: singleQuoteStringEndAction, + continueStateCondition: this.ContinueState.SingleQuoteString + }, { + pattern: /^(?:(?:[^'\\]|\\(?:['"\bfnrtv]|[^'"\bfnrtv0-9xu]|0|x[0-9a-fA-F][0-9a-fA-F]|(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))*)\\$/, + action: singleQuoteStringMiddleAction, + continueStateCondition: this.ContinueState.SingleQuoteString + }, { + pattern: /^(?:"(?:[^"\\]|\\(?:['"\bfnrtv]|[^'"\bfnrtv0-9xu]|0|x[0-9a-fA-F][0-9a-fA-F]|(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))*)\\$/, + action: doubleQuoteStringStartAction + }, { + pattern: /^(?:(?:[^"\\]|\\(?:['"\bfnrtv]|[^'"\bfnrtv0-9xu]|0|x[0-9a-fA-F][0-9a-fA-F]|(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))*")/, + action: doubleQuoteStringEndAction, + continueStateCondition: this.ContinueState.DoubleQuoteString + }, { + pattern: /^(?:(?:[^"\\]|\\(?:['"\bfnrtv]|[^'"\bfnrtv0-9xu]|0|x[0-9a-fA-F][0-9a-fA-F]|(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))*)\\$/, + action: doubleQuoteStringMiddleAction, + continueStateCondition: this.ContinueState.DoubleQuoteString + }, { + pattern: /^(?:(?:[a-zA-Z]|[$_]|\\(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))(?:(?:[a-zA-Z]|[$_]|\\(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))|[0-9])*)/, + action: identOrKeywordAction, + dontAppendNonToken: true + }, { + pattern: /^\)/, + action: rightParenAction, + dontAppendNonToken: true + }, { + pattern: /^(?:<=|>=|===|==|!=|!==|\+\+|\-\-|<<|>>|>>>|&&|\|\||\+=|\-=|\*=|%=|<<=|>>=|>>>=|&=|\|=|^=|[{}\(\[\]\.;,<>\+\-\*%&\|\^!~\?:=])/, + action: punctuatorAction, + dontAppendNonToken: true + }, { + pattern: /^(?:\/=?)/, + action: divPunctuatorAction, + stateCondition: this.LexState.DivisionAllowed, + dontAppendNonToken: true + }, { + pattern: /^(?:\/(?:(?:\\.)|[^\\*\/])(?:(?:\\.)|[^\\/])*\/(?:(?:[a-zA-Z]|[$_]|\\(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))|[0-9])*)/, + action: regExpLiteralAction + }, { + pattern: /^(?:\/(?:(?:\\.)|[^\\*\/])(?:(?:\\.)|[^\\/])*)\\$/, + action: regExpStartAction + }, { + pattern: /^(?:(?:(?:\\.)|[^\\/])*\/(?:(?:[a-zA-Z]|[$_]|\\(?:u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))|[0-9])*)/, + action: regExpEndAction, + continueStateCondition: this.ContinueState.RegExp + }, { + pattern: /^(?:(?:(?:\\.)|[^\\/])*)\\$/, + action: regExpMiddleAction, + continueStateCondition: this.ContinueState.RegExp + }]; + + function singleLineCommentAction(token) { - if (!cell) - return; - delete cell._commentContinues; - delete cell._singleQuoteStringContinues; - delete cell._doubleQuoteStringContinues; - delete cell._regexpContinues; - }, - - findMultilineRegExpStart: function(str) + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-comment")); + } + + function multiLineSingleLineCommentAction(token) { - var match = /^\/(?:[^\/\\]|\\.)*\\$/.exec(str); - if (!match || !/\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[0])) - return null; - this.previousMatchLength = match[0].length; - return this.createSpan(match[0], "webkit-javascript-regexp"); - }, - - findSingleLineRegExp: function(str) + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-comment")); + } + + function multiLineCommentStartAction(token) { - var match = /^(\/(?:[^\/\\]|\\.)*\/([gim]{0,3}))(.?)/.exec(str); - if (!match || !(match[2].length > 0 || /\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[1]) || /\.|;|,/.test(match[3]))) - return null; - this.previousMatchLength = match[1].length; - return this.createSpan(match[1], "webkit-javascript-regexp"); - }, + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-comment")); + this.continueState = this.ContinueState.Comment; + } + + function multiLineCommentEndAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-comment")); + this.continueState = this.ContinueState.None; + } + + function multiLineCommentMiddleAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-comment")); + } + + function numericLiteralAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-number")); + this.lexState = this.LexState.DivisionAllowed; + } + + function stringLiteralAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-string")); + this.lexState = this.LexState.Initial; + } + + function singleQuoteStringStartAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-string")); + this.continueState = this.ContinueState.SingleQuoteString; + } + + function singleQuoteStringEndAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-string")); + this.continueState = this.ContinueState.None; + } + + function singleQuoteStringMiddleAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-string")); + } + + function doubleQuoteStringStartAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-string")); + this.continueState = this.ContinueState.DoubleQuoteString; + } + + function doubleQuoteStringEndAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-string")); + this.continueState = this.ContinueState.None; + } + + function doubleQuoteStringMiddleAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-string")); + } + + function regExpLiteralAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-regexp")); + this.lexState = this.LexState.Initial; + } - syntaxHighlightLine: function(line, prevLine) + function regExpStartAction(token) { - var messageBubble = line.lastChild; - if (messageBubble && messageBubble.nodeType === Node.ELEMENT_NODE && messageBubble.hasStyleClass("webkit-html-message-bubble")) - line.removeChild(messageBubble); - else - messageBubble = null; + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-regexp")); + this.continueState = this.ContinueState.RegExp; + } - var code = line.textContent; + function regExpEndAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-regexp")); + this.continueState = this.ContinueState.None; + } - while (line.firstChild) - line.removeChild(line.firstChild); + function regExpMiddleAction(token) + { + this.cursor += token.length; + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-regexp")); + } + + function identOrKeywordAction(token) + { + const keywords = ["null", "true", "false", "break", "case", "catch", "const", "default", "finally", "for", "instanceof", "new", "var", "continue", "function", "return", "void", "delete", "if", "this", "do", "while", "else", "in", "switch", "throw", "try", "typeof", "with", "debugger", "class", "enum", "export", "extends", "import", "super", "get", "set"]; + this.cursor += token.length; + if (keywords.indexOf(token) === -1) { + this.nonToken += token; + this.lexState = this.LexState.DivisionAllowed; + } else { + this.appendNonToken(); + this.lineFragment.appendChild(this.createSpan(token, "webkit-javascript-keyword")); + this.lexState = this.LexState.Initial; + } + } + + function divPunctuatorAction(token) + { + this.cursor += token.length; + this.nonToken += token; + this.lexState = this.LexState.Initial; + } + + function rightParenAction(token) + { + this.cursor += token.length; + this.nonToken += token; + this.lexState = this.LexState.DivisionAllowed; + } + + function punctuatorAction(token) + { + this.cursor += token.length; + this.nonToken += token; + this.lexState = this.LexState.Initial; + } +} - var token; - var tmp = 0; - var i = 0; - this.previousMatchLength = 0; +WebInspector.JavaScriptSourceSyntaxHighlighter.prototype = { + process: function() + { + // Split up the work into chunks so we don't block the + // UI thread while processing. - if (prevLine) { - if (prevLine._commentContinues) { - if (!(token = this.findMultilineCommentEnd(code))) { - token = this.createSpan(code, "webkit-javascript-comment"); - line._commentContinues = true; - } - } else if (prevLine._singleQuoteStringContinues) { - if (!(token = this.findMultilineSingleQuoteStringEnd(code))) { - token = this.createSpan(code, "webkit-javascript-string"); - line._singleQuoteStringContinues = true; + var rows = this.table.rows; + var rowsLength = rows.length; + const tokensPerChunk = 100; + const lineLengthLimit = 20000; + + var boundProcessChunk = processChunk.bind(this); + var processChunkInterval = setInterval(boundProcessChunk, 25); + boundProcessChunk(); + + function processChunk() + { + for (var i = 0; i < tokensPerChunk; i++) { + if (this.cursor >= this.lineCode.length) + moveToNextLine.call(this); + if (this.lineIndex >= rowsLength) { + this.sourceFrame.dispatchEventToListeners("syntax highlighting complete"); + return; } - } else if (prevLine._doubleQuoteStringContinues) { - if (!(token = this.findMultilineDoubleQuoteStringEnd(code))) { - token = this.createSpan(code, "webkit-javascript-string"); - line._doubleQuoteStringContinues = true; + if (this.cursor > lineLengthLimit) { + var codeFragment = this.lineCode.substring(this.cursor); + this.nonToken += codeFragment; + this.cursor += codeFragment.length; } - } else if (prevLine._regexpContinues) { - if (!(token = this.findMultilineRegExpEnd(code))) { - token = this.createSpan(code, "webkit-javascript-regexp"); - line._regexpContinues = true; + + this.lex(); + } + } + + function moveToNextLine() + { + this.appendNonToken(); + + var row = rows[this.lineIndex]; + var line = row ? row.cells[1] : null; + if (line && this.lineFragment) { + var messageBubble = null; + if (line.lastChild && line.lastChild.nodeType === Node.ELEMENT_NODE && line.lastChild.hasStyleClass("webkit-html-message-bubble")) { + messageBubble = line.lastChild; + line.removeChild(messageBubble); } + + Element.prototype.removeChildren.call(line); + + line.appendChild(this.lineFragment); + if (messageBubble) + line.appendChild(messageBubble); + this.lineFragment = null; } - if (token) { - i += this.previousMatchLength ? this.previousMatchLength : code.length; - tmp = i; - line.appendChild(token); + this.lineIndex++; + if (this.lineIndex >= rowsLength && processChunkInterval) { + clearInterval(processChunkInterval); + this.sourceFrame.dispatchEventToListeners("syntax highlighting complete"); + return; } + row = rows[this.lineIndex]; + line = row ? row.cells[1] : null; + this.lineCode = line.textContent; + this.lineFragment = document.createDocumentFragment(); + this.cursor = 0; + if (!line) + moveToNextLine(); } - - for ( ; i < code.length; ++i) { - var codeFragment = code.substr(i); - var prevChar = code[i - 1]; - token = this.findSingleLineComment(codeFragment); - if (!token) { - if ((token = this.findMultilineCommentStart(codeFragment))) - line._commentContinues = true; - else if (!prevChar || /^\W/.test(prevChar)) { - token = this.findNumber(codeFragment) || - this.findKeyword(codeFragment) || - this.findSingleLineString(codeFragment) || - this.findSingleLineRegExp(codeFragment); - if (!token) { - if (token = this.findMultilineSingleQuoteStringStart(codeFragment)) - line._singleQuoteStringContinues = true; - else if (token = this.findMultilineDoubleQuoteStringStart(codeFragment)) - line._doubleQuoteStringContinues = true; - else if (token = this.findMultilineRegExpStart(codeFragment)) - line._regexpContinues = true; + }, + + lex: function() + { + var token = null; + var codeFragment = this.lineCode.substring(this.cursor); + + for (var i = 0; i < this.rules.length; i++) { + var rule = this.rules[i]; + var ruleContinueStateCondition = typeof rule.continueStateCondition === "undefined" ? this.ContinueState.None : rule.continueStateCondition; + if (this.continueState === ruleContinueStateCondition) { + if (typeof rule.stateCondition !== "undefined" && this.lexState !== rule.stateCondition) + continue; + var match = rule.pattern.exec(codeFragment); + if (match) { + token = match[0]; + if (token) { + if (!rule.dontAppendNonToken) + this.appendNonToken(); + return rule.action.call(this, token); } } } - - if (token) { - if (tmp !== i) - line.appendChild(document.createTextNode(code.substring(tmp, i))); - line.appendChild(token); - i += this.previousMatchLength - 1; - tmp = i + 1; - } } + this.nonToken += codeFragment[0]; + this.cursor++; + }, + + appendNonToken: function () + { + if (this.nonToken.length > 0) { + this.lineFragment.appendChild(document.createTextNode(this.nonToken)); + this.nonToken = ""; + } + }, + + syntaxHighlightNode: function(node) + { + this.lineCode = node.textContent; + this.lineFragment = document.createDocumentFragment(); + this.cursor = 0; + while (true) { + if (this.cursor >= this.lineCode.length) { + var codeFragment = this.lineCode.substring(this.cursor); + this.nonToken += codeFragment; + this.cursor += codeFragment.length; + this.appendNonToken(); + while (node.firstChild) + node.removeChild(node.firstChild); + node.appendChild(this.lineFragment); + this.lineFragment =null; + return; + } - if (tmp < code.length) - line.appendChild(document.createTextNode(code.substring(tmp, i))); - - if (messageBubble) - line.appendChild(messageBubble); + this.lex(); + } } } diff --git a/WebCore/inspector/front-end/StoragePanel.js b/WebCore/inspector/front-end/StoragePanel.js index 089bb98..1aa11ee 100644 --- a/WebCore/inspector/front-end/StoragePanel.js +++ b/WebCore/inspector/front-end/StoragePanel.js @@ -31,21 +31,7 @@ WebInspector.StoragePanel = function(database) { WebInspector.Panel.call(this); - this.sidebarElement = document.createElement("div"); - this.sidebarElement.id = "storage-sidebar"; - this.sidebarElement.className = "sidebar"; - this.element.appendChild(this.sidebarElement); - - this.sidebarResizeElement = document.createElement("div"); - this.sidebarResizeElement.className = "sidebar-resizer-vertical"; - this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarDragging.bind(this), false); - this.element.appendChild(this.sidebarResizeElement); - - this.sidebarTreeElement = document.createElement("ol"); - this.sidebarTreeElement.className = "sidebar-tree"; - this.sidebarElement.appendChild(this.sidebarTreeElement); - - this.sidebarTree = new TreeOutline(this.sidebarTreeElement); + this.createSidebar(); this.databasesListTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("DATABASES"), {}, true); this.sidebarTree.appendChild(this.databasesListTreeElement); @@ -86,12 +72,6 @@ WebInspector.StoragePanel.prototype = { return [this.storageViewStatusBarItemsContainer]; }, - show: function() - { - WebInspector.Panel.prototype.show.call(this); - this._updateSidebarWidth(); - }, - reset: function() { if (this._databases) { @@ -133,11 +113,6 @@ WebInspector.StoragePanel.prototype = { this.sidebarTree.selectedTreeElement.deselect(); }, - handleKeyEvent: function(event) - { - this.sidebarTree.handleKeyEvent(event); - }, - addDatabase: function(database) { this._databases.push(database); @@ -416,49 +391,10 @@ WebInspector.StoragePanel.prototype = { return null; }, - _startSidebarDragging: function(event) - { - WebInspector.elementDragStart(this.sidebarResizeElement, this._sidebarDragging.bind(this), this._endSidebarDragging.bind(this), event, "col-resize"); - }, - - _sidebarDragging: function(event) - { - this._updateSidebarWidth(event.pageX); - - event.preventDefault(); - }, - - _endSidebarDragging: function(event) + updateMainViewWidth: function(width) { - WebInspector.elementDragEnd(event); - }, - - _updateSidebarWidth: function(width) - { - if (this.sidebarElement.offsetWidth <= 0) { - // The stylesheet hasn't loaded yet or the window is closed, - // so we can't calculate what is need. Return early. - return; - } - - if (!("_currentSidebarWidth" in this)) - this._currentSidebarWidth = this.sidebarElement.offsetWidth; - - if (typeof width === "undefined") - width = this._currentSidebarWidth; - - width = Number.constrain(width, Preferences.minSidebarWidth, window.innerWidth / 2); - - this._currentSidebarWidth = width; - - this.sidebarElement.style.width = width + "px"; this.storageViews.style.left = width + "px"; this.storageViewStatusBarItemsContainer.style.left = width + "px"; - this.sidebarResizeElement.style.left = (width - 3) + "px"; - - var visibleView = this.visibleView; - if (visibleView && "resize" in visibleView) - visibleView.resize(); } } @@ -558,7 +494,7 @@ WebInspector.DOMStorageSidebarTreeElement.prototype = { get mainTitle() { - return this.domStorage.domain; + return this.domStorage.domain ? this.domStorage.domain : WebInspector.UIString("Local Files"); }, set mainTitle(x) @@ -592,6 +528,16 @@ WebInspector.CookieSidebarTreeElement.prototype = { { WebInspector.panels.storage.showCookies(this._cookieDomain); }, + + get mainTitle() + { + return this._cookieDomain ? this._cookieDomain : WebInspector.UIString("Local Files"); + }, + + set mainTitle(x) + { + // Do nothing. + }, get subtitle() { diff --git a/WebCore/inspector/front-end/StylesSidebarPane.js b/WebCore/inspector/front-end/StylesSidebarPane.js index 3582f96..a95dae3 100644 --- a/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/WebCore/inspector/front-end/StylesSidebarPane.js @@ -376,7 +376,7 @@ WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyl { WebInspector.PropertiesSection.call(this, styleRule.selectorText); - this.titleElement.addEventListener("click", function(e) { e.stopPropagation(); }, false); + this.titleElement.addEventListener("click", this._clickSelector.bind(this), false); this.titleElement.addEventListener("dblclick", this._dblclickSelector.bind(this), false); this.element.addEventListener("dblclick", this._dblclickEmptySpace.bind(this), false); @@ -591,6 +591,19 @@ WebInspector.StylePropertiesSection.prototype = { return item; }, + _clickSelector: function(event) + { + event.stopPropagation(); + + // Don't search "Computed Styles", "Style Attribute", or Mapped Attributes. + if (this.computedStyle || !this.rule || this.rule.isUser) + return; + + var searchElement = document.getElementById("search"); + searchElement.value = this.styleRule.selectorText; + WebInspector.performSearch({ target: searchElement }); + }, + _dblclickEmptySpace: function(event) { this.expand(); diff --git a/WebCore/inspector/front-end/SummaryBar.js b/WebCore/inspector/front-end/SummaryBar.js index bbf2b1a..1c31449 100644 --- a/WebCore/inspector/front-end/SummaryBar.js +++ b/WebCore/inspector/front-end/SummaryBar.js @@ -74,8 +74,7 @@ WebInspector.SummaryBar.prototype = { if (!size) continue; - var color = this.categories[category].color; - var colorString = "rgb(" + color.r + ", " + color.g + ", " + color.b + ")"; + var colorString = this.categories[category].color; var fillSegment = {color: colorString, value: size}; fillSegments.push(fillSegment); diff --git a/WebCore/inspector/front-end/TestController.js b/WebCore/inspector/front-end/TestController.js new file mode 100644 index 0000000..12e1ac7 --- /dev/null +++ b/WebCore/inspector/front-end/TestController.js @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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.TestController = function(callId) +{ + this._callId = callId; + this._waitUntilDone = false; +} + +WebInspector.TestController.prototype = { + waitUntilDone: function() + { + this._waitUntilDone = true; + }, + + notifyDone: function(result) + { + var message = typeof result === "undefined" ? "<undefined>" : JSON.stringify(result); + InspectorController.didEvaluateForTestInFrontend(this._callId, message); + }, + + runAfterPendingDispatches: function(callback) + { + if (WebInspector.pendingDispatches === 0) { + callback(); + return; + } + + setTimeout(this.runAfterPendingDispatches.bind(this), 0, callback); + } +} + +WebInspector.evaluateForTestInFrontend = function(callId, script) +{ + var controller = new WebInspector.TestController(callId); + try { + var result; + if (window[script] && typeof window[script] === "function") + result = window[script].call(this, controller); + else + result = window.eval(script); + + if (!controller._waitUntilDone) + controller.notifyDone(result); + } catch (e) { + controller.notifyDone(e.toString()); + } +} diff --git a/WebCore/inspector/front-end/TimelineAgent.js b/WebCore/inspector/front-end/TimelineAgent.js index 6d18732..cbbb736 100644 --- a/WebCore/inspector/front-end/TimelineAgent.js +++ b/WebCore/inspector/front-end/TimelineAgent.js @@ -33,22 +33,31 @@ WebInspector.TimelineAgent = function() { } // Must be kept in sync with TimelineItem.h -WebInspector.TimelineAgent.ItemType = { - DOMDispatch : 0, - Layout : 1, - RecalculateStyles : 2, - Paint : 3, - ParseHTML : 4 +WebInspector.TimelineAgent.RecordType = { + DOMDispatch : 0, + Layout : 1, + RecalculateStyles : 2, + Paint : 3, + ParseHTML : 4, + TimerInstall : 5, + TimerRemove : 6, + TimerFire : 7, + XHRReadyStateChange : 8, + XHRLoad : 9, + EvaluateScriptTag : 10 }; -WebInspector.addItemToTimeline = function(record) { - // Not implemented. +WebInspector.addRecordToTimeline = function(record) { + if (WebInspector.panels.timeline) + WebInspector.panels.timeline.addRecordToTimeline(record); } -WebInspector.timelineWasEnabled = function() { - // Not implemented. +WebInspector.timelineProfilerWasStarted = function() { + if (WebInspector.panels.timeline) + WebInspector.panels.timeline.timelineWasStarted(); } -WebInspector.timelineWasDisabled = function() { - // Not implemented. +WebInspector.timelineProfilerWasStopped = function() { + if (WebInspector.panels.timeline) + WebInspector.panels.timeline.timelineWasStopped(); } diff --git a/WebCore/inspector/front-end/TimelinePanel.js b/WebCore/inspector/front-end/TimelinePanel.js new file mode 100644 index 0000000..df63a0b --- /dev/null +++ b/WebCore/inspector/front-end/TimelinePanel.js @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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.TimelinePanel = function() +{ + WebInspector.AbstractTimelinePanel.call(this); + + this.element.addStyleClass("timeline"); + + this.createInterface(); + this.summaryBar.element.id = "timeline-summary"; + this.itemsGraphsElement.id = "timeline-graphs"; + + this._createStatusbarButtons(); + + this.calculator = new WebInspector.TimelineCalculator(); + + this.filter(this.filterAllElement, false); +} + +WebInspector.TimelinePanel.prototype = { + toolbarItemClass: "timeline", + + get toolbarItemLabel() + { + return WebInspector.UIString("Timeline"); + }, + + get statusBarItems() + { + return [this.toggleTimelineButton.element, this.clearButton.element]; + }, + + get categories() + { + if (!this._categories) { + this._categories = { + loading: new WebInspector.TimelineCategory("loading", WebInspector.UIString("Loading"), "rgb(47,102,236)"), + scripting: new WebInspector.TimelineCategory("scripting", WebInspector.UIString("Scripting"), "rgb(157,231,119)"), + rendering: new WebInspector.TimelineCategory("rendering", WebInspector.UIString("Rendering"), "rgb(164,60,255)"), + other: new WebInspector.TimelineCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)") + }; + } + return this._categories; + }, + + populateSidebar: function() + { + this.itemsTreeElement = new WebInspector.SidebarSectionTreeElement(WebInspector.UIString("RECORDS"), {}, true); + this.itemsTreeElement.expanded = true; + this.sidebarTree.appendChild(this.itemsTreeElement); + }, + + _createStatusbarButtons: function() + { + this.toggleTimelineButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item"); + this.toggleTimelineButton.addEventListener("click", this._toggleTimelineButtonClicked.bind(this), false); + + this.clearButton = new WebInspector.StatusBarButton("", "timeline-clear-status-bar-item"); + this.clearButton.addEventListener("click", this.reset.bind(this), false); + }, + + timelineWasStarted: function() + { + this.toggleTimelineButton.toggled = true; + }, + + timelineWasStopped: function() + { + this.toggleTimelineButton.toggled = false; + }, + + addRecordToTimeline: function(record) + { + var formattedRecord = this._formatRecord(record); + // Glue subsequent records with same category and title together if they are closer than 100ms to each other. + if (this._lastRecord && (!record.children || !record.children.length) && + this._lastRecord.category == formattedRecord.category && + this._lastRecord.title == formattedRecord.title && + this._lastRecord.details == formattedRecord.details && + formattedRecord.startTime - this._lastRecord.endTime < 0.1) { + this._lastRecord.endTime = formattedRecord.endTime; + this._lastRecord.count++; + this.refreshItem(this._lastRecord); + } else { + this.addItem(formattedRecord); + + for (var i = 0; record.children && i < record.children.length; ++i) + this.addRecordToTimeline(record.children[i]); + this._lastRecord = record.children && record.children.length ? null : formattedRecord; + } + }, + + createItemTreeElement: function(item) + { + return new WebInspector.TimelineRecordTreeElement(item); + }, + + createItemGraph: function(item) + { + return new WebInspector.TimelineGraph(item); + }, + + _toggleTimelineButtonClicked: function() + { + if (InspectorController.timelineProfilerEnabled()) + InspectorController.stopTimelineProfiler(); + else + InspectorController.startTimelineProfiler(); + }, + + _formatRecord: function(record) + { + if (!this._recordStyles) { + this._recordStyles = {}; + var recordTypes = WebInspector.TimelineAgent.RecordType; + this._recordStyles[recordTypes.DOMDispatch] = { title: WebInspector.UIString("DOM Event"), category: this.categories.scripting }; + this._recordStyles[recordTypes.Layout] = { title: WebInspector.UIString("Layout"), category: this.categories.rendering }; + this._recordStyles[recordTypes.RecalculateStyles] = { title: WebInspector.UIString("Recalculate Style"), category: this.categories.rendering }; + this._recordStyles[recordTypes.Paint] = { title: WebInspector.UIString("Paint"), category: this.categories.rendering }; + this._recordStyles[recordTypes.ParseHTML] = { title: WebInspector.UIString("Parse"), category: this.categories.loading }; + this._recordStyles[recordTypes.TimerInstall] = { title: WebInspector.UIString("Install Timer"), category: this.categories.scripting }; + this._recordStyles[recordTypes.TimerRemove] = { title: WebInspector.UIString("Remove Timer"), category: this.categories.scripting }; + this._recordStyles[recordTypes.TimerFire] = { title: WebInspector.UIString("Timer Fired"), category: this.categories.scripting }; + this._recordStyles[recordTypes.XHRReadyStateChange] = { title: WebInspector.UIString("XHR Ready State Change"), category: this.categories.scripting }; + this._recordStyles[recordTypes.XHRLoad] = { title: WebInspector.UIString("XHR Load"), category: this.categories.scripting }; + this._recordStyles[recordTypes.EvaluateScriptTag] = { title: WebInspector.UIString("Evaluate Script"), category: this.categories.scripting }; + this._recordStyles["Other"] = { title: WebInspector.UIString("Other"), icon: 0, category: this.categories.other }; + } + + var style = this._recordStyles[record.type]; + if (!style) + style = this._recordStyles["Other"]; + + var formattedRecord = {}; + formattedRecord.category = style.category; + formattedRecord.title = style.title; + formattedRecord.startTime = record.startTime / 1000; + formattedRecord.data = record.data; + formattedRecord.count = 1; + formattedRecord.type = record.type; + formattedRecord.details = this._getRecordDetails(record); + formattedRecord.endTime = (typeof record.endTime !== "undefined") ? record.endTime / 1000 : formattedRecord.startTime; + return formattedRecord; + }, + + _getRecordDetails: function(record) + { + switch (record.type) { + case WebInspector.TimelineAgent.RecordType.DOMDispatch: + return record.data.type; + case WebInspector.TimelineAgent.RecordType.TimerInstall: + case WebInspector.TimelineAgent.RecordType.TimerRemove: + case WebInspector.TimelineAgent.RecordType.TimerFire: + return record.data.timerId; + case WebInspector.TimelineAgent.RecordType.XHRReadyStateChange: + case WebInspector.TimelineAgent.RecordType.XHRLoad: + case WebInspector.TimelineAgent.RecordType.EvaluateScriptTag: + return record.data.url; + default: + return ""; + } + }, + + reset: function() + { + WebInspector.AbstractTimelinePanel.prototype.reset.call(this); + this._lastRecord = null; + } +} + +WebInspector.TimelinePanel.prototype.__proto__ = WebInspector.AbstractTimelinePanel.prototype; + + +WebInspector.TimelineCategory = function(name, title, color) +{ + WebInspector.AbstractTimelineCategory.call(this, name, title, color); +} + +WebInspector.TimelineCategory.prototype = { +} + +WebInspector.TimelineCategory.prototype.__proto__ = WebInspector.AbstractTimelineCategory.prototype; + + +WebInspector.TimelineRecordTreeElement = function(record) +{ + this._record = record; + + // Pass an empty title, the title gets made later in onattach. + TreeElement.call(this, "", null, false); +} + +WebInspector.TimelineRecordTreeElement.prototype = { + onattach: function() + { + this.listItemElement.removeChildren(); + this.listItemElement.addStyleClass("timeline-tree-item"); + this.listItemElement.addStyleClass("timeline-category-" + this._record.category.name); + + var iconElement = document.createElement("span"); + iconElement.className = "timeline-tree-icon"; + this.listItemElement.appendChild(iconElement); + + this.typeElement = document.createElement("span"); + this.typeElement.className = "type"; + this.typeElement.textContent = this._record.title; + this.listItemElement.appendChild(this.typeElement); + + if (this._record.details) { + var separatorElement = document.createElement("span"); + separatorElement.className = "separator"; + separatorElement.textContent = " "; + + var dataElement = document.createElement("span"); + dataElement.className = "data"; + dataElement.textContent = "(" + this._record.details + ")"; + dataElement.addStyleClass("dimmed"); + this.listItemElement.appendChild(separatorElement); + this.listItemElement.appendChild(dataElement); + } + }, + + refresh: function() + { + if (this._record.count > 1) + this.typeElement.textContent = this._record.title + " x " + this._record.count; + } +} + +WebInspector.TimelineRecordTreeElement.prototype.__proto__ = TreeElement.prototype; + + +WebInspector.TimelineCalculator = function() +{ + WebInspector.AbstractTimelineCalculator.call(this); +} + +WebInspector.TimelineCalculator.prototype = { + computeBarGraphPercentages: function(record) + { + var start = ((record.startTime - this.minimumBoundary) / this.boundarySpan) * 100; + var end = ((record.endTime - this.minimumBoundary) / this.boundarySpan) * 100; + return {start: start, end: end}; + }, + + computePercentageFromEventTime: function(eventTime) + { + return ((eventTime - this.minimumBoundary) / this.boundarySpan) * 100; + }, + + computeBarGraphLabels: function(record) + { + return {tooltip: record.title}; + }, + + updateBoundaries: function(record) + { + var didChange = false; + + var lowerBound = record.startTime; + + if (typeof this.minimumBoundary === "undefined" || lowerBound < this.minimumBoundary) { + this.minimumBoundary = lowerBound; + didChange = true; + } + + var upperBound = record.endTime; + if (typeof this.maximumBoundary === "undefined" || upperBound > this.maximumBoundary) { + this.maximumBoundary = upperBound; + didChange = true; + } + + return didChange; + }, + + formatValue: function(value) + { + return Number.secondsToString(value, WebInspector.UIString.bind(WebInspector)); + }, + +} + +WebInspector.TimelineCalculator.prototype.__proto__ = WebInspector.AbstractTimelineCalculator.prototype; + + +WebInspector.TimelineGraph = function(record) +{ + this.record = record; + + this._graphElement = document.createElement("div"); + this._graphElement.className = "timeline-graph-side"; + + this._barAreaElement = document.createElement("div"); + this._barAreaElement.className = "timeline-graph-bar-area hidden"; + this._graphElement.appendChild(this._barAreaElement); + + this._barElement = document.createElement("div"); + this._barElement.className = "timeline-graph-bar"; + this._barAreaElement.appendChild(this._barElement); + + this._graphElement.addStyleClass("timeline-category-" + record.category.name); +} + +WebInspector.TimelineGraph.prototype = { + get graphElement() + { + return this._graphElement; + }, + + refreshLabelPositions: function() + { + }, + + refresh: function(calculator) + { + var percentages = calculator.computeBarGraphPercentages(this.record); + var labels = calculator.computeBarGraphLabels(this.record); + + this._percentages = percentages; + + this._barAreaElement.removeStyleClass("hidden"); + + if (!this._graphElement.hasStyleClass("timeline-category-" + this.record.category.name)) { + this._graphElement.removeMatchingStyleClasses("timeline-category-\\w+"); + this._graphElement.addStyleClass("timeline-category-" + this.record.category.name); + } + + this._barElement.style.setProperty("left", percentages.start + "%"); + this._barElement.style.setProperty("right", (100 - percentages.end) + "%"); + + var tooltip = (labels.tooltip || ""); + this._barElement.title = tooltip; + } +} diff --git a/WebCore/inspector/front-end/TopDownProfileDataGridTree.js b/WebCore/inspector/front-end/TopDownProfileDataGridTree.js index b9d8b94..1b07883 100644 --- a/WebCore/inspector/front-end/TopDownProfileDataGridTree.js +++ b/WebCore/inspector/front-end/TopDownProfileDataGridTree.js @@ -33,7 +33,7 @@ WebInspector.TopDownProfileDataGridNode = function(/*ProfileView*/ profileView, } WebInspector.TopDownProfileDataGridNode.prototype = { - _populate: function(event) + _sharedPopulate: function() { var children = this._remainingChildren; var childrenLength = children.length; @@ -41,9 +41,6 @@ WebInspector.TopDownProfileDataGridNode.prototype = { for (var i = 0; i < childrenLength; ++i) this.appendChild(new WebInspector.TopDownProfileDataGridNode(this.profileView, children[i], this.tree)); - if (this.removeEventListener) - this.removeEventListener("populate", this._populate, this); - this._remainingChildren = null; }, @@ -105,7 +102,9 @@ WebInspector.TopDownProfileDataGridTree.prototype = { this.sort(this.lastComparator, true); }, - _merge: WebInspector.TopDownProfileDataGridNode.prototype._merge + _merge: WebInspector.TopDownProfileDataGridNode.prototype._merge, + + _sharedPopulate: WebInspector.TopDownProfileDataGridNode.prototype._sharedPopulate } WebInspector.TopDownProfileDataGridTree.prototype.__proto__ = WebInspector.ProfileDataGridTree.prototype; diff --git a/WebCore/inspector/front-end/WebKit.qrc b/WebCore/inspector/front-end/WebKit.qrc index c0f282c..a1d671e 100644 --- a/WebCore/inspector/front-end/WebKit.qrc +++ b/WebCore/inspector/front-end/WebKit.qrc @@ -1,6 +1,7 @@ <!DOCTYPE RCC><RCC version="1.0"> <qresource prefix="/webkit/inspector"> <file>inspector.html</file> + <file>AbstractTimelinePanel.js</file> <file>BottomUpProfileDataGridTree.js</file> <file>Breakpoint.js</file> <file>BreakpointsSidebarPane.js</file> @@ -57,8 +58,10 @@ <file>StoragePanel.js</file> <file>StylesSidebarPane.js</file> <file>SummaryBar.js</file> + <file>TestController.js</file> <file>TextPrompt.js</file> <file>TimelineAgent.js</file> + <file>TimelinePanel.js</file> <file>TopDownProfileDataGridTree.js</file> <file>treeoutline.js</file> <file>utilities.js</file> diff --git a/WebCore/inspector/front-end/inspector.css b/WebCore/inspector/front-end/inspector.css index 3d7c99a..78ab23d 100644 --- a/WebCore/inspector/front-end/inspector.css +++ b/WebCore/inspector/front-end/inspector.css @@ -203,6 +203,10 @@ body.attached #search-results-matches { background-image: url(Images/scriptsIcon.png); } +.toolbar-item.timeline .toolbar-icon { + background-image: url(Images/timelineIcon.png); +} + .toolbar-item.storage .toolbar-icon { background-image: url(Images/storageIcon.png); } @@ -548,7 +552,7 @@ body.drawer-visible #drawer { margin-right: 4px; text-align: left; font-size: 11px; - font-family: Helvetia, Arial, sans-serif; + font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-shadow: none; color: white; @@ -826,11 +830,6 @@ body.drawer-visible #drawer { vertical-align: top; } -.invisible { - color: inherit; - text-decoration: none; -} - .webkit-line-gutter-backdrop { /* Keep this in sync with view-source.css (.webkit-line-gutter-backdrop) */ width: 31px; @@ -1164,6 +1163,7 @@ body.drawer-visible #drawer { .add-attribute { margin-left: 1px; margin-right: 1px; + white-space: nowrap; } .placard { @@ -1645,6 +1645,10 @@ li.editing .swatch, li.editing .enabled-button, li.editing-sub-part .delete-but background-position: -46px 0px; } +.pane > .title > select > option, .pane > .title > select > hr { + color: black; +} + .pane > .body { position: relative; display: none; @@ -2213,12 +2217,8 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des margin: 0 0 5px 20px; } -.panel-enabler-view button { - font-size: 13px; - margin: 6px 0 0 0; - padding: 3px 20px; +.panel-enabler-view button, .pane button { color: rgb(6, 6, 6); - height: 24px; background-color: transparent; border: 1px solid rgb(165, 165, 165); background-color: rgb(237, 237, 237); @@ -2227,12 +2227,24 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des -webkit-appearance: none; } -.panel-enabler-view button:active { +.panel-enabler-view button { + font-size: 13px; + margin: 6px 0 0 0; + padding: 3px 20px; + height: 24px; +} + +.pane button { + margin: 6px 0 6px 3px; + padding: 2px 9px; +} + +.panel-enabler-view button:active, .pane button:active { background-color: rgb(215, 215, 215); background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239))); } -body.inactive .panel-enabler-view button, .panel-enabler-view button:disabled { +body.inactive .panel-enabler-view button, .panel-enabler-view button:disabled, body.inactive .pane button, .pane button:disabled { color: rgb(130, 130, 130); border-color: rgb(212, 212, 212); background-color: rgb(239, 239, 239); @@ -2402,67 +2414,65 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { } #resources-filter { - height: 24px; - padding: 2px 10px 0; - background: -webkit-gradient(linear, left top, left bottom, from(rgb(233, 233, 233)), to(rgb(207, 207, 207))); - border-bottom: 1px solid rgb(177, 177, 177); - overflow: hidden; + background: -webkit-gradient(linear, left top, left bottom, from(rgb(236, 236, 236)), to(rgb(217, 217, 217))); + border-bottom: 1px solid rgb(64%, 64%, 64%); } #console-filter { - height: 24px; + margin-top: 1px; +} + +.scope-bar { + height: 23px; padding: 2px 10px 0; overflow: hidden; } -#resources-filter li, #console-filter li { +.scope-bar li { display: inline-block; - margin: 1px 1px 0 0; - padding: 0 6px 3px; - font-size: 12px; + margin: 1px 2px 0 0; + padding: 1px 7px 3px; + font-size: 11px; line-height: 12px; font-weight: bold; - color: rgb(40, 40, 40); - border: 1px solid transparent; - border-bottom: 0; + color: rgb(46, 46, 46); background: transparent; + text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0; -webkit-border-radius: 8px; - text-shadow: rgba(255, 255, 255, 0.5) 1px 1px 0; + vertical-align: middle; } -#console-filter div.divider { - margin-left: 5px; - margin-right: 5px; - /* Only want a border-left here because border on both sides - made the divider too thick */ - border-left: 1px solid gray; - display: inline; +.scope-bar .divider { + margin: 1px 9px 0 8px; + background-color: rgba(0, 0, 0, 0.4); + height: 16px; + width: 1px; + vertical-align: middle; + display: inline-block; } -#resources-filter li.selected, #resources-filter li:hover, #resources-filter li:active, -#console-filter li.selected, #console-filter li:hover, #console-filter li:active { +.scope-bar li.selected, .scope-bar li:hover, .scope-bar li:active { color: white; - text-shadow: rgb(80, 80, 80) 1px 1px 1px; - background: rgba(20, 20, 20, 0.4); - border-color: rgba(20, 20, 20, 0.2); - -webkit-box-shadow: 0 1px 0px rgba(255, 255, 255, 0.5); + text-shadow: rgba(0, 0, 0, 0.4) 0 1px 0; } -#resources-filter li:hover, -#console-filter li:hover { - background: rgba(20, 20, 20, 0.4); - border-color: transparent; - -webkit-box-shadow: none; +.scope-bar li:hover { + background: rgba(0, 0, 0, 0.2); +} + +.scope-bar li.selected { + background: rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5) inset, 0 -1px 1px rgba(255, 255, 255, 0.25) inset, 0 1px 0 rgba(255, 255, 255, 0.5); } -#resources-filter li:active, -#console-filter li:active { - background: rgba(20, 20, 20, 0.6); +.scope-bar li:active { + background: rgba(0, 0, 0, 0.5); + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5) inset, 0 -1px 1px rgba(255, 255, 255, 0.25) inset, 0 1px 0 rgba(255, 255, 255, 0.5); } #resources-container { position: absolute; - top: 24px; + top: 23px; left: 0; bottom: 0; right: 0; @@ -2520,6 +2530,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { -webkit-background-size: 1px 6px; -webkit-background-origin: padding; -webkit-background-clip: padding; + z-index: 400; } .summary-graph-legend { @@ -2566,6 +2577,16 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { z-index: -100; } +#resources-event-dividers { + position: absolute; + left: 0; + right: 0; + height: 100%; + top: 0; + z-index: 300; + pointer-events: none; +} + #resources-dividers-label-bar { position: absolute; top: 93px; @@ -2586,6 +2607,14 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { background-color: rgba(0, 0, 0, 0.1); } +.resources-event-divider-padding { + position: absolute; + width: 8px; + top: 0; + bottom: 0; + pointer-events: auto; +} + .resources-onload-divider { position: absolute; width: 2px; @@ -2814,7 +2843,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { #resource-views { position: absolute; - top: 24px; + top: 23px; right: 0; left: 200px; bottom: 0; @@ -2835,7 +2864,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { } .resources .sidebar-resizer-vertical { - top: 24px; + top: 23px; } .sidebar-tree, .sidebar-tree .children { @@ -2982,7 +3011,7 @@ body.inactive .sidebar-tree-item .disclosure-button:active { padding: 1px 4px; text-align: center; font-size: 11px; - font-family: Helvetia, Arial, sans-serif; + font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-shadow: none; color: white; @@ -3235,6 +3264,125 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches { background-image: url(Images/searchSmallGray.png); } +/* Timeline Style */ + +#timeline-summary { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; +} + +.timeline-clear-status-bar-item .glyph { + -webkit-mask-image: url(Images/clearConsoleButtonGlyph.png); +} + +.timeline-tree-item { + height: 18px; + padding-left: 10px; + padding-top: 2px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.timeline-tree-item .type { + padding-left: 14px; +} + +.timeline-tree-item .timeline-tree-icon { + background-image: url(Images/timelineDots.png); + margin-top: 2px; + width: 12px; + height: 12px; + position: absolute; +} + +.timeline-tree-item:nth-of-type(2n) { + background-color: rgba(0, 0, 0, 0.05); +} + +.timeline-tree-item .data.dimmed { + color: rgba(0, 0, 0, 0.7); +} + +.timeline-category-loading, .timeline-category-scripting, .timeline-category-rendering { + display: none; +} + +.filter-all .timeline-category-loading, .filter-loading .timeline-category-loading, +.filter-all .timeline-category-scripting, .filter-scripting .timeline-category-scripting, +.filter-all .timeline-category-rendering, .filter-rendering .timeline-category-rendering { + display: list-item; +} + +#timeline-graphs { + position: absolute; + left: 0; + right: 0; + max-height: 100%; + top: 19px; +} + +.timeline-graph-side { + position: relative; + height: 18px; + padding: 0 5px; + white-space: nowrap; + margin-top: 0px; + border-top: 1px solid transparent; + overflow: hidden; +} + +.timeline-graph-bar-area { + position: absolute; + top: 0; + bottom: 0; + right: 8px; + left: 9px; +} + +.timeline-graph-bar { + position: absolute; + top: 0; + bottom: 0; + margin: auto -5px; + border-width: 4px 4px 5px; + height: 9px; + min-width: 7px; + opacity: 0.8; + -webkit-border-image: url(Images/timelineBarGray.png) 4 4 5 4; +} + +.timeline-graph-side:nth-of-type(2n) { + background-color: rgba(0, 0, 0, 0.05); +} + +.timeline-category-loading .timeline-graph-bar { + -webkit-border-image: url(Images/timelineBarBlue.png) 4 4 5 4; +} + +.timeline-category-scripting .timeline-graph-bar { + -webkit-border-image: url(Images/timelineBarOrange.png) 4 4 5 4; +} + +.timeline-category-rendering .timeline-graph-bar { + -webkit-border-image: url(Images/timelineBarPurple.png) 4 4 5 4; +} + +.timeline-category-loading .timeline-tree-icon { + background-position-y: 0px; +} + +.timeline-category-scripting .timeline-tree-icon { + background-position-y: 48px; +} + +.timeline-category-rendering .timeline-tree-icon { + background-position-y: 72px; +} + /* Profiler Style */ #profile-views { diff --git a/WebCore/inspector/front-end/inspector.html b/WebCore/inspector/front-end/inspector.html index db9bfd7..04ab07a 100644 --- a/WebCore/inspector/front-end/inspector.html +++ b/WebCore/inspector/front-end/inspector.html @@ -34,6 +34,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="utilities.js"></script> <script type="text/javascript" src="treeoutline.js"></script> <script type="text/javascript" src="inspector.js"></script> + <script type="text/javascript" src="InspectorControllerStub.js"></script> <script type="text/javascript" src="Object.js"></script> <script type="text/javascript" src="KeyboardShortcut.js"></script> <script type="text/javascript" src="TextPrompt.js"></script> @@ -44,6 +45,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="Drawer.js"></script> <script type="text/javascript" src="ChangesView.js"></script> <script type="text/javascript" src="ConsoleView.js"></script> + <script type="text/javascript" src="Panel.js"></script> + <script type="text/javascript" src="AbstractTimelinePanel.js"></script> <script type="text/javascript" src="Resource.js"></script> <script type="text/javascript" src="ResourceCategory.js"></script> <script type="text/javascript" src="Database.js"></script> @@ -69,7 +72,6 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="EventListenersSidebarPane.js"></script> <script type="text/javascript" src="Color.js"></script> <script type="text/javascript" src="StylesSidebarPane.js"></script> - <script type="text/javascript" src="Panel.js"></script> <script type="text/javascript" src="PanelEnablerView.js"></script> <script type="text/javascript" src="StatusBarButton.js"></script> <script type="text/javascript" src="SummaryBar.js"></script> @@ -94,6 +96,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="InjectedScript.js"></script> <script type="text/javascript" src="InjectedScriptAccess.js"></script> <script type="text/javascript" src="TimelineAgent.js"></script> + <script type="text/javascript" src="TimelinePanel.js"></script> + <script type="text/javascript" src="TestController.js"></script> </head> <body class="detached"> <div id="toolbar"> @@ -109,7 +113,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. </div> <div id="drawer"> <div id="console-view"><div id="console-messages"><div id="console-prompt" spellcheck="false"><br></div></div></div> - <div id="drawer-status-bar" class="status-bar"><div id="other-drawer-status-bar-items"><button id="clear-console-status-bar-item" class="status-bar-item"><div class="glyph"></div><div class="glyph shadow"></div></button><div id="console-filter" class="status-bar-item"></div></div></div> + <div id="drawer-status-bar" class="status-bar"><div id="other-drawer-status-bar-items"><button id="clear-console-status-bar-item" class="status-bar-item"><div class="glyph"></div><div class="glyph shadow"></div></button><div id="console-filter" class="scope-bar status-bar-item"></div></div></div> </div> </body> </html> diff --git a/WebCore/inspector/front-end/inspector.js b/WebCore/inspector/front-end/inspector.js index f090d3a..c24d589 100644 --- a/WebCore/inspector/front-end/inspector.js +++ b/WebCore/inspector/front-end/inspector.js @@ -139,10 +139,15 @@ var WebInspector = { this.panels.resources = new WebInspector.ResourcesPanel(); if (hiddenPanels.indexOf("scripts") === -1) this.panels.scripts = new WebInspector.ScriptsPanel(); - if (hiddenPanels.indexOf("profiles") === -1) + if (hiddenPanels.indexOf("profiles") === -1) { this.panels.profiles = new WebInspector.ProfilesPanel(); + this.panels.profiles.registerProfileType(new WebInspector.CPUProfileType()); + } + if (hiddenPanels.indexOf("timeline") === -1 && hiddenPanels.indexOf("timeline") === -1) + this.panels.timeline = new WebInspector.TimelinePanel(); + if (hiddenPanels.indexOf("storage") === -1 && hiddenPanels.indexOf("databases") === -1) - this.panels.storage = new WebInspector.StoragePanel(); + this.panels.storage = new WebInspector.StoragePanel(); }, _loadPreferences: function() @@ -367,6 +372,7 @@ WebInspector.loaded = function() document.body.addStyleClass("platform-" + platform); this._loadPreferences(); + this.pendingDispatches = 0; this.drawer = new WebInspector.Drawer(); this.console = new WebInspector.ConsoleView(this.drawer); @@ -377,13 +383,13 @@ WebInspector.loaded = function() this.domAgent = new WebInspector.DOMAgent(); this.resourceCategories = { - documents: new WebInspector.ResourceCategory(WebInspector.UIString("Documents"), "documents"), - stylesheets: new WebInspector.ResourceCategory(WebInspector.UIString("Stylesheets"), "stylesheets"), - images: new WebInspector.ResourceCategory(WebInspector.UIString("Images"), "images"), - scripts: new WebInspector.ResourceCategory(WebInspector.UIString("Scripts"), "scripts"), - xhr: new WebInspector.ResourceCategory(WebInspector.UIString("XHR"), "xhr"), - fonts: new WebInspector.ResourceCategory(WebInspector.UIString("Fonts"), "fonts"), - other: new WebInspector.ResourceCategory(WebInspector.UIString("Other"), "other") + documents: new WebInspector.ResourceCategory("documents", WebInspector.UIString("Documents"), "rgb(47,102,236)"), + stylesheets: new WebInspector.ResourceCategory("stylesheets", WebInspector.UIString("Stylesheets"), "rgb(157,231,119)"), + images: new WebInspector.ResourceCategory("images", WebInspector.UIString("Images"), "rgb(164,60,255)"), + scripts: new WebInspector.ResourceCategory("scripts", WebInspector.UIString("Scripts"), "rgb(255,121,0)"), + xhr: new WebInspector.ResourceCategory("xhr", WebInspector.UIString("XHR"), "rgb(231,231,10)"), + fonts: new WebInspector.ResourceCategory("fonts", WebInspector.UIString("Fonts"), "rgb(255,82,62)"), + other: new WebInspector.ResourceCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)") }; this.panels = {}; @@ -454,8 +460,6 @@ WebInspector.loaded = function() // this._updateErrorAndWarningCounts(); var searchField = document.getElementById("search"); - searchField.addEventListener("keydown", this.searchKeyDown.bind(this), false); - searchField.addEventListener("keyup", this.searchKeyUp.bind(this), false); searchField.addEventListener("search", this.performSearch.bind(this), false); // when the search is emptied toolbarElement.addEventListener("mousedown", this.toolbarDragStart, true); @@ -486,7 +490,16 @@ window.addEventListener("load", windowLoaded, false); WebInspector.dispatch = function() { var methodName = arguments[0]; var parameters = Array.prototype.slice.call(arguments, 1); - WebInspector[methodName].apply(this, parameters); + + // We'd like to enforce asynchronous interaction between the inspector controller and the frontend. + // This is important to LayoutTests. + function delayDispatch() + { + WebInspector[methodName].apply(WebInspector, parameters); + WebInspector.pendingDispatches--; + } + WebInspector.pendingDispatches++; + setTimeout(delayDispatch, 0); } WebInspector.windowUnload = function(event) @@ -502,13 +515,19 @@ WebInspector.windowResize = function(event) WebInspector.windowFocused = function(event) { - if (event.target.nodeType === Node.DOCUMENT_NODE) + // Fires after blur, so when focusing on either the main inspector + // or an <iframe> within the inspector we should always remove the + // "inactive" class. + if (event.target.document.nodeType === Node.DOCUMENT_NODE) document.body.removeStyleClass("inactive"); } -WebInspector.windowBlured = function(event) +WebInspector.windowBlurred = function(event) { - if (event.target.nodeType === Node.DOCUMENT_NODE) + // Leaving the main inspector or an <iframe> within the inspector. + // We can add "inactive" now, and if we are moving the focus to another + // part of the inspector then windowFocused will correct this. + if (event.target.document.nodeType === Node.DOCUMENT_NODE) document.body.addStyleClass("inactive"); } @@ -547,10 +566,9 @@ WebInspector.documentClick = function(event) WebInspector.showResourceForURL(anchor.href, anchor.lineNumber, anchor.preferredPanel); } else { - var profileStringRegEx = new RegExp("webkit-profile://.+/([0-9]+)"); - var profileString = profileStringRegEx.exec(anchor.href); + var profileString = WebInspector.ProfileType.URLRegExp.exec(anchor.href); if (profileString) - WebInspector.showProfileById(profileString[1]) + WebInspector.showProfileForURL(anchor.href); } } @@ -618,7 +636,9 @@ WebInspector.documentKeyDown = function(event) break; - case "U+005B": // [ key + // Windows and Mac have two different definitions of [, so accept both. + case "U+005B": + case "U+00DB": // [ key if (isMac) var isRotateLeft = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey; else @@ -633,7 +653,9 @@ WebInspector.documentKeyDown = function(event) break; - case "U+005D": // ] key + // Windows and Mac have two different definitions of ], so accept both. + case "U+005D": + case "U+00DD": // ] key if (isMac) var isRotateRight = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey; else @@ -1043,7 +1065,8 @@ WebInspector.addDatabase = function(payload) WebInspector.addCookieDomain = function(domain) { - this.panels.storage.addCookieDomain(domain); + if (this.panels.storage) + this.panels.storage.addCookieDomain(domain); } WebInspector.addDOMStorage = function(payload) @@ -1156,7 +1179,7 @@ WebInspector.didCommitLoad = function() WebInspector.setDocument(null); } -WebInspector.addMessageToConsole = function(payload) +WebInspector.addConsoleMessage = function(payload) { var consoleMessage = new WebInspector.ConsoleMessage( payload.source, @@ -1170,6 +1193,11 @@ WebInspector.addMessageToConsole = function(payload) this.console.addMessage(consoleMessage); } +WebInspector.updateConsoleMessageRepeatCount = function(count) +{ + this.console.updateMessageRepeatCount(count); +} + WebInspector.log = function(message) { // remember 'this' for setInterval() callback @@ -1254,14 +1282,15 @@ WebInspector.log = function(message) logMessage(message); } -WebInspector.addProfile = function(profile) +WebInspector.addProfileHeader = function(profile) { - this.panels.profiles.addProfile(profile); + this.panels.profiles.addProfileHeader(WebInspector.CPUProfileType.TypeId, new WebInspector.CPUProfile(profile)); } WebInspector.setRecordingProfile = function(isProfiling) { - this.panels.profiles.setRecordingProfile(isProfiling); + this.panels.profiles.getProfileType(WebInspector.CPUProfileType.TypeId).setRecordingProfile(isProfiling); + this.panels.profiles.updateProfileTypeButtons(); } WebInspector.drawLoadingPieChart = function(canvas, percent) { @@ -1367,13 +1396,9 @@ WebInspector.linkifyStringAsFragment = function(string) var nonLink = string.substring(0, linkIndex); container.appendChild(document.createTextNode(nonLink)); - var profileStringRegEx = new RegExp("webkit-profile://(.+)/[0-9]+"); - var profileStringMatches = profileStringRegEx.exec(title); - var profileTitle; + var profileStringMatches = WebInspector.ProfileType.URLRegExp.exec(title); if (profileStringMatches) - profileTitle = profileStringMatches[1]; - if (profileTitle) - title = WebInspector.panels.profiles.displayTitleForProfileLink(profileTitle); + title = WebInspector.panels.profiles.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]); var realURL = (linkString.indexOf("www.") === 0 ? "http://" + linkString : linkString); container.appendChild(WebInspector.linkifyURLAsNode(realURL, title, null, (realURL in WebInspector.resourceURLMap))); @@ -1386,9 +1411,9 @@ WebInspector.linkifyStringAsFragment = function(string) return container; } -WebInspector.showProfileById = function(uid) { +WebInspector.showProfileForURL = function(url) { WebInspector.showProfilesPanel(); - WebInspector.panels.profiles.showProfileById(uid); + WebInspector.panels.profiles.showProfileForURL(url); } WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal) @@ -1417,26 +1442,15 @@ WebInspector.linkifyURL = function(url, linkText, classes, isExternal) WebInspector.addMainEventListeners = function(doc) { - doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), true); - doc.defaultView.addEventListener("blur", this.windowBlured.bind(this), true); + doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), false); + doc.defaultView.addEventListener("blur", this.windowBlurred.bind(this), false); doc.addEventListener("click", this.documentClick.bind(this), true); } WebInspector.searchKeyDown = function(event) { - if (event.keyIdentifier !== "Enter") - return; - - // Call preventDefault since this was the Enter key. This prevents a "search" event - // from firing for key down. We handle the Enter key on key up in searchKeyUp. This - // stops performSearch from being called twice in a row. - event.preventDefault(); -} - -WebInspector.searchKeyUp = function(event) -{ - if (event.keyIdentifier !== "Enter") - return; + if (!isEnterKey(event)) + return false; // Select all of the text so the user can easily type an entirely new query. event.target.select(); @@ -1445,14 +1459,33 @@ WebInspector.searchKeyUp = function(event) // performance is poor because of searching on every key. The search field has // the incremental attribute set, so we still get incremental searches. this.performSearch(event); + + // Call preventDefault since this was the Enter key. This prevents a "search" event + // from firing for key down. This stops performSearch from being called twice in a row. + event.preventDefault(); } WebInspector.performSearch = function(event) { var query = event.target.value; var forceSearch = event.keyIdentifier === "Enter"; + var isShortSearch = (query.length < 3); + + // Clear a leftover short search flag due to a non-conflicting forced search. + if (isShortSearch && this.shortSearchWasForcedByKeyEvent && this.currentQuery !== query) + delete this.shortSearchWasForcedByKeyEvent; + + // Indicate this was a forced search on a short query. + if (isShortSearch && forceSearch) + this.shortSearchWasForcedByKeyEvent = true; + + if (!query || !query.length || (!forceSearch && isShortSearch)) { + // Prevent clobbering a short search forced by the user. + if (this.shortSearchWasForcedByKeyEvent) { + delete this.shortSearchWasForcedByKeyEvent; + return; + } - if (!query || !query.length || (!forceSearch && query.length < 3)) { delete this.currentQuery; for (var panelName in this.panels) { @@ -1525,7 +1558,8 @@ WebInspector.UIString = function(string) string = window.localizedStrings[string]; else { if (!(string in this.missingLocalizedStrings)) { - console.error("Localized string \"" + string + "\" not found."); + if (!WebInspector.InspectorControllerStub) + console.error("Localized string \"" + string + "\" not found."); this.missingLocalizedStrings[string] = true; } @@ -1608,7 +1642,7 @@ WebInspector.startEditing = function(element, committedCallback, cancelledCallba if (event.handled) return; - if (event.keyIdentifier === "Enter") { + if (isEnterKey(event)) { editingCommitted.call(element); event.preventDefault(); } else if (event.keyCode === 27) { // Escape key @@ -1630,11 +1664,6 @@ WebInspector._toolbarItemClicked = function(event) this.currentPanel = toolbarItem.panel; } -WebInspector.evaluateForTestInFrontend = function(callId, script) -{ - InspectorController.didEvaluateForTestInFrontend(callId, JSON.stringify(window.eval(script))); -} - // This table maps MIME types to the Resource.Types which are valid for them. // The following line: // "text/html": {0: 1}, diff --git a/WebCore/inspector/front-end/utilities.js b/WebCore/inspector/front-end/utilities.js index 6df23de..e9d185f 100644 --- a/WebCore/inspector/front-end/utilities.js +++ b/WebCore/inspector/front-end/utilities.js @@ -817,3 +817,8 @@ String.format = function(format, substitutions, formatters, initialValue, append return { formattedResult: result, unusedSubstitutions: unusedSubstitutions }; } + +function isEnterKey(event) { + // Check if in IME. + return event.keyCode !== 229 && event.keyIdentifier === "Enter"; +} |