[go: nahoru, domu]

blob: 9ede49d5aac9632e423b33fb3fc75dcc06ce062d [file] [log] [blame]
/*
* 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;