UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

245 lines (217 loc) 7.75 kB
// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @unrestricted */ ObjectUI.CustomPreviewSection = class { /** * @param {!SDK.RemoteObject} object */ constructor(object) { this._sectionElement = createElementWithClass('span', 'custom-expandable-section'); this._object = object; this._expanded = false; this._cachedContent = null; const customPreview = object.customPreview(); let headerJSON; try { headerJSON = JSON.parse(customPreview.header); } catch (e) { Common.console.error('Broken formatter: header is invalid json ' + e); return; } this._header = this._renderJSONMLTag(headerJSON); if (this._header.nodeType === Node.TEXT_NODE) { Common.console.error('Broken formatter: header should be an element node.'); return; } if (customPreview.hasBody) { this._header.classList.add('custom-expandable-section-header'); this._header.addEventListener('click', this._onClick.bind(this), false); this._expandIcon = UI.Icon.create('smallicon-triangle-right', 'custom-expand-icon'); this._header.insertBefore(this._expandIcon, this._header.firstChild); } this._sectionElement.appendChild(this._header); } /** * @return {!Element} */ element() { return this._sectionElement; } /** * @param {*} jsonML * @return {!Node} */ _renderJSONMLTag(jsonML) { if (!Array.isArray(jsonML)) return createTextNode(jsonML + ''); const array = /** @type {!Array.<*>} */ (jsonML); return array[0] === 'object' ? this._layoutObjectTag(array) : this._renderElement(array); } /** * * @param {!Array.<*>} object * @return {!Node} */ _renderElement(object) { const tagName = object.shift(); if (!ObjectUI.CustomPreviewSection._tagsWhiteList.has(tagName)) { Common.console.error('Broken formatter: element ' + tagName + ' is not allowed!'); return createElement('span'); } const element = createElement(/** @type {string} */ (tagName)); if ((typeof object[0] === 'object') && !Array.isArray(object[0])) { const attributes = object.shift(); for (const key in attributes) { const value = attributes[key]; if ((key !== 'style') || (typeof value !== 'string')) continue; element.setAttribute(key, value); } } this._appendJsonMLTags(element, object); return element; } /** * @param {!Array.<*>} objectTag * @return {!Node} */ _layoutObjectTag(objectTag) { objectTag.shift(); const attributes = objectTag.shift(); const remoteObject = this._object.runtimeModel().createRemoteObject( /** @type {!Protocol.Runtime.RemoteObject} */ (attributes)); if (remoteObject.customPreview()) return (new ObjectUI.CustomPreviewSection(remoteObject)).element(); const sectionElement = ObjectUI.ObjectPropertiesSection.defaultObjectPresentation(remoteObject); sectionElement.classList.toggle('custom-expandable-section-standard-section', remoteObject.hasChildren); return sectionElement; } /** * @param {!Node} parentElement * @param {!Array.<*>} jsonMLTags */ _appendJsonMLTags(parentElement, jsonMLTags) { for (let i = 0; i < jsonMLTags.length; ++i) parentElement.appendChild(this._renderJSONMLTag(jsonMLTags[i])); } /** * @param {!Event} event */ _onClick(event) { event.consume(true); if (this._cachedContent) this._toggleExpand(); else this._loadBody(); } _toggleExpand() { this._expanded = !this._expanded; this._header.classList.toggle('expanded', this._expanded); this._cachedContent.classList.toggle('hidden', !this._expanded); if (this._expanded) this._expandIcon.setIconType('smallicon-triangle-down'); else this._expandIcon.setIconType('smallicon-triangle-right'); } _loadBody() { /** * @suppressReceiverCheck * @suppressGlobalPropertiesCheck * @suppress {undefinedVars} * @this {Object} * @param {function(!Object, *):*} bindRemoteObject * @param {*=} formatter * @param {*=} config */ function load(bindRemoteObject, formatter, config) { /** * @param {*} jsonMLObject * @throws {string} error message */ function substituteObjectTagsInCustomPreview(jsonMLObject) { if (!jsonMLObject || (typeof jsonMLObject !== 'object') || (typeof jsonMLObject.splice !== 'function')) return; const obj = jsonMLObject.length; if (!(typeof obj === 'number' && obj >>> 0 === obj && (obj > 0 || 1 / obj > 0))) return; let startIndex = 1; if (jsonMLObject[0] === 'object') { const attributes = jsonMLObject[1]; const originObject = attributes['object']; const config = attributes['config']; if (typeof originObject === 'undefined') throw 'Illegal format: obligatory attribute "object" isn\'t specified'; jsonMLObject[1] = bindRemoteObject(originObject, config); startIndex = 2; } for (let i = startIndex; i < jsonMLObject.length; ++i) substituteObjectTagsInCustomPreview(jsonMLObject[i]); } try { const body = formatter.body(this, config); substituteObjectTagsInCustomPreview(body); return body; } catch (e) { console.error('Custom Formatter Failed: ' + e); return null; } } const customPreview = this._object.customPreview(); const args = [{objectId: customPreview.bindRemoteObjectFunctionId}, {objectId: customPreview.formatterObjectId}]; if (customPreview.configObjectId) args.push({objectId: customPreview.configObjectId}); this._object.callFunctionJSON(load, args, onBodyLoaded.bind(this)); /** * @param {*} bodyJsonML * @this {ObjectUI.CustomPreviewSection} */ function onBodyLoaded(bodyJsonML) { if (!bodyJsonML) return; this._cachedContent = this._renderJSONMLTag(bodyJsonML); this._sectionElement.appendChild(this._cachedContent); this._toggleExpand(); } } }; /** * @unrestricted */ ObjectUI.CustomPreviewComponent = class { /** * @param {!SDK.RemoteObject} object */ constructor(object) { this._object = object; this._customPreviewSection = new ObjectUI.CustomPreviewSection(object); this.element = createElementWithClass('span', 'source-code'); const shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'object_ui/customPreviewComponent.css'); this.element.addEventListener('contextmenu', this._contextMenuEventFired.bind(this), false); shadowRoot.appendChild(this._customPreviewSection.element()); } expandIfPossible() { if (this._object.customPreview().hasBody && this._customPreviewSection) this._customPreviewSection._loadBody(); } /** * @param {!Event} event */ _contextMenuEventFired(event) { const contextMenu = new UI.ContextMenu(event); if (this._customPreviewSection) { contextMenu.revealSection().appendItem( Common.UIString('Show as JavaScript object'), this._disassemble.bind(this)); } contextMenu.appendApplicableItems(this._object); contextMenu.show(); } _disassemble() { this.element.shadowRoot.textContent = ''; this._customPreviewSection = null; this.element.shadowRoot.appendChild(ObjectUI.ObjectPropertiesSection.defaultObjectPresentation(this._object)); } }; ObjectUI.CustomPreviewSection._tagsWhiteList = new Set(['span', 'div', 'ol', 'li', 'table', 'tr', 'td']);