blob: 074ac847b9e66cc65e301ec3cd55a3fa2b093952 [file] [log] [blame]
/*
* Copyright (C) 2013, 2015 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.ResourceClusterContentView = class ResourceClusterContentView extends WI.ClusterContentView
{
constructor(resource)
{
super(resource);
this._resource = resource;
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)
{
let pathComponent = new WI.HierarchicalPathComponent(displayName, className, identifier, false, true);
pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
pathComponent.comparisonData = resource;
return pathComponent;
}
this._requestContentView = null;
this._responseContentView = null;
this._customResponseContentView = null;
this._customResponseContentViewConstructor = null;
this._requestPathComponent = createPathComponent.call(this, WI.UIString("Request"), WI.ResourceClusterContentView.RequestIconStyleClassName, WI.ResourceClusterContentView.RequestIdentifier);
this._responsePathComponent = createPathComponent.call(this, WI.UIString("Response"), WI.ResourceClusterContentView.ResponseIconStyleClassName, WI.ResourceClusterContentView.ResponseIdentifier);
this._customResponsePathComponent = createPathComponent.call(this, WI.UIString("Custom"), WI.ResourceClusterContentView.ResponseIconStyleClassName, WI.ResourceClusterContentView.CustomResponseIdentifier);
if (this._canShowRequestContentView()) {
this._requestPathComponent.nextSibling = this._responsePathComponent;
this._responsePathComponent.previousSibling = this._requestPathComponent;
}
// 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.ResponseIdentifier);
this._tryEnableCustomResponseContentView();
}
// Public
get resource()
{
return this._resource;
}
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 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 customResponseContentView()
{
if (!this._canShowCustomResponseContentView())
return null;
if (!this._customResponseContentView)
this._customResponseContentView = new this._customResponseContentViewConstructor(this._resource);
return this._customResponseContentView;
}
get selectionPathComponents()
{
let currentContentView = this._contentViewContainer.currentContentView;
if (!currentContentView)
return [];
if (!this._canShowRequestContentView() && !this._canShowCustomResponseContentView())
return currentContentView.selectionPathComponents;
// 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);
}
shown()
{
super.shown();
if (this._shownInitialContent)
return;
this._showContentViewForIdentifier(this._currentContentViewSetting.value);
}
closed()
{
super.closed();
this._shownInitialContent = false;
}
saveToCookie(cookie)
{
cookie[WI.ResourceClusterContentView.ContentViewIdentifierCookieKey] = this._currentContentViewSetting.value;
}
restoreFromCookie(cookie)
{
let contentView = this._showContentViewForIdentifier(cookie[WI.ResourceClusterContentView.ContentViewIdentifierCookieKey]);
if (typeof contentView.revealPosition === "function" && "lineNumber" in cookie && "columnNumber" in cookie)
contentView.revealPosition(new WI.SourceCodePosition(cookie.lineNumber, cookie.columnNumber));
}
showRequest()
{
this._shownInitialContent = true;
return this._showContentViewForIdentifier(WI.ResourceClusterContentView.RequestIdentifier);
}
showResponse(positionToReveal, textRangeToSelect, forceUnformatted)
{
this._shownInitialContent = true;
if (!this._resource.finished) {
this._positionToReveal = positionToReveal;
this._textRangeToSelect = textRangeToSelect;
this._forceUnformatted = forceUnformatted;
}
let responseContentView = this._showContentViewForIdentifier(WI.ResourceClusterContentView.ResponseIdentifier);
if (typeof responseContentView.revealPosition === "function")
responseContentView.revealPosition(positionToReveal, textRangeToSelect, forceUnformatted);
return responseContentView;
}
// Private
_canShowRequestContentView()
{
let requestData = this._resource.requestData;
if (!requestData)
return false;
if (this._resource.hasRequestFormParameters())
return false;
return true;
}
_canShowCustomResponseContentView()
{
return !!this._customResponseContentViewConstructor;
}
_contentViewForResourceType(type)
{
switch (type) {
case WI.Resource.Type.Document:
case WI.Resource.Type.Script:
case WI.Resource.Type.Stylesheet:
return new WI.TextResourceContentView(this._resource);
case WI.Resource.Type.Image:
if (this._resource.mimeTypeComponents.type === "image/svg+xml")
return new WI.SVGImageResourceClusterContentView(this._resource);
return new WI.ImageResourceContentView(this._resource);
case WI.Resource.Type.Font:
return new WI.FontResourceContentView(this._resource);
case WI.Resource.Type.WebSocket:
return new WI.WebSocketContentView(this._resource);
default:
return null;
}
}
_pathComponentForContentView(contentView)
{
console.assert(contentView);
if (!contentView)
return null;
if (contentView === this._requestContentView)
return this._requestPathComponent;
if (contentView === this._responseContentView)
return this._responsePathComponent;
if (contentView === this._customResponseContentView)
return this._customResponsePathComponent;
console.error("Unknown 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._customResponseContentView)
return WI.ResourceClusterContentView.CustomResponseIdentifier;
console.error("Unknown contentView.");
return null;
}
_showContentViewForIdentifier(identifier)
{
let contentViewToShow = null;
switch (identifier) {
case WI.ResourceClusterContentView.RequestIdentifier:
contentViewToShow = this.requestContentView;
break;
case WI.ResourceClusterContentView.ResponseIdentifier:
contentViewToShow = this.responseContentView;
break;
case WI.ResourceClusterContentView.CustomResponseIdentifier:
contentViewToShow = this.customResponseContentView;
break;
}
if (!contentViewToShow)
contentViewToShow = this.responseContentView;
console.assert(contentViewToShow);
this._currentContentViewSetting.value = this._identifierForContentView(contentViewToShow);
return this.contentViewContainer.showContentView(contentViewToShow);
}
_pathComponentSelected(event)
{
this._showContentViewForIdentifier(event.data.pathComponent.representedObject);
}
_resourceTypeDidChange(event)
{
// Since resource views are based on the type, we need to make a new content view and tell the container to replace this
// content view with the new one. Make a new ResourceContentView which will use the new resource type to make the correct
// concrete ResourceContentView subclass.
let currentResponseContentView = this._responseContentView;
if (!currentResponseContentView)
return;
this._responseContentView = null;
this.contentViewContainer.replaceContentView(currentResponseContentView, this.responseContentView);
}
_resourceLoadingDidFinish(event)
{
this._tryEnableCustomResponseContentView();
if ("_positionToReveal" in this) {
if (this._contentViewContainer.currentContentView === this._responseContentView)
this._responseContentView.revealPosition(this._positionToReveal, this._textRangeToSelect, this._forceUnformatted);
delete this._positionToReveal;
delete this._textRangeToSelect;
delete this._forceUnformatted;
}
}
_tryEnableCustomResponseContentView()
{
if (!this._resource.hasResponse())
return;
this._customResponseContentViewConstructor = this._customContentViewConstructorForResource(this._resource);
if (!this._customResponseContentViewConstructor)
return;
console.assert(this._customResponseContentViewConstructor.customContentViewDisplayName, "Custom Response ContentViews should have a static customContentViewDisplayName method.", this._customResponseContentViewConstructor);
this._responsePathComponent.nextSibling = this._customResponsePathComponent;
this._customResponsePathComponent.previousSibling = this._responsePathComponent;
this._customResponsePathComponent.displayName = this._customResponseContentViewConstructor.customContentViewDisplayName();
this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
}
_customContentViewConstructorForResource(resource)
{
let mimeType = this._resource.mimeType;
let fileExtension = WI.fileExtensionForMIMEType(mimeType);
if (fileExtension === "json")
return WI.JSONResourceContentView;
return null;
}
};
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.CustomResponseIdentifier = "custom-response";