2010-02-26 Ilya Tikhonovsky <loislo@chromium.org>
authorpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 26 Feb 2010 11:26:08 +0000 (11:26 +0000)
committerpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 26 Feb 2010 11:26:08 +0000 (11:26 +0000)
        Reviewed by Pavel Feldman.

        WebInspector: While the current timeline view in DevTools provides a great overview of
        things happening, we should make it easier to locate the cause of an event,
        e.g., link to JS where relevant.
        Caller info support for all kind of Timeline events and new Function Call event will be added.
        JSC can be patched the same way as it will be done for V8.

        https://bugs.webkit.org/show_bug.cgi?id=33995

        * bindings/js/ScriptCallStack.cpp:
        (WebCore::ScriptCallStack::callLocation):
        * bindings/js/ScriptCallStack.h:
        * bindings/v8/ScriptCallStack.cpp:
        (WebCore::ScriptCallStack::create):
        (WebCore::ScriptCallStack::callLocation):
        * bindings/v8/ScriptCallStack.h:
        * bindings/v8/V8Proxy.cpp:
        (WebCore::V8Proxy::callFunction):
        * inspector/InspectorTimelineAgent.cpp:
        (WebCore::InspectorTimelineAgent::InspectorTimelineAgent):
        (WebCore::InspectorTimelineAgent::~InspectorTimelineAgent):
        (WebCore::InspectorTimelineAgent::willCallFunction):
        (WebCore::InspectorTimelineAgent::didCallFunction):
        (WebCore::InspectorTimelineAgent::pushCurrentRecord):
        * inspector/InspectorTimelineAgent.h:
        (WebCore::):
        (WebCore::InspectorTimelineAgent::instanceCount):
        * inspector/TimelineRecordFactory.cpp:
        (WebCore::TimelineRecordFactory::createGenericRecord):
        (WebCore::TimelineRecordFactory::createFunctionCallData):
        * inspector/TimelineRecordFactory.h:
        * inspector/front-end/Popover.js:
        (WebInspector.Popover.prototype.hideWhenClicked):
        (WebInspector.Popover.prototype._positionElement):
        * inspector/front-end/TimelineAgent.js:
        * inspector/front-end/TimelinePanel.js:
        (WebInspector.TimelinePanel):
        (WebInspector.TimelinePanel.prototype._innerAddRecordToTimeline):
        (WebInspector.TimelinePanel.prototype._formatRecord):
        (WebInspector.TimelinePanel.prototype._getRecordDetails):
        (WebInspector.TimelinePanel.prototype.reset):
        (WebInspector.TimelinePanel.prototype._closeRecordDetails):
        (WebInspector.TimelinePanel.prototype._onScroll):
        (WebInspector.TimelinePanel.prototype._refresh):
        (WebInspector.TimelinePanel.prototype._refreshRecords):
        (WebInspector.TimelineRecordListRow):
        (WebInspector.TimelineRecordListRow.prototype.update):
        (WebInspector.TimelineRecordListRow.prototype._createCell):
        (WebInspector.TimelineRecordListRow.prototype._createRow):
        (WebInspector.TimelineRecordListRow.prototype._createLinkRow):
        (WebInspector.TimelineRecordListRow.prototype._generateBubbleContent):
        (WebInspector.TimelineRecordListRow.prototype._onClick):
        * inspector/front-end/WebKit.qrc:
        * inspector/front-end/inspector.js:
        (WebInspector.linkifyResourceAsNode):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@55277 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/timeline-enum-stability-expected.txt
LayoutTests/inspector/timeline-test.js
WebCore/ChangeLog
WebCore/English.lproj/localizedStrings.js
WebCore/bindings/js/ScriptCallStack.cpp
WebCore/bindings/js/ScriptCallStack.h
WebCore/bindings/v8/ScriptCallStack.cpp
WebCore/bindings/v8/ScriptCallStack.h
WebCore/bindings/v8/V8Proxy.cpp
WebCore/inspector/InspectorTimelineAgent.cpp
WebCore/inspector/InspectorTimelineAgent.h
WebCore/inspector/TimelineRecordFactory.cpp
WebCore/inspector/TimelineRecordFactory.h
WebCore/inspector/front-end/Popover.js
WebCore/inspector/front-end/TimelineAgent.js
WebCore/inspector/front-end/TimelinePanel.js
WebCore/inspector/front-end/inspector.css
WebCore/inspector/front-end/inspector.js
WebKit/chromium/DEPS

index 1742f90..1bba428 100644 (file)
@@ -1,3 +1,16 @@
+2010-02-26  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Reviewed by Pavel Feldman.
+
+        WebInspector: While the current timeline view in DevTools provides a great overview of
+        things happening, we should make it easier to locate the cause of an event,
+        e.g., link to JS where relevant.
+        Caller info support for all kind of Timeline events and new Function Call event will be added.
+        https://bugs.webkit.org/show_bug.cgi?id=33995
+
+        * inspector/timeline-enum-stability-expected.txt:
+        * inspector/timeline-test.js
+
 2010-02-26  Shinichiro Hamaji  <hamaji@chromium.org>
 
         Unreviewed. Skip failing tests.
