Web Inspector: Move the computation that results in UI strings from JSC to the Web...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / TypeTokenView.js
1 /*
2  * Copyright (C) 2014 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
26 WebInspector.TypeTokenView = function(tokenAnnotator, shouldHaveRightMargin, shouldHaveLeftMargin, titleType, functionOrVariableName)
27 {
28     console.assert(titleType === WebInspector.TypeTokenView.TitleType.Variable  || titleType === WebInspector.TypeTokenView.TitleType.ReturnStatement);
29
30     WebInspector.Object.call(this);
31
32     var span = document.createElement("span");
33     span.classList.add("type-token");
34     if (shouldHaveRightMargin)
35         span.classList.add("type-token-right-spacing");
36     if (shouldHaveLeftMargin)
37         span.classList.add("type-token-left-spacing");
38
39     this.element = span;
40     this._tokenAnnotator = tokenAnnotator;
41     this._types = null;
42     this._typeSet = null;
43     this._colorClass = null;
44
45     this._popoverTitle = WebInspector.TypeTokenView.titleForPopover(titleType, functionOrVariableName);
46
47     this._setUpMouseoverHandlers();
48 };
49
50 WebInspector.TypeTokenView.titleForPopover = function(titleType, functionOrVariableName)
51 {
52     var titleString = null;
53     if (titleType === WebInspector.TypeTokenView.TitleType.Variable)
54         titleString = WebInspector.UIString("Type information for variable: %s").format(functionOrVariableName);
55     else {
56         if (functionOrVariableName)
57             titleString = WebInspector.UIString("Return type for function: %s").format(functionOrVariableName);
58         else
59             titleString = WebInspector.UIString("Return type for anonymous function");
60     }
61
62     return titleString;
63 };
64
65 WebInspector.TypeTokenView.TitleType = {
66     Variable: "title-type-variable",
67     ReturnStatement: "title-type-return-statement"
68 };
69
70 WebInspector.TypeTokenView.ColorClassForType = {
71     "String": "type-token-string",
72     "Function": "type-token-function",
73     "Number": "type-token-number",
74     "Integer": "type-token-number",
75     "Undefined": "type-token-empty",
76     "Null": "type-token-empty",
77     "(?)": "type-token-empty",
78     "Boolean": "type-token-boolean",
79     "(many)": "type-token-many"
80 };
81
82 WebInspector.TypeTokenView.DelayHoverTime = 350;
83
84 WebInspector.TypeTokenView.prototype = {
85     constructor: WebInspector.TypeTokenView,
86     __proto__: WebInspector.Object.prototype,
87
88     // Public
89
90     update: function(types)
91     {
92         this._types = types;
93         this._typeSet = WebInspector.TypeSet.fromPayload(this._types);
94
95         var title = this._displayTypeName();
96         this.element.textContent = title;
97         var hashString = title[title.length - 1] === "?" ? title.slice(0, title.length - 1) : title;
98
99         if (this._colorClass)
100             this.element.classList.remove(this._colorClass);
101
102         this._colorClass = WebInspector.TypeTokenView.ColorClassForType[hashString] || "type-token-default";
103         this.element.classList.add(this._colorClass);
104     },
105
106     // Private
107
108     _setUpMouseoverHandlers: function()
109     {
110         var timeoutID = null;
111
112         this.element.addEventListener("mouseover", function() {
113             function showPopoverAfterDelay()
114             {
115                 timeoutID = null;
116
117                 var domRect = this.element.getBoundingClientRect();
118                 var bounds = new WebInspector.Rect(domRect.left, domRect.top, domRect.width, domRect.height);
119                 this._tokenAnnotator.sourceCodeTextEditor.showPopoverForTypes(this._types, bounds, this._popoverTitle);
120             }
121
122             if (this._shouldShowPopover())
123                 timeoutID = setTimeout(showPopoverAfterDelay.bind(this), WebInspector.TypeTokenView.DelayHoverTime);
124         }.bind(this));
125
126         this.element.addEventListener("mouseout", function() {
127             if (timeoutID)
128                 clearTimeout(timeoutID);
129         }.bind(this));
130     },
131
132     _shouldShowPopover: function()
133     {
134         if (!this._types.isValid)
135             return false;
136
137         if (this._typeSet.primitiveTypeNames.length > 1)
138             return true;
139
140         if (this._types.structures && this._types.structures.length)
141             return true;
142
143         return false;
144     },
145
146     _displayTypeName: function()
147     {
148         if (!this._types.isValid)
149             return "";
150
151         var typeSet = this._typeSet;
152
153         if (this._types.leastCommonAncestor && !this._typeSet.primitiveTypeNames.length) {
154             if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Object))
155                 return this._types.leastCommonAncestor;
156             if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Object | WebInspector.TypeSet.NullOrUndefinedTypeBits))
157                 return this._types.leastCommonAncestor + "?";
158         }
159
160         // The order of these checks are important. 
161         // For example, if a value is only a function, it is contained in TypeFunction, but it is also contained in (TypeFunction | TypeNull).
162         // Therefore, more specific types must be checked first.
163
164         // The strings returned here should match those in TypeTokenView.ColorClassForType
165         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Function))
166             return "Function";
167         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Undefined))
168             return "Undefined";
169         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Null))
170             return "Null";
171         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Boolean))
172             return "Boolean";
173         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Integer))
174             return "Integer";
175         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Number | WebInspector.TypeSet.TypeBit.Integer))
176             return "Number";
177         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.String))
178             return "String";
179
180         if (typeSet.isContainedIn(WebInspector.TypeSet.NullOrUndefinedTypeBits))
181             return "(?)";
182
183         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Function | WebInspector.TypeSet.NullOrUndefinedTypeBits))
184             return "Function?";
185         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Boolean | WebInspector.TypeSet.NullOrUndefinedTypeBits))
186             return "Boolean?";
187         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Integer | WebInspector.TypeSet.NullOrUndefinedTypeBits))
188             return "Integer?";
189         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Number | WebInspector.TypeSet.TypeBit.Integer | WebInspector.TypeSet.NullOrUndefinedTypeBits))
190             return "Number?";
191         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.String | WebInspector.TypeSet.NullOrUndefinedTypeBits))
192             return "String?";
193        
194         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Object | WebInspector.TypeSet.TypeBit.Function | WebInspector.TypeSet.TypeBit.String))
195             return "Object";
196         if (typeSet.isContainedIn(WebInspector.TypeSet.TypeBit.Object | WebInspector.TypeSet.TypeBit.Function | WebInspector.TypeSet.TypeBit.String | WebInspector.TypeSet.NullOrUndefinedTypeBits))
197             return "Object?";
198
199         return WebInspector.UIString("(many)");
200     }
201 };