UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

607 lines (532 loc) 22.5 kB
/* * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) IBM Corp. 2009 All rights reserved. * Copyright (C) 2010 Google 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE 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 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. */ Network.RequestHeadersView = class extends UI.VBox { /** * @param {!SDK.NetworkRequest} request */ constructor(request) { super(); this.registerRequiredCSS('network/requestHeadersView.css'); this.element.classList.add('request-headers-view'); this._request = request; this._decodeRequestParameters = true; this._showRequestHeadersText = false; this._showResponseHeadersText = false; /** @type {?UI.TreeElement} */ this._highlightedElement = null; const root = new UI.TreeOutlineInShadow(); root.registerRequiredCSS('network/requestHeadersTree.css'); root.element.classList.add('request-headers-tree'); root.setFocusable(false); root.makeDense(); root.expandTreeElementsWhenArrowing = true; this.element.appendChild(root.element); const generalCategory = new Network.RequestHeadersView.Category(root, 'general', Common.UIString('General')); generalCategory.hidden = false; this._urlItem = generalCategory.createLeaf(); this._requestMethodItem = generalCategory.createLeaf(); this._statusCodeItem = generalCategory.createLeaf(); this._remoteAddressItem = generalCategory.createLeaf(); this._remoteAddressItem.hidden = true; this._referrerPolicyItem = generalCategory.createLeaf(); this._referrerPolicyItem.hidden = true; this._responseHeadersCategory = new Network.RequestHeadersView.Category(root, 'responseHeaders', ''); this._requestHeadersCategory = new Network.RequestHeadersView.Category(root, 'requestHeaders', ''); this._queryStringCategory = new Network.RequestHeadersView.Category(root, 'queryString', ''); this._formDataCategory = new Network.RequestHeadersView.Category(root, 'formData', ''); this._requestPayloadCategory = new Network.RequestHeadersView.Category(root, 'requestPayload', Common.UIString('Request Payload')); } /** * @override */ wasShown() { this._clearHighlight(); this._request.addEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this._refreshRemoteAddress, this); this._request.addEventListener(SDK.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this); this._request.addEventListener( SDK.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this); this._request.addEventListener(SDK.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this); this._refreshURL(); this._refreshQueryString(); this._refreshRequestHeaders(); this._refreshResponseHeaders(); this._refreshHTTPInformation(); this._refreshRemoteAddress(); this._refreshReferrerPolicy(); } /** * @override */ willHide() { this._request.removeEventListener(SDK.NetworkRequest.Events.RemoteAddressChanged, this._refreshRemoteAddress, this); this._request.removeEventListener( SDK.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this); this._request.removeEventListener( SDK.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this); this._request.removeEventListener(SDK.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this); } /** * @param {string} name * @param {string} value * @return {!DocumentFragment} */ _formatHeader(name, value) { const fragment = createDocumentFragment(); fragment.createChild('div', 'header-name').textContent = name + ': '; fragment.createChild('span', 'header-separator'); fragment.createChild('div', 'header-value source-code').textContent = value; return fragment; } /** * @param {string} value * @param {string} className * @param {boolean} decodeParameters * @return {!Element} */ _formatParameter(value, className, decodeParameters) { let errorDecoding = false; if (decodeParameters) { value = value.replace(/\+/g, ' '); if (value.indexOf('%') >= 0) { try { value = decodeURIComponent(value); } catch (e) { errorDecoding = true; } } } const div = createElementWithClass('div', className); if (value === '') div.classList.add('empty-value'); if (errorDecoding) div.createChild('span', 'header-decode-error').textContent = Common.UIString('(unable to decode value)'); else div.textContent = value; return div; } _refreshURL() { this._urlItem.title = this._formatHeader(Common.UIString('Request URL'), this._request.url()); } _refreshQueryString() { const queryString = this._request.queryString(); const queryParameters = this._request.queryParameters; this._queryStringCategory.hidden = !queryParameters; if (queryParameters) { this._refreshParams( Common.UIString('Query String Parameters'), queryParameters, queryString, this._queryStringCategory); } } async _refreshFormData() { this._formDataCategory.hidden = true; this._requestPayloadCategory.hidden = true; const formData = await this._request.requestFormData(); if (!formData) return; const formParameters = await this._request.formParameters(); if (formParameters) { this._formDataCategory.hidden = false; this._refreshParams(Common.UIString('Form Data'), formParameters, formData, this._formDataCategory); } else { this._requestPayloadCategory.hidden = false; try { const json = JSON.parse(formData); this._refreshRequestJSONPayload(json, formData); } catch (e) { this._populateTreeElementWithSourceText(this._requestPayloadCategory, formData); } } } /** * @param {!UI.TreeElement} treeElement * @param {?string} sourceText */ _populateTreeElementWithSourceText(treeElement, sourceText) { const max_len = 3000; const text = (sourceText || '').trim(); const trim = text.length > max_len; const sourceTextElement = createElementWithClass('span', 'header-value source-code'); sourceTextElement.textContent = trim ? text.substr(0, max_len) : text; const sourceTreeElement = new UI.TreeElement(sourceTextElement); sourceTreeElement.selectable = false; treeElement.removeChildren(); treeElement.appendChild(sourceTreeElement); if (!trim) return; const showMoreButton = createElementWithClass('button', 'request-headers-show-more-button'); showMoreButton.textContent = Common.UIString('Show more'); showMoreButton.addEventListener('click', () => { showMoreButton.remove(); sourceTextElement.textContent = text; }); sourceTextElement.appendChild(showMoreButton); } /** * @param {string} title * @param {?Array.<!SDK.NetworkRequest.NameValue>} params * @param {?string} sourceText * @param {!UI.TreeElement} paramsTreeElement */ _refreshParams(title, params, sourceText, paramsTreeElement) { paramsTreeElement.removeChildren(); paramsTreeElement.listItemElement.removeChildren(); paramsTreeElement.listItemElement.createTextChild(title); const headerCount = createElementWithClass('span', 'header-count'); headerCount.textContent = Common.UIString('\u00A0(%d)', params.length); paramsTreeElement.listItemElement.appendChild(headerCount); /** * @param {!Event} event * @this {Network.RequestHeadersView} */ function toggleViewSource(event) { paramsTreeElement[Network.RequestHeadersView._viewSourceSymbol] = !paramsTreeElement[Network.RequestHeadersView._viewSourceSymbol]; this._refreshParams(title, params, sourceText, paramsTreeElement); event.consume(); } paramsTreeElement.listItemElement.appendChild(this._createViewSourceToggle( paramsTreeElement[Network.RequestHeadersView._viewSourceSymbol], toggleViewSource.bind(this))); if (paramsTreeElement[Network.RequestHeadersView._viewSourceSymbol]) { this._populateTreeElementWithSourceText(paramsTreeElement, sourceText); return; } const toggleTitle = this._decodeRequestParameters ? Common.UIString('view URL encoded') : Common.UIString('view decoded'); const toggleButton = this._createToggleButton(toggleTitle); toggleButton.addEventListener('click', this._toggleURLDecoding.bind(this), false); paramsTreeElement.listItemElement.appendChild(toggleButton); for (let i = 0; i < params.length; ++i) { const paramNameValue = createDocumentFragment(); if (params[i].name !== '') { const name = this._formatParameter(params[i].name + ': ', 'header-name', this._decodeRequestParameters); const value = this._formatParameter(params[i].value, 'header-value source-code', this._decodeRequestParameters); paramNameValue.appendChild(name); paramNameValue.createChild('span', 'header-separator'); paramNameValue.appendChild(value); } else { paramNameValue.appendChild( this._formatParameter(Common.UIString('(empty)'), 'empty-request-header', this._decodeRequestParameters)); } const paramTreeElement = new UI.TreeElement(paramNameValue); paramTreeElement.selectable = false; paramsTreeElement.appendChild(paramTreeElement); } } /** * @param {*} parsedObject * @param {string} sourceText */ _refreshRequestJSONPayload(parsedObject, sourceText) { const treeElement = this._requestPayloadCategory; treeElement.removeChildren(); const listItem = this._requestPayloadCategory.listItemElement; listItem.removeChildren(); listItem.createTextChild(this._requestPayloadCategory.title); /** * @param {!Event} event * @this {Network.RequestHeadersView} */ function toggleViewSource(event) { treeElement[Network.RequestHeadersView._viewSourceSymbol] = !treeElement[Network.RequestHeadersView._viewSourceSymbol]; this._refreshRequestJSONPayload(parsedObject, sourceText); event.consume(); } listItem.appendChild(this._createViewSourceToggle( treeElement[Network.RequestHeadersView._viewSourceSymbol], toggleViewSource.bind(this))); if (treeElement[Network.RequestHeadersView._viewSourceSymbol]) { this._populateTreeElementWithSourceText(this._requestPayloadCategory, sourceText); } else { const object = SDK.RemoteObject.fromLocalObject(parsedObject); const section = new ObjectUI.ObjectPropertiesSection(object, object.description); section.expand(); section.editable = false; treeElement.appendChild(new UI.TreeElement(section.element)); } } /** * @param {boolean} viewSource * @param {function(!Event)} handler * @return {!Element} */ _createViewSourceToggle(viewSource, handler) { const viewSourceToggleTitle = viewSource ? Common.UIString('view parsed') : Common.UIString('view source'); const viewSourceToggleButton = this._createToggleButton(viewSourceToggleTitle); viewSourceToggleButton.addEventListener('click', handler, false); return viewSourceToggleButton; } /** * @param {!Event} event */ _toggleURLDecoding(event) { this._decodeRequestParameters = !this._decodeRequestParameters; this._refreshQueryString(); this._refreshFormData(); event.consume(); } _refreshRequestHeaders() { const treeElement = this._requestHeadersCategory; const headers = this._request.requestHeaders().slice(); headers.sort(function(a, b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()); }); const headersText = this._request.requestHeadersText(); if (this._showRequestHeadersText && headersText) this._refreshHeadersText(Common.UIString('Request Headers'), headers.length, headersText, treeElement); else this._refreshHeaders(Common.UIString('Request Headers'), headers, treeElement, headersText === undefined); if (headersText) { const toggleButton = this._createHeadersToggleButton(this._showRequestHeadersText); toggleButton.addEventListener('click', this._toggleRequestHeadersText.bind(this), false); treeElement.listItemElement.appendChild(toggleButton); } this._refreshFormData(); } _refreshResponseHeaders() { const treeElement = this._responseHeadersCategory; const headers = this._request.sortedResponseHeaders.slice(); const headersText = this._request.responseHeadersText; if (this._showResponseHeadersText) this._refreshHeadersText(Common.UIString('Response Headers'), headers.length, headersText, treeElement); else this._refreshHeaders(Common.UIString('Response Headers'), headers, treeElement); if (headersText) { const toggleButton = this._createHeadersToggleButton(this._showResponseHeadersText); toggleButton.addEventListener('click', this._toggleResponseHeadersText.bind(this), false); treeElement.listItemElement.appendChild(toggleButton); } } _refreshHTTPInformation() { const requestMethodElement = this._requestMethodItem; requestMethodElement.hidden = !this._request.statusCode; const statusCodeElement = this._statusCodeItem; statusCodeElement.hidden = !this._request.statusCode; if (this._request.statusCode) { const statusCodeFragment = createDocumentFragment(); statusCodeFragment.createChild('div', 'header-name').textContent = Common.UIString('Status Code') + ': '; statusCodeFragment.createChild('span', 'header-separator'); const statusCodeImage = statusCodeFragment.createChild('label', 'resource-status-image', 'dt-icon-label'); statusCodeImage.title = this._request.statusCode + ' ' + this._request.statusText; if (this._request.statusCode < 300 || this._request.statusCode === 304) statusCodeImage.type = 'smallicon-green-ball'; else if (this._request.statusCode < 400) statusCodeImage.type = 'smallicon-orange-ball'; else statusCodeImage.type = 'smallicon-red-ball'; requestMethodElement.title = this._formatHeader(Common.UIString('Request Method'), this._request.requestMethod); const statusTextElement = statusCodeFragment.createChild('div', 'header-value source-code'); let statusText = this._request.statusCode + ' ' + this._request.statusText; if (this._request.fetchedViaServiceWorker) { statusText += ' ' + Common.UIString('(from ServiceWorker)'); statusTextElement.classList.add('status-from-cache'); } else if (this._request.redirectSource() && this._request.redirectSource().signedExchangeInfo()) { statusText += ' ' + Common.UIString('(from signed-exchange)'); statusTextElement.classList.add('status-from-cache'); } else if (this._request.cached()) { if (this._request.cachedInMemory()) statusText += ' ' + Common.UIString('(from memory cache)'); else statusText += ' ' + Common.UIString('(from disk cache)'); statusTextElement.classList.add('status-from-cache'); } statusTextElement.textContent = statusText; statusCodeElement.title = statusCodeFragment; } } /** * @param {string} title * @param {!UI.TreeElement} headersTreeElement * @param {number} headersLength */ _refreshHeadersTitle(title, headersTreeElement, headersLength) { headersTreeElement.listItemElement.removeChildren(); headersTreeElement.listItemElement.createTextChild(title); const headerCount = Common.UIString('\u00A0(%d)', headersLength); headersTreeElement.listItemElement.createChild('span', 'header-count').textContent = headerCount; } /** * @param {string} title * @param {!Array.<!SDK.NetworkRequest.NameValue>} headers * @param {!UI.TreeElement} headersTreeElement * @param {boolean=} provisionalHeaders */ _refreshHeaders(title, headers, headersTreeElement, provisionalHeaders) { headersTreeElement.removeChildren(); const length = headers.length; this._refreshHeadersTitle(title, headersTreeElement, length); if (provisionalHeaders) { const cautionText = Common.UIString('Provisional headers are shown'); const cautionFragment = createDocumentFragment(); cautionFragment.createChild('label', '', 'dt-icon-label').type = 'smallicon-warning'; cautionFragment.createChild('div', 'caution').textContent = cautionText; const cautionTreeElement = new UI.TreeElement(cautionFragment); cautionTreeElement.selectable = false; headersTreeElement.appendChild(cautionTreeElement); } headersTreeElement.hidden = !length && !provisionalHeaders; for (let i = 0; i < length; ++i) { const headerTreeElement = new UI.TreeElement(this._formatHeader(headers[i].name, headers[i].value)); headerTreeElement.selectable = false; headersTreeElement.appendChild(headerTreeElement); headerTreeElement[Network.RequestHeadersView._headerNameSymbol] = headers[i].name; } } /** * @param {string} title * @param {number} count * @param {string} headersText * @param {!UI.TreeElement} headersTreeElement */ _refreshHeadersText(title, count, headersText, headersTreeElement) { this._populateTreeElementWithSourceText(headersTreeElement, headersText); this._refreshHeadersTitle(title, headersTreeElement, count); } _refreshRemoteAddress() { const remoteAddress = this._request.remoteAddress(); const treeElement = this._remoteAddressItem; treeElement.hidden = !remoteAddress; if (remoteAddress) treeElement.title = this._formatHeader(Common.UIString('Remote Address'), remoteAddress); } _refreshReferrerPolicy() { const referrerPolicy = this._request.referrerPolicy(); const treeElement = this._referrerPolicyItem; treeElement.hidden = !referrerPolicy; if (referrerPolicy) treeElement.title = this._formatHeader(Common.UIString('Referrer Policy'), referrerPolicy); } /** * @param {!Event} event */ _toggleRequestHeadersText(event) { this._showRequestHeadersText = !this._showRequestHeadersText; this._refreshRequestHeaders(); event.consume(); } /** * @param {!Event} event */ _toggleResponseHeadersText(event) { this._showResponseHeadersText = !this._showResponseHeadersText; this._refreshResponseHeaders(); event.consume(); } /** * @param {string} title * @return {!Element} */ _createToggleButton(title) { const button = createElementWithClass('span', 'header-toggle'); button.textContent = title; return button; } /** * @param {boolean} isHeadersTextShown * @return {!Element} */ _createHeadersToggleButton(isHeadersTextShown) { const toggleTitle = isHeadersTextShown ? Common.UIString('view parsed') : Common.UIString('view source'); return this._createToggleButton(toggleTitle); } _clearHighlight() { if (this._highlightedElement) this._highlightedElement.listItemElement.classList.remove('header-highlight'); this._highlightedElement = null; } /** * @param {?UI.TreeElement} category * @param {string} name */ _revealAndHighlight(category, name) { this._clearHighlight(); for (const element of category.children()) { if (element[Network.RequestHeadersView._headerNameSymbol] !== name) continue; this._highlightedElement = element; element.reveal(); element.listItemElement.classList.add('header-highlight'); return; } } /** * @param {string} header */ revealRequestHeader(header) { this._revealAndHighlight(this._requestHeadersCategory, header); } /** * @param {string} header */ revealResponseHeader(header) { this._revealAndHighlight(this._responseHeadersCategory, header); } }; Network.RequestHeadersView._headerNameSymbol = Symbol('HeaderName'); Network.RequestHeadersView._viewSourceSymbol = Symbol('ViewSource'); /** * @unrestricted */ Network.RequestHeadersView.Category = class extends UI.TreeElement { /** * @param {!UI.TreeOutline} root * @param {string} name * @param {string=} title */ constructor(root, name, title) { super(title || '', true); this.selectable = false; this.toggleOnClick = true; this.hidden = true; this._expandedSetting = Common.settings.createSetting('request-info-' + name + '-category-expanded', true); this.expanded = this._expandedSetting.get(); root.appendChild(this); } /** * @return {!UI.TreeElement} */ createLeaf() { const leaf = new UI.TreeElement(); leaf.selectable = false; this.appendChild(leaf); return leaf; } /** * @override */ onexpand() { this._expandedSetting.set(true); } /** * @override */ oncollapse() { this._expandedSetting.set(false); } };