index 6ec8883..20510d9 100644 (file)
@@ -17,4 +17,5 @@ WebInspector.TimelineAgent.RecordType.MarkTimeline : 11
 WebInspector.TimelineAgent.RecordType.ResourceSendRequest : 12
 WebInspector.TimelineAgent.RecordType.ResourceReceiveResponse : 13
 WebInspector.TimelineAgent.RecordType.ResourceFinish : 14
+WebInspector.TimelineAgent.RecordType.FunctionCall : 15
 
index bf159ce..d9b2fec 100644 (file)
@@ -24,20 +24,25 @@ function printTimelineRecords(performActions, typeName, formatter)
     });
 
     evaluateInWebInspector("frontend_getTimelineResults", function(timelineRecords) {
-        if (typeof(timelineRecords) === "string")
-            output("Error fetching Timeline results: " + timelineRecords);
-        else {
-            for (var i = 0; i < timelineRecords.length; ++i) {
-                var record = timelineRecords[i];
-                if (typeName && record.type === timelineAgentRecordType[typeName])
-                    printTimelineRecordProperties(record);
-                if (formatter)
-                    formatter(record);
+        try {
+            if (typeof(timelineRecords) === "string")
+                output("Error fetching Timeline results: " + timelineRecords);
+            else {
+                for (var i = 0; i < timelineRecords.length; ++i) {
+                    var record = timelineRecords[i];
+                    if (typeName && record.type === timelineAgentRecordType[typeName])
+                        printTimelineRecordProperties(record);
+                    if (formatter)
+                        formatter(record);
+                }
             }
+            if (window.layoutTestController)
+                layoutTestController.setTimelineProfilingEnabled(false);
+            notifyDone();
+        } catch (e) {
+            console.log("An exception was caught: " + e.toString());
+            notifyDone(e.toString());
         }
-        if (window.layoutTestController)
-            layoutTestController.setTimelineProfilingEnabled(false);
-        notifyDone();
     });
 }
 
@@ -125,7 +130,7 @@ function frontend_getTimelineResults() {
         if (!records)
             return;
         for (var i = 0; i < records.length; ++i) {
-            result.push(records[i].record);
+            result.push(records[i].originalRecordForTests);
             addRecords(records[i].children);
         }
     }
index 841287d..4b209e2 100644 (file)
@@ -1,3 +1,62 @@
+2010-02-26  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Reviewed by Pavel Feldman.
+
+        WebInspector: While the current timeline view in DevTools provides a great overview of
+        things happening, we should make it easier to locate the cause of an event,
+        e.g., link to JS where relevant.
+        Caller info support for all kind of Timeline events and new Function Call event will be added.
+        JSC can be patched the same way as it will be done for V8.
+
+        https://bugs.webkit.org/show_bug.cgi?id=33995
+
+        * bindings/js/ScriptCallStack.cpp:
+        (WebCore::ScriptCallStack::callLocation):
+        * bindings/js/ScriptCallStack.h:
+        * bindings/v8/ScriptCallStack.cpp:
+        (WebCore::ScriptCallStack::create):
+        (WebCore::ScriptCallStack::callLocation):
+        * bindings/v8/ScriptCallStack.h:
+        * bindings/v8/V8Proxy.cpp:
+        (WebCore::V8Proxy::callFunction):
+        * inspector/InspectorTimelineAgent.cpp:
+        (WebCore::InspectorTimelineAgent::InspectorTimelineAgent):
+        (WebCore::InspectorTimelineAgent::~InspectorTimelineAgent):
+        (WebCore::InspectorTimelineAgent::willCallFunction):
+        (WebCore::InspectorTimelineAgent::didCallFunction):
+        (WebCore::InspectorTimelineAgent::pushCurrentRecord):
+        * inspector/InspectorTimelineAgent.h:
+        (WebCore::):
+        (WebCore::InspectorTimelineAgent::instanceCount):
+        * inspector/TimelineRecordFactory.cpp:
+        (WebCore::TimelineRecordFactory::createGenericRecord):
+        (WebCore::TimelineRecordFactory::createFunctionCallData):
+        * inspector/TimelineRecordFactory.h:
+        * inspector/front-end/Popover.js:
+        (WebInspector.Popover.prototype.hideWhenClicked):
+        (WebInspector.Popover.prototype._positionElement):
+        * inspector/front-end/TimelineAgent.js:
+        * inspector/front-end/TimelinePanel.js:
+        (WebInspector.TimelinePanel):
+        (WebInspector.TimelinePanel.prototype._innerAddRecordToTimeline):
+        (WebInspector.TimelinePanel.prototype._formatRecord):
+        (WebInspector.TimelinePanel.prototype._getRecordDetails):
+        (WebInspector.TimelinePanel.prototype.reset):
+        (WebInspector.TimelinePanel.prototype._closeRecordDetails):
+        (WebInspector.TimelinePanel.prototype._onScroll):
+        (WebInspector.TimelinePanel.prototype._refresh):
+        (WebInspector.TimelinePanel.prototype._refreshRecords):
+        (WebInspector.TimelineRecordListRow):
+        (WebInspector.TimelineRecordListRow.prototype.update):
+        (WebInspector.TimelineRecordListRow.prototype._createCell):
+        (WebInspector.TimelineRecordListRow.prototype._createRow):
+        (WebInspector.TimelineRecordListRow.prototype._createLinkRow):
+        (WebInspector.TimelineRecordListRow.prototype._generateBubbleContent):
+        (WebInspector.TimelineRecordListRow.prototype._onClick):
+        * inspector/front-end/WebKit.qrc:
+        * inspector/front-end/inspector.js:
+        (WebInspector.linkifyResourceAsNode):
+
 2010-02-26  Csaba Osztrogon√°c  <ossy@webkit.org>
 
         Unreviewed. Roll-out r55263 because it broke fast/forms/textarea-type-spaces-pretty-diff.html.
index 12666f8..ccae1dd 100644 (file)
Binary files a/WebCore/English.lproj/localizedStrings.js and b/WebCore/English.lproj/localizedStrings.js differ
index a435588..c8eadd1 100644 (file)
@@ -101,4 +101,9 @@ void ScriptCallStack::initialize()
     m_initialized = true;
 }
 
