blob: 0f9a0126ab21e8e70866a8fd35f72fbfcda6fc58 [file] [log] [blame]
// 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.
/**
* @constructor
* @param {!WebInspector.RemoteObject} object
*/
WebInspector.CustomPreviewSection = function(object)
{
this._sectionElement = createElementWithClass("span", "custom-expandable-section");
this._object = object;
this._expanded = false;
this._cachedContent = null;
var customPreview = object.customPreview();
try {
var headerJSON = JSON.parse(customPreview.header);
} catch (e) {
WebInspector.console.error("Broken formatter: header is invalid json " + e);
return;
}
this._header = this._renderJSONMLTag(headerJSON);
if (this._header.nodeType === Node.TEXT_NODE) {
WebInspector.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._sectionElement.appendChild(this._header);
}
/**
* @constructor
* @param {!WebInspector.RemoteObject} object
*/
WebInspector.CustomPreviewComponent = function(object)
{
this._object = object;
this._customPreviewSection = new WebInspector.CustomPreviewSection(object);
this.element = createElementWithClass("span", "source-code");
var shadowRoot = WebInspector.createShadowRootWithCoreStyles(this.element);
this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), false);
shadowRoot.appendChild(WebInspector.Widget.createStyleElement("components/customPreviewSection.css"));
shadowRoot.appendChild(this._customPreviewSection.element());
}
WebInspector.CustomPreviewComponent.prototype = {
expandIfPossible: function()
{
if (this._object.customPreview().hasBody && this._customPreviewSection)
this._customPreviewSection._loadBody();
},
/**
* @param {!Event} event
*/
_contextMenuEventFired: function(event)
{
var contextMenu = new WebInspector.ContextMenu(event);
if (this._customPreviewSection)
contextMenu.appendItem(WebInspector.UIString.capitalize("Show as Javascript ^object" ), this._disassemble.bind(this));
contextMenu.appendApplicableItems(this._object);
contextMenu.show();
},
_disassemble: function()
{
this.element.shadowRoot.textContent = "";
this._customPreviewSection = null;
this.element.shadowRoot.appendChild(WebInspector.ObjectPropertiesSection.defaultObjectPresentation(this._object));
}
}
WebInspector.CustomPreviewSection._tagsWhiteList = new Set(["span", "div", "ol", "li","table", "tr", "td"]);
WebInspector.CustomPreviewSection.prototype = {
/**
* @return {!Element}
*/
element: function()
{
return this._sectionElement;
},
/**
* @param {*} jsonML
* @return {!Node}
*/
_renderJSONMLTag: function(jsonML)
{
if (!Array.isArray(jsonML))
return createTextNode(jsonML + "");
var array = /** @type {!Array.<*>} */(jsonML);
if (array[0] === "object")
return this._layoutObjectTag(array);
else
return this._renderElement(array);
},
/**
*
* @param {!Array.<*>} object
* @return {!Node}
*/
_renderElement: function(object)
{
var tagName = object.shift();
if (!WebInspector.CustomPreviewSection._tagsWhiteList.has(tagName)) {
WebInspector.console.error("Broken formatter: element " + tagName + " is not allowed!");
return createElement("span");
}
var element = createElement(/** @type {string} */ (tagName));
if ((typeof object[0] == "object") && !Array.isArray(object[0])) {
var attributes = object.shift();
for (var key in attributes) {
var 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: function(objectTag)
{
objectTag.shift();
var attributes = objectTag.shift();
var remoteObject = this._object.target().runtimeModel.createRemoteObject(/** @type {!RuntimeAgent.RemoteObject} */ (attributes));
if (remoteObject.customPreview())
return (new WebInspector.CustomPreviewSection(remoteObject)).element();
var sectionElement = WebInspector.ObjectPropertiesSection.defaultObjectPresentation(remoteObject);
sectionElement.classList.toggle("custom-expandable-section-standard-section", remoteObject.hasChildren);
return sectionElement;
},
/**
* @param {!Node} parentElement
* @param {!Array.<*>} jsonMLTags
*/
_appendJsonMLTags: function(parentElement, jsonMLTags)
{
for (var i = 0; i < jsonMLTags.length; ++i)
parentElement.appendChild(this._renderJSONMLTag(jsonMLTags[i]));
},
/**
* @param {!Event} event
*/
_onClick: function(event)
{
event.consume(true);
if (this._cachedContent)
this._toggleExpand();
else
this._loadBody();
},
_toggleExpand: function()
{
this._expanded = !this._expanded;
this._header.classList.toggle("expanded", this._expanded);
this._cachedContent.classList.toggle("hidden", !this._expanded);
},
_loadBody: function()
{
/**
* @suppressReceiverCheck
* @suppressGlobalPropertiesCheck
* @suppress {undefinedVars}
* @this {Object}
* @param {*=} formatter
* @param {*=} config
*/
function load(formatter, config)
{
/**
* @param {*} jsonMLObject
* @throws {string} error message
*/
function substituteObjectTagsInCustomPreview(jsonMLObject)
{
if (!jsonMLObject || (typeof jsonMLObject !== "object") || (typeof jsonMLObject.splice !== "function"))
return;
var obj = jsonMLObject.length;
if (!(typeof obj === "number" && obj >>> 0 === obj && (obj > 0 || 1 / obj > 0)))
return;
var startIndex = 1;
if (jsonMLObject[0] === "object") {
var attributes = jsonMLObject[1];
var originObject = attributes["object"];
var config = attributes["config"];
if (typeof originObject === "undefined")
throw "Illegal format: obligatory attribute \"object\" isn't specified";
jsonMLObject[1] = bindRemoteObject(originObject, false, false, null, false, config);
startIndex = 2;
}
for (var i = startIndex; i < jsonMLObject.length; ++i)
substituteObjectTagsInCustomPreview(jsonMLObject[i]);
}
try {
var body = formatter.body(this, config);
substituteObjectTagsInCustomPreview(body);
return body;
} catch (e) {
console.error("Custom Formatter Failed: " + e);
return null;
}
}
var customPreview = this._object.customPreview();
var args = [{objectId: customPreview.formatterObjectId}];
if (customPreview.configObjectId)
args.push({objectId: customPreview.configObjectId});
this._object.callFunctionJSON(load, args, onBodyLoaded.bind(this));
/**
* @param {*} bodyJsonML
* @this {WebInspector.CustomPreviewSection}
*/
function onBodyLoaded(bodyJsonML)
{
if (!bodyJsonML)
return;
this._cachedContent = this._renderJSONMLTag(bodyJsonML);
this._sectionElement.appendChild(this._cachedContent);
this._toggleExpand();
}
}
}