Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / components / CustomPreviewSection.js
blob0f9a0126ab21e8e70866a8fd35f72fbfcda6fc58
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.
5 /**
6 * @constructor
7 * @param {!WebInspector.RemoteObject} object
8 */
9 WebInspector.CustomPreviewSection = function(object)
11 this._sectionElement = createElementWithClass("span", "custom-expandable-section");
12 this._object = object;
13 this._expanded = false;
14 this._cachedContent = null;
15 var customPreview = object.customPreview();
17 try {
18 var headerJSON = JSON.parse(customPreview.header);
19 } catch (e) {
20 WebInspector.console.error("Broken formatter: header is invalid json " + e);
21 return;
23 this._header = this._renderJSONMLTag(headerJSON);
24 if (this._header.nodeType === Node.TEXT_NODE) {
25 WebInspector.console.error("Broken formatter: header should be an element node.");
26 return;
29 if (customPreview.hasBody) {
30 this._header.classList.add("custom-expandable-section-header");
31 this._header.addEventListener("click", this._onClick.bind(this), false);
34 this._sectionElement.appendChild(this._header);
37 /**
38 * @constructor
39 * @param {!WebInspector.RemoteObject} object
41 WebInspector.CustomPreviewComponent = function(object)
43 this._object = object;
44 this._customPreviewSection = new WebInspector.CustomPreviewSection(object);
45 this.element = createElementWithClass("span", "source-code");
46 var shadowRoot = WebInspector.createShadowRootWithCoreStyles(this.element);
47 this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), false);
48 shadowRoot.appendChild(WebInspector.Widget.createStyleElement("components/customPreviewSection.css"));
49 shadowRoot.appendChild(this._customPreviewSection.element());
52 WebInspector.CustomPreviewComponent.prototype = {
53 expandIfPossible: function()
55 if (this._object.customPreview().hasBody && this._customPreviewSection)
56 this._customPreviewSection._loadBody();
59 /**
60 * @param {!Event} event
62 _contextMenuEventFired: function(event)
64 var contextMenu = new WebInspector.ContextMenu(event);
65 if (this._customPreviewSection)
66 contextMenu.appendItem(WebInspector.UIString.capitalize("Show as Javascript ^object" ), this._disassemble.bind(this));
67 contextMenu.appendApplicableItems(this._object);
68 contextMenu.show();
71 _disassemble: function()
73 this.element.shadowRoot.textContent = "";
74 this._customPreviewSection = null;
75 this.element.shadowRoot.appendChild(WebInspector.ObjectPropertiesSection.defaultObjectPresentation(this._object));
79 WebInspector.CustomPreviewSection._tagsWhiteList = new Set(["span", "div", "ol", "li","table", "tr", "td"]);
81 WebInspector.CustomPreviewSection.prototype = {
83 /**
84 * @return {!Element}
86 element: function()
88 return this._sectionElement;
91 /**
92 * @param {*} jsonML
93 * @return {!Node}
95 _renderJSONMLTag: function(jsonML)
97 if (!Array.isArray(jsonML))
98 return createTextNode(jsonML + "");
100 var array = /** @type {!Array.<*>} */(jsonML);
101 if (array[0] === "object")
102 return this._layoutObjectTag(array);
103 else
104 return this._renderElement(array);
109 * @param {!Array.<*>} object
110 * @return {!Node}
112 _renderElement: function(object)
114 var tagName = object.shift();
115 if (!WebInspector.CustomPreviewSection._tagsWhiteList.has(tagName)) {
116 WebInspector.console.error("Broken formatter: element " + tagName + " is not allowed!");
117 return createElement("span");
119 var element = createElement(/** @type {string} */ (tagName));
120 if ((typeof object[0] == "object") && !Array.isArray(object[0])) {
121 var attributes = object.shift();
122 for (var key in attributes) {
123 var value = attributes[key];
124 if ((key !== "style") || (typeof value !== "string"))
125 continue;
127 element.setAttribute(key, value);
131 this._appendJsonMLTags(element, object);
132 return element;
136 * @param {!Array.<*>} objectTag
137 * @return {!Node}
139 _layoutObjectTag: function(objectTag)
141 objectTag.shift();
142 var attributes = objectTag.shift();
143 var remoteObject = this._object.target().runtimeModel.createRemoteObject(/** @type {!RuntimeAgent.RemoteObject} */ (attributes));
144 if (remoteObject.customPreview())
145 return (new WebInspector.CustomPreviewSection(remoteObject)).element();
147 var sectionElement = WebInspector.ObjectPropertiesSection.defaultObjectPresentation(remoteObject);
148 sectionElement.classList.toggle("custom-expandable-section-standard-section", remoteObject.hasChildren);
149 return sectionElement;
153 * @param {!Node} parentElement
154 * @param {!Array.<*>} jsonMLTags
156 _appendJsonMLTags: function(parentElement, jsonMLTags)
158 for (var i = 0; i < jsonMLTags.length; ++i)
159 parentElement.appendChild(this._renderJSONMLTag(jsonMLTags[i]));
163 * @param {!Event} event
165 _onClick: function(event)
167 event.consume(true);
168 if (this._cachedContent)
169 this._toggleExpand();
170 else
171 this._loadBody();
174 _toggleExpand: function()
176 this._expanded = !this._expanded;
177 this._header.classList.toggle("expanded", this._expanded);
178 this._cachedContent.classList.toggle("hidden", !this._expanded);
181 _loadBody: function()
184 * @suppressReceiverCheck
185 * @suppressGlobalPropertiesCheck
186 * @suppress {undefinedVars}
187 * @this {Object}
188 * @param {*=} formatter
189 * @param {*=} config
191 function load(formatter, config)
194 * @param {*} jsonMLObject
195 * @throws {string} error message
197 function substituteObjectTagsInCustomPreview(jsonMLObject)
199 if (!jsonMLObject || (typeof jsonMLObject !== "object") || (typeof jsonMLObject.splice !== "function"))
200 return;
202 var obj = jsonMLObject.length;
203 if (!(typeof obj === "number" && obj >>> 0 === obj && (obj > 0 || 1 / obj > 0)))
204 return;
206 var startIndex = 1;
207 if (jsonMLObject[0] === "object") {
208 var attributes = jsonMLObject[1];
209 var originObject = attributes["object"];
210 var config = attributes["config"];
211 if (typeof originObject === "undefined")
212 throw "Illegal format: obligatory attribute \"object\" isn't specified";
214 jsonMLObject[1] = bindRemoteObject(originObject, false, false, null, false, config);
215 startIndex = 2;
217 for (var i = startIndex; i < jsonMLObject.length; ++i)
218 substituteObjectTagsInCustomPreview(jsonMLObject[i]);
221 try {
222 var body = formatter.body(this, config);
223 substituteObjectTagsInCustomPreview(body);
224 return body;
225 } catch (e) {
226 console.error("Custom Formatter Failed: " + e);
227 return null;
231 var customPreview = this._object.customPreview();
232 var args = [{objectId: customPreview.formatterObjectId}];
233 if (customPreview.configObjectId)
234 args.push({objectId: customPreview.configObjectId});
235 this._object.callFunctionJSON(load, args, onBodyLoaded.bind(this));
238 * @param {*} bodyJsonML
239 * @this {WebInspector.CustomPreviewSection}
241 function onBodyLoaded(bodyJsonML)
243 if (!bodyJsonML)
244 return;
246 this._cachedContent = this._renderJSONMLTag(bodyJsonML);
247 this._sectionElement.appendChild(this._cachedContent);
248 this._toggleExpand();