UNPKG

chrome-devtools-frontend

Version:
209 lines (197 loc) • 8.35 kB
/* * Copyright (C) 2011 Google Inc. All rights reserved. * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> * Copyright (C) 2009 Joseph Pecoraro * * 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. */ import * as Bindings from '../bindings/bindings.js'; import * as i18n from '../i18n/i18n.js'; import * as SDK from '../sdk/sdk.js'; // eslint-disable-line no-unused-vars import * as UI from '../ui/ui.js'; import {Linkifier} from './Linkifier.js'; export const UIStrings = { /** *@description Text to stop preventing the debugger from stepping into library code */ removeFromIgnore: 'Remove from ignore list', /** *@description Text for scripts that should not be stepped into when debugging */ addToIgnore: 'Add script to ignore list', /** *@description Show all link text content in JSPresentation Utils */ showMoreFrame: 'Show 1 more frame', /** *@description Show all link text content in JSPresentation Utils *@example {2} PH1 */ showSMoreFrames: 'Show {PH1} more frames', /** *@description Text indicating that source url of a link is currently unknown */ unknownSource: 'unknown', }; const str_ = i18n.i18n.registerUIStrings('components/JSPresentationUtils.js', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); /** * @param {?SDK.SDKModel.Target} target * @param {!Linkifier} linkifier * @param {!Options=} options * @return {{element: !HTMLElement, links: !Array<!HTMLElement>}} */ export function buildStackTracePreviewContents(target, linkifier, options = { stackTrace: undefined, contentUpdated: undefined, tabStops: undefined }) { const {stackTrace, contentUpdated, tabStops} = options; const element = /** @type {!HTMLElement} */ (document.createElement('span')); element.classList.add('monospace'); element.style.display = 'inline-block'; const shadowRoot = UI.Utils.createShadowRootWithCoreStyles( element, {cssFile: 'components/jsUtils.css', enableLegacyPatching: false, delegatesFocus: undefined}); const contentElement = shadowRoot.createChild('table', 'stack-preview-container'); let totalHiddenCallFramesCount = 0; let totalCallFramesCount = 0; /** @type {!Array<!HTMLElement>} */ const links = []; /** * @param {!Protocol.Runtime.StackTrace} stackTrace * @return {boolean} */ function appendStackTrace(stackTrace) { let hiddenCallFrames = 0; for (const stackFrame of stackTrace.callFrames) { totalCallFramesCount++; let shouldHide = totalCallFramesCount > 30 && stackTrace.callFrames.length > 31; const row = document.createElement('tr'); row.createChild('td').textContent = '\n'; row.createChild('td', 'function-name').textContent = UI.UIUtils.beautifyFunctionName(stackFrame.functionName); const link = linkifier.maybeLinkifyConsoleCallFrame( target, stackFrame, {tabStop: Boolean(tabStops), className: undefined, columnNumber: undefined}); if (link) { link.addEventListener('contextmenu', populateContextMenu.bind(null, link)); const uiLocation = Linkifier.uiLocation(link); if (uiLocation && Bindings.IgnoreListManager.IgnoreListManager.instance().isIgnoreListedUISourceCode( uiLocation.uiSourceCode)) { shouldHide = true; } row.createChild('td').textContent = ' @ '; row.createChild('td').appendChild(link); // Linkifier is using a workaround with the 'zero width space' (\u200b). // TODO(szuend): Remove once the Linkfier is no longer using the workaround. if (!link.textContent || link.textContent === '\u200b') { link.textContent = i18nString(UIStrings.unknownSource); } links.push(link); } if (shouldHide) { row.classList.add('ignore-listed'); ++hiddenCallFrames; } contentElement.appendChild(row); } totalHiddenCallFramesCount += hiddenCallFrames; return stackTrace.callFrames.length === hiddenCallFrames; } /** * @param {!Element} link * @param {!Event} event */ function populateContextMenu(link, event) { const contextMenu = new UI.ContextMenu.ContextMenu(event); event.consume(true); const uiLocation = Linkifier.uiLocation(link); if (uiLocation && Bindings.IgnoreListManager.IgnoreListManager.instance().canIgnoreListUISourceCode(uiLocation.uiSourceCode)) { if (Bindings.IgnoreListManager.IgnoreListManager.instance().isIgnoreListedUISourceCode(uiLocation.uiSourceCode)) { contextMenu.debugSection().appendItem( i18nString(UIStrings.removeFromIgnore), () => Bindings.IgnoreListManager.IgnoreListManager.instance().unIgnoreListUISourceCode( uiLocation.uiSourceCode)); } else { contextMenu.debugSection().appendItem( i18nString(UIStrings.addToIgnore), () => Bindings.IgnoreListManager.IgnoreListManager.instance().ignoreListUISourceCode( uiLocation.uiSourceCode)); } } contextMenu.appendApplicableItems(event); contextMenu.show(); } if (!stackTrace) { return {element, links}; } appendStackTrace(stackTrace); let asyncStackTrace = stackTrace.parent; while (asyncStackTrace) { if (!asyncStackTrace.callFrames.length) { asyncStackTrace = asyncStackTrace.parent; continue; } const row = contentElement.createChild('tr'); row.createChild('td').textContent = '\n'; row.createChild('td', 'stack-preview-async-description').textContent = UI.UIUtils.asyncStackTraceLabel(asyncStackTrace.description); row.createChild('td'); row.createChild('td'); if (appendStackTrace(asyncStackTrace)) { row.classList.add('ignore-listed'); } asyncStackTrace = asyncStackTrace.parent; } if (totalHiddenCallFramesCount) { const row = contentElement.createChild('tr', 'show-ignore-listed-link'); row.createChild('td').textContent = '\n'; const cell = /** @type {!HTMLTableCellElement} */ (row.createChild('td')); cell.colSpan = 4; const showAllLink = cell.createChild('span', 'link'); if (totalHiddenCallFramesCount === 1) { showAllLink.textContent = i18nString(UIStrings.showMoreFrame); } else { showAllLink.textContent = i18nString(UIStrings.showSMoreFrames, {PH1: totalHiddenCallFramesCount}); } showAllLink.addEventListener('click', () => { contentElement.classList.add('show-ignore-listed'); if (contentUpdated) { contentUpdated(); } }, false); } return {element, links}; } /** * @typedef {{ * stackTrace: (!Protocol.Runtime.StackTrace|undefined), * contentUpdated: ((function(): *) | undefined), * tabStops: (boolean|undefined) * }} */ // @ts-ignore typedef export let Options;