Web Inspector: provide a way to view XML/HTML/SVG resource responses as a DOM tree
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Sep 2019 02:57:26 +0000 (02:57 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Sep 2019 02:57:26 +0000 (02:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201046
<rdar://problem/54446087>

Reviewed by Joseph Pecoraro.

* UserInterface/Views/ResourceClusterContentView.js:
(WI.ResourceClusterContentView):
(WI.ResourceClusterContentView.prototype.showRequest):
(WI.ResourceClusterContentView.prototype.showResponse):
(WI.ResourceClusterContentView.prototype.get customRequestDOMContentView): Added.
(WI.ResourceClusterContentView.prototype.get customRequestJSONContentView): Added.
(WI.ResourceClusterContentView.prototype.get customResponseDOMContentView): Added.
(WI.ResourceClusterContentView.prototype.get customResponseJSONContentView): Added.
(WI.ResourceClusterContentView.prototype.get customResponseTextContentView): Added.
(WI.ResourceClusterContentView.prototype._createPathComponent): Added.
(WI.ResourceClusterContentView.prototype._canShowCustomRequestContentView):
(WI.ResourceClusterContentView.prototype._canShowCustomResponseContentView):
(WI.ResourceClusterContentView.prototype._contentViewForResourceType):
(WI.ResourceClusterContentView.prototype._pathComponentForContentView):
(WI.ResourceClusterContentView.prototype._identifierForContentView):
(WI.ResourceClusterContentView.prototype._showContentViewForIdentifier):
(WI.ResourceClusterContentView.prototype._resourceLoadingDidFinish):
(WI.ResourceClusterContentView.prototype._canUseDOMContentViewForContent): Added.
(WI.ResourceClusterContentView.prototype._normalizeMIMETypeForDOM): Added.
(WI.ResourceClusterContentView.prototype._tryEnableCustomRequestContentViews): Added.
(WI.ResourceClusterContentView.prototype._tryEnableCustomResponseContentViews): Added.
(WI.ResourceClusterContentView.createPathComponent): Deleted.
(WI.ResourceClusterContentView.prototype._tryEnableCustomRequestContentView): Deleted.
(WI.ResourceClusterContentView.prototype._tryEnableCustomResponseContentView): Deleted.
* UserInterface/Base/Main.js:
(WI.showResourceRequest):
* UserInterface/Views/PathComponentIcons.css:
(.object-icon .icon): Added.
* UserInterface/Main.html:
* UserInterface/Views/SVGImageResourceClusterContentView.js: Removed.
Reworked to allow more than one custom request/response content view at the same time. As
such, merge the `WI.SVGImageResourceClusterContentView` into this class.

* UserInterface/Views/LocalRemoteObjectContentView.js: Added.
(WI.LocalRemoteObjectContentView):
(WI.LocalRemoteObjectContentView.prototype.get expression):
(WI.LocalRemoteObjectContentView.prototype.renderRemoteObject):
(WI.LocalRemoteObjectContentView.prototype.initialLayout):
(WI.LocalRemoteObjectContentView.prototype.attached):
(WI.LocalRemoteObjectContentView.prototype.closed):
* UserInterface/Views/LocalRemoteObjectContentView.css: Added.
(.content-view.local-remote-object):
* UserInterface/Views/LocalDOMContentView.js: Added.
(WI.LocalDOMContentView):
(WI.LocalDOMContentView.prototype.get expression):
(WI.LocalDOMContentView.prototype.renderRemoteObject):
* UserInterface/Views/LocalJSONContentView.js: Added.
(WI.LocalJSONContentView):
(WI.LocalJSONContentView.prototype.get expression):
(WI.LocalJSONContentView.prototype.renderRemoteObject):
* UserInterface/Views/JSONContentView.js: Removed.
* UserInterface/Views/JSONContentView.css: Removed.
Rework `WI.JSONContentView` into a more generic set of classes that render an object we send
to the inspected page for instrumentation.

* UserInterface/Views/DOMTreeOutline.js:
(WI.DOMTreeOutline.prototype.populateContextMenu):
(WI.DOMTreeOutline.prototype._onmousemove):
(WI.DOMTreeOutline.prototype._onmouseout):
(WI.DOMTreeOutline.prototype._ondragstart):
(WI.DOMTreeOutline.prototype._ondragover):
(WI.DOMTreeOutline.prototype._ondragleave):
(WI.DOMTreeOutline.prototype._ondragend):
(WI.DOMTreeOutline.prototype._hideElements):
* UserInterface/Views/DOMTreeElement.js:
(WI.DOMTreeElement.prototype.populateDOMNodeContextMenu):
* UserInterface/Views/ContextMenuUtilities.js:
(WI.appendContextMenuItemsForDOMNode):
Ensure that interactions that would modify the DOM tree only happen when editable. If the
`WI.DOMTreeOutline` represents a "local" `WI.DOMNode` (one that's been sent to the inspected
page for instrumentation, and shouldn't be part of the main #document), don't allow any
editing actions to be performed.

* UserInterface/Views/TextContentView.js:
(WI.TextContentView):
Allow a `representedObject` object to be provided and used instead of the given `string`.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Images/Object.svg: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249451 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Base/Main.js
Source/WebInspectorUI/UserInterface/Images/Object.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js
Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js
Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js
Source/WebInspectorUI/UserInterface/Views/LocalDOMContentView.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/LocalJSONContentView.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/LocalRemoteObjectContentView.css [moved from Source/WebInspectorUI/UserInterface/Views/JSONContentView.css with 97% similarity]
Source/WebInspectorUI/UserInterface/Views/LocalRemoteObjectContentView.js [moved from Source/WebInspectorUI/UserInterface/Views/JSONContentView.js with 80% similarity]
Source/WebInspectorUI/UserInterface/Views/PathComponentIcons.css
Source/WebInspectorUI/UserInterface/Views/ResourceClusterContentView.js
Source/WebInspectorUI/UserInterface/Views/SVGImageResourceClusterContentView.js [deleted file]
Source/WebInspectorUI/UserInterface/Views/TextContentView.js

index 661c34e..72a2228 100644 (file)
@@ -1,5 +1,93 @@
 2019-09-03  Devin Rousso  <drousso@apple.com>
 
+        Web Inspector: provide a way to view XML/HTML/SVG resource responses as a DOM tree
+        https://bugs.webkit.org/show_bug.cgi?id=201046
+        <rdar://problem/54446087>
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Views/ResourceClusterContentView.js:
+        (WI.ResourceClusterContentView):
+        (WI.ResourceClusterContentView.prototype.showRequest):
+        (WI.ResourceClusterContentView.prototype.showResponse):
+        (WI.ResourceClusterContentView.prototype.get customRequestDOMContentView): Added.
+        (WI.ResourceClusterContentView.prototype.get customRequestJSONContentView): Added.
+        (WI.ResourceClusterContentView.prototype.get customResponseDOMContentView): Added.
+        (WI.ResourceClusterContentView.prototype.get customResponseJSONContentView): Added.
+        (WI.ResourceClusterContentView.prototype.get customResponseTextContentView): Added.
+        (WI.ResourceClusterContentView.prototype._createPathComponent): Added.
+        (WI.ResourceClusterContentView.prototype._canShowCustomRequestContentView):
+        (WI.ResourceClusterContentView.prototype._canShowCustomResponseContentView):
+        (WI.ResourceClusterContentView.prototype._contentViewForResourceType):
+        (WI.ResourceClusterContentView.prototype._pathComponentForContentView):
+        (WI.ResourceClusterContentView.prototype._identifierForContentView):
+        (WI.ResourceClusterContentView.prototype._showContentViewForIdentifier):
+        (WI.ResourceClusterContentView.prototype._resourceLoadingDidFinish):
+        (WI.ResourceClusterContentView.prototype._canUseDOMContentViewForContent): Added.
+        (WI.ResourceClusterContentView.prototype._normalizeMIMETypeForDOM): Added.
+        (WI.ResourceClusterContentView.prototype._tryEnableCustomRequestContentViews): Added.
+        (WI.ResourceClusterContentView.prototype._tryEnableCustomResponseContentViews): Added.
+        (WI.ResourceClusterContentView.createPathComponent): Deleted.
+        (WI.ResourceClusterContentView.prototype._tryEnableCustomRequestContentView): Deleted.
+        (WI.ResourceClusterContentView.prototype._tryEnableCustomResponseContentView): Deleted.
+        * UserInterface/Base/Main.js:
+        (WI.showResourceRequest):
+        * UserInterface/Views/PathComponentIcons.css:
+        (.object-icon .icon): Added.
+        * UserInterface/Main.html:
+        * UserInterface/Views/SVGImageResourceClusterContentView.js: Removed.
+        Reworked to allow more than one custom request/response content view at the same time. As
+        such, merge the `WI.SVGImageResourceClusterContentView` into this class.
+
+        * UserInterface/Views/LocalRemoteObjectContentView.js: Added.
+        (WI.LocalRemoteObjectContentView):
+        (WI.LocalRemoteObjectContentView.prototype.get expression):
+        (WI.LocalRemoteObjectContentView.prototype.renderRemoteObject):
+        (WI.LocalRemoteObjectContentView.prototype.initialLayout):
+        (WI.LocalRemoteObjectContentView.prototype.attached):
+        (WI.LocalRemoteObjectContentView.prototype.closed):
+        * UserInterface/Views/LocalRemoteObjectContentView.css: Added.
+        (.content-view.local-remote-object):
+        * UserInterface/Views/LocalDOMContentView.js: Added.
+        (WI.LocalDOMContentView):
+        (WI.LocalDOMContentView.prototype.get expression):
+        (WI.LocalDOMContentView.prototype.renderRemoteObject):
+        * UserInterface/Views/LocalJSONContentView.js: Added.
+        (WI.LocalJSONContentView):
+        (WI.LocalJSONContentView.prototype.get expression):
+        (WI.LocalJSONContentView.prototype.renderRemoteObject):
+        * UserInterface/Views/JSONContentView.js: Removed.
+        * UserInterface/Views/JSONContentView.css: Removed.
+        Rework `WI.JSONContentView` into a more generic set of classes that render an object we send
+        to the inspected page for instrumentation.
+
+        * UserInterface/Views/DOMTreeOutline.js:
+        (WI.DOMTreeOutline.prototype.populateContextMenu):
+        (WI.DOMTreeOutline.prototype._onmousemove):
+        (WI.DOMTreeOutline.prototype._onmouseout):
+        (WI.DOMTreeOutline.prototype._ondragstart):
+        (WI.DOMTreeOutline.prototype._ondragover):
+        (WI.DOMTreeOutline.prototype._ondragleave):
+        (WI.DOMTreeOutline.prototype._ondragend):
+        (WI.DOMTreeOutline.prototype._hideElements):
+        * UserInterface/Views/DOMTreeElement.js:
+        (WI.DOMTreeElement.prototype.populateDOMNodeContextMenu):
+        * UserInterface/Views/ContextMenuUtilities.js:
+        (WI.appendContextMenuItemsForDOMNode):
+        Ensure that interactions that would modify the DOM tree only happen when editable. If the
+        `WI.DOMTreeOutline` represents a "local" `WI.DOMNode` (one that's been sent to the inspected
+        page for instrumentation, and shouldn't be part of the main #document), don't allow any
+        editing actions to be performed.
+
+        * UserInterface/Views/TextContentView.js:
+        (WI.TextContentView):
+        Allow a `representedObject` object to be provided and used instead of the given `string`.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Images/Object.svg: Added.
+
+2019-09-03  Devin Rousso  <drousso@apple.com>
+
         Web Inspector: implement blackboxing of script resources
         https://bugs.webkit.org/show_bug.cgi?id=17240
         <rdar://problem/5732847>
index e43e2d6..aa527a8 100644 (file)
@@ -310,8 +310,6 @@ localizedStrings["Cross-Origin Restrictions"] = "Cross-Origin Restrictions";
 localizedStrings["Current"] = "Current";
 localizedStrings["Current State"] = "Current State";
 localizedStrings["Custom"] = "Custom";
-localizedStrings["Custom Request"] = "Custom Request";
-localizedStrings["Custom Response"] = "Custom Response";
 localizedStrings["DNS"] = "DNS";
 localizedStrings["DOM"] = "DOM";
 localizedStrings["DOM Content Loaded \u2014 %s"] = "DOM Content Loaded \u2014 %s";
@@ -876,7 +874,8 @@ localizedStrings["Repeating Linear Gradient"] = "Repeating Linear Gradient";
 localizedStrings["Repeating Radial Gradient"] = "Repeating Radial Gradient";
 localizedStrings["Request"] = "Request";
 localizedStrings["Request & Response"] = "Request & Response";
-localizedStrings["Request (JSON)"] = "Request (JSON)";
+localizedStrings["Request (DOM Tree)"] = "Request (DOM Tree)";
+localizedStrings["Request (Object Tree)"] = "Request (Object Tree)";
 localizedStrings["Request Cookies"] = "Request Cookies";
 localizedStrings["Request Data"] = "Request Data";
 localizedStrings["Request Headers"] = "Request Headers";
@@ -893,7 +892,9 @@ localizedStrings["Resource was loaded with the \u201Cdata\u201D scheme."] = "Res
 localizedStrings["Resource was served from the cache."] = "Resource was served from the cache.";
 localizedStrings["Resources"] = "Resources";
 localizedStrings["Response"] = "Response";
-localizedStrings["Response (JSON)"] = "Response (JSON)";
+localizedStrings["Response (DOM Tree)"] = "Response (DOM Tree)";
+localizedStrings["Response (Object Tree)"] = "Response (Object Tree)";
+localizedStrings["Response (Text)"] = "Response (Text)";
 localizedStrings["Response Cookies"] = "Response Cookies";
 localizedStrings["Response Headers"] = "Response Headers";
 localizedStrings["Response:"] = "Response:";
index 9853c0f..f907ea4 100644 (file)
@@ -1405,7 +1405,7 @@ WI.showOriginalOrFormattedSourceCodeTextRange = function(sourceCodeTextRange, op
 
 WI.showResourceRequest = function(resource, options = {})
 {
-    WI.showRepresentedObject(resource, {[WI.ResourceClusterContentView.ContentViewIdentifierCookieKey]: WI.ResourceClusterContentView.CustomRequestIdentifier}, options);
+    WI.showRepresentedObject(resource, {[WI.ResourceClusterContentView.ContentViewIdentifierCookieKey]: WI.ResourceClusterContentView.Identifier.Request}, options);
 };
 
 WI.debuggerToggleBreakpoints = function(event)
diff --git a/Source/WebInspectorUI/UserInterface/Images/Object.svg b/Source/WebInspectorUI/UserInterface/Images/Object.svg
new file mode 100644 (file)
index 0000000..867bafd
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2019 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(246, 222, 146)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 Z"/>
+    <path fill="rgb(204, 181, 108)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
+    <path fill="rgb(216, 193, 115)" d="M 7.984375 3 L 6.585938 3 C 6.101562 3 5.714844 3.050781 5.394531 3.160156 C 4.984375 3.296875 4.648438 3.527344 4.382812 3.84375 C 4.121094 4.160156 3.953125 4.546875 3.886719 4.984375 C 3.847656 5.265625 3.828125 5.636719 3.828125 6.152344 C 3.828125 6.21875 3.828125 6.277344 3.824219 6.332031 L 3 6.339844 L 3 9.652344 L 3.820312 9.683594 C 3.824219 9.796875 3.828125 9.941406 3.828125 10.136719 C 3.828125 10.8125 3.90625 11.308594 4.082031 11.691406 C 4.300781 12.1875 4.679688 12.558594 5.171875 12.777344 C 5.546875 12.941406 6.011719 13.023438 6.585938 13.023438 L 7.980469 13.023438 L 7.980469 9.652344 L 7.1875 9.652344 L 7.171875 9.316406 C 7.144531 8.789062 7.050781 8.363281 6.882812 8.015625 C 7.015625 7.730469 7.105469 7.433594 7.144531 7.121094 C 7.160156 7.015625 7.175781 6.820312 7.195312 6.363281 L 7.984375 6.363281 Z M 6.984375 4 L 6.984375 5.363281 L 6.785156 5.363281 C 6.367188 5.363281 6.277344 5.417969 6.265625 5.425781 C 6.265625 5.425781 6.21875 5.488281 6.214844 5.742188 C 6.199219 6.386719 6.175781 6.804688 6.152344 7 C 6.125 7.210938 6.0625 7.417969 5.964844 7.613281 C 5.894531 7.753906 5.789062 7.886719 5.644531 8.011719 C 5.78125 8.132812 5.890625 8.269531 5.96875 8.421875 C 6.085938 8.648438 6.152344 8.957031 6.171875 9.363281 L 6.226562 10.515625 C 6.234375 10.539062 6.257812 10.574219 6.296875 10.601562 C 6.308594 10.605469 6.394531 10.652344 6.78125 10.652344 L 6.980469 10.652344 L 6.980469 12.023438 L 6.585938 12.023438 C 6.152344 12.023438 5.820312 11.96875 5.574219 11.859375 C 5.308594 11.742188 5.113281 11.550781 4.992188 11.285156 C 4.882812 11.035156 4.828125 10.660156 4.828125 10.136719 C 4.828125 9.453125 4.796875 9.230469 4.78125 9.152344 C 4.753906 9.03125 4.699219 8.925781 4.617188 8.824219 C 4.554688 8.75 4.40625 8.703125 4.195312 8.695312 L 4 8.6875 L 4 7.328125 L 4.199219 7.328125 C 4.414062 7.324219 4.570312 7.265625 4.667969 7.144531 C 4.714844 7.085938 4.828125 6.875 4.828125 6.152344 C 4.828125 5.691406 4.84375 5.355469 4.878906 5.128906 C 4.914062 4.875 5.007812 4.65625 5.152344 4.484375 C 5.296875 4.3125 5.484375 4.183594 5.710938 4.105469 C 5.925781 4.035156 6.210938 4 6.585938 4 L 6.984375 4"/>
+    <path fill="rgb(216, 193, 115)" d="M 9.414062 3 L 8.015625 3 L 8.015625 6.363281 L 8.808594 6.363281 C 8.824219 6.804688 8.851562 7.113281 8.890625 7.328125 C 8.933594 7.570312 9.011719 7.804688 9.117188 8.019531 C 9 8.261719 8.921875 8.519531 8.878906 8.785156 C 8.851562 8.921875 8.828125 9.136719 8.808594 9.652344 L 8.019531 9.652344 L 8.019531 13.023438 L 9.421875 13.023438 C 9.90625 13.023438 10.296875 12.96875 10.617188 12.859375 C 11.003906 12.730469 11.355469 12.496094 11.617188 12.179688 C 11.886719 11.855469 12.054688 11.472656 12.117188 11.035156 C 12.15625 10.753906 12.171875 10.382812 12.171875 9.863281 C 12.171875 9.800781 12.175781 9.742188 12.175781 9.691406 L 13 9.683594 L 13 6.371094 L 12.179688 6.339844 C 12.175781 6.230469 12.171875 6.078125 12.171875 5.886719 C 12.171875 5.210938 12.09375 4.714844 11.921875 4.328125 C 11.699219 3.839844 11.324219 3.464844 10.828125 3.246094 C 10.453125 3.078125 9.988281 3 9.414062 3 M 9.414062 4 C 9.847656 4 10.179688 4.054688 10.425781 4.160156 C 10.691406 4.277344 10.886719 4.472656 11.007812 4.738281 C 11.117188 4.988281 11.171875 5.363281 11.171875 5.886719 C 11.171875 6.59375 11.203125 6.816406 11.222656 6.886719 C 11.253906 7.003906 11.308594 7.105469 11.390625 7.203125 C 11.425781 7.246094 11.523438 7.316406 11.808594 7.328125 L 12 7.335938 L 12 8.695312 L 11.800781 8.695312 C 11.585938 8.699219 11.433594 8.757812 11.332031 8.878906 C 11.273438 8.953125 11.171875 9.175781 11.171875 9.863281 C 11.171875 10.332031 11.15625 10.667969 11.125 10.894531 C 11.089844 11.144531 10.996094 11.363281 10.851562 11.539062 C 10.707031 11.714844 10.519531 11.839844 10.292969 11.914062 C 10.082031 11.984375 9.796875 12.023438 9.421875 12.023438 L 9.019531 12.023438 L 9.019531 10.652344 L 9.21875 10.652344 C 9.636719 10.652344 9.726562 10.59375 9.730469 10.59375 C 9.730469 10.59375 9.785156 10.53125 9.789062 10.277344 C 9.808594 9.582031 9.828125 9.148438 9.863281 8.945312 C 9.898438 8.730469 9.972656 8.527344 10.078125 8.339844 C 10.144531 8.21875 10.238281 8.109375 10.355469 8.007812 C 10.230469 7.902344 10.140625 7.796875 10.074219 7.6875 C 9.976562 7.523438 9.910156 7.34375 9.875 7.148438 C 9.839844 6.96875 9.816406 6.644531 9.800781 6.183594 C 9.78125 5.597656 9.761719 5.480469 9.757812 5.457031 C 9.695312 5.402344 9.609375 5.363281 9.214844 5.363281 L 9.015625 5.363281 L 9.015625 4 L 9.414062 4"/>
+    <path fill="white" d="M 6.585938 12.023438 C 6.152344 12.023438 5.820312 11.96875 5.574219 11.863281 C 5.308594 11.746094 5.113281 11.550781 4.992188 11.285156 C 4.882812 11.035156 4.828125 10.660156 4.828125 10.136719 C 4.828125 9.453125 4.796875 9.226562 4.78125 9.152344 C 4.753906 9.03125 4.699219 8.925781 4.617188 8.824219 C 4.558594 8.75 4.40625 8.703125 4.191406 8.695312 L 4 8.6875 L 4 7.328125 L 4.199219 7.328125 C 4.414062 7.324219 4.566406 7.265625 4.667969 7.144531 C 4.714844 7.085938 4.828125 6.875 4.828125 6.152344 C 4.828125 5.691406 4.84375 5.355469 4.878906 5.128906 C 4.914062 4.875 5.007812 4.660156 5.152344 4.484375 C 5.296875 4.308594 5.484375 4.183594 5.710938 4.109375 C 5.925781 4.035156 6.210938 4 6.585938 4 L 6.984375 4 L 6.984375 5.367188 L 6.785156 5.367188 C 6.363281 5.367188 6.273438 5.417969 6.265625 5.425781 C 6.265625 5.425781 6.21875 5.488281 6.214844 5.742188 C 6.199219 6.386719 6.175781 6.804688 6.152344 7 C 6.125 7.210938 6.0625 7.417969 5.964844 7.613281 C 5.894531 7.753906 5.789062 7.882812 5.644531 8.011719 C 5.78125 8.132812 5.890625 8.273438 5.96875 8.421875 C 6.085938 8.648438 6.152344 8.957031 6.171875 9.367188 L 6.226562 10.515625 C 6.234375 10.539062 6.257812 10.574219 6.300781 10.601562 C 6.308594 10.605469 6.394531 10.65625 6.78125 10.65625 L 6.980469 10.65625 L 6.980469 12.023438 Z"/>
+    <path fill="white" d="M 9.019531 12.023438 L 9.019531 10.65625 L 9.21875 10.65625 C 9.636719 10.65625 9.726562 10.59375 9.734375 10.589844 C 9.734375 10.589844 9.785156 10.53125 9.789062 10.277344 C 9.804688 9.582031 9.832031 9.148438 9.863281 8.945312 C 9.898438 8.730469 9.96875 8.527344 10.078125 8.339844 C 10.144531 8.21875 10.238281 8.109375 10.351562 8.007812 C 10.230469 7.902344 10.140625 7.796875 10.074219 7.6875 C 9.976562 7.523438 9.910156 7.34375 9.875 7.152344 C 9.839844 6.96875 9.816406 6.644531 9.800781 6.183594 C 9.78125 5.597656 9.761719 5.476562 9.757812 5.457031 C 9.695312 5.402344 9.609375 5.367188 9.214844 5.367188 L 9.015625 5.367188 L 9.015625 4 L 9.414062 4 C 9.847656 4 10.179688 4.050781 10.425781 4.160156 C 10.691406 4.277344 10.886719 4.472656 11.007812 4.738281 C 11.117188 4.988281 11.171875 5.363281 11.171875 5.886719 C 11.171875 6.589844 11.207031 6.816406 11.222656 6.886719 C 11.253906 7.003906 11.308594 7.105469 11.390625 7.203125 C 11.425781 7.246094 11.527344 7.316406 11.804688 7.328125 L 12 7.335938 L 12 8.695312 L 11.800781 8.695312 C 11.585938 8.699219 11.433594 8.757812 11.332031 8.878906 C 11.273438 8.953125 11.171875 9.175781 11.171875 9.867188 C 11.171875 10.332031 11.15625 10.667969 11.125 10.890625 C 11.089844 11.144531 11 11.363281 10.851562 11.539062 C 10.707031 11.710938 10.519531 11.839844 10.292969 11.914062 C 10.082031 11.988281 9.792969 12.023438 9.417969 12.023438 Z"/>
+</svg>
index d83754c..44f3c78 100644 (file)
     <link rel="stylesheet" href="Views/InlineSwatch.css">
     <link rel="stylesheet" href="Views/InputPopover.css">
     <link rel="stylesheet" href="Views/IssueTreeElement.css">
-    <link rel="stylesheet" href="Views/JSONContentView.css">
     <link rel="stylesheet" href="Views/LayerDetailsSidebarPanel.css">
     <link rel="stylesheet" href="Views/LayerTreeDetailsSidebarPanel.css">
     <link rel="stylesheet" href="Views/Layers3DContentView.css">
     <link rel="stylesheet" href="Views/LayoutTimelineOverviewGraph.css">
     <link rel="stylesheet" href="Views/LayoutTimelineView.css">
+    <link rel="stylesheet" href="Views/LocalRemoteObjectContentView.css">
     <link rel="stylesheet" href="Views/LogContentView.css">
     <link rel="stylesheet" href="Views/LogIcon.css">
     <link rel="stylesheet" href="Views/MediaTimelineOverviewGraph.css">
 
     <script src="Views/AuditTestContentView.js"></script>
     <script src="Views/CollectionContentView.js"></script>
+    <script src="Views/LocalRemoteObjectContentView.js"></script>
 
     <script src="Views/ActivateButtonNavigationItem.js"></script>
     <script src="Views/ActivateButtonToolbarItem.js"></script>
     <script src="Views/InlineSwatch.js"></script>
     <script src="Views/InputPopover.js"></script>
     <script src="Views/IssueTreeElement.js"></script>
-    <script src="Views/JSONContentView.js"></script>
     <script src="Views/LayerDetailsSidebarPanel.js"></script>
     <script src="Views/LayerTreeDataGridNode.js"></script>
     <script src="Views/LayerTreeDetailsSidebarPanel.js"></script>
     <script src="Views/LayoutTimelineDataGridNode.js"></script>
     <script src="Views/LayoutTimelineOverviewGraph.js"></script>
     <script src="Views/LayoutTimelineView.js"></script>
+    <script src="Views/LocalDOMContentView.js"></script>
+    <script src="Views/LocalJSONContentView.js"></script>
     <script src="Views/LogContentView.js"></script>
     <script src="Views/MediaTimelineDataGridNode.js"></script>
     <script src="Views/MediaTimelineOverviewGraph.js"></script>
     <script src="Views/StyleOriginView.js"></script>
 
     <script src="Views/SpringEditor.js"></script>
-    <script src="Views/SVGImageResourceClusterContentView.js"></script>
     <script src="Views/StackTraceView.js"></script>
     <script src="Views/StackedAreaChart.js"></script>
     <script src="Views/StackedColumnChart.js"></script>
index f8370f9..afbf301 100644 (file)
@@ -204,114 +204,116 @@ WI.appendContextMenuItemsForDOMNode = function(contextMenu, domNode, options = {
 
     contextMenu.appendSeparator();
 
-    if (domNode.isCustomElement()) {
-        contextMenu.appendItem(WI.UIString("Jump to Definition"), () => {
-            function didGetFunctionDetails(error, response) {
-                if (error)
-                    return;
-
-                let location = response.location;
-                let sourceCode = WI.debuggerManager.scriptForIdentifier(location.scriptId, WI.mainTarget);
-                if (!sourceCode)
-                    return;
-
-                let sourceCodeLocation = sourceCode.createSourceCodeLocation(location.lineNumber, location.columnNumber || 0);
-                WI.showSourceCodeLocation(sourceCodeLocation, {
-                    ignoreNetworkTab: true,
-                    ignoreSearchTab: true,
-                });
-            }
-
-            WI.RemoteObject.resolveNode(domNode).then((remoteObject) => {
-                remoteObject.getProperty("constructor", (error, result, wasThrown) => {
+    if (!options.disallowEditing) {
+        if (domNode.isCustomElement()) {
+            contextMenu.appendItem(WI.UIString("Jump to Definition"), () => {
+                function didGetFunctionDetails(error, response) {
                     if (error)
                         return;
-                    if (result.type === "function")
-                        remoteObject.target.DebuggerAgent.getFunctionDetails(result.objectId, didGetFunctionDetails);
-                    result.release();
+
+                    let location = response.location;
+                    let sourceCode = WI.debuggerManager.scriptForIdentifier(location.scriptId, WI.mainTarget);
+                    if (!sourceCode)
+                        return;
+
+                    let sourceCodeLocation = sourceCode.createSourceCodeLocation(location.lineNumber, location.columnNumber || 0);
+                    WI.showSourceCodeLocation(sourceCodeLocation, {
+                        ignoreNetworkTab: true,
+                        ignoreSearchTab: true,
+                    });
+                }
+
+                WI.RemoteObject.resolveNode(domNode).then((remoteObject) => {
+                    remoteObject.getProperty("constructor", (error, result, wasThrown) => {
+                        if (error)
+                            return;
+                        if (result.type === "function")
+                            remoteObject.target.DebuggerAgent.getFunctionDetails(result.objectId, didGetFunctionDetails);
+                        result.release();
+                    });
+                    remoteObject.release();
                 });
-                remoteObject.release();
             });
-        });
 
-        contextMenu.appendSeparator();
-    }
+            contextMenu.appendSeparator();
+        }
 
-    if (WI.cssManager.canForcePseudoClasses() && domNode.attached) {
-        contextMenu.appendSeparator();
+        if (WI.cssManager.canForcePseudoClasses() && domNode.attached) {
+            contextMenu.appendSeparator();
 
-        let pseudoSubMenu = contextMenu.appendSubMenuItem(WI.UIString("Forced Pseudo-Classes", "A context menu item to force (override) a DOM node's pseudo-classes"));
+            let pseudoSubMenu = contextMenu.appendSubMenuItem(WI.UIString("Forced Pseudo-Classes", "A context menu item to force (override) a DOM node's pseudo-classes"));
 
-        let enabledPseudoClasses = domNode.enabledPseudoClasses;
-        WI.CSSManager.ForceablePseudoClasses.forEach((pseudoClass) => {
-            let enabled = enabledPseudoClasses.includes(pseudoClass);
-            pseudoSubMenu.appendCheckboxItem(pseudoClass.capitalize(), () => {
-                domNode.setPseudoClassEnabled(pseudoClass, !enabled);
-            }, enabled);
-        });
-    }
+            let enabledPseudoClasses = domNode.enabledPseudoClasses;
+            WI.CSSManager.ForceablePseudoClasses.forEach((pseudoClass) => {
+                let enabled = enabledPseudoClasses.includes(pseudoClass);
+                pseudoSubMenu.appendCheckboxItem(pseudoClass.capitalize(), () => {
+                    domNode.setPseudoClassEnabled(pseudoClass, !enabled);
+                }, enabled);
+            });
+        }
 
-    if (WI.domDebuggerManager.supported && isElement && !domNode.isPseudoElement() && attached) {
-        contextMenu.appendSeparator();
+        if (WI.domDebuggerManager.supported && isElement && !domNode.isPseudoElement() && attached) {
+            contextMenu.appendSeparator();
 
-        WI.appendContextMenuItemsForDOMNodeBreakpoints(contextMenu, domNode, options);
-    }
+            WI.appendContextMenuItemsForDOMNodeBreakpoints(contextMenu, domNode, options);
+        }
 
-    contextMenu.appendSeparator();
+        contextMenu.appendSeparator();
 
-    if (!options.excludeLogElement && !domNode.isInUserAgentShadowTree() && !domNode.isPseudoElement()) {
-        let label = isElement ? WI.UIString("Log Element", "Log (print) DOM element to Console") : WI.UIString("Log Node", "Log (print) DOM node to Console");
-        contextMenu.appendItem(label, () => {
-            WI.RemoteObject.resolveNode(domNode, WI.RuntimeManager.ConsoleObjectGroup).then((remoteObject) => {
-                let text = isElement ? WI.UIString("Selected Element", "Selected DOM element") : WI.UIString("Selected Node", "Selected DOM node");
-                const addSpecialUserLogClass = true;
-                WI.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, addSpecialUserLogClass);
+        if (!options.excludeLogElement && !domNode.isInUserAgentShadowTree() && !domNode.isPseudoElement()) {
+            let label = isElement ? WI.UIString("Log Element", "Log (print) DOM element to Console") : WI.UIString("Log Node", "Log (print) DOM node to Console");
+            contextMenu.appendItem(label, () => {
+                WI.RemoteObject.resolveNode(domNode, WI.RuntimeManager.ConsoleObjectGroup).then((remoteObject) => {
+                    let text = isElement ? WI.UIString("Selected Element", "Selected DOM element") : WI.UIString("Selected Node", "Selected DOM node");
+                    const addSpecialUserLogClass = true;
+                    WI.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, addSpecialUserLogClass);
+                });
             });
-        });
-    }
+        }
 
-    if (!options.excludeRevealElement && window.DOMAgent && attached) {
-        contextMenu.appendItem(WI.repeatedUIString.revealInDOMTree(), () => {
-            WI.domManager.inspectElement(domNode.id);
-        });
-    }
+        if (!options.excludeRevealElement && window.DOMAgent && attached) {
+            contextMenu.appendItem(WI.repeatedUIString.revealInDOMTree(), () => {
+                WI.domManager.inspectElement(domNode.id);
+            });
+        }
 
-    if (WI.settings.experimentalEnableLayersTab.value && window.LayerTreeAgent && attached) {
-        contextMenu.appendItem(WI.UIString("Reveal in Layers Tab", "Open Layers tab and select the layer corresponding to this node"), () => {
-            WI.showLayersTab({nodeToSelect: domNode});
-        });
-    }
+        if (WI.settings.experimentalEnableLayersTab.value && window.LayerTreeAgent && attached) {
+            contextMenu.appendItem(WI.UIString("Reveal in Layers Tab", "Open Layers tab and select the layer corresponding to this node"), () => {
+                WI.showLayersTab({nodeToSelect: domNode});
+            });
+        }
 
-    if (window.PageAgent && attached) {
-        contextMenu.appendItem(WI.UIString("Capture Screenshot", "Capture screenshot of the selected DOM node"), () => {
-            PageAgent.snapshotNode(domNode.id, (error, dataURL) => {
-                if (error) {
-                    const target = WI.mainTarget;
-                    const source = WI.ConsoleMessage.MessageSource.Other;
-                    const level = WI.ConsoleMessage.MessageLevel.Error;
-                    let consoleMessage = new WI.ConsoleMessage(target, source, level, error);
-                    consoleMessage.shouldRevealConsole = true;
-
-                    WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
-                    return;
-                }
+        if (window.PageAgent && attached) {
+            contextMenu.appendItem(WI.UIString("Capture Screenshot", "Capture screenshot of the selected DOM node"), () => {
+                PageAgent.snapshotNode(domNode.id, (error, dataURL) => {
+                    if (error) {
+                        const target = WI.mainTarget;
+                        const source = WI.ConsoleMessage.MessageSource.Other;
+                        const level = WI.ConsoleMessage.MessageLevel.Error;
+                        let consoleMessage = new WI.ConsoleMessage(target, source, level, error);
+                        consoleMessage.shouldRevealConsole = true;
+
+                        WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
+                        return;
+                    }
 
-                WI.FileUtilities.save({
-                    url: WI.FileUtilities.inspectorURLForFilename(WI.FileUtilities.screenshotString() + ".png"),
-                    content: parseDataURL(dataURL).data,
-                    base64Encoded: true,
+                    WI.FileUtilities.save({
+                        url: WI.FileUtilities.inspectorURLForFilename(WI.FileUtilities.screenshotString() + ".png"),
+                        content: parseDataURL(dataURL).data,
+                        base64Encoded: true,
+                    });
                 });
             });
-        });
-    }
+        }
 
-    if (isElement && attached) {
-        contextMenu.appendItem(WI.UIString("Scroll into View", "Scroll selected DOM node into view on the inspected web page"), () => {
-            domNode.scrollIntoView();
-        });
-    }
+        if (isElement && attached) {
+            contextMenu.appendItem(WI.UIString("Scroll into View", "Scroll selected DOM node into view on the inspected web page"), () => {
+                domNode.scrollIntoView();
+            });
+        }
 
-    contextMenu.appendSeparator();
+        contextMenu.appendSeparator();
+    }
 };
 
 WI.appendContextMenuItemsForDOMNodeBreakpoints = function(contextMenu, domNode, options = {})
index dababa4..40200cc 100644 (file)
@@ -824,7 +824,7 @@ WI.DOMTreeElement = class DOMTreeElement extends WI.TreeElement
             });
         }
 
-        if (attributeName && isNonShadowEditable) {
+        if (attributeName) {
             subMenus.copy.appendItem(WI.UIString("Attribute"), () => {
                 let text = attributeName;
                 let attributeValue = this.representedObject.getAttribute(attributeName);
@@ -855,16 +855,18 @@ WI.DOMTreeElement = class DOMTreeElement extends WI.TreeElement
         for (let subMenu of Object.values(subMenus))
             contextMenu.pushItem(subMenu);
 
-        if (this.selected && this.treeOutline && this.treeOutline.selectedTreeElements.length > 1) {
-            let forceHidden = !this.treeOutline.selectedTreeElements.every((treeElement) => treeElement.isNodeHidden);
-            let label = forceHidden ? WI.UIString("Hide Elements") : WI.UIString("Show Elements");
-            contextMenu.appendItem(label, () => {
-                this.treeOutline.toggleSelectedElementsVisibility(forceHidden);
-            });
-        } else {
-            contextMenu.appendItem(WI.UIString("Toggle Visibility"), () => {
-                this.toggleElementVisibility();
-            });
+        if (this.treeOutline.editable) {
+            if (this.selected && this.treeOutline && this.treeOutline.selectedTreeElements.length > 1) {
+                let forceHidden = !this.treeOutline.selectedTreeElements.every((treeElement) => treeElement.isNodeHidden);
+                let label = forceHidden ? WI.UIString("Hide Elements") : WI.UIString("Show Elements");
+                contextMenu.appendItem(label, () => {
+                    this.treeOutline.toggleSelectedElementsVisibility(forceHidden);
+                });
+            } else if (isEditableNode) {
+                contextMenu.appendItem(WI.UIString("Toggle Visibility"), () => {
+                    this.toggleElementVisibility();
+                });
+            }
         }
     }
 
index 85481d7..a932c6d 100644 (file)
@@ -58,6 +58,7 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
         this._editable = false;
         this._editing = false;
         this._visible = false;
+        this._usingLocalDOMNode = false;
 
         this._hideElementsKeyboardShortcut = new WI.KeyboardShortcut(null, "H", this._hideElements.bind(this), this.element);
         this._hideElementsKeyboardShortcut.implicitlyPreventsDefault = false;
@@ -118,6 +119,12 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
         return this._isXMLMimeType;
     }
 
+    markAsUsingLocalDOMNode()
+    {
+        this._editable = false;
+        this._usingLocalDOMNode = true;
+    }
+
     selectedDOMNode()
     {
         return this._selectedDOMNode;
@@ -287,6 +294,7 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
             treeElement.populateDOMNodeContextMenu(contextMenu, subMenus, event, subMenus);
 
         let options = {
+            disallowEditing: !this._editable,
             excludeRevealElement: this._excludeRevealElementContextMenu,
             copySubMenu: subMenus.copy,
         };
@@ -440,6 +448,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
 
     _onmousemove(event)
     {
+        if (this._usingLocalDOMNode)
+            return;
+
         let element = this.treeElementFromEvent(event);
         if (element && this._previousHoveredElement === element)
             return;
@@ -463,6 +474,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
 
     _onmouseout(event)
     {
+        if (this._usingLocalDOMNode)
+            return;
+
         var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
         if (nodeUnderMouse && this.element.contains(nodeUnderMouse))
             return;
@@ -477,6 +491,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
 
     _ondragstart(event)
     {
+        if (!this._editable)
+            return false;
+
         let treeElement = this.treeElementFromEvent(event);
         if (!treeElement)
             return false;
@@ -500,6 +517,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
 
     _ondragover(event)
     {
+        if (!this._editable)
+            return false;
+
         if (event.dataTransfer.types.includes(WI.GeneralStyleDetailsSidebarPanel.ToggledClassesDragType)) {
             event.preventDefault();
             event.dataTransfer.dropEffect = "copy";
@@ -531,6 +551,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
 
     _ondragleave(event)
     {
+        if (!this._editable)
+            return false;
+
         this._clearDragOverTreeElementMarker();
         event.preventDefault();
         return false;
@@ -553,6 +576,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
 
     _ondrop(event)
     {
+        if (!this._editable)
+            return;
+
         event.preventDefault();
 
         function callback(error, newNodeId)
@@ -592,6 +618,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
 
     _ondragend(event)
     {
+        if (!this._editable)
+            return;
+
         event.preventDefault();
         this._clearDragOverTreeElementMarker();
         delete this._nodeBeingDragged;
@@ -633,6 +662,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
 
     _hideElements(event, keyboardShortcut)
     {
+        if (!this._editable)
+            return;
+
         if (!this.selectedTreeElement || WI.isEditingAnyField())
             return;
 
diff --git a/Source/WebInspectorUI/UserInterface/Views/LocalDOMContentView.js b/Source/WebInspectorUI/UserInterface/Views/LocalDOMContentView.js
new file mode 100644 (file)
index 0000000..eee8dee
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.LocalDOMContentView = class LocalDOMContentView extends WI.LocalRemoteObjectContentView
+{
+    constructor(content, mimeType, representedObject)
+    {
+        console.assert(typeof content === "string");
+        console.assert(typeof mimeType === "string");
+
+        super(representedObject);
+
+        this._content = content;
+        this._mimeType = mimeType;
+    }
+
+    // Protected
+
+    get expression()
+    {
+        return `(new DOMParser).parseFromString(${JSON.stringify(this._content)}, ${JSON.stringify(this._mimeType)})`;
+    }
+
+    renderRemoteObject(remoteObject)
+    {
+        remoteObject.pushNodeToFrontend((nodeId) => {
+            let domNode = WI.domManager.nodeForId(nodeId);
+            console.assert(domNode);
+
+            domNode.getSubtree(3, () => {
+                let treeOutline = new WI.DOMTreeOutline({omitRootDOMNode: true, selectable: false});
+                treeOutline.markAsUsingLocalDOMNode();
+                treeOutline.setVisible(true);
+                treeOutline.rootDOMNode = domNode;
+                this.element.appendChild(treeOutline.element);
+            });
+        });
+    }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/LocalJSONContentView.js b/Source/WebInspectorUI/UserInterface/Views/LocalJSONContentView.js
new file mode 100644 (file)
index 0000000..7253be7
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.LocalJSONContentView = class LocalJSONContentView extends WI.LocalRemoteObjectContentView
+{
+    constructor(json, representedObject)
+    {
+        console.assert(typeof json === "string" && json.isJSON());
+
+        super(representedObject);
+
+        this._json = json;
+    }
+
+    // Protected
+
+    get expression()
+    {
+        return this._json;
+    }
+
+    renderRemoteObject(remoteObject)
+    {
+        let objectTree = new WI.ObjectTreeView(remoteObject);
+        objectTree.showOnlyJSON();
+        objectTree.expand();
+
+        this.element.appendChild(objectTree.element);
+    }
+};
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WI.JSONContentView = class JSONContentView extends WI.ContentView
+WI.LocalRemoteObjectContentView = class LocalRemoteObjectContentView extends WI.ContentView
 {
-    constructor(json, representedObject)
+    constructor(representedObject)
     {
-        console.assert(typeof json === "string" && json.isJSON());
-
         super(representedObject);
 
-        this._json = json;
         this._remoteObject = null;
         this._spinnerTimeout = undefined;
 
-        this.element.classList.add("json");
+        this.element.classList.add("local-remote-object");
     }
 
     // Protected
 
+    get expression()
+    {
+        throw WI.NotImplementedError.subclassMustOverride();
+    }
+
+    renderRemoteObject(remoteObject)
+    {
+        throw WI.NotImplementedError.subclassMustOverride();
+    }
+
     initialLayout()
     {
         super.initialLayout();
 
-        const options = {
-            expression: "(" + this._json + ")",
+        let target = WI.assumingMainTarget();
+        let options = {
+            expression: "(" + this.expression + ")",
             doNotPauseOnExceptionsAndMuteConsole: true,
             generatePreview: true,
         };
-        RuntimeAgent.evaluate.invoke(options, (error, result, wasThrown) => {
+        target.RuntimeAgent.evaluate.invoke(options, (error, result, wasThrown) => {
             console.assert(!error);
             console.assert(!wasThrown);
 
             this._remoteObject = WI.RemoteObject.fromPayload(result);
 
-            let objectTree = new WI.ObjectTreeView(this._remoteObject);
-            objectTree.showOnlyJSON();
-            objectTree.expand();
-
             if (this._spinnerTimeout) {
                 clearTimeout(this._spinnerTimeout);
                 this._spinnerTimeout = undefined;
             }
 
             this.element.removeChildren();
-            this.element.appendChild(objectTree.element);
+
+            this.renderRemoteObject(this._remoteObject);
         });
     }
 
index 12051b3..c74187a 100644 (file)
     content: url(../Images/DOMNode.svg);
 }
 
+.object-icon .icon {
+    content: url(../Images/Object.svg);
+}
+
 .function-icon .icon {
     content: url(../Images/Function.svg);
 }
index 282b945..f0bee23 100644 (file)
@@ -33,102 +33,36 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
         this._resource.addEventListener(WI.Resource.Event.TypeDidChange, this._resourceTypeDidChange, this);
         this._resource.addEventListener(WI.Resource.Event.LoadingDidFinish, this._resourceLoadingDidFinish, this);
 
-        function createPathComponent(displayName, className, identifier)
-        {
-            const textOnly = false;
-            const showSelectorArrows = true;
-            let pathComponent = new WI.HierarchicalPathComponent(displayName, className, identifier, textOnly, showSelectorArrows);
-            pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
-            pathComponent.comparisonData = resource;
-            return pathComponent;
-        }
-
-        this._requestContentView = null;
-        this._responseContentView = null;
-        this._customRequestContentView = null;
-        this._customRequestContentViewInitializer = null;
-        this._customResponseContentView = null;
-        this._customResponseContentViewInitializer = null;
-
-        this._requestPathComponent = createPathComponent.call(this, WI.UIString("Request"), WI.ResourceClusterContentView.RequestIconStyleClassName, WI.ResourceClusterContentView.RequestIdentifier);
-        this._customRequestPathComponent = createPathComponent.call(this, WI.UIString("Custom Request"), WI.ResourceClusterContentView.RequestIconStyleClassName, WI.ResourceClusterContentView.CustomRequestIdentifier);
-        this._responsePathComponent = createPathComponent.call(this, WI.UIString("Response"), WI.ResourceClusterContentView.ResponseIconStyleClassName, WI.ResourceClusterContentView.ResponseIdentifier);
-        this._customResponsePathComponent = createPathComponent.call(this, WI.UIString("Custom Response"), WI.ResourceClusterContentView.ResponseIconStyleClassName, WI.ResourceClusterContentView.CustomResponseIdentifier);
+        this._responsePathComponent = this._createPathComponent({
+            displayName: WI.UIString("Response"),
+            identifier: ResourceClusterContentView.Identifier.Response,
+            styleClassNames: ["response-icon"],
+        });
 
         if (this._canShowRequestContentView()) {
-            this._requestPathComponent.nextSibling = this._responsePathComponent;
-            this._responsePathComponent.previousSibling = this._requestPathComponent;
-
-            this._tryEnableCustomRequestContentView();
+            this._requestPathComponent = this._createPathComponent({
+                displayName: WI.UIString("Request"),
+                identifier: ResourceClusterContentView.Identifier.Request,
+                styleClassNames: ["request-icon"],
+                nextSibling: this._responsePathComponent,
+            });
+
+            this._tryEnableCustomRequestContentViews();
         }
 
         // FIXME: Since a custom response content view may only become available after a response is received
         // we need to figure out a way to restore / prefer the custom content view. For example if users
         // always want to prefer the JSON view to the normal Response text view.
 
-        this._currentContentViewSetting = new WI.Setting("resource-current-view-" + this._resource.url.hash, WI.ResourceClusterContentView.CustomResponseIdentifier);
+        this._currentContentViewSetting = new WI.Setting("resource-current-view-" + this._resource.url.hash, ResourceClusterContentView.Identifier.Response);
 
-        this._tryEnableCustomResponseContentView();
+        this._tryEnableCustomResponseContentViews();
     }
 
     // Public
 
     get resource() { return this._resource; }
 
-    get requestContentView()
-    {
-        if (!this._canShowRequestContentView())
-            return null;
-
-        if (this._requestContentView)
-            return this._requestContentView;
-
-        this._requestContentView = new WI.TextContentView(this._resource.requestData || "", this._resource.requestDataContentType);
-
-        return this._requestContentView;
-    }
-
-    get responseContentView()
-    {
-        if (this._responseContentView)
-            return this._responseContentView;
-
-        this._responseContentView = this._contentViewForResourceType(this._resource.type);
-        if (this._responseContentView)
-            return this._responseContentView;
-
-        let typeFromMIMEType = WI.Resource.typeFromMIMEType(this._resource.mimeType);
-        this._responseContentView = this._contentViewForResourceType(typeFromMIMEType);
-        if (this._responseContentView)
-            return this._responseContentView;
-
-        if (WI.shouldTreatMIMETypeAsText(this._resource.mimeType)) {
-            this._responseContentView = new WI.TextResourceContentView(this._resource);
-            return this._responseContentView;
-        }
-
-        this._responseContentView = new WI.GenericResourceContentView(this._resource);
-        return this._responseContentView;
-    }
-
-    get customRequestContentView()
-    {
-        if (!this._customRequestContentView && this._customRequestContentViewInitializer) {
-            this._customRequestContentView = this._customRequestContentViewInitializer();
-            this._customRequestContentViewInitializer = null;
-        }
-        return this._customRequestContentView;
-    }
-
-    get customResponseContentView()
-    {
-        if (!this._customResponseContentView && this._customResponseContentViewInitializer) {
-            this._customResponseContentView = this._customResponseContentViewInitializer();
-            this._customResponseContentViewInitializer = null;
-        }
-        return this._customResponseContentView;
-    }
-
     get selectionPathComponents()
     {
         let currentContentView = this._contentViewContainer.currentContentView;
@@ -171,7 +105,7 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
     {
         this._shownInitialContent = true;
 
-        return this._showContentViewForIdentifier(WI.ResourceClusterContentView.CustomRequestIdentifier);
+        return this._showContentViewForIdentifier(ResourceClusterContentView.Identifier.Request);
     }
 
     showResponse(positionToReveal, textRangeToSelect, forceUnformatted)
@@ -184,7 +118,7 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
             this._forceUnformatted = forceUnformatted;
         }
 
-        let responseContentView = this._showContentViewForIdentifier(WI.ResourceClusterContentView.ResponseIdentifier);
+        let responseContentView = this._showContentViewForIdentifier(ResourceClusterContentView.Identifier.Response);
         if (typeof responseContentView.revealPosition === "function")
             responseContentView.revealPosition(positionToReveal, textRangeToSelect, forceUnformatted);
         return responseContentView;
@@ -192,6 +126,99 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
 
     // Private
 
+    get requestContentView()
+    {
+        if (!this._canShowRequestContentView())
+            return null;
+
+        if (this._requestContentView)
+            return this._requestContentView;
+
+        this._requestContentView = new WI.TextContentView(this._resource.requestData || "", this._resource.requestDataContentType);
+
+        return this._requestContentView;
+    }
+
+    get responseContentView()
+    {
+        if (this._responseContentView)
+            return this._responseContentView;
+
+        this._responseContentView = this._contentViewForResourceType(this._resource.type);
+        if (this._responseContentView)
+            return this._responseContentView;
+
+        let typeFromMIMEType = WI.Resource.typeFromMIMEType(this._resource.mimeType);
+        this._responseContentView = this._contentViewForResourceType(typeFromMIMEType);
+        if (this._responseContentView)
+            return this._responseContentView;
+
+        if (WI.shouldTreatMIMETypeAsText(this._resource.mimeType)) {
+            this._responseContentView = new WI.TextResourceContentView(this._resource);
+            return this._responseContentView;
+        }
+
+        this._responseContentView = new WI.GenericResourceContentView(this._resource);
+        return this._responseContentView;
+    }
+
+    get customRequestDOMContentView()
+    {
+        if (!this._customRequestDOMContentView && this._customRequestDOMContentViewInitializer)
+            this._customRequestDOMContentView = this._customRequestDOMContentViewInitializer();
+        return this._customRequestDOMContentView;
+    }
+
+    get customRequestJSONContentView()
+    {
+        if (!this._customRequestJSONContentView && this._customRequestJSONContentViewInitializer)
+            this._customRequestJSONContentView = this._customRequestJSONContentViewInitializer();
+        return this._customRequestJSONContentView;
+    }
+
+    get customResponseDOMContentView()
+    {
+        if (!this._customResponseDOMContentView && this._customResponseDOMContentViewInitializer)
+            this._customResponseDOMContentView = this._customResponseDOMContentViewInitializer();
+        return this._customResponseDOMContentView;
+    }
+
+    get customResponseJSONContentView()
+    {
+        if (!this._customResponseJSONContentView && this._customResponseJSONContentViewInitializer)
+            this._customResponseJSONContentView = this._customResponseJSONContentViewInitializer();
+        return this._customResponseJSONContentView;
+    }
+
+    get customResponseTextContentView()
+    {
+        if (!this._customResponseTextContentView && this._customResponseTextContentViewInitializer)
+            this._customResponseTextContentView = this._customResponseTextContentViewInitializer();
+        return this._customResponseTextContentView;
+    }
+
+    _createPathComponent({displayName, styleClassNames, identifier, previousSibling, nextSibling})
+    {
+        const textOnly = false;
+        const showSelectorArrows = true;
+        let pathComponent = new WI.HierarchicalPathComponent(displayName, styleClassNames, identifier, textOnly, showSelectorArrows);
+        pathComponent.comparisonData = this._resource;
+
+        if (previousSibling) {
+            previousSibling.nextSibling = pathComponent;
+            pathComponent.previousSibling = previousSibling;
+        }
+
+        if (nextSibling) {
+            nextSibling.previousSibling = pathComponent;
+            pathComponent.nextSibling = nextSibling;
+        }
+
+        pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
+
+        return pathComponent;
+    }
+
     _canShowRequestContentView()
     {
         let requestData = this._resource.requestData;
@@ -206,12 +233,12 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
 
     _canShowCustomRequestContentView()
     {
-        return !!(this._customRequestContentView || this._customRequestContentViewInitializer);
+        return !!(this._customRequestDOMContentViewInitializer || this._customRequestJSONContentViewInitializer);
     }
 
     _canShowCustomResponseContentView()
     {
-        return !!(this._customResponseContentView || this._customResponseContentViewInitializer);
+        return !!(this._customResponseDOMContentViewInitializer || this._customResponseJSONContentViewInitializer || this._customResponseTextContentViewInitializer);
     }
 
     _contentViewForResourceType(type)
@@ -223,8 +250,6 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
             return new WI.TextResourceContentView(this._resource);
 
         case WI.Resource.Type.Image:
-            if (WI.fileExtensionForMIMEType(this._resource.mimeType) === "svg")
-                return new WI.SVGImageResourceClusterContentView(this._resource);
             return new WI.ImageResourceContentView(this._resource);
 
         case WI.Resource.Type.Font:
@@ -240,35 +265,61 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
 
     _pathComponentForContentView(contentView)
     {
-        console.assert(contentView);
-        if (!contentView)
-            return null;
-        if (contentView === this._requestContentView)
+        switch (contentView) {
+        case this._requestContentView:
             return this._requestPathComponent;
-        if (contentView === this._responseContentView)
+
+        case this._customRequestDOMContentView:
+            return this._customRequestDOMPathComponent;
+
+        case this._customRequestJSONContentView:
+            return this._customRequestJSONPathComponent;
+
+        case this._responseContentView:
             return this._responsePathComponent;
-        if (contentView === this._customRequestContentView)
-            return this._customRequestPathComponent;
-        if (contentView === this._customResponseContentView)
-            return this._customResponsePathComponent;
-        console.error("Unknown contentView.");
+
+        case this._customResponseDOMContentView:
+            return this._customResponseDOMPathComponent;
+
+        case this._customResponseJSONContentView:
+            return this._customResponseJSONPathComponent;
+
+        case this._customResponseTextContentView:
+            return this._customResponseTextPathComponent;
+        }
+
+        console.error("Unknown contentView", contentView);
         return null;
     }
 
     _identifierForContentView(contentView)
     {
         console.assert(contentView);
-        if (!contentView)
-            return null;
-        if (contentView === this._requestContentView)
-            return WI.ResourceClusterContentView.RequestIdentifier;
-        if (contentView === this._responseContentView)
-            return WI.ResourceClusterContentView.ResponseIdentifier;
-        if (contentView === this._customRequestContentView)
-            return WI.ResourceClusterContentView.CustomRequestIdentifier;
-        if (contentView === this._customResponseContentView)
-            return WI.ResourceClusterContentView.CustomResponseIdentifier;
-        console.error("Unknown contentView.");
+
+        switch (contentView) {
+        case this._requestContentView:
+            return ResourceClusterContentView.Identifier.Request;
+
+        case this._customRequestDOMContentView:
+            return ResourceClusterContentView.Identifier.RequestDOM;
+
+        case this._customRequestJSONContentView:
+            return ResourceClusterContentView.Identifier.RequestJSON;
+
+        case this._responseContentView:
+            return ResourceClusterContentView.Identifier.Response;
+
+        case this._customResponseDOMContentView:
+            return ResourceClusterContentView.Identifier.ResponseDOM;
+
+        case this._customResponseJSONContentView:
+            return ResourceClusterContentView.Identifier.ResponseJSON;
+
+        case this._customResponseTextContentView:
+            return ResourceClusterContentView.Identifier.ResponseText;
+        }
+
+        console.error("Unknown contentView", contentView);
         return null;
     }
 
@@ -278,22 +329,37 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
 
         // This is expected to fall through all the way to the `default`.
         switch (identifier) {
-        case WI.ResourceClusterContentView.CustomRequestIdentifier:
-            contentViewToShow = this.customRequestContentView;
+        case ResourceClusterContentView.Identifier.RequestDOM:
+            contentViewToShow = this.customRequestDOMContentView;
+            if (contentViewToShow)
+                break;
+            // fallthrough
+        case ResourceClusterContentView.Identifier.RequestJSON:
+            contentViewToShow = this.customRequestJSONContentView;
             if (contentViewToShow)
                 break;
             // fallthrough
-        case WI.ResourceClusterContentView.RequestIdentifier:
+        case ResourceClusterContentView.Identifier.Request:
             contentViewToShow = this.requestContentView;
             if (contentViewToShow)
                 break;
             // fallthrough
-        case WI.ResourceClusterContentView.CustomResponseIdentifier:
-            contentViewToShow = this.customResponseContentView;
+        case ResourceClusterContentView.Identifier.ResponseDOM:
+            contentViewToShow = this.customResponseDOMContentView;
             if (contentViewToShow)
                 break;
             // fallthrough
-        case WI.ResourceClusterContentView.ResponseIdentifier:
+        case ResourceClusterContentView.Identifier.ResponseJSON:
+            contentViewToShow = this.customResponseJSONContentView;
+            if (contentViewToShow)
+                break;
+            // fallthrough
+        case ResourceClusterContentView.Identifier.ResponseText:
+            contentViewToShow = this.customResponseTextContentView;
+            if (contentViewToShow)
+                break;
+            // fallthrough
+        case ResourceClusterContentView.Identifier.Response:
         default:
             contentViewToShow = this.responseContentView;
             break;
@@ -328,7 +394,7 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
 
     _resourceLoadingDidFinish(event)
     {
-        this._tryEnableCustomResponseContentView();
+        this._tryEnableCustomResponseContentViews();
 
         if ("_positionToReveal" in this) {
             if (this._contentViewContainer.currentContentView === this._responseContentView)
@@ -345,21 +411,88 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
         return typeof content === "string" && content.isJSON((json) => json && (typeof json === "object" || Array.isArray(json)));
     }
 
-    _tryEnableCustomRequestContentView()
+    _canUseDOMContentViewForContent(content, mimeType)
+    {
+        if (typeof content !== "string")
+            return false;
+
+        switch (mimeType) {
+        case "text/html":
+            return true;
+
+        case "text/xml":
+        case "application/xml":
+        case "application/xhtml+xml":
+        case "image/svg+xml":
+            try {
+                let dom = (new DOMParser).parseFromString(content, mimeType);
+                return !dom.querySelector("parsererror");
+            } catch { }
+            return false;
+        }
+
+        return false;
+    }
+
+    _normalizeMIMETypeForDOM(mimeType)
     {
-        if (!this._canUseJSONContentViewForContent(this._resource.requestData))
+        mimeType = parseMIMEType(mimeType).type;
+
+        if (mimeType.endsWith("/html") || mimeType.endsWith("+html"))
+            return "text/html";
+
+        if (mimeType.endsWith("/xml") || mimeType.endsWith("+xml")) {
+            if (mimeType !== "application/xhtml+xml" && mimeType !== "image/svg+xml")
+                return "application/xml";
+        }
+
+        if (mimeType.endsWith("/xhtml") || mimeType.endsWith("+xhtml"))
+            return "application/xhtml+xml";
+
+        if (mimeType.endsWith("/svg") || mimeType.endsWith("+svg"))
+            return "image/svg+xml";
+
+        return mimeType;
+    }
+
+    _tryEnableCustomRequestContentViews()
+    {
+        let content = this._resource.requestData;
+
+        if (this._canUseJSONContentViewForContent(content)) {
+            this._customRequestJSONContentViewInitializer = () => new WI.LocalJSONContentView(content, this._resource);
+
+            this._customRequestJSONPathComponent = this._createPathComponent({
+                displayName: WI.UIString("Request (Object Tree)"),
+                styleClassNames: ["object-icon"],
+                identifier: ResourceClusterContentView.Identifier.RequestJSON,
+                previousSibling: this._requestPathComponent,
+                nextSibling: this._responsePathComponent,
+            });
+
+            this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
             return;
+        }
 
-        this._customRequestContentViewInitializer = () => new WI.JSONContentView(this._resource.requestData, this._resource);
+        let mimeType = this._normalizeMIMETypeForDOM(this._resource.requestDataContentType);
 
-        this._customRequestPathComponent.displayName = WI.UIString("Request (JSON)");
-        this._customRequestPathComponent.previousSibling = this._requestPathComponent;
-        this._customRequestPathComponent.nextSibling = this._responsePathComponent;
-        this._requestPathComponent.nextSibling = this._customRequestPathComponent;
-        this._responsePathComponent.previousSibling = this._customRequestPathComponent;
+        if (this._canUseDOMContentViewForContent(content, mimeType)) {
+            this._customRequestDOMContentViewInitializer = () => new WI.LocalDOMContentView(content, mimeType, this._resource);
+
+            this._customRequestDOMPathComponent = this._createPathComponent({
+                displayName: WI.UIString("Request (DOM Tree)"),
+                styleClassNames: ["dom-document-icon"],
+                identifier: ResourceClusterContentView.Identifier.RequestDOM,
+                previousSibling: this._requestPathComponent,
+                nextSibling: this._responsePathComponent,
+            });
+
+            this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
+            return;
+        }
     }
 
-    _tryEnableCustomResponseContentView()
+    _tryEnableCustomResponseContentViews()
     {
         if (!this._resource.hasResponse())
             return;
@@ -370,25 +503,61 @@ WI.ResourceClusterContentView = class ResourceClusterContentView extends WI.Clus
 
         this._resource.requestContent()
         .then(({error, content}) => {
-            if (error || !content || !this._canUseJSONContentViewForContent(content))
+            if (error || typeof content !== "string")
                 return;
 
-            this._customResponseContentViewInitializer = () => new WI.JSONContentView(content, this._resource);
+            if (this._canUseJSONContentViewForContent(content)) {
+                this._customResponseJSONContentViewInitializer = () => new WI.LocalJSONContentView(content, this._resource);
 
-            this._customResponsePathComponent.displayName = WI.UIString("Response (JSON)");
-            this._customResponsePathComponent.previousSibling = this._responsePathComponent;
-            this._responsePathComponent.nextSibling = this._customResponsePathComponent;
+                this._customResponseJSONPathComponent = this._createPathComponent({
+                    displayName: WI.UIString("Response (Object Tree)"),
+                    styleClassNames: ["object-icon"],
+                    identifier: ResourceClusterContentView.Identifier.ResponseJSON,
+                    previousSibling: this._responsePathComponent,
+                });
 
-            this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
+                this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
+                return;
+            }
+
+            let mimeType = this._normalizeMIMETypeForDOM(this._resource.mimeType);
+
+            if (this._canUseDOMContentViewForContent(content, mimeType)) {
+                if (mimeType === "image/svg+xml") {
+                    this._customResponseTextContentViewInitializer = () => new WI.TextContentView(content, mimeType, this._resource);
+
+                    this._customResponseTextPathComponent = this._createPathComponent({
+                        displayName: WI.UIString("Response (Text)"),
+                        styleClassNames: ["source-icon"],
+                        identifier: ResourceClusterContentView.Identifier.ResponseText,
+                        previousSibling: this._responsePathComponent,
+                    });
+                }
+
+                this._customResponseDOMContentViewInitializer = () => new WI.LocalDOMContentView(content, mimeType, this._resource);
+
+                this._customResponseDOMPathComponent = this._createPathComponent({
+                    displayName: WI.UIString("Response (DOM Tree)"),
+                    styleClassNames: ["dom-document-icon"],
+                    identifier: ResourceClusterContentView.Identifier.ResponseDOM,
+                    previousSibling: this._customResponseTextPathComponent || this._responsePathComponent,
+                });
+
+                this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
+                return;
+            }
         });
     }
 };
 
 WI.ResourceClusterContentView.ContentViewIdentifierCookieKey = "resource-cluster-content-view-identifier";
 
-WI.ResourceClusterContentView.RequestIconStyleClassName = "request-icon";
-WI.ResourceClusterContentView.ResponseIconStyleClassName = "response-icon";
-WI.ResourceClusterContentView.RequestIdentifier = "request";
-WI.ResourceClusterContentView.ResponseIdentifier = "response";
-WI.ResourceClusterContentView.CustomRequestIdentifier = "custom-request";
-WI.ResourceClusterContentView.CustomResponseIdentifier = "custom-response";
+WI.ResourceClusterContentView.Identifier = {
+    Request: "request",
+    RequestDOM: "request-dom",
+    RequestJSON: "request-json",
+    Response: "response",
+    ResponseDOM: "response-dom",
+    ResponseJSON: "response-json",
+    ResponseText: "response-text",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/SVGImageResourceClusterContentView.js b/Source/WebInspectorUI/UserInterface/Views/SVGImageResourceClusterContentView.js
deleted file mode 100644 (file)
index 5a566a5..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2017 Devin Rousso <webkit@devinrousso.com>. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-WI.SVGImageResourceClusterContentView = class SVGImageResourceClusterContentView extends WI.ClusterContentView
-{
-    constructor(resource)
-    {
-        super(resource);
-
-        this._resource = resource;
-
-        let createPathComponent = (displayName, className, identifier) => {
-            const textOnly = false;
-            const showSelectorArrows = true;
-            let pathComponent = new WI.HierarchicalPathComponent(displayName, className, identifier, textOnly, showSelectorArrows);
-            pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
-            pathComponent.comparisonData = resource;
-            return pathComponent;
-        };
-
-        this._imagePathComponent = createPathComponent(WI.UIString("Image"), "image-icon", WI.SVGImageResourceClusterContentView.Identifier.Image);
-        this._sourcePathComponent = createPathComponent(WI.UIString("Source"), "source-icon", WI.SVGImageResourceClusterContentView.Identifier.Source);
-
-        this._imagePathComponent.nextSibling = this._sourcePathComponent;
-        this._sourcePathComponent.previousSibling = this._imagePathComponent;
-
-        this._currentContentViewSetting = new WI.Setting("svg-image-resource-cluster-current-view-" + this._resource.url.hash, WI.SVGImageResourceClusterContentView.Identifier.Image);
-    }
-
-    // Public
-
-    get resource() { return this._resource; }
-
-    get selectionPathComponents()
-    {
-        let currentContentView = this._contentViewContainer.currentContentView;
-        if (!currentContentView)
-            return [];
-
-        // Append the current view's path components to the path component representing the current view.
-        let components = [this._pathComponentForContentView(currentContentView)];
-        return components.concat(currentContentView.selectionPathComponents);
-    }
-
-    // Protected
-
-    shown()
-    {
-        super.shown();
-
-        if (this._shownInitialContent)
-            return;
-
-        this._showContentViewForIdentifier(this._currentContentViewSetting.value);
-    }
-
-    closed()
-    {
-        super.closed();
-
-        this._shownInitialContent = false;
-    }
-
-    saveToCookie(cookie)
-    {
-        cookie[WI.SVGImageResourceClusterContentView.ContentViewIdentifierCookieKey] = this._currentContentViewSetting.value;
-    }
-
-    restoreFromCookie(cookie)
-    {
-        let contentView = this._showContentViewForIdentifier(cookie[WI.SVGImageResourceClusterContentView.ContentViewIdentifierCookieKey]);
-        if (typeof contentView.revealPosition === "function" && "lineNumber" in cookie && "columnNumber" in cookie)
-            contentView.revealPosition(new WI.SourceCodePosition(cookie.lineNumber, cookie.columnNumber));
-    }
-
-    // Private
-
-    _pathComponentForContentView(contentView)
-    {
-        console.assert(contentView);
-        if (!contentView)
-            return null;
-        if (contentView instanceof WI.ImageResourceContentView)
-            return this._imagePathComponent;
-        if (contentView instanceof WI.TextContentView)
-            return this._sourcePathComponent;
-        console.error("Unknown contentView.");
-        return null;
-    }
-
-    _identifierForContentView(contentView)
-    {
-        console.assert(contentView);
-        if (!contentView)
-            return null;
-        if (contentView instanceof WI.ImageResourceContentView)
-            return WI.SVGImageResourceClusterContentView.Identifier.Image;
-        if (contentView instanceof WI.TextContentView)
-            return WI.SVGImageResourceClusterContentView.Identifier.Source;
-        console.error("Unknown contentView.");
-        return null;
-    }
-
-    _showContentViewForIdentifier(identifier)
-    {
-        let contentViewToShow = null;
-
-        switch (identifier) {
-        case WI.SVGImageResourceClusterContentView.Identifier.Image:
-            contentViewToShow = new WI.ImageResourceContentView(this._resource);
-            break;
-
-        case WI.SVGImageResourceClusterContentView.Identifier.Source:
-            contentViewToShow = new WI.TextContentView("", this._resource.mimeType);
-
-            this._resource.requestContent().then((result) => {
-                if (typeof result.content === "string") {
-                    contentViewToShow.textEditor.string = result.content;
-                    return;
-                }
-
-                blobAsText(result.content, (text) => {
-                    contentViewToShow.textEditor.string = text;
-                });
-            });
-            break;
-
-        default:
-            // Default to showing the image.
-            contentViewToShow = new WI.ImageResourceContentView(this._resource);
-            break;
-        }
-
-        this._currentContentViewSetting.value = this._identifierForContentView(contentViewToShow);
-
-        return this.contentViewContainer.showContentView(contentViewToShow);
-    }
-
-    _pathComponentSelected(event)
-    {
-        this._showContentViewForIdentifier(event.data.pathComponent.representedObject);
-    }
-};
-
-WI.SVGImageResourceClusterContentView.ContentViewIdentifierCookieKey = "svg-image-resource-cluster-content-view-identifier";
-
-WI.SVGImageResourceClusterContentView.Identifier = {
-    Image: "image",
-    Source: "source",
-};
index 37bc503..1753219 100644 (file)
@@ -25,9 +25,9 @@
 
 WI.TextContentView = class TextContentView extends WI.ContentView
 {
-    constructor(string, mimeType)
+    constructor(string, mimeType, representedObject)
     {
-        super(string);
+        super(representedObject || string);
 
         this.element.classList.add("text");