+bool ScriptCallStack::callLocation(String*, int*)
+{
+    return false;
+}
+
 } // namespace WebCore
index 52362ea..f5f8ae0 100644 (file)
@@ -53,6 +53,7 @@ namespace WebCore {
         // frame retrieval methods
         const ScriptCallFrame &at(unsigned);
         unsigned size();
+        static bool callLocation(String*, int*);
 
     private:
         void initialize();
index 21063ed..d563d42 100644 (file)
@@ -43,16 +43,19 @@ namespace WebCore {
 ScriptCallStack* ScriptCallStack::create(const v8::Arguments& arguments, unsigned skipArgumentCount) {
     String sourceName;
     int sourceLineNumber;
-    if (!V8Proxy::sourceName(sourceName)) {
-        return 0;
-    }
-    if (!V8Proxy::sourceLineNumber(sourceLineNumber)) {
-        return 0;
-    }
-    sourceLineNumber += 1;
+    if (!callLocation(&sourceName, &sourceLineNumber))
+      return 0;
     return new ScriptCallStack(arguments, skipArgumentCount, sourceName, sourceLineNumber);
 }
 
+bool ScriptCallStack::callLocation(String* sourceName, int* sourceLineNumber)
+{
+    if (!V8Proxy::sourceName(*sourceName) || !V8Proxy::sourceLineNumber(*sourceLineNumber))
+        return false;
+    *sourceLineNumber += 1;
+    return true;
+}
+
 ScriptCallStack::ScriptCallStack(const v8::Arguments& arguments, unsigned skipArgumentCount, String sourceName, int sourceLineNumber)
     : m_lastCaller(String(), sourceName, sourceLineNumber, arguments, skipArgumentCount)
     , m_scriptState(ScriptState::current())
index 8ac394c..db27f15 100644 (file)
@@ -47,6 +47,8 @@ namespace WebCore {
         static ScriptCallStack* create(const v8::Arguments&, unsigned skipArgumentCount = 0);
         ~ScriptCallStack();
 
+        static bool callLocation(String* sourceName, int* sourceLineNumber);
+
         const ScriptCallFrame& at(unsigned) const;
         // FIXME: implement retrieving and storing call stack trace
         unsigned size() const { return 1; }
index fade001..9baaf3c 100644 (file)
@@ -473,9 +473,29 @@ v8::Local<v8::Value> V8Proxy::callFunction(v8::Handle<v8::Function> function, v8
         // execution finishs before firing the timer.
         m_frame->keepAlive();
 
+#if ENABLE(INSPECTOR)
+        InspectorTimelineAgent* timelineAgent = 0;
+        if (InspectorTimelineAgent::instanceCount()) {
+            timelineAgent = m_frame->page() ? m_frame->page()->inspectorTimelineAgent() : 0;
+            if (timelineAgent) {
+                v8::ScriptOrigin origin = function->GetScriptOrigin();
+                if (!origin.ResourceName().IsEmpty())
+                    timelineAgent->willCallFunction(v8ValueToWebCoreString(origin.ResourceName()), function->GetScriptLineNumber() + 1);
+                else
+                    timelineAgent = 0;
+            }
+        }
+#endif // !ENABLE(INSPECTOR)
+
         m_recursion++;
         result = function->Call(receiver, argc, args);
         m_recursion--;
+
+#if ENABLE(INSPECTOR)
+        if (timelineAgent && m_frame->page() && timelineAgent == m_frame->page()->inspectorTimelineAgent())
+            timelineAgent->didCallFunction();
+#endif // !ENABLE(INSPECTOR)
+
     }
 
     // Release the storage mutex if applicable.
index cbf6ee1..0624c87 100644 (file)
 
 namespace WebCore {
 
+int InspectorTimelineAgent::s_instanceCount = 0;
+
 InspectorTimelineAgent::InspectorTimelineAgent(InspectorFrontend* frontend)
     : m_frontend(frontend)
 {
+    ++s_instanceCount;
     ASSERT(m_frontend);
 }
 
 InspectorTimelineAgent::~InspectorTimelineAgent()
 {
+    ASSERT(s_instanceCount);
+    --s_instanceCount;
+}
+
+void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine)
+{
+    pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(m_frontend, scriptName, scriptLine), FunctionCallTimelineRecordType);
+}
+
+void InspectorTimelineAgent::didCallFunction()
+{
+    didCompleteCurrentRecord(FunctionCallTimelineRecordType);
 }
 
 void InspectorTimelineAgent::willDispatchEvent(const Event& event)
@@ -108,7 +123,7 @@ void InspectorTimelineAgent::didWriteHTML(unsigned int endLine)
         didCompleteCurrentRecord(ParseHTMLTimelineRecordType);
     }
 }
-   
+
 void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot)
 {
     ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds());
@@ -240,9 +255,9 @@ double InspectorTimelineAgent::currentTimeInMilliseconds()
 
 void InspectorTimelineAgent::pushCurrentRecord(ScriptObject data, TimelineRecordType type)
 {
-    m_recordStack.append(TimelineRecordEntry(TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds()), data, m_frontend->newScriptArray(), type));
+    ScriptObject record = TimelineRecordFactory::createGenericRecord(m_frontend, currentTimeInMilliseconds());
+    m_recordStack.append(TimelineRecordEntry(record, data, m_frontend->newScriptArray(), type));
 }
-
 } // namespace WebCore
 
 #endif // ENABLE(INSPECTOR)
index fd86ba7..7f6646c 100644 (file)
@@ -63,6 +63,7 @@ namespace WebCore {
         ResourceSendRequestTimelineRecordType = 12,
         ResourceReceiveResponseTimelineRecordType = 13,
         ResourceFinishTimelineRecordType = 14,
+        FunctionCallTimelineRecordType = 15,
     };
 
     class InspectorTimelineAgent : public Noncopyable {
@@ -74,6 +75,9 @@ namespace WebCore {
         void resetFrontendProxyObject(InspectorFrontend*);
 
         // Methods called from WebCore.
+        void willCallFunction(const String& scriptName, int scriptLine);
+        void didCallFunction();
+
         void willDispatchEvent(const Event&);
         void didDispatchEvent();
 
@@ -108,7 +112,9 @@ namespace WebCore {
         void didReceiveResourceResponse(unsigned long, const ResourceResponse&);
         void didFinishLoadingResource(unsigned long, bool didFail);
 
+        static int instanceCount() { return s_instanceCount; }
         static InspectorTimelineAgent* retrieve(ScriptExecutionContext*);
+
     private:
         struct TimelineRecordEntry {
             TimelineRecordEntry(ScriptObject record, ScriptObject data, ScriptArray children, TimelineRecordType type) : record(record), data(data), children(children), type(type) { }
@@ -127,8 +133,9 @@ namespace WebCore {
         void addRecordToTimeline(ScriptObject, TimelineRecordType);
 
         InspectorFrontend* m_frontend;
-        
+
         Vector< TimelineRecordEntry > m_recordStack;
+        static int s_instanceCount;
     };
 
 inline InspectorTimelineAgent* InspectorTimelineAgent::retrieve(ScriptExecutionContext* context)
index 525e61d..73981a2 100644 (file)
@@ -39,6 +39,7 @@
 #include "ResourceRequest.h"
 #include "ResourceResponse.h"
 #include "ScriptArray.h"
+#include "ScriptCallStack.h"
 #include "ScriptObject.h"
 
 namespace WebCore {
@@ -47,9 +48,24 @@ ScriptObject TimelineRecordFactory::createGenericRecord(InspectorFrontend* front
 {
     ScriptObject record = frontend->newScriptObject();
     record.set("startTime", startTime);
+
+    String sourceName;
+    int sourceLineNumber;
+    if (ScriptCallStack::callLocation(&sourceName, &sourceLineNumber) && sourceName != "undefined") {
+        record.set("callerScriptName", sourceName);
+        record.set("callerScriptLine", sourceLineNumber);
+    }
     return record;
 }
 
+ScriptObject TimelineRecordFactory::createFunctionCallData(InspectorFrontend* frontend, const String& scriptName, int scriptLine)
+{
+    ScriptObject data = frontend->newScriptObject();
+    data.set("scriptName", scriptName);
+    data.set("scriptLine", scriptLine);
+    return data;
+}
+
 ScriptObject TimelineRecordFactory::createEventDispatchData(InspectorFrontend* frontend, const Event& event)
 {
     ScriptObject data = frontend->newScriptObject();
index b7437f5..910c295 100644 (file)
@@ -46,6 +46,8 @@ namespace WebCore {
     public:
         static ScriptObject createGenericRecord(InspectorFrontend*, double startTime);
 
+        static ScriptObject createFunctionCallData(InspectorFrontend*, const String& scriptName, int scriptLine);
+
         static ScriptObject createEventDispatchData(InspectorFrontend*, const Event&);
 
         static ScriptObject createGenericTimerData(InspectorFrontend*, int timerId);
index 70e4ac9..866cb24 100644 (file)
@@ -54,8 +54,10 @@ WebInspector.Popover.prototype = {
         var preferredWidth = preferredWidth || this.contentElement.offsetWidth;
         var preferredHeight = preferredHeight || this.contentElement.offsetHeight;
 
-        this.contentElement.addStyleClass("content");
-        this.element.appendChild(this.contentElement);
+        var contentDiv = document.createElement("div");
+        contentDiv.className = "content";
+        contentDiv.appendChild(this.contentElement);
+        this.element.appendChild(contentDiv);
         document.body.appendChild(this.element);
         this._positionElement(anchor, preferredWidth, preferredHeight);
     },
@@ -66,12 +68,18 @@ WebInspector.Popover.prototype = {
         document.body.removeChild(this.element);
     },
 
+    hideWhenClicked: function()
+    {
+        this.element.addEventListener("click", this.hide.bind(this));
+    },
+
     _positionElement: function(anchorElement, preferredWidth, preferredHeight)
     {
         const borderWidth = 25;
         const scrollerWidth = 11;
-        const arrowHeight = 10;
+        const arrowHeight = 15;
         const arrowOffset = 15;
+        const borderRadius = 10;
         
         // Skinny tooltips are not pretty, their arrow location is not nice.
         preferredWidth = Math.max(preferredWidth, 50);
@@ -87,7 +95,7 @@ WebInspector.Popover.prototype = {
             anchorElement = anchorElement.parentElement;
         }
 
-        var newElementPosition = { x: 0, y: 0, width: preferredWidth + borderWidth * 2, height: preferredHeight + borderWidth * 2 };
+        var newElementPosition = { x: 0, y: 0, width: preferredWidth + scrollerWidth, height: preferredHeight };
 
         var verticalAlignment;
         var roomAbove = anchorBox.y;
@@ -95,53 +103,45 @@ WebInspector.Popover.prototype = {
 
         if (roomAbove > roomBelow) {
             // Positioning above the anchor.
-            if (anchorBox.y > newElementPosition.height)
-                newElementPosition.y = anchorBox.y - newElementPosition.height;
+            if (anchorBox.y > newElementPosition.height + arrowHeight + borderRadius)
+                newElementPosition.y = anchorBox.y - newElementPosition.height - arrowHeight;
             else {
-                newElementPosition.y = 0;
-                newElementPosition.height = anchorBox.y - newElementPosition.y;
-                // Reserve room for vertical scroller anyways.
-                newElementPosition.width += scrollerWidth;
+                newElementPosition.y = borderRadius * 2;
+                newElementPosition.height = anchorBox.y - borderRadius * 2 - arrowHeight;
             }
             verticalAlignment = "bottom";
         } else {
             // Positioning below the anchor.
-            newElementPosition.y = anchorBox.y + anchorBox.height;
-            if (newElementPosition.y + newElementPosition.height >= totalHeight) {
-                newElementPosition.height = totalHeight - anchorBox.y - anchorBox.height;
-                // Reserve room for vertical scroller.
-                newElementPosition.width += scrollerWidth;
-            }
+            newElementPosition.y = anchorBox.y + anchorBox.height + arrowHeight;
+            if (newElementPosition.y + newElementPosition.height + arrowHeight - borderWidth >= totalHeight)
+                newElementPosition.height = totalHeight - anchorBox.y - anchorBox.height - borderRadius * 2 - arrowHeight;
             // Align arrow.
-            newElementPosition.y -= arrowHeight;
             verticalAlignment = "top";
         }
 
         var horizontalAlignment;
         if (anchorBox.x + newElementPosition.width < totalWidth) {
-            newElementPosition.x = Math.max(0, anchorBox.x) - borderWidth - arrowOffset;
+            newElementPosition.x = Math.max(borderRadius, anchorBox.x - arrowOffset);
             horizontalAlignment = "left";
-        } else if (newElementPosition.width < totalWidth) {
-            newElementPosition.x = totalWidth - newElementPosition.width;
+        } else if (newElementPosition.width + borderRadius * 2 < totalWidth) {
+            newElementPosition.x = totalWidth - newElementPosition.width - borderRadius;
             horizontalAlignment = "right";
             // Position arrow accurately.
-            this._popupArrowElement.style.right = totalWidth - anchorBox.x - borderWidth - anchorBox.width + "px";
+            this._popupArrowElement.style.right = totalWidth - anchorBox.x - anchorBox.width - borderRadius + "px";
         } else {
-            newElementPosition.x = 0;
-            newElementPosition.width = totalWidth;
+            newElementPosition.x = borderRadius;
+            newElementPosition.width = totalWidth - scrollerWidth - borderRadius * 2;
+            newElementPosition.height += scrollerWidth;
             horizontalAlignment = "left";
             if (verticalAlignment === "bottom")
                 newElementPosition.y -= scrollerWidth;
             // Position arrow accurately.
-            this._popupArrowElement.style.left = anchorBox.x - borderWidth + "px";
+            this._popupArrowElement.style.left = anchorBox.x + "px";
         }
 
-        // Reserve room for horizontal scroller.
-        newElementPosition.height += scrollerWidth;
-
         this.element.className = "popover " + verticalAlignment + "-" + horizontalAlignment + "-arrow";
-        this.element.positionAt(newElementPosition.x, newElementPosition.y);
-        this.element.style.width = newElementPosition.width  + "px";
-        this.element.style.height = newElementPosition.height  + "px";
+        this.element.positionAt(newElementPosition.x - borderWidth, newElementPosition.y - borderWidth);
+        this.element.style.width = newElementPosition.width + borderWidth * 2 + "px";
+        this.element.style.height = newElementPosition.height + borderWidth * 2 + "px";
     }
 }
index c9e9d64..50dce10 100644 (file)
@@ -48,7 +48,8 @@ WebInspector.TimelineAgent.RecordType = {
     MarkTimeline : 11,
     ResourceSendRequest : 12,
     ResourceReceiveResponse : 13,
-    ResourceFinish : 14
+    ResourceFinish : 14,
+    FunctionCall : 15
 };
 
 WebInspector.addRecordToTimeline = function(record) {
index b6d5fde..21a76af 100644 (file)
@@ -79,8 +79,11 @@ WebInspector.TimelinePanel = function()
 
     this._records = [];
     this._sendRequestRecords = {};
+    this._timerRecords = {};
+
     this._calculator = new WebInspector.TimelineCalculator();
     this._boundariesAreValid = true;
+    this._scrollTop = 0;
 }
 
 WebInspector.TimelinePanel.prototype = {
@@ -150,6 +153,8 @@ WebInspector.TimelinePanel.prototype = {
                 this._lastRecord.category == formattedRecord.category &&
                 this._lastRecord.title == formattedRecord.title &&
                 this._lastRecord.details == formattedRecord.details &&
+                this._lastRecord.callerScriptName == formattedRecord.callerScriptName &&
+                this._lastRecord.callerScriptLine == formattedRecord.callerScriptLine &&
                 formattedRecord.startTime - this._lastRecord.endTime < 0.1) {
             this._lastRecord.endTime = formattedRecord.endTime;
             this._lastRecord.count++;
@@ -186,6 +191,7 @@ WebInspector.TimelinePanel.prototype = {
             this._recordStyles[recordTypes.ResourceSendRequest] = { title: WebInspector.UIString("Send Request"), category: this.categories.loading };
             this._recordStyles[recordTypes.ResourceReceiveResponse] = { title: WebInspector.UIString("Receive Response"), category: this.categories.loading };
             this._recordStyles[recordTypes.ResourceFinish] = { title: WebInspector.UIString("Finish Loading"), category: this.categories.loading };
+            this._recordStyles[recordTypes.FunctionCall] = { title: WebInspector.UIString("Function Call"), category: this.categories.scripting };
         }
 
         var style = this._recordStyles[record.type];
@@ -200,7 +206,9 @@ WebInspector.TimelinePanel.prototype = {
         formattedRecord.count = 1;
         formattedRecord.type = record.type;
         formattedRecord.endTime = (typeof record.endTime !== "undefined") ? record.endTime / 1000 : formattedRecord.startTime;
-        formattedRecord.record = record;
+        formattedRecord.originalRecordForTests = record;
+        formattedRecord.callerScriptName = record.callerScriptName;
+        formattedRecord.callerScriptLine = record.callerScriptLine;
 
         // Make resource receive record last since request was sent; make finish record last since response received.
         if (record.type === WebInspector.TimelineAgent.RecordType.ResourceSendRequest) {
@@ -210,12 +218,30 @@ WebInspector.TimelinePanel.prototype = {
             if (sendRequestRecord) { // False if we started instrumentation in the middle of request.
                 sendRequestRecord._responseReceivedFormattedTime = formattedRecord.startTime;
                 formattedRecord.startTime = sendRequestRecord.startTime;
-                sendRequestRecord.details = this._getRecordDetails(record);
+                formattedRecord.details = this._getRecordDetails(sendRequestRecord);
+                formattedRecord.callerScriptName = sendRequestRecord.callerScriptName;
+                formattedRecord.callerScriptLine = sendRequestRecord.callerScriptLine;
             }
         } else if (record.type === WebInspector.TimelineAgent.RecordType.ResourceFinish) {
             var sendRequestRecord = this._sendRequestRecords[record.data.identifier];
-            if (sendRequestRecord) // False for main resource.
+            if (sendRequestRecord) {// False for main resource.
                 formattedRecord.startTime = sendRequestRecord._responseReceivedFormattedTime;
+                formattedRecord.callerScriptName = sendRequestRecord.callerScriptName;
+                formattedRecord.callerScriptLine = sendRequestRecord.callerScriptLine;
+            }
+        } else if (record.type === WebInspector.TimelineAgent.RecordType.TimerInstall) {
+            formattedRecord.timeout = record.data.timeout;
+            formattedRecord.singleShot = record.data.singleShot;
+            this._timerRecords[record.data.timerId] = formattedRecord;
+        } else if (record.type === WebInspector.TimelineAgent.RecordType.TimerFire ||
+                   record.type === WebInspector.TimelineAgent.RecordType.TimerRemove) {
+            var timerInstalledRecord = this._timerRecords[record.data.timerId];
+            if (timerInstalledRecord) {
+                formattedRecord.callSiteScriptName = timerInstalledRecord.callerScriptName;
+                formattedRecord.callSiteScriptLine = timerInstalledRecord.callerScriptLine;
+                formattedRecord.timeout = timerInstalledRecord.timeout;
+                formattedRecord.singleShot = timerInstalledRecord.singleShot;
+            }
         }
         formattedRecord.details = this._getRecordDetails(record);
 
@@ -225,6 +251,8 @@ WebInspector.TimelinePanel.prototype = {
     _getRecordDetails: function(record)
     {
         switch (record.type) {
+        case WebInspector.TimelineAgent.RecordType.FunctionCall:
+            return WebInspector.displayNameForURL(record.data.scriptName + ":" + record.data.scriptLine);
         case WebInspector.TimelineAgent.RecordType.EventDispatch:
             return record.data ? record.data.type : "";
         case WebInspector.TimelineAgent.RecordType.Paint:
@@ -264,6 +292,7 @@ WebInspector.TimelinePanel.prototype = {
     },
 
     resize: function() {
+        this._closeRecordDetails();
         this._scheduleRefresh();
     },
 
@@ -271,11 +300,23 @@ WebInspector.TimelinePanel.prototype = {
     {
         this._lastRecord = null;
         this._sendRequestRecords = {};
+        this._timerRecords = {};
         this._records = [];
         this._boundariesAreValid = false;
         this._overviewPane.reset();
         this._adjustScrollPosition(0);
         this._refresh();
+        this._closeRecordDetails();
+    },
+
+    _closeRecordDetails: function()
+    {
+        var details = WebInspector.TimelinePanel.recordDetails;
+        if (details) {
+            details.hide();
+            delete details;
+            WebInspector.TimelinePanel.recordDetails = null;
+        }
     },
 
     show: function()
@@ -288,6 +329,7 @@ WebInspector.TimelinePanel.prototype = {
 
     _onScroll: function(event)
     {
+        this._closeRecordDetails();
         var scrollTop = this._containerElement.scrollTop;
         var dividersTop = Math.max(0, scrollTop);
         this._timelineGrid.setScrollAndDividerTop(scrollTop, dividersTop);
@@ -296,6 +338,7 @@ WebInspector.TimelinePanel.prototype = {
 
     _windowChanged: function()
     {
+        this._closeRecordDetails();
         this._scheduleRefresh();
     },
 
@@ -321,7 +364,7 @@ WebInspector.TimelinePanel.prototype = {
             clearTimeout(this._refreshTimeout);
             delete this._refreshTimeout;
         }
-      
+
         if (!this._boundariesAreValid)
             this._overviewPane.update(this._records);
         this._refreshRecords(!this._boundariesAreValid);
@@ -391,7 +434,7 @@ WebInspector.TimelinePanel.prototype = {
                 this._graphRowsElement.appendChild(graphRowElement);
             }
 
-            listRowElement.listRow.update(record, isEven);
+            listRowElement.listRow.update(record, isEven, this._calculator, visibleTop);
             graphRowElement.graphRow.update(record, isEven, this._calculator, width, expandOffset, i);
 
             listRowElement = listRowElement.nextSibling;
@@ -499,6 +542,7 @@ WebInspector.TimelineRecordListRow = function()
 {
     this.element = document.createElement("div");
     this.element.listRow = this;
+    this.element.style.cursor = "pointer";
     var iconElement = document.createElement("span");
     iconElement.className = "timeline-tree-icon";
     this.element.appendChild(iconElement);
@@ -520,11 +564,16 @@ WebInspector.TimelineRecordListRow = function()
     this.element.appendChild(separatorElement);
     this.element.appendChild(this._dataElement);
     this.element.appendChild(this._repeatCountElement);
+    this.element.addEventListener("click", this._onClick.bind(this));
 }
 
 WebInspector.TimelineRecordListRow.prototype = {
-    update: function(record, isEven)
+    update: function(record, isEven, calculator, offset)
     {
+        this._record = record;
+        this._calculator = calculator;
+        this._offset = offset;
+
         this.element.className = "timeline-tree-item timeline-category-" + record.category.name + (isEven ? " even" : "");
         this._typeElement.textContent = record.title;
 
@@ -542,13 +591,92 @@ WebInspector.TimelineRecordListRow.prototype = {
             this._repeatCountElement.textContent = "";
     },
 
+    _createCell: function(content, styleName)
+    {
+        var text = document.createElement("label");
+        text.appendChild(document.createTextNode(content));
+        var cell = document.createElement("td");
+        cell.className = "timeline-details";
+        if (styleName)
+             cell.className += " " + styleName;
+        cell.textContent = content;
+        return cell;
+    },
+
+    _createRow: function(title, content)
+    {
+        var row = document.createElement("tr");
+        row.appendChild(this._createCell(title, "timeline-details-row-title"));
+        row.appendChild(this._createCell(content, "timeline-details-row-data"));
+        return row;
+    },
+
+    _createLinkRow: function(title, content)
+    {
+        var row = document.createElement("tr");
+        row.appendChild(this._createCell(title, "timeline-details-row-title"));
+        var cell = document.createElement("td");
+        cell.appendChild(content);
+        row.appendChild(cell);
+        return row;
+    },
+
+    _generateBubbleContent: function()
+    {
+        var bubbleContentTable = document.createElement("table");
+        var titleCell = this._createCell(WebInspector.UIString("%s - Details", this._record.title), "timeline-details-title");
+        titleCell.colSpan = 2;
+        titleCell.appendChild(document.createElement("hr"));
+        var titleRow = document.createElement("tr");
+        titleRow.appendChild(titleCell);
+        bubbleContentTable.appendChild(titleRow);
+        var text = Number.secondsToString(this._record.endTime - this._record.startTime) + " (@" +
+            this._calculator.formatValue(this._record.startTime - this._calculator.minimumBoundary) + ")";
+        bubbleContentTable.appendChild(this._createRow(WebInspector.UIString("Duration"), text));
+
+        if (this._record.details) {
+            if ( this._record.type == WebInspector.TimelineAgent.RecordType.TimerInstall ||
+                 this._record.type == WebInspector.TimelineAgent.RecordType.TimerFire ||
+                 this._record.type == WebInspector.TimelineAgent.RecordType.TimerRemove) {
+                bubbleContentTable.appendChild(this._createRow(WebInspector.UIString("TimerId"), this._record.data.timerId));
+                if (this._record.timeout) {
+                    bubbleContentTable.appendChild(this._createRow(WebInspector.UIString("Timeout"), this._record.timeout));
+                    bubbleContentTable.appendChild(this._createRow(WebInspector.UIString("Repeats"), !this._record.singleShot));
+                }
+                if (this._record.callSiteScriptLine) {
+                    var link = WebInspector.linkifyResourceAsNode(this._record.callSiteScriptName, "scripts", this._record.callSiteScriptLine);
+                    bubbleContentTable.appendChild(this._createLinkRow(WebInspector.UIString("Call Site"), link));
+                }
+            } else if ( this._record.type == WebInspector.TimelineAgent.RecordType.FunctionCall ) {
+                var link = WebInspector.linkifyResourceAsNode(this._record.data.scriptName, "scripts", this._record.data.scriptLine);
+                bubbleContentTable.appendChild(this._createLinkRow(WebInspector.UIString("Location"), link));
+            } else
+                bubbleContentTable.appendChild(this._createRow(WebInspector.UIString("Details"), this._record.details));
+        }
+
+        if (this._record.callerScriptName) {
+            var link = WebInspector.linkifyResourceAsNode(this._record.callerScriptName, "scripts", this._record.callerScriptLine);
+            bubbleContentTable.appendChild(this._createLinkRow(WebInspector.UIString("Caller"), link));
+        }
+        return bubbleContentTable;
+    },
+
+    _onClick: function(event)
+    {
+        if (this._record) {
+            var details = new WebInspector.Popover(this._generateBubbleContent());
+            details.hideWhenClicked();
+            details.show(this.element);
+            WebInspector.TimelinePanel.recordDetails = details;
+        }
+    },
+
     dispose: function()
     {
         this.element.parentElement.removeChild(this.element);
     }
 }
 
-
 WebInspector.TimelineRecordGraphRow = function(graphContainer, refreshCallback, rowHeight)
 {
     this.element = document.createElement("div");
index 606dfed..cf51bd8 100644 (file)
@@ -3847,3 +3847,18 @@ ol.breakpoint-list {
     margin: -1px;
     background-color: rgb(255, 255, 194);
 }
+
+.timeline-details-row-title {
+    font-weight: bold;
+    text-align: right;
+    white-space: nowrap;
+}
+
+.timeline-details-row-data {
+    white-space: nowrap;
+}
+
+.timeline-details-title {
+    font-weight: bold;
+    white-space: nowrap;
+}
\ No newline at end of file
index 9b36cd3..6c903e8 100644 (file)
@@ -1550,6 +1550,17 @@ WebInspector.linkifyURL = function(url, linkText, classes, isExternal, tooltipTe
     return WebInspector.linkifyURLAsNode(url, linkText, classes, isExternal, tooltipText).outerHTML;
 }
 
+WebInspector.linkifyResourceAsNode = function(url, preferredPanel, lineNumber, classes, tooltipText)
+{
+    var linkText = WebInspector.displayNameForURL(url);
+    if (lineNumber)
+        linkText += ":" + lineNumber;
+    var node = WebInspector.linkifyURLAsNode(url, linkText, classes, false, tooltipText);
+    node.lineNumber = lineNumber;
+    node.preferredPanel = preferredPanel;
+    return node;
+}
+
 WebInspector.completeURL = function(baseURL, href)
 {
     var match = baseURL.match(WebInspector.URLRegExp);
index 9c45b83..91601c7 100644 (file)
@@ -43,7 +43,7 @@ vars = {
   'openvcdiff_rev': '28',
   'ots_rev': '26',
   'skia_rev': '490',
-  'v8_rev': '3781',
+  'v8_rev': '3963',
 
   # Windows:
   'cygwin_rev': '11984',