Web Inspector: Create a UI for displaying JavaScript type information
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / TypePropertiesSection.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.TypePropertiesSection = function(types, title, subtitle)
27 {
28     this.emptyPlaceholder = WebInspector.UIString("No Properties");
29     this.types = types;
30
31     WebInspector.PropertiesSection.call(this, title, subtitle);
32 };
33
34 WebInspector.TypePropertiesSection.prototype = {
35     constructor: WebInspector.TypePropertiesSection,
36     __proto__: WebInspector.PropertiesSection.prototype,
37
38     onpopulate: function()
39     {
40         this.propertiesTreeOutline.removeChildren();
41
42         var primitiveTypeNames = this.types.globalPrimitiveTypeNames || this.types.localPrimitiveTypeNames;
43         var structures = this.types.globalStructures || this.types.localStructures;
44         var properties = [];
45         for (var struct of structures) {
46             properties.push({
47                 name: struct.constructorName,
48                 structure: struct
49             });
50         }
51         for (var primitiveName of primitiveTypeNames) {
52             properties.push({
53                 name: primitiveName,
54                 structure: null
55             });
56         }
57
58         properties.sort(WebInspector.TypePropertiesSection.PropertyComparator);
59         for (var property of properties)
60             this.propertiesTreeOutline.appendChild(new WebInspector.TypePropertyTreeElement(property));
61
62         if (!this.propertiesTreeOutline.children.length) {
63             var title = document.createElement("div");
64             title.className = "info";
65             title.textContent = this.emptyPlaceholder;
66             var infoElement = new TreeElement(title, null, false);
67             this.propertiesTreeOutline.appendChild(infoElement);
68         }
69
70         this.dispatchEventToListeners(WebInspector.Section.Event.VisibleContentDidChange);
71
72         if (properties.length === 1)
73             this.propertiesTreeOutline.children[0].expandRecursively();
74     }
75 };
76
77 // This is mostly identical to ObjectPropertiesSection.compareProperties.
78 // But this checks for equality because we can have two objects named the same thing.
79 WebInspector.TypePropertiesSection.PropertyComparator = function(propertyA, propertyB)
80 {
81     var a = propertyA.name;
82     var b = propertyB.name;
83     if (a.indexOf("__proto__") !== -1)
84         return 1;
85     if (b.indexOf("__proto__") !== -1)
86         return -1;
87     if (a === b)
88         return 1;
89
90     var diff = 0;
91     var chunk = /^\d+|^\D+/;
92     var chunka, chunkb, anum, bnum;
93     while (diff === 0) {
94         if (!a && b)
95             return -1;
96         if (!b && a)
97             return 1;
98         chunka = a.match(chunk)[0];
99         chunkb = b.match(chunk)[0];
100         anum = !isNaN(chunka);
101         bnum = !isNaN(chunkb);
102         if (anum && !bnum)
103             return -1;
104         if (bnum && !anum)
105             return 1;
106         if (anum && bnum) {
107             diff = chunka - chunkb;
108             if (diff === 0 && chunka.length !== chunkb.length) {
109                 if (!+chunka && !+chunkb) // chunks are strings of all 0s (special case)
110                     return chunka.length - chunkb.length;
111                 else
112                     return chunkb.length - chunka.length;
113             }
114         } else if (chunka !== chunkb)
115             return (chunka < chunkb) ? -1 : 1;
116         a = a.substring(chunka.length);
117         b = b.substring(chunkb.length);
118     }
119
120     return diff;
121 };
122
123 WebInspector.TypePropertyTreeElement = function(property)
124 {
125     this.property = property;
126
127     this.nameElement = document.createElement("span");
128     this.nameElement.className = "name";
129     this.nameElement.textContent = this.property.name;
130
131     TreeElement.call(this, this.nameElement, null, false);
132
133     this.toggleOnClick = true;
134     this.hasChildren = !!this.property.structure;
135 };
136
137 WebInspector.TypePropertyTreeElement.prototype = {
138     constructor: WebInspector.TypePropertyTreeElement,
139     __proto__: TreeElement.prototype,
140
141     onpopulate: function()
142     {
143         this.removeChildren();
144
145         var properties = [];
146         for (var fieldName of this.property.structure.fields) {
147             properties.push({
148                 name: fieldName,
149                 structure: null
150             });
151         }
152
153         properties.sort(WebInspector.TypePropertiesSection.PropertyComparator);
154
155         if (this.property.structure.prototypeStructure) {
156             properties.push({
157                 name: this.property.structure.prototypeStructure.constructorName + " (" + WebInspector.UIString("Prototype") + ")",
158                 structure: this.property.structure.prototypeStructure
159             });
160         }
161
162         for (var property of properties)
163             this.appendChild(new WebInspector.TypePropertyTreeElement(property));
164     }
165 };
166