Changeset 248485 in webkit
- Timestamp:
- Aug 9, 2019 3:12:12 PM (5 years ago)
- Location:
- trunk/Source/WebInspectorUI
- Files:
-
- 7 edited
- 2 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebInspectorUI/ChangeLog
r248480 r248485 1 2019-08-09 Devin Rousso <drousso@apple.com> 2 3 REGRESSION (Safari 6): Web Inspector: JSON may not be pretty printed if served as text/html 4 https://bugs.webkit.org/show_bug.cgi?id=122898 5 <rdar://problem/15241419> 6 7 Reviewed by Joseph Pecoraro. 8 9 Check the request/response data to see if it's JSON parsable. If so, allow the user to elect 10 to view the request/response as a JSON preview instead of raw (or pretty printed) text. 11 12 Prefer the JSON view wherever possible. 13 14 * UserInterface/Views/ResourceClusterContentView.js: 15 (WI.ResourceClusterContentView): 16 (WI.ResourceClusterContentView.prototype.get requestContentView): 17 (WI.ResourceClusterContentView.prototype.get customRequestContentView): Added. 18 (WI.ResourceClusterContentView.prototype.get customResponseContentView): 19 (WI.ResourceClusterContentView.prototype.get selectionPathComponents): 20 (WI.ResourceClusterContentView.prototype.showRequest): 21 (WI.ResourceClusterContentView.prototype._canShowCustomRequestContentView): Added. 22 (WI.ResourceClusterContentView.prototype._canShowCustomResponseContentView): 23 (WI.ResourceClusterContentView.prototype._contentViewForResourceType): 24 (WI.ResourceClusterContentView.prototype._pathComponentForContentView): 25 (WI.ResourceClusterContentView.prototype._identifierForContentView): 26 (WI.ResourceClusterContentView.prototype._showContentViewForIdentifier): 27 (WI.ResourceClusterContentView.prototype._canUseJSONContentViewForContent): Added. 28 (WI.ResourceClusterContentView.prototype._tryEnableCustomRequestContentView): Added. 29 (WI.ResourceClusterContentView.prototype._tryEnableCustomResponseContentView): 30 (WI.ResourceClusterContentView.prototype.saveToCookie): Deleted. 31 (WI.ResourceClusterContentView.prototype._customContentViewConstructorForResource): Deleted. 32 Since the current view is already saved in a `WI.Setting`, there's no need to save that 33 state to a cookie, as it'll be restored elsewhere. 34 35 * UserInterface/Base/Main.js: 36 (WI.showResourceRequest): 37 38 * UserInterface/Main.html: 39 * UserInterface/Views/JSONContentView.js: Added. 40 (WI.JSONContentView): 41 (WI.JSONContentView.prototype.initialLayout): 42 (WI.JSONContentView.prototype.attached): 43 (WI.JSONContentView.prototype.closed): 44 * UserInterface/Views/JSONContentView.css: Added. 45 (.content-view.json): 46 * Source/WebInspectorUI/UserInterface/Views/JSONResourceContentView.js: Deleted. 47 * Source/WebInspectorUI/UserInterface/Views/JSONResourceContentView.css: Deleted. 48 Create a more generic content view that shows a preview for the given JSON parsable string. 49 50 * UserInterface/Base/Utilities.js: 51 (String.prototype.isJSON): Added. 52 * UserInterface/Views/WebSocketDataGridNode.js: 53 (WI.WebSocketDataGridNode.prototype.appendContextMenuItems): 54 Utility function for checking if a string is JSON parsable. 55 56 * Localizations/en.lproj/localizedStrings.js: 57 1 58 2019-08-09 Devin Rousso <drousso@apple.com> 2 59 -
trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
r248287 r248485 306 306 localizedStrings["Current State"] = "Current State"; 307 307 localizedStrings["Custom"] = "Custom"; 308 localizedStrings["Custom Request"] = "Custom Request"; 309 localizedStrings["Custom Response"] = "Custom Response"; 308 310 localizedStrings["DNS"] = "DNS"; 309 311 localizedStrings["DOM"] = "DOM"; … … 616 618 localizedStrings["JP2"] = "JP2"; 617 619 localizedStrings["JPEG"] = "JPEG"; 618 localizedStrings["JSON"] = "JSON";619 620 localizedStrings["JavaScript"] = "JavaScript"; 620 621 localizedStrings["JavaScript & Events"] = "JavaScript & Events"; … … 870 871 localizedStrings["Request"] = "Request"; 871 872 localizedStrings["Request & Response"] = "Request & Response"; 873 localizedStrings["Request (JSON)"] = "Request (JSON)"; 872 874 localizedStrings["Request Cookies"] = "Request Cookies"; 873 875 localizedStrings["Request Data"] = "Request Data"; … … 886 888 localizedStrings["Resources"] = "Resources"; 887 889 localizedStrings["Response"] = "Response"; 890 localizedStrings["Response (JSON)"] = "Response (JSON)"; 888 891 localizedStrings["Response Cookies"] = "Response Cookies"; 889 892 localizedStrings["Response Headers"] = "Response Headers"; … … 1176 1179 localizedStrings["URL Breakpoint\u2026"] = "URL Breakpoint\u2026"; 1177 1180 localizedStrings["Unable to determine path to property from root"] = "Unable to determine path to property from root"; 1178 localizedStrings["Unable to parse as JSON: %s"] = "Unable to parse as JSON: %s";1179 1181 localizedStrings["Unable to show certificate for \u201C%s\u201D"] = "Unable to show certificate for \u201C%s\u201D"; 1180 1182 /* Break (pause) on uncaught (unhandled) exceptions */ -
trunk/Source/WebInspectorUI/UserInterface/Base/Main.js
r248391 r248485 1406 1406 WI.showResourceRequest = function(resource, options = {}) 1407 1407 { 1408 WI.showRepresentedObject(resource, {[WI.ResourceClusterContentView.ContentViewIdentifierCookieKey]: WI.ResourceClusterContentView. RequestIdentifier}, options);1408 WI.showRepresentedObject(resource, {[WI.ResourceClusterContentView.ContentViewIdentifierCookieKey]: WI.ResourceClusterContentView.CustomRequestIdentifier}, options); 1409 1409 }; 1410 1410 -
trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js
r247033 r248485 735 735 }); 736 736 737 Object.defineProperty(String.prototype, "isJSON", 738 { 739 value(predicate) 740 { 741 try { 742 let json = JSON.parse(this); 743 return !predicate || predicate(json); 744 } catch { } 745 return false; 746 } 747 }); 748 737 749 Object.defineProperty(String.prototype, "truncateStart", 738 750 { -
trunk/Source/WebInspectorUI/UserInterface/Main.html
r248327 r248485 124 124 <link rel="stylesheet" href="Views/InputPopover.css"> 125 125 <link rel="stylesheet" href="Views/IssueTreeElement.css"> 126 <link rel="stylesheet" href="Views/JSON ResourceContentView.css">126 <link rel="stylesheet" href="Views/JSONContentView.css"> 127 127 <link rel="stylesheet" href="Views/LayerDetailsSidebarPanel.css"> 128 128 <link rel="stylesheet" href="Views/LayerTreeDetailsSidebarPanel.css"> … … 707 707 <script src="Views/InputPopover.js"></script> 708 708 <script src="Views/IssueTreeElement.js"></script> 709 <script src="Views/JSON ResourceContentView.js"></script>709 <script src="Views/JSONContentView.js"></script> 710 710 <script src="Views/LayerDetailsSidebarPanel.js"></script> 711 711 <script src="Views/LayerTreeDataGridNode.js"></script> -
trunk/Source/WebInspectorUI/UserInterface/Views/JSONContentView.css
r248481 r248485 1 1 /* 2 * Copyright (C) 201 7Apple Inc. All rights reserved.2 * Copyright (C) 2019 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 24 24 */ 25 25 26 .content-view. resource.json {26 .content-view.json { 27 27 padding: 10px; 28 28 overflow: scroll; -
trunk/Source/WebInspectorUI/UserInterface/Views/JSONContentView.js
r248481 r248485 1 1 /* 2 * Copyright (C) 201 7Apple Inc. All rights reserved.2 * Copyright (C) 2019 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 24 24 */ 25 25 26 WI.JSON ResourceContentView = class JSONResourceContentView extends WI.ResourceContentView26 WI.JSONContentView = class JSONContentView extends WI.ContentView 27 27 { 28 constructor( resource)28 constructor(json, representedObject) 29 29 { 30 super(resource, "json");30 console.assert(typeof json === "string" && json.isJSON()); 31 31 32 super(representedObject); 33 34 this._json = json; 32 35 this._remoteObject = null; 33 }36 this._spinnerTimeout = undefined; 34 37 35 // Static 36 37 static customContentViewDisplayName() 38 { 39 return WI.UIString("JSON"); 38 this.element.classList.add("json"); 40 39 } 41 40 42 41 // Protected 43 42 44 contentAvailable(content, base64Encoded)43 initialLayout() 45 44 { 46 try { 47 JSON.parse(content); 48 } catch (e) { 49 this.showMessage(WI.UIString("Unable to parse as JSON: %s").format(e.message)); 50 return; 51 } 45 super.initialLayout(); 52 46 53 47 const options = { 54 expression: "(" + content + ")", 55 includeCommandLineAPI: false, 48 expression: "(" + this._json + ")", 56 49 doNotPauseOnExceptionsAndMuteConsole: true, 57 contextId: undefined,58 returnByValue: false,59 50 generatePreview: true, 60 51 }; 61 this.resource.target.RuntimeAgent.evaluate.invoke(options, (error, result, wasThrown) => { 62 if (error || wasThrown) { 63 this.showMessage(WI.UIString("Unable to parse as JSON: %s").format(result.description)); 64 return; 65 } 52 RuntimeAgent.evaluate.invoke(options, (error, result, wasThrown) => { 53 console.assert(!error); 54 console.assert(!wasThrown); 66 55 67 this.removeLoadingIndicator(); 68 69 this._remoteObject = WI.RemoteObject.fromPayload(result, this.resource.target); 56 this._remoteObject = WI.RemoteObject.fromPayload(result); 70 57 71 58 let objectTree = new WI.ObjectTreeView(this._remoteObject); … … 73 60 objectTree.expand(); 74 61 62 if (this._spinnerTimeout) { 63 clearTimeout(this._spinnerTimeout); 64 this._spinnerTimeout = undefined; 65 } 66 67 this.element.removeChildren(); 75 68 this.element.appendChild(objectTree.element); 76 }, this.resource.target); 69 }); 70 } 71 72 attached() 73 { 74 super.attached(); 75 76 if (this._spinnerTimeout || this._remoteObject) 77 return; 78 79 this._spinnerTimeout = setTimeout(() => { 80 console.assert(this._spinnerTimeout); 81 82 let spinner = new WI.IndeterminateProgressSpinner; 83 this.element.appendChild(spinner.element); 84 85 this._spinnerTimeout = undefined; 86 }, 100); 77 87 } 78 88 79 89 closed() 80 90 { 91 super.closed(); 92 81 93 if (this._remoteObject) { 82 94 this._remoteObject.release(); -
trunk/Source/WebInspectorUI/UserInterface/Views/ResourceClusterContentView.js
r248274 r248485 36 36 function createPathComponent(displayName, className, identifier) 37 37 { 38 let pathComponent = new WI.HierarchicalPathComponent(displayName, className, identifier, false, true); 38 const textOnly = false; 39 const showSelectorArrows = true; 40 let pathComponent = new WI.HierarchicalPathComponent(displayName, className, identifier, textOnly, showSelectorArrows); 39 41 pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this); 40 42 pathComponent.comparisonData = resource; … … 44 46 this._requestContentView = null; 45 47 this._responseContentView = null; 48 this._customRequestContentView = null; 49 this._customRequestContentViewInitializer = null; 46 50 this._customResponseContentView = null; 47 this._customResponseContentView Constructor = null;51 this._customResponseContentViewInitializer = null; 48 52 49 53 this._requestPathComponent = createPathComponent.call(this, WI.UIString("Request"), WI.ResourceClusterContentView.RequestIconStyleClassName, WI.ResourceClusterContentView.RequestIdentifier); 54 this._customRequestPathComponent = createPathComponent.call(this, WI.UIString("Custom Request"), WI.ResourceClusterContentView.RequestIconStyleClassName, WI.ResourceClusterContentView.CustomRequestIdentifier); 50 55 this._responsePathComponent = createPathComponent.call(this, WI.UIString("Response"), WI.ResourceClusterContentView.ResponseIconStyleClassName, WI.ResourceClusterContentView.ResponseIdentifier); 51 this._customResponsePathComponent = createPathComponent.call(this, WI.UIString("Custom "), WI.ResourceClusterContentView.ResponseIconStyleClassName, WI.ResourceClusterContentView.CustomResponseIdentifier);56 this._customResponsePathComponent = createPathComponent.call(this, WI.UIString("Custom Response"), WI.ResourceClusterContentView.ResponseIconStyleClassName, WI.ResourceClusterContentView.CustomResponseIdentifier); 52 57 53 58 if (this._canShowRequestContentView()) { 54 59 this._requestPathComponent.nextSibling = this._responsePathComponent; 55 60 this._responsePathComponent.previousSibling = this._requestPathComponent; 61 62 this._tryEnableCustomRequestContentView(); 56 63 } 57 64 … … 60 67 // always want to prefer the JSON view to the normal Response text view. 61 68 62 this._currentContentViewSetting = new WI.Setting("resource-current-view-" + this._resource.url.hash, WI.ResourceClusterContentView. ResponseIdentifier);69 this._currentContentViewSetting = new WI.Setting("resource-current-view-" + this._resource.url.hash, WI.ResourceClusterContentView.CustomResponseIdentifier); 63 70 64 71 this._tryEnableCustomResponseContentView(); … … 67 74 // Public 68 75 69 get resource() 70 { 71 return this._resource; 76 get resource() { return this._resource; } 77 78 get requestContentView() 79 { 80 if (!this._canShowRequestContentView()) 81 return null; 82 83 if (this._requestContentView) 84 return this._requestContentView; 85 86 this._requestContentView = new WI.TextContentView(this._resource.requestData || "", this._resource.requestDataContentType); 87 88 return this._requestContentView; 72 89 } 73 90 … … 95 112 } 96 113 97 get requestContentView() 98 { 99 if (!this._canShowRequestContentView()) 100 return null; 101 102 if (this._requestContentView) 103 return this._requestContentView; 104 105 this._requestContentView = new WI.TextContentView(this._resource.requestData || "", this._resource.requestDataContentType); 106 107 return this._requestContentView; 114 get customRequestContentView() 115 { 116 if (!this._customRequestContentView && this._customRequestContentViewInitializer) { 117 this._customRequestContentView = this._customRequestContentViewInitializer(); 118 this._customRequestContentViewInitializer = null; 119 } 120 return this._customRequestContentView; 108 121 } 109 122 110 123 get customResponseContentView() 111 124 { 112 if (!this._canShowCustomResponseContentView()) 113 return null; 114 115 if (!this._customResponseContentView) 116 this._customResponseContentView = new this._customResponseContentViewConstructor(this._resource); 117 125 if (!this._customResponseContentView && this._customResponseContentViewInitializer) { 126 this._customResponseContentView = this._customResponseContentViewInitializer(); 127 this._customResponseContentViewInitializer = null; 128 } 118 129 return this._customResponseContentView; 119 130 } … … 125 136 return []; 126 137 127 if (!this._canShowRequestContentView() && !this._canShowCustomRe sponseContentView())138 if (!this._canShowRequestContentView() && !this._canShowCustomRequestContentView() && !this._canShowCustomResponseContentView()) 128 139 return currentContentView.selectionPathComponents; 129 140 … … 150 161 } 151 162 152 saveToCookie(cookie)153 {154 cookie[WI.ResourceClusterContentView.ContentViewIdentifierCookieKey] = this._currentContentViewSetting.value;155 }156 157 163 restoreFromCookie(cookie) 158 164 { … … 166 172 this._shownInitialContent = true; 167 173 168 return this._showContentViewForIdentifier(WI.ResourceClusterContentView. RequestIdentifier);174 return this._showContentViewForIdentifier(WI.ResourceClusterContentView.CustomRequestIdentifier); 169 175 } 170 176 … … 199 205 } 200 206 207 _canShowCustomRequestContentView() 208 { 209 return !!(this._customRequestContentView || this._customRequestContentViewInitializer); 210 } 211 201 212 _canShowCustomResponseContentView() 202 213 { 203 return !! this._customResponseContentViewConstructor;214 return !!(this._customResponseContentView || this._customResponseContentViewInitializer); 204 215 } 205 216 … … 213 224 214 225 case WI.Resource.Type.Image: 215 if ( this._resource.mimeTypeComponents.type === "image/svg+xml")226 if (WI.fileExtensionForMIMEType(this._resource.mimeType) === "svg") 216 227 return new WI.SVGImageResourceClusterContentView(this._resource); 217 228 return new WI.ImageResourceContentView(this._resource); … … 237 248 if (contentView === this._responseContentView) 238 249 return this._responsePathComponent; 250 if (contentView === this._customRequestContentView) 251 return this._customRequestPathComponent; 239 252 if (contentView === this._customResponseContentView) 240 253 return this._customResponsePathComponent; … … 252 265 if (contentView === this._responseContentView) 253 266 return WI.ResourceClusterContentView.ResponseIdentifier; 267 if (contentView === this._customRequestContentView) 268 return WI.ResourceClusterContentView.CustomRequestIdentifier; 254 269 if (contentView === this._customResponseContentView) 255 270 return WI.ResourceClusterContentView.CustomResponseIdentifier; … … 262 277 let contentViewToShow = null; 263 278 279 // This is expected to fall through all the way to the `default`. 264 280 switch (identifier) { 281 case WI.ResourceClusterContentView.CustomRequestIdentifier: 282 contentViewToShow = this.customRequestContentView; 283 if (contentViewToShow) 284 break; 285 // fallthrough 265 286 case WI.ResourceClusterContentView.RequestIdentifier: 266 287 contentViewToShow = this.requestContentView; 267 break; 288 if (contentViewToShow) 289 break; 290 // fallthrough 291 case WI.ResourceClusterContentView.CustomResponseIdentifier: 292 contentViewToShow = this.customResponseContentView; 293 if (contentViewToShow) 294 break; 295 // fallthrough 268 296 case WI.ResourceClusterContentView.ResponseIdentifier: 297 default: 269 298 contentViewToShow = this.responseContentView; 270 299 break; 271 case WI.ResourceClusterContentView.CustomResponseIdentifier: 272 contentViewToShow = this.customResponseContentView; 273 break; 274 } 275 276 if (!contentViewToShow) 277 contentViewToShow = this.responseContentView; 300 } 278 301 279 302 console.assert(contentViewToShow); … … 318 341 } 319 342 343 _canUseJSONContentViewForContent(content) 344 { 345 return content.isJSON((json) => json && (typeof json === "object" || Array.isArray(json))); 346 } 347 348 _tryEnableCustomRequestContentView() 349 { 350 if (!this._canUseJSONContentViewForContent(this._resource.requestData)) 351 return; 352 353 this._customRequestContentViewInitializer = () => new WI.JSONContentView(this._resource.requestData, this._resource); 354 355 this._customRequestPathComponent.displayName = WI.UIString("Request (JSON)"); 356 this._customRequestPathComponent.previousSibling = this._requestPathComponent; 357 this._customRequestPathComponent.nextSibling = this._responsePathComponent; 358 this._requestPathComponent.nextSibling = this._customRequestPathComponent; 359 this._responsePathComponent.previousSibling = this._customRequestPathComponent; 360 } 361 320 362 _tryEnableCustomResponseContentView() 321 363 { … … 323 365 return; 324 366 325 this._customResponseContentViewConstructor = this._customContentViewConstructorForResource(this._resource); 326 if (!this._customResponseContentViewConstructor) 327 return; 328 329 console.assert(this._customResponseContentViewConstructor.customContentViewDisplayName, "Custom Response ContentViews should have a static customContentViewDisplayName method.", this._customResponseContentViewConstructor); 330 331 this._responsePathComponent.nextSibling = this._customResponsePathComponent; 332 this._customResponsePathComponent.previousSibling = this._responsePathComponent; 333 this._customResponsePathComponent.displayName = this._customResponseContentViewConstructor.customContentViewDisplayName(); 334 335 this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange); 336 } 337 338 _customContentViewConstructorForResource(resource) 339 { 340 let mimeType = this._resource.mimeType; 341 let fileExtension = WI.fileExtensionForMIMEType(mimeType); 342 if (fileExtension === "json") 343 return WI.JSONResourceContentView; 344 345 return null; 367 this._resource.requestContent() 368 .then(({error, content}) => { 369 if (error || !content || !this._canUseJSONContentViewForContent(content)) 370 return; 371 372 this._customResponseContentViewInitializer = () => new WI.JSONContentView(content, this._resource); 373 374 this._customResponsePathComponent.displayName = WI.UIString("Response (JSON)"); 375 this._customResponsePathComponent.previousSibling = this._responsePathComponent; 376 this._responsePathComponent.nextSibling = this._customResponsePathComponent; 377 378 this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange); 379 }); 346 380 } 347 381 }; … … 353 387 WI.ResourceClusterContentView.RequestIdentifier = "request"; 354 388 WI.ResourceClusterContentView.ResponseIdentifier = "response"; 389 WI.ResourceClusterContentView.CustomRequestIdentifier = "custom-request"; 355 390 WI.ResourceClusterContentView.CustomResponseIdentifier = "custom-response"; -
trunk/Source/WebInspectorUI/UserInterface/Views/WebSocketDataGridNode.js
r224433 r248485 67 67 }); 68 68 69 try { 70 // The result of this is unnecessary, as we just need the string to evaluate. 71 // We still need to execute this, however, in order to try-catch if it fails. 72 JSON.parse(this._data.data); 73 69 if (this._data.data.isJSON()) { 74 70 contextMenu.appendItem(WI.UIString("Log Frame Value"), () => { 75 71 const options = { … … 83 79 WI.runtimeManager.evaluateInInspectedWindow(expression, options, logResult); 84 80 }); 85 } catch { }81 } 86 82 87 83 contextMenu.appendSeparator();
Note: See TracChangeset
for help on using the changeset viewer.