OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 /** | |
5 * @unrestricted | |
6 */ | |
7 Components.RemoteObjectPreviewFormatter = class { | |
8 /** | |
9 * @param {!Protocol.Runtime.PropertyPreview} a | |
10 * @param {!Protocol.Runtime.PropertyPreview} b | |
11 * @return {number} | |
12 */ | |
13 static _objectPropertyComparator(a, b) { | |
14 if (a.type !== 'function' && b.type === 'function') | |
15 return -1; | |
16 if (a.type === 'function' && b.type !== 'function') | |
17 return 1; | |
18 return 0; | |
19 } | |
20 | |
21 /** | |
22 * @param {!Element} parentElement | |
23 * @param {!Protocol.Runtime.ObjectPreview} preview | |
24 * @param {boolean} isEntry | |
25 */ | |
26 appendObjectPreview(parentElement, preview, isEntry) { | |
27 const previewExperimentEnabled = Runtime.experiments.isEnabled('objectPrevie
ws'); | |
28 var description = preview.description; | |
29 if (preview.type !== 'object' || preview.subtype === 'null' || (previewExper
imentEnabled && isEntry)) { | |
30 parentElement.appendChild(this.renderPropertyPreview(preview.type, preview
.subtype, description)); | |
31 return; | |
32 } | |
33 const isArrayOrTypedArray = preview.subtype === 'array' || preview.subtype =
== 'typedarray'; | |
34 if (description) { | |
35 if (previewExperimentEnabled) { | |
36 // Hide the description for plain objects and plain arrays. | |
37 const plainObjectDescription = 'Object'; | |
38 const size = SDK.RemoteObject.arrayLength(preview) || SDK.RemoteObject.m
apOrSetEntriesCount(preview); | |
39 var text = preview.subtype === 'typedarray' ? SDK.RemoteObject.arrayName
FromDescription(description) : ''; | |
40 if (isArrayOrTypedArray) | |
41 text += size > 1 ? ('(' + size + ')') : ''; | |
42 else | |
43 text = description === plainObjectDescription ? '' : description; | |
44 if (text.length > 0) | |
45 parentElement.createChild('span', 'object-description').textContent =
text + ' '; | |
46 } else if (preview.subtype !== 'array') { | |
47 parentElement.createTextChildren(description, ' '); | |
48 } | |
49 } | |
50 | |
51 parentElement.createTextChild(isArrayOrTypedArray ? '[' : '{'); | |
52 if (preview.entries) | |
53 this._appendEntriesPreview(parentElement, preview); | |
54 else if (isArrayOrTypedArray) | |
55 this._appendArrayPropertiesPreview(parentElement, preview); | |
56 else | |
57 this._appendObjectPropertiesPreview(parentElement, preview); | |
58 if (preview.overflow) | |
59 parentElement.createChild('span').textContent = '\u2026'; | |
60 parentElement.createTextChild(isArrayOrTypedArray ? ']' : '}'); | |
61 } | |
62 | |
63 /** | |
64 * @param {string} description | |
65 * @return {string} | |
66 */ | |
67 _abbreviateFullQualifiedClassName(description) { | |
68 var abbreviatedDescription = description.split('.'); | |
69 for (var i = 0; i < abbreviatedDescription.length - 1; ++i) | |
70 abbreviatedDescription[i] = abbreviatedDescription[i].trimMiddle(3); | |
71 return abbreviatedDescription.join('.'); | |
72 } | |
73 | |
74 /** | |
75 * @param {!Element} parentElement | |
76 * @param {!Protocol.Runtime.ObjectPreview} preview | |
77 */ | |
78 _appendObjectPropertiesPreview(parentElement, preview) { | |
79 var properties = preview.properties.filter(p => p.type !== 'accessor') | |
80 .stableSort(Components.RemoteObjectPreviewFormatter._ob
jectPropertyComparator); | |
81 for (var i = 0; i < properties.length; ++i) { | |
82 if (i > 0) | |
83 parentElement.createTextChild(', '); | |
84 | |
85 var property = properties[i]; | |
86 parentElement.appendChild(this._renderDisplayName(property.name)); | |
87 parentElement.createTextChild(': '); | |
88 parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property]
)); | |
89 } | |
90 } | |
91 | |
92 /** | |
93 * @param {!Element} parentElement | |
94 * @param {!Protocol.Runtime.ObjectPreview} preview | |
95 */ | |
96 _appendArrayPropertiesPreview(parentElement, preview) { | |
97 var arrayLength = SDK.RemoteObject.arrayLength(preview); | |
98 var indexProperties = preview.properties.filter(p => toArrayIndex(p.name) !=
= -1).stableSort(arrayEntryComparator); | |
99 var otherProperties = preview.properties.filter(p => toArrayIndex(p.name) ==
= -1) | |
100 .stableSort(Components.RemoteObjectPreviewFormatte
r._objectPropertyComparator); | |
101 | |
102 /** | |
103 * @param {!Protocol.Runtime.PropertyPreview} a | |
104 * @param {!Protocol.Runtime.PropertyPreview} b | |
105 * @return {number} | |
106 */ | |
107 function arrayEntryComparator(a, b) { | |
108 return toArrayIndex(a.name) - toArrayIndex(b.name); | |
109 } | |
110 | |
111 /** | |
112 * @param {string} name | |
113 * @return {number} | |
114 */ | |
115 function toArrayIndex(name) { | |
116 var index = name >>> 0; | |
117 if (String(index) === name && index < arrayLength) | |
118 return index; | |
119 return -1; | |
120 } | |
121 | |
122 // Gaps can be shown when all properties are guaranteed to be in the preview
. | |
123 var canShowGaps = !preview.overflow; | |
124 var lastNonEmptyArrayIndex = -1; | |
125 var elementsAdded = false; | |
126 for (var i = 0; i < indexProperties.length; ++i) { | |
127 if (elementsAdded) | |
128 parentElement.createTextChild(', '); | |
129 | |
130 var property = indexProperties[i]; | |
131 var index = toArrayIndex(property.name); | |
132 if (canShowGaps && index - lastNonEmptyArrayIndex > 1) { | |
133 appendUndefined(index); | |
134 parentElement.createTextChild(', '); | |
135 } | |
136 if (!canShowGaps && i !== index) { | |
137 parentElement.appendChild(this._renderDisplayName(property.name)); | |
138 parentElement.createTextChild(': '); | |
139 } | |
140 parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property]
)); | |
141 lastNonEmptyArrayIndex = index; | |
142 elementsAdded = true; | |
143 } | |
144 | |
145 if (canShowGaps && arrayLength - lastNonEmptyArrayIndex > 1) { | |
146 if (elementsAdded) | |
147 parentElement.createTextChild(', '); | |
148 appendUndefined(arrayLength); | |
149 } | |
150 | |
151 for (var i = 0; i < otherProperties.length; ++i) { | |
152 if (elementsAdded) | |
153 parentElement.createTextChild(', '); | |
154 | |
155 var property = otherProperties[i]; | |
156 parentElement.appendChild(this._renderDisplayName(property.name)); | |
157 parentElement.createTextChild(': '); | |
158 parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property]
)); | |
159 elementsAdded = true; | |
160 } | |
161 | |
162 /** | |
163 * @param {number} index | |
164 */ | |
165 function appendUndefined(index) { | |
166 var span = parentElement.createChild('span', 'object-value-undefined'); | |
167 span.textContent = Common.UIString('undefined × %d', index - lastNonEmptyA
rrayIndex - 1); | |
168 elementsAdded = true; | |
169 } | |
170 } | |
171 | |
172 /** | |
173 * @param {!Element} parentElement | |
174 * @param {!Protocol.Runtime.ObjectPreview} preview | |
175 */ | |
176 _appendEntriesPreview(parentElement, preview) { | |
177 for (var i = 0; i < preview.entries.length; ++i) { | |
178 if (i > 0) | |
179 parentElement.createTextChild(', '); | |
180 | |
181 var entry = preview.entries[i]; | |
182 if (entry.key) { | |
183 this.appendObjectPreview(parentElement, entry.key, true /* isEntry */); | |
184 parentElement.createTextChild(' => '); | |
185 } | |
186 this.appendObjectPreview(parentElement, entry.value, true /* isEntry */); | |
187 } | |
188 } | |
189 | |
190 /** | |
191 * @param {string} name | |
192 * @return {!Element} | |
193 */ | |
194 _renderDisplayName(name) { | |
195 var result = createElementWithClass('span', 'name'); | |
196 var needsQuotes = /^\s|\s$|^$|\n/.test(name); | |
197 result.textContent = needsQuotes ? '"' + name.replace(/\n/g, '\u21B5') + '"'
: name; | |
198 return result; | |
199 } | |
200 | |
201 /** | |
202 * @param {!Array.<!Protocol.Runtime.PropertyPreview>} propertyPath | |
203 * @return {!Element} | |
204 */ | |
205 _renderPropertyPreviewOrAccessor(propertyPath) { | |
206 var property = propertyPath.peekLast(); | |
207 return this.renderPropertyPreview(property.type, /** @type {string} */ (prop
erty.subtype), property.value); | |
208 } | |
209 | |
210 /** | |
211 * @param {string} type | |
212 * @param {string=} subtype | |
213 * @param {string=} description | |
214 * @return {!Element} | |
215 */ | |
216 renderPropertyPreview(type, subtype, description) { | |
217 var span = createElementWithClass('span', 'object-value-' + (subtype || type
)); | |
218 description = description || ''; | |
219 | |
220 if (type === 'accessor') { | |
221 span.textContent = '(...)'; | |
222 span.title = Common.UIString('The property is computed with a getter'); | |
223 return span; | |
224 } | |
225 | |
226 if (type === 'function') { | |
227 span.textContent = 'function'; | |
228 return span; | |
229 } | |
230 | |
231 if (type === 'object' && subtype === 'node' && description) { | |
232 Components.DOMPresentationUtils.createSpansForNodeTitle(span, description)
; | |
233 return span; | |
234 } | |
235 | |
236 if (type === 'string') { | |
237 span.createTextChildren('"', description.replace(/\n/g, '\u21B5'), '"'); | |
238 return span; | |
239 } | |
240 | |
241 if (type === 'object' && !subtype) { | |
242 span.textContent = this._abbreviateFullQualifiedClassName(description); | |
243 span.title = description; | |
244 return span; | |
245 } | |
246 | |
247 span.textContent = description; | |
248 return span; | |
249 } | |
250 }; | |
OLD | NEW |