1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26WebInspector.ScopeChainDetailsSidebarPanel = function() {
27    WebInspector.DetailsSidebarPanel.call(this, "scope-chain", WebInspector.UIString("Scope Chain"), WebInspector.UIString("Scope Chain"), "Images/NavigationItemVariable.svg", "5");
28
29    this._callFrame = null;
30
31    // Update on console prompt eval as objects in the scope chain may have changed.
32    WebInspector.runtimeManager.addEventListener(WebInspector.RuntimeManager.Event.DidEvaluate, this.needsRefresh, this);
33};
34
35WebInspector.ScopeChainDetailsSidebarPanel.prototype = {
36    constructor: WebInspector.ScopeChainDetailsSidebarPanel,
37
38    // Public
39
40    inspect: function(objects)
41    {
42        // Convert to a single item array if needed.
43        if (!(objects instanceof Array))
44            objects = [objects];
45
46        var callFrameToInspect = null;
47
48        // Iterate over the objects to find a WebInspector.CallFrame to inspect.
49        for (var i = 0; i < objects.length; ++i) {
50            if (!(objects[i] instanceof WebInspector.CallFrame))
51                continue;
52            callFrameToInspect = objects[i];
53            break;
54        }
55
56        this.callFrame = callFrameToInspect;
57
58        return !!this.callFrame;
59    },
60
61    get callFrame()
62    {
63        return this._callFrame;
64    },
65
66    set callFrame(callFrame)
67    {
68        if (callFrame === this._callFrame)
69            return;
70
71        this._callFrame = callFrame;
72
73        this.needsRefresh();
74    },
75
76    refresh: function()
77    {
78        var callFrame = this.callFrame;
79        if (!callFrame)
80            return;
81
82        var detailsSections = [];
83        var foundLocalScope = false;
84
85        var sectionCountByType = {};
86        for (var type in WebInspector.ScopeChainNode.Type)
87            sectionCountByType[WebInspector.ScopeChainNode.Type[type]] = 0;
88
89        var scopeChain = callFrame.scopeChain;
90        for (var i = 0; i < scopeChain.length; ++i) {
91            var scope = scopeChain[i];
92
93            var title = null;
94            var extraProperties = null;
95            var collapsedByDefault = false;
96            var dontHighlightNonEnumerableProperties = true;
97
98            ++sectionCountByType[scope.type];
99
100            switch (scope.type) {
101                case WebInspector.ScopeChainNode.Type.Local:
102                    foundLocalScope = true;
103                    collapsedByDefault = false;
104                    dontHighlightNonEnumerableProperties = true;
105
106                    title = WebInspector.UIString("Local Variables");
107
108                    if (callFrame.thisObject)
109                        extraProperties = [new WebInspector.RemoteObjectProperty("this", callFrame.thisObject)];
110                    break;
111
112                case WebInspector.ScopeChainNode.Type.Closure:
113                    title = WebInspector.UIString("Closure Variables");
114                    dontHighlightNonEnumerableProperties = true;
115                    collapsedByDefault = false;
116                    break;
117
118                case WebInspector.ScopeChainNode.Type.Catch:
119                    title = WebInspector.UIString("Catch Variables");
120                    dontHighlightNonEnumerableProperties = true;
121                    collapsedByDefault = false;
122                    break;
123
124                case WebInspector.ScopeChainNode.Type.With:
125                    title = WebInspector.UIString("With Object Properties");
126                    collapsedByDefault = foundLocalScope;
127                    dontHighlightNonEnumerableProperties = false;
128                    break;
129
130                case WebInspector.ScopeChainNode.Type.Global:
131                    title = WebInspector.UIString("Global Variables");
132                    dontHighlightNonEnumerableProperties = false;
133                    collapsedByDefault = true;
134                    break;
135            }
136
137            var detailsSectionIdentifier = scope.type + "-" + sectionCountByType[scope.type];
138
139            var section = new WebInspector.ObjectPropertiesSection(scope.object, null, null, null, true, extraProperties, WebInspector.ScopeVariableTreeElement);
140            section.dontHighlightNonEnumerablePropertiesAtTopLevel = dontHighlightNonEnumerableProperties;
141            section.__propertyIdentifierPrefix = detailsSectionIdentifier;
142
143            var detailsSection = new WebInspector.DetailsSection(detailsSectionIdentifier, title, null, null, collapsedByDefault);
144            detailsSection.groups[0].rows = [new WebInspector.DetailsSectionPropertiesRow(section)];
145            detailsSections.push(detailsSection);
146        }
147
148        function delayedWork()
149        {
150            // Clear the timeout so we don't update the interface twice.
151            clearTimeout(timeout);
152
153            // Bail if the call frame changed while we were waiting for the async response.
154            if (this.callFrame !== callFrame)
155                return;
156
157            this.element.removeChildren();
158            for (var i = 0; i < detailsSections.length; ++i)
159                this.element.appendChild(detailsSections[i].element);
160        }
161
162        // We need a timeout in place in case there are long running, pending backend dispatches. This can happen
163        // if the debugger is paused in code that was executed from the console. The console will be waiting for
164        // the result of the execution and without a timeout we would never update the scope variables.
165        var timeout = setTimeout(delayedWork.bind(this), 50);
166
167        // Since ObjectPropertiesSection populates asynchronously, we want to wait to replace the existing content
168        // until after all the pending asynchronous requests are completed. This prevents severe flashing while stepping.
169        InspectorBackend.runAfterPendingDispatches(delayedWork.bind(this));
170    }
171};
172
173WebInspector.ScopeChainDetailsSidebarPanel.prototype.__proto__ = WebInspector.DetailsSidebarPanel.prototype;
174