| 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 |