OLD | NEW |
| (Empty) |
1 // Copyright 2014 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.CustomPreviewSection = class { | |
8 /** | |
9 * @param {!SDK.RemoteObject} object | |
10 */ | |
11 constructor(object) { | |
12 this._sectionElement = createElementWithClass('span', 'custom-expandable-sec
tion'); | |
13 this._object = object; | |
14 this._expanded = false; | |
15 this._cachedContent = null; | |
16 var customPreview = object.customPreview(); | |
17 | |
18 try { | |
19 var headerJSON = JSON.parse(customPreview.header); | |
20 } catch (e) { | |
21 Common.console.error('Broken formatter: header is invalid json ' + e); | |
22 return; | |
23 } | |
24 this._header = this._renderJSONMLTag(headerJSON); | |
25 if (this._header.nodeType === Node.TEXT_NODE) { | |
26 Common.console.error('Broken formatter: header should be an element node.'
); | |
27 return; | |
28 } | |
29 | |
30 if (customPreview.hasBody) { | |
31 this._header.classList.add('custom-expandable-section-header'); | |
32 this._header.addEventListener('click', this._onClick.bind(this), false); | |
33 this._expandIcon = UI.Icon.create('smallicon-triangle-right', 'custom-expa
nd-icon'); | |
34 this._header.insertBefore(this._expandIcon, this._header.firstChild); | |
35 } | |
36 | |
37 this._sectionElement.appendChild(this._header); | |
38 } | |
39 | |
40 /** | |
41 * @return {!Element} | |
42 */ | |
43 element() { | |
44 return this._sectionElement; | |
45 } | |
46 | |
47 /** | |
48 * @param {*} jsonML | |
49 * @return {!Node} | |
50 */ | |
51 _renderJSONMLTag(jsonML) { | |
52 if (!Array.isArray(jsonML)) | |
53 return createTextNode(jsonML + ''); | |
54 | |
55 var array = /** @type {!Array.<*>} */ (jsonML); | |
56 return array[0] === 'object' ? this._layoutObjectTag(array) : this._renderEl
ement(array); | |
57 } | |
58 | |
59 /** | |
60 * | |
61 * @param {!Array.<*>} object | |
62 * @return {!Node} | |
63 */ | |
64 _renderElement(object) { | |
65 var tagName = object.shift(); | |
66 if (!Components.CustomPreviewSection._tagsWhiteList.has(tagName)) { | |
67 Common.console.error('Broken formatter: element ' + tagName + ' is not all
owed!'); | |
68 return createElement('span'); | |
69 } | |
70 var element = createElement(/** @type {string} */ (tagName)); | |
71 if ((typeof object[0] === 'object') && !Array.isArray(object[0])) { | |
72 var attributes = object.shift(); | |
73 for (var key in attributes) { | |
74 var value = attributes[key]; | |
75 if ((key !== 'style') || (typeof value !== 'string')) | |
76 continue; | |
77 | |
78 element.setAttribute(key, value); | |
79 } | |
80 } | |
81 | |
82 this._appendJsonMLTags(element, object); | |
83 return element; | |
84 } | |
85 | |
86 /** | |
87 * @param {!Array.<*>} objectTag | |
88 * @return {!Node} | |
89 */ | |
90 _layoutObjectTag(objectTag) { | |
91 objectTag.shift(); | |
92 var attributes = objectTag.shift(); | |
93 var remoteObject = this._object.target().runtimeModel.createRemoteObject( | |
94 /** @type {!Protocol.Runtime.RemoteObject} */ (attributes)); | |
95 if (remoteObject.customPreview()) | |
96 return (new Components.CustomPreviewSection(remoteObject)).element(); | |
97 | |
98 var sectionElement = Components.ObjectPropertiesSection.defaultObjectPresent
ation(remoteObject); | |
99 sectionElement.classList.toggle('custom-expandable-section-standard-section'
, remoteObject.hasChildren); | |
100 return sectionElement; | |
101 } | |
102 | |
103 /** | |
104 * @param {!Node} parentElement | |
105 * @param {!Array.<*>} jsonMLTags | |
106 */ | |
107 _appendJsonMLTags(parentElement, jsonMLTags) { | |
108 for (var i = 0; i < jsonMLTags.length; ++i) | |
109 parentElement.appendChild(this._renderJSONMLTag(jsonMLTags[i])); | |
110 } | |
111 | |
112 /** | |
113 * @param {!Event} event | |
114 */ | |
115 _onClick(event) { | |
116 event.consume(true); | |
117 if (this._cachedContent) | |
118 this._toggleExpand(); | |
119 else | |
120 this._loadBody(); | |
121 } | |
122 | |
123 _toggleExpand() { | |
124 this._expanded = !this._expanded; | |
125 this._header.classList.toggle('expanded', this._expanded); | |
126 this._cachedContent.classList.toggle('hidden', !this._expanded); | |
127 if (this._expanded) | |
128 this._expandIcon.setIconType('smallicon-triangle-down'); | |
129 else | |
130 this._expandIcon.setIconType('smallicon-triangle-right'); | |
131 } | |
132 | |
133 _loadBody() { | |
134 /** | |
135 * @suppressReceiverCheck | |
136 * @suppressGlobalPropertiesCheck | |
137 * @suppress {undefinedVars} | |
138 * @this {Object} | |
139 * @param {function(!Object, *):*} bindRemoteObject | |
140 * @param {*=} formatter | |
141 * @param {*=} config | |
142 */ | |
143 function load(bindRemoteObject, formatter, config) { | |
144 /** | |
145 * @param {*} jsonMLObject | |
146 * @throws {string} error message | |
147 */ | |
148 function substituteObjectTagsInCustomPreview(jsonMLObject) { | |
149 if (!jsonMLObject || (typeof jsonMLObject !== 'object') || (typeof jsonM
LObject.splice !== 'function')) | |
150 return; | |
151 | |
152 var obj = jsonMLObject.length; | |
153 if (!(typeof obj === 'number' && obj >>> 0 === obj && (obj > 0 || 1 / ob
j > 0))) | |
154 return; | |
155 | |
156 var startIndex = 1; | |
157 if (jsonMLObject[0] === 'object') { | |
158 var attributes = jsonMLObject[1]; | |
159 var originObject = attributes['object']; | |
160 var config = attributes['config']; | |
161 if (typeof originObject === 'undefined') | |
162 throw 'Illegal format: obligatory attribute "object" isn\'t specifie
d'; | |
163 | |
164 jsonMLObject[1] = bindRemoteObject(originObject, config); | |
165 startIndex = 2; | |
166 } | |
167 for (var i = startIndex; i < jsonMLObject.length; ++i) | |
168 substituteObjectTagsInCustomPreview(jsonMLObject[i]); | |
169 } | |
170 | |
171 try { | |
172 var body = formatter.body(this, config); | |
173 substituteObjectTagsInCustomPreview(body); | |
174 return body; | |
175 } catch (e) { | |
176 console.error('Custom Formatter Failed: ' + e); | |
177 return null; | |
178 } | |
179 } | |
180 | |
181 var customPreview = this._object.customPreview(); | |
182 var args = [{objectId: customPreview.bindRemoteObjectFunctionId}, {objectId:
customPreview.formatterObjectId}]; | |
183 if (customPreview.configObjectId) | |
184 args.push({objectId: customPreview.configObjectId}); | |
185 this._object.callFunctionJSON(load, args, onBodyLoaded.bind(this)); | |
186 | |
187 /** | |
188 * @param {*} bodyJsonML | |
189 * @this {Components.CustomPreviewSection} | |
190 */ | |
191 function onBodyLoaded(bodyJsonML) { | |
192 if (!bodyJsonML) | |
193 return; | |
194 | |
195 this._cachedContent = this._renderJSONMLTag(bodyJsonML); | |
196 this._sectionElement.appendChild(this._cachedContent); | |
197 this._toggleExpand(); | |
198 } | |
199 } | |
200 }; | |
201 | |
202 /** | |
203 * @unrestricted | |
204 */ | |
205 Components.CustomPreviewComponent = class { | |
206 /** | |
207 * @param {!SDK.RemoteObject} object | |
208 */ | |
209 constructor(object) { | |
210 this._object = object; | |
211 this._customPreviewSection = new Components.CustomPreviewSection(object); | |
212 this.element = createElementWithClass('span', 'source-code'); | |
213 var shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'components
/customPreviewComponent.css'); | |
214 this.element.addEventListener('contextmenu', this._contextMenuEventFired.bin
d(this), false); | |
215 shadowRoot.appendChild(this._customPreviewSection.element()); | |
216 } | |
217 | |
218 expandIfPossible() { | |
219 if (this._object.customPreview().hasBody && this._customPreviewSection) | |
220 this._customPreviewSection._loadBody(); | |
221 } | |
222 | |
223 /** | |
224 * @param {!Event} event | |
225 */ | |
226 _contextMenuEventFired(event) { | |
227 var contextMenu = new UI.ContextMenu(event); | |
228 if (this._customPreviewSection) | |
229 contextMenu.appendItem(Common.UIString.capitalize('Show as Javascript ^obj
ect'), this._disassemble.bind(this)); | |
230 contextMenu.appendApplicableItems(this._object); | |
231 contextMenu.show(); | |
232 } | |
233 | |
234 _disassemble() { | |
235 this.element.shadowRoot.textContent = ''; | |
236 this._customPreviewSection = null; | |
237 this.element.shadowRoot.appendChild(Components.ObjectPropertiesSection.defau
ltObjectPresentation(this._object)); | |
238 } | |
239 }; | |
240 | |
241 Components.CustomPreviewSection._tagsWhiteList = new Set(['span', 'div', 'ol', '
li', 'table', 'tr', 'td']); | |
OLD | NEW |