f42d4c5141fa39fd3e15e0f655a0f917c6a9fab0
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / FormattedValue.js
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.FormattedValue = {};
27
28 WebInspector.FormattedValue.classNameForTypes = function(type, subtype)
29 {
30     return "formatted-" + (subtype ? subtype : type);
31 };
32
33 WebInspector.FormattedValue.classNameForObject = function(object)
34 {
35     return WebInspector.FormattedValue.classNameForTypes(object.type, object.subtype);
36 };
37
38 WebInspector.FormattedValue.createLinkifiedElementString = function(string)
39 {
40     var span = document.createElement("span");
41     span.className = "formatted-string";
42     span.append("\"", WebInspector.linkifyStringAsFragment(string.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")), "\"");
43     return span;
44 };
45
46 WebInspector.FormattedValue.createElementForNode = function(object)
47 {
48     var span = document.createElement("span");
49     span.className = "formatted-node";
50
51     object.pushNodeToFrontend(function(nodeId) {
52         if (!nodeId) {
53             span.textContent = object.description;
54             return;
55         }
56
57         var treeOutline = new WebInspector.DOMTreeOutline;
58         treeOutline.setVisible(true);
59         treeOutline.rootDOMNode = WebInspector.domTreeManager.nodeForId(nodeId);
60         if (!treeOutline.children[0].hasChildren)
61             treeOutline.element.classList.add("single-node");
62         span.appendChild(treeOutline.element);
63     });
64
65     return span;
66 };
67
68 WebInspector.FormattedValue.createElementForError = function(object)
69 {
70     var span = document.createElement("span");
71     span.classList.add("formatted-error");
72     span.textContent = object.description;
73
74     if (!object.preview)
75         return span;
76
77     function previewToObject(preview)
78     {
79         var result = {};
80         for (var property of preview.propertyPreviews)
81             result[property.name] = property.value;
82
83         return result;
84     }
85
86     var preview = previewToObject(object.preview);
87     if (!preview.sourceURL)
88         return span;
89
90     var sourceLinkWithPrefix = WebInspector.ErrorObjectView.makeSourceLinkWithPrefix(preview.sourceURL, preview.line, preview.column);
91     span.append(sourceLinkWithPrefix);
92     return span;
93 };
94
95 WebInspector.FormattedValue.createElementForNodePreview = function(preview)
96 {
97     var value = preview.value || preview.description;
98     var span = document.createElement("span");
99     span.className = "formatted-node-preview syntax-highlighted";
100
101     // Comment node preview.
102     if (value.startsWith("<!--")) {
103         var comment = span.appendChild(document.createElement("span"));
104         comment.className = "html-comment";
105         comment.textContent = value;
106         return span;
107     }
108
109     // Doctype node preview.
110     if (value.startsWith("<!DOCTYPE")) {
111         var doctype = span.appendChild(document.createElement("span"));
112         doctype.className = "html-doctype";
113         doctype.textContent = value;
114         return span;
115     }
116
117     // Element node previews have a very strict format, with at most a single attribute.
118     // We can style it up like a DOMNode without interactivity.
119     var matches = value.match(/^<(\S+?)(?: (\S+?)="(.*?)")?>$/);
120
121     // Remaining node types are often #text, #document, etc, with attribute nodes potentially being any string.
122     if (!matches) {
123         console.assert(!value.startsWith("<"), "Unexpected node preview format: " + value);
124         span.textContent = value;
125         return span;
126     }
127
128     var tag = document.createElement("span");
129     tag.className = "html-tag";
130     tag.append("<");
131
132     var tagName = tag.appendChild(document.createElement("span"));
133     tagName.className = "html-tag-name";
134     tagName.textContent = matches[1];
135
136     if (matches[2]) {
137         tag.append(" ");
138         var attribute = tag.appendChild(document.createElement("span"));
139         attribute.className = "html-attribute";
140         var attributeName = attribute.appendChild(document.createElement("span"));
141         attributeName.className = "html-attribute-name";
142         attributeName.textContent = matches[2];
143         attribute.append("=\"");
144         var attributeValue = attribute.appendChild(document.createElement("span"));
145         attributeValue.className = "html-attribute-value";
146         attributeValue.textContent = matches[3];
147         attribute.append("\"");
148     }
149
150     tag.append(">");
151     span.appendChild(tag);
152
153     return span;
154 };
155
156 WebInspector.FormattedValue.createElementForFunctionWithName = function(description)
157 {
158     var span = document.createElement("span");
159     span.classList.add("formatted-function");
160     span.textContent = description.substring(0, description.indexOf("("));
161     return span;
162 };
163
164 WebInspector.FormattedValue.createElementForTypesAndValue = function(type, subtype, displayString, size, isPreview, hadException)
165 {
166     var span = document.createElement("span");
167     span.classList.add(WebInspector.FormattedValue.classNameForTypes(type, subtype));
168
169     // Exception.
170     if (hadException) {
171         span.textContent = "[Exception: " + displayString + "]";
172         return span;
173     }
174
175     // String: quoted and replace newlines as nice unicode symbols.
176     if (type === "string") {
177         displayString = displayString.truncate(WebInspector.FormattedValue.MAX_PREVIEW_STRING_LENGTH);
178         span.textContent = doubleQuotedString(displayString.replace(/\n/g, "\u21B5"));
179         return span;
180     }
181
182     // Function: if class, show the description, otherwise elide in previews.
183     if (type === "function") {
184         if (subtype === "class")
185             span.textContent = displayString;
186         else
187             span.textContent = isPreview ? "function" : displayString;
188         return span;
189     }
190
191     // Everything else, the description/value string.
192     span.textContent = displayString;
193
194     // If there is a size, include it.
195     if (size !== undefined && (subtype === "array" || subtype === "set" || subtype === "map" || subtype === "weakmap" || subtype === "weakset")) {
196         var sizeElement = span.appendChild(document.createElement("span"));
197         sizeElement.className = "size";
198         sizeElement.textContent = " (" + size + ")";
199     }
200
201     return span;
202 };
203
204 WebInspector.FormattedValue.createElementForRemoteObject = function(object, hadException)
205 {
206     return WebInspector.FormattedValue.createElementForTypesAndValue(object.type, object.subtype, object.description, object.size, false, hadException);
207 };
208
209 WebInspector.FormattedValue.createElementForObjectPreview = function(objectPreview)
210 {
211     return WebInspector.FormattedValue.createElementForTypesAndValue(objectPreview.type, objectPreview.subtype, objectPreview.description, objectPreview.size, true, false);
212 };
213
214 WebInspector.FormattedValue.createElementForPropertyPreview = function(propertyPreview)
215 {
216     return WebInspector.FormattedValue.createElementForTypesAndValue(propertyPreview.type, propertyPreview.subtype, propertyPreview.value, undefined, true, false);
217 };
218
219 WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForObjectPreview = function(objectPreview, previewViewMode)
220 {
221     if (objectPreview.subtype === "node")
222         return WebInspector.FormattedValue.createElementForNodePreview(objectPreview);
223
224     if (objectPreview.type === "function")
225         return WebInspector.FormattedValue.createElementForFunctionWithName(objectPreview.description);
226
227     return new WebInspector.ObjectPreviewView(objectPreview, previewViewMode).element;
228 };
229
230 WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForRemoteObject = function(object, previewViewMode)
231 {
232     if (object.subtype === "node")
233         return WebInspector.FormattedValue.createElementForNode(object);
234
235     if (object.subtype === "error")
236         return WebInspector.FormattedValue.createElementForError(object);
237
238     if (object.preview)
239         return new WebInspector.ObjectPreviewView(object.preview, previewViewMode);
240
241     return WebInspector.FormattedValue.createElementForRemoteObject(object);
242 };
243
244 WebInspector.FormattedValue.createObjectTreeOrFormattedValueForRemoteObject = function(object, propertyPath, forceExpanding)
245 {
246     if (object.subtype === "node")
247         return WebInspector.FormattedValue.createElementForNode(object);
248
249     if (object.subtype === "null")
250         return WebInspector.FormattedValue.createElementForRemoteObject(object);
251
252     if (object.type === "object" || object.subtype === "class") {
253         var objectTree = new WebInspector.ObjectTreeView(object, null, propertyPath, forceExpanding);
254         return objectTree.element;
255     }
256
257     return WebInspector.FormattedValue.createElementForRemoteObject(object);
258 };
259
260 WebInspector.FormattedValue.MAX_PREVIEW_STRING_LENGTH = 140;