mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 21:54:47 +08:00
Move and rename files. NOAUTOREVERT=true NOPRESUBMIT=true NOTREECHECKS=true Bug: 768828 TBR=darin@chromium.org NOTRY=true Change-Id: I66d3b155808bc5bdbf237b80208e1e552bcf7f28 Reviewed-on: https://chromium-review.googlesource.com/1001153 Reviewed-by: Blink Reformat <blink-reformat@chromium.org> Commit-Queue: Blink Reformat <blink-reformat@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#549061} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 0aee4434a4dba42a42abaea9bfbc0cd196a63bc1
435 lines
13 KiB
JavaScript
435 lines
13 KiB
JavaScript
// Copyright 2014 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
/**
|
|
* @unrestricted
|
|
* @implements {SDK.SDKModelObserver<!SDK.DebuggerModel>}
|
|
*/
|
|
Bindings.BlackboxManager = class {
|
|
/**
|
|
* @param {!Bindings.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
|
|
*/
|
|
constructor(debuggerWorkspaceBinding) {
|
|
this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
|
|
|
|
SDK.targetManager.addModelListener(
|
|
SDK.DebuggerModel, SDK.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
|
|
SDK.targetManager.addModelListener(
|
|
SDK.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, this._globalObjectCleared, this);
|
|
Common.moduleSetting('skipStackFramesPattern').addChangeListener(this._patternChanged.bind(this));
|
|
Common.moduleSetting('skipContentScripts').addChangeListener(this._patternChanged.bind(this));
|
|
|
|
/** @type {!Set<function()>} */
|
|
this._listeners = new Set();
|
|
|
|
/** @type {!Map<!SDK.DebuggerModel, !Map<string, !Array<!Protocol.Debugger.ScriptPosition>>>} */
|
|
this._debuggerModelData = new Map();
|
|
/** @type {!Map<string, boolean>} */
|
|
this._isBlackboxedURLCache = new Map();
|
|
|
|
SDK.targetManager.observeModels(SDK.DebuggerModel, this);
|
|
}
|
|
|
|
/**
|
|
* @param {function()} listener
|
|
*/
|
|
addChangeListener(listener) {
|
|
this._listeners.add(listener);
|
|
}
|
|
|
|
/**
|
|
* @param {function()} listener
|
|
*/
|
|
removeChangeListener(listener) {
|
|
this._listeners.delete(listener);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.DebuggerModel} debuggerModel
|
|
*/
|
|
modelAdded(debuggerModel) {
|
|
this._setBlackboxPatterns(debuggerModel);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.DebuggerModel} debuggerModel
|
|
*/
|
|
modelRemoved(debuggerModel) {
|
|
this._debuggerModelData.delete(debuggerModel);
|
|
this._isBlackboxedURLCache.clear();
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel} debuggerModel
|
|
* @return {!Promise<boolean>}
|
|
*/
|
|
_setBlackboxPatterns(debuggerModel) {
|
|
const regexPatterns = Common.moduleSetting('skipStackFramesPattern').getAsArray();
|
|
const patterns = /** @type {!Array<string>} */ ([]);
|
|
for (const item of regexPatterns) {
|
|
if (!item.disabled && item.pattern)
|
|
patterns.push(item.pattern);
|
|
}
|
|
return debuggerModel.setBlackboxPatterns(patterns);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} location
|
|
* @return {boolean}
|
|
*/
|
|
isBlackboxedRawLocation(location) {
|
|
const script = location.script();
|
|
if (!script)
|
|
return false;
|
|
const positions = this._scriptPositions(script);
|
|
if (!positions)
|
|
return this._isBlackboxedScript(script);
|
|
const index = positions.lowerBound(location, comparator);
|
|
return !!(index % 2);
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} a
|
|
* @param {!Protocol.Debugger.ScriptPosition} b
|
|
* @return {number}
|
|
*/
|
|
function comparator(a, b) {
|
|
if (a.lineNumber !== b.lineNumber)
|
|
return a.lineNumber - b.lineNumber;
|
|
return a.columnNumber - b.columnNumber;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @return {boolean}
|
|
*/
|
|
isBlackboxedUISourceCode(uiSourceCode) {
|
|
const projectType = uiSourceCode.project().type();
|
|
const isContentScript = projectType === Workspace.projectTypes.ContentScripts;
|
|
if (isContentScript && Common.moduleSetting('skipContentScripts').get())
|
|
return true;
|
|
const url = this._uiSourceCodeURL(uiSourceCode);
|
|
return url ? this.isBlackboxedURL(url) : false;
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
* @param {boolean=} isContentScript
|
|
* @return {boolean}
|
|
*/
|
|
isBlackboxedURL(url, isContentScript) {
|
|
if (this._isBlackboxedURLCache.has(url))
|
|
return !!this._isBlackboxedURLCache.get(url);
|
|
if (isContentScript && Common.moduleSetting('skipContentScripts').get())
|
|
return true;
|
|
const regex = Common.moduleSetting('skipStackFramesPattern').asRegExp();
|
|
const isBlackboxed = regex && regex.test(url);
|
|
this._isBlackboxedURLCache.set(url, isBlackboxed);
|
|
return isBlackboxed;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @param {?SDK.SourceMap} sourceMap
|
|
* @return {!Promise<undefined>}
|
|
*/
|
|
sourceMapLoaded(script, sourceMap) {
|
|
if (!sourceMap)
|
|
return Promise.resolve();
|
|
const previousScriptState = this._scriptPositions(script);
|
|
if (!previousScriptState)
|
|
return Promise.resolve();
|
|
|
|
const hasBlackboxedMappings = sourceMap.sourceURLs().some(url => this.isBlackboxedURL(url));
|
|
const mappings = hasBlackboxedMappings ? sourceMap.mappings().slice() : [];
|
|
if (!mappings.length) {
|
|
if (previousScriptState.length > 0)
|
|
return this._setScriptState(script, []);
|
|
return Promise.resolve();
|
|
}
|
|
mappings.sort(mappingComparator);
|
|
|
|
let currentBlackboxed = false;
|
|
let isBlackboxed = false;
|
|
const positions = [];
|
|
// If content in script file begin is not mapped and one or more ranges are blackboxed then blackbox it.
|
|
if (mappings[0].lineNumber !== 0 || mappings[0].columnNumber !== 0) {
|
|
positions.push({lineNumber: 0, columnNumber: 0});
|
|
currentBlackboxed = true;
|
|
}
|
|
for (const mapping of mappings) {
|
|
if (mapping.sourceURL && currentBlackboxed !== this.isBlackboxedURL(mapping.sourceURL)) {
|
|
positions.push({lineNumber: mapping.lineNumber, columnNumber: mapping.columnNumber});
|
|
currentBlackboxed = !currentBlackboxed;
|
|
}
|
|
isBlackboxed = currentBlackboxed || isBlackboxed;
|
|
}
|
|
return this._setScriptState(script, !isBlackboxed ? [] : positions);
|
|
/**
|
|
* @param {!SDK.SourceMapEntry} a
|
|
* @param {!SDK.SourceMapEntry} b
|
|
* @return {number}
|
|
*/
|
|
function mappingComparator(a, b) {
|
|
if (a.lineNumber !== b.lineNumber)
|
|
return a.lineNumber - b.lineNumber;
|
|
return a.columnNumber - b.columnNumber;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @return {?string}
|
|
*/
|
|
_uiSourceCodeURL(uiSourceCode) {
|
|
return uiSourceCode.project().type() === Workspace.projectTypes.Debugger ? null : uiSourceCode.url();
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @return {boolean}
|
|
*/
|
|
canBlackboxUISourceCode(uiSourceCode) {
|
|
const url = this._uiSourceCodeURL(uiSourceCode);
|
|
return url ? !!this._urlToRegExpString(url) : false;
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
*/
|
|
blackboxUISourceCode(uiSourceCode) {
|
|
const url = this._uiSourceCodeURL(uiSourceCode);
|
|
if (url)
|
|
this._blackboxURL(url);
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
*/
|
|
unblackboxUISourceCode(uiSourceCode) {
|
|
const url = this._uiSourceCodeURL(uiSourceCode);
|
|
if (url)
|
|
this._unblackboxURL(url);
|
|
}
|
|
|
|
blackboxContentScripts() {
|
|
Common.moduleSetting('skipContentScripts').set(true);
|
|
}
|
|
|
|
unblackboxContentScripts() {
|
|
Common.moduleSetting('skipContentScripts').set(false);
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
*/
|
|
_blackboxURL(url) {
|
|
const regexPatterns = Common.moduleSetting('skipStackFramesPattern').getAsArray();
|
|
const regexValue = this._urlToRegExpString(url);
|
|
if (!regexValue)
|
|
return;
|
|
let found = false;
|
|
for (let i = 0; i < regexPatterns.length; ++i) {
|
|
const item = regexPatterns[i];
|
|
if (item.pattern === regexValue) {
|
|
item.disabled = false;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
regexPatterns.push({pattern: regexValue});
|
|
Common.moduleSetting('skipStackFramesPattern').setAsArray(regexPatterns);
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
*/
|
|
_unblackboxURL(url) {
|
|
let regexPatterns = Common.moduleSetting('skipStackFramesPattern').getAsArray();
|
|
const regexValue = Bindings.blackboxManager._urlToRegExpString(url);
|
|
if (!regexValue)
|
|
return;
|
|
regexPatterns = regexPatterns.filter(function(item) {
|
|
return item.pattern !== regexValue;
|
|
});
|
|
for (let i = 0; i < regexPatterns.length; ++i) {
|
|
const item = regexPatterns[i];
|
|
if (item.disabled)
|
|
continue;
|
|
try {
|
|
const regex = new RegExp(item.pattern);
|
|
if (regex.test(url))
|
|
item.disabled = true;
|
|
} catch (e) {
|
|
}
|
|
}
|
|
Common.moduleSetting('skipStackFramesPattern').setAsArray(regexPatterns);
|
|
}
|
|
|
|
_patternChanged() {
|
|
this._isBlackboxedURLCache.clear();
|
|
|
|
/** @type {!Array<!Promise>} */
|
|
const promises = [];
|
|
for (const debuggerModel of SDK.targetManager.models(SDK.DebuggerModel)) {
|
|
promises.push(this._setBlackboxPatterns(debuggerModel));
|
|
for (const script of debuggerModel.scripts())
|
|
promises.push(this._addScript(script).then(loadSourceMap.bind(this, script)));
|
|
}
|
|
Promise.all(promises).then(() => {
|
|
const listeners = Array.from(this._listeners);
|
|
for (const listener of listeners)
|
|
listener();
|
|
this._patternChangeFinishedForTests();
|
|
});
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @return {!Promise<undefined>}
|
|
* @this {Bindings.BlackboxManager}
|
|
*/
|
|
function loadSourceMap(script) {
|
|
return this.sourceMapLoaded(script, this._debuggerWorkspaceBinding.sourceMapForScript(script));
|
|
}
|
|
}
|
|
|
|
_patternChangeFinishedForTests() {
|
|
// This method is sniffed in tests.
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_globalObjectCleared(event) {
|
|
const debuggerModel = /** @type {!SDK.DebuggerModel} */ (event.data);
|
|
this._debuggerModelData.delete(debuggerModel);
|
|
this._isBlackboxedURLCache.clear();
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_parsedScriptSource(event) {
|
|
const script = /** @type {!SDK.Script} */ (event.data);
|
|
this._addScript(script);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @return {!Promise<undefined>}
|
|
*/
|
|
_addScript(script) {
|
|
if (!script.sourceURL && !script.sourceMapURL)
|
|
return Promise.resolve();
|
|
const blackboxed = this._isBlackboxedScript(script);
|
|
return this._setScriptState(script, blackboxed ? [{lineNumber: 0, columnNumber: 0}] : []);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @return {boolean}
|
|
*/
|
|
_isBlackboxedScript(script) {
|
|
return this.isBlackboxedURL(script.sourceURL, script.isContentScript());
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @return {?Array<!Protocol.Debugger.ScriptPosition>}
|
|
*/
|
|
_scriptPositions(script) {
|
|
if (this._debuggerModelData.has(script.debuggerModel))
|
|
return this._debuggerModelData.get(script.debuggerModel).get(script.scriptId) || null;
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @param {!Array<!Protocol.Debugger.ScriptPosition>} positions
|
|
*/
|
|
_setScriptPositions(script, positions) {
|
|
const debuggerModel = script.debuggerModel;
|
|
if (!this._debuggerModelData.has(debuggerModel))
|
|
this._debuggerModelData.set(debuggerModel, new Map());
|
|
this._debuggerModelData.get(debuggerModel).set(script.scriptId, positions);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @param {!Array<!Protocol.Debugger.ScriptPosition>} positions
|
|
* @return {!Promise<undefined>}
|
|
*/
|
|
_setScriptState(script, positions) {
|
|
const previousScriptState = this._scriptPositions(script);
|
|
if (previousScriptState) {
|
|
let hasChanged = false;
|
|
hasChanged = previousScriptState.length !== positions.length;
|
|
for (let i = 0; !hasChanged && i < positions.length; ++i) {
|
|
hasChanged = positions[i].lineNumber !== previousScriptState[i].lineNumber ||
|
|
positions[i].columnNumber !== previousScriptState[i].columnNumber;
|
|
}
|
|
if (!hasChanged)
|
|
return Promise.resolve();
|
|
} else {
|
|
if (positions.length === 0)
|
|
return Promise.resolve().then(updateState.bind(this, false));
|
|
}
|
|
|
|
return script.setBlackboxedRanges(positions).then(updateState.bind(this));
|
|
|
|
/**
|
|
* @param {boolean} success
|
|
* @this {Bindings.BlackboxManager}
|
|
*/
|
|
function updateState(success) {
|
|
if (success) {
|
|
this._setScriptPositions(script, positions);
|
|
this._debuggerWorkspaceBinding.updateLocations(script);
|
|
const isBlackboxed = positions.length !== 0;
|
|
if (!isBlackboxed && script.sourceMapURL)
|
|
this._debuggerWorkspaceBinding.maybeLoadSourceMap(script);
|
|
} else {
|
|
const hasPositions = !!this._scriptPositions(script);
|
|
if (!hasPositions)
|
|
this._setScriptPositions(script, []);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
* @return {string}
|
|
*/
|
|
_urlToRegExpString(url) {
|
|
const parsedURL = new Common.ParsedURL(url);
|
|
if (parsedURL.isAboutBlank() || parsedURL.isDataURL())
|
|
return '';
|
|
if (!parsedURL.isValid)
|
|
return '^' + url.escapeForRegExp() + '$';
|
|
let name = parsedURL.lastPathComponent;
|
|
if (name)
|
|
name = '/' + name;
|
|
else if (parsedURL.folderPathComponents)
|
|
name = parsedURL.folderPathComponents + '/';
|
|
if (!name)
|
|
name = parsedURL.host;
|
|
if (!name)
|
|
return '';
|
|
const scheme = parsedURL.scheme;
|
|
let prefix = '';
|
|
if (scheme && scheme !== 'http' && scheme !== 'https') {
|
|
prefix = '^' + scheme + '://';
|
|
if (scheme === 'chrome-extension')
|
|
prefix += parsedURL.host + '\\b';
|
|
prefix += '.*';
|
|
}
|
|
return prefix + name.escapeForRegExp() + (url.endsWith(name) ? '$' : '\\b');
|
|
}
|
|
};
|
|
|
|
/** @type {!Bindings.BlackboxManager} */
|
|
Bindings.blackboxManager;
|