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
438 lines
15 KiB
JavaScript
438 lines
15 KiB
JavaScript
/*
|
|
* Copyright (C) 2012 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:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * 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.
|
|
* * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
|
* OWNER OR 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.
|
|
*/
|
|
/**
|
|
* @implements {Bindings.DebuggerSourceMapping}
|
|
* @unrestricted
|
|
*/
|
|
Bindings.ResourceScriptMapping = class {
|
|
/**
|
|
* @param {!SDK.DebuggerModel} debuggerModel
|
|
* @param {!Workspace.Workspace} workspace
|
|
* @param {!Bindings.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
|
|
*/
|
|
constructor(debuggerModel, workspace, debuggerWorkspaceBinding) {
|
|
this._debuggerModel = debuggerModel;
|
|
this._workspace = workspace;
|
|
this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
|
|
/** @type {!Map.<!Workspace.UISourceCode, !Bindings.ResourceScriptFile>} */
|
|
this._uiSourceCodeToScriptFile = new Map();
|
|
|
|
/** @type {!Map<string, !Bindings.ContentProviderBasedProject>} */
|
|
this._projects = new Map();
|
|
|
|
/** @type {!Set<!SDK.Script>} */
|
|
this._acceptedScripts = new Set();
|
|
const runtimeModel = debuggerModel.runtimeModel();
|
|
this._eventListeners = [
|
|
this._debuggerModel.addEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this),
|
|
this._debuggerModel.addEventListener(
|
|
SDK.DebuggerModel.Events.FailedToParseScriptSource, this._parsedScriptSource, this),
|
|
this._debuggerModel.addEventListener(
|
|
SDK.DebuggerModel.Events.GlobalObjectCleared, this._globalObjectCleared, this),
|
|
runtimeModel.addEventListener(
|
|
SDK.RuntimeModel.Events.ExecutionContextDestroyed, this._executionContextDestroyed, this),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @return {!Bindings.ContentProviderBasedProject}
|
|
*/
|
|
_project(script) {
|
|
const frameId = script[Bindings.ResourceScriptMapping._frameIdSymbol];
|
|
const prefix = script.isContentScript() ? 'js:extensions:' : 'js::';
|
|
const projectId = prefix + this._debuggerModel.target().id() + ':' + frameId;
|
|
let project = this._projects.get(projectId);
|
|
if (!project) {
|
|
const projectType =
|
|
script.isContentScript() ? Workspace.projectTypes.ContentScripts : Workspace.projectTypes.Network;
|
|
project = new Bindings.ContentProviderBasedProject(
|
|
this._workspace, projectId, projectType, '' /* displayName */, false /* isServiceProject */);
|
|
Bindings.NetworkProject.setTargetForProject(project, this._debuggerModel.target());
|
|
this._projects.set(projectId, project);
|
|
}
|
|
return project;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.DebuggerModel.Location} rawLocation
|
|
* @return {?Workspace.UILocation}
|
|
*/
|
|
rawLocationToUILocation(rawLocation) {
|
|
const script = rawLocation.script();
|
|
if (!script)
|
|
return null;
|
|
const project = this._project(script);
|
|
const uiSourceCode = project.uiSourceCodeForURL(script.sourceURL);
|
|
if (!uiSourceCode)
|
|
return null;
|
|
const scriptFile = this._uiSourceCodeToScriptFile.get(uiSourceCode);
|
|
if (!scriptFile)
|
|
return null;
|
|
if ((scriptFile.hasDivergedFromVM() && !scriptFile.isMergingToVM()) || scriptFile.isDivergingFromVM())
|
|
return null;
|
|
if (!scriptFile._hasScripts([script]))
|
|
return null;
|
|
const lineNumber = rawLocation.lineNumber - (script.isInlineScriptWithSourceURL() ? script.lineOffset : 0);
|
|
let columnNumber = rawLocation.columnNumber || 0;
|
|
if (script.isInlineScriptWithSourceURL() && !lineNumber && columnNumber)
|
|
columnNumber -= script.columnOffset;
|
|
return uiSourceCode.uiLocation(lineNumber, columnNumber);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {?SDK.DebuggerModel.Location}
|
|
*/
|
|
uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber) {
|
|
const scriptFile = this._uiSourceCodeToScriptFile.get(uiSourceCode);
|
|
if (!scriptFile)
|
|
return null;
|
|
const script = scriptFile._script;
|
|
if (script.isInlineScriptWithSourceURL()) {
|
|
return this._debuggerModel.createRawLocation(
|
|
script, lineNumber + script.lineOffset, lineNumber ? columnNumber : columnNumber + script.columnOffset);
|
|
}
|
|
return this._debuggerModel.createRawLocation(script, lineNumber, columnNumber);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
* @return {boolean}
|
|
*/
|
|
_acceptsScript(script) {
|
|
if (!script.sourceURL || script.isLiveEdit() || (script.isInlineScript() && !script.hasSourceURL))
|
|
return false;
|
|
// Filter out embedder injected content scripts.
|
|
if (script.isContentScript() && !script.hasSourceURL) {
|
|
const parsedURL = new Common.ParsedURL(script.sourceURL);
|
|
if (!parsedURL.isValid)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_parsedScriptSource(event) {
|
|
const script = /** @type {!SDK.Script} */ (event.data);
|
|
if (!this._acceptsScript(script))
|
|
return;
|
|
this._acceptedScripts.add(script);
|
|
const originalContentProvider = script.originalContentProvider();
|
|
const frameId = Bindings.frameIdForScript(script);
|
|
script[Bindings.ResourceScriptMapping._frameIdSymbol] = frameId;
|
|
|
|
const url = script.sourceURL;
|
|
const project = this._project(script);
|
|
|
|
// Remove previous UISourceCode, if any
|
|
const oldUISourceCode = project.uiSourceCodeForURL(url);
|
|
if (oldUISourceCode) {
|
|
const scriptFile = this._uiSourceCodeToScriptFile.get(oldUISourceCode);
|
|
this._removeScript(scriptFile._script);
|
|
}
|
|
|
|
// Create UISourceCode.
|
|
const uiSourceCode = project.createUISourceCode(url, originalContentProvider.contentType());
|
|
Bindings.NetworkProject.setInitialFrameAttribution(uiSourceCode, frameId);
|
|
const metadata = Bindings.metadataForURL(this._debuggerModel.target(), frameId, url);
|
|
|
|
// Bind UISourceCode to scripts.
|
|
const scriptFile = new Bindings.ResourceScriptFile(this, uiSourceCode, [script]);
|
|
this._uiSourceCodeToScriptFile.set(uiSourceCode, scriptFile);
|
|
|
|
project.addUISourceCodeWithProvider(uiSourceCode, originalContentProvider, metadata, 'text/javascript');
|
|
this._debuggerWorkspaceBinding.updateLocations(script);
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @return {?Bindings.ResourceScriptFile}
|
|
*/
|
|
scriptFile(uiSourceCode) {
|
|
return this._uiSourceCodeToScriptFile.get(uiSourceCode) || null;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script} script
|
|
*/
|
|
_removeScript(script) {
|
|
if (!this._acceptedScripts.has(script))
|
|
return;
|
|
this._acceptedScripts.delete(script);
|
|
const project = this._project(script);
|
|
const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (project.uiSourceCodeForURL(script.sourceURL));
|
|
const scriptFile = this._uiSourceCodeToScriptFile.get(uiSourceCode);
|
|
scriptFile.dispose();
|
|
this._uiSourceCodeToScriptFile.delete(uiSourceCode);
|
|
project.removeFile(script.sourceURL);
|
|
this._debuggerWorkspaceBinding.updateLocations(script);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_executionContextDestroyed(event) {
|
|
const executionContext = /** @type {!SDK.ExecutionContext} */ (event.data);
|
|
const scripts = this._debuggerModel.scriptsForExecutionContext(executionContext);
|
|
for (const script of scripts)
|
|
this._removeScript(script);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_globalObjectCleared(event) {
|
|
const scripts = Array.from(this._acceptedScripts);
|
|
for (const script of scripts)
|
|
this._removeScript(script);
|
|
}
|
|
|
|
resetForTest() {
|
|
const scripts = Array.from(this._acceptedScripts);
|
|
for (const script of scripts)
|
|
this._removeScript(script);
|
|
}
|
|
|
|
dispose() {
|
|
Common.EventTarget.removeEventListeners(this._eventListeners);
|
|
const scripts = Array.from(this._acceptedScripts);
|
|
for (const script of scripts)
|
|
this._removeScript(script);
|
|
for (const project of this._projects.values())
|
|
project.removeProject();
|
|
this._projects.clear();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
Bindings.ResourceScriptFile = class extends Common.Object {
|
|
/**
|
|
* @param {!Bindings.ResourceScriptMapping} resourceScriptMapping
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {!Array.<!SDK.Script>} scripts
|
|
*/
|
|
constructor(resourceScriptMapping, uiSourceCode, scripts) {
|
|
super();
|
|
console.assert(scripts.length);
|
|
|
|
this._resourceScriptMapping = resourceScriptMapping;
|
|
this._uiSourceCode = uiSourceCode;
|
|
|
|
if (this._uiSourceCode.contentType().isScript())
|
|
this._script = scripts[scripts.length - 1];
|
|
|
|
this._uiSourceCode.addEventListener(
|
|
Workspace.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
|
|
this._uiSourceCode.addEventListener(
|
|
Workspace.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
|
|
}
|
|
|
|
/**
|
|
* @param {!Array.<!SDK.Script>} scripts
|
|
* @return {boolean}
|
|
*/
|
|
_hasScripts(scripts) {
|
|
return this._script && this._script === scripts[0];
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
_isDiverged() {
|
|
if (this._uiSourceCode.isDirty())
|
|
return true;
|
|
if (!this._script)
|
|
return false;
|
|
if (typeof this._scriptSource === 'undefined')
|
|
return false;
|
|
const workingCopy = this._uiSourceCode.workingCopy();
|
|
if (!workingCopy)
|
|
return false;
|
|
|
|
// Match ignoring sourceURL.
|
|
if (!workingCopy.startsWith(this._scriptSource.trimRight()))
|
|
return true;
|
|
const suffix = this._uiSourceCode.workingCopy().substr(this._scriptSource.length);
|
|
return !!suffix.length && !suffix.match(SDK.Script.sourceURLRegex);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_workingCopyChanged(event) {
|
|
this._update();
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_workingCopyCommitted(event) {
|
|
if (this._uiSourceCode.project().canSetFileContent())
|
|
return;
|
|
if (!this._script)
|
|
return;
|
|
const debuggerModel = this._resourceScriptMapping._debuggerModel;
|
|
const breakpoints = Bindings.breakpointManager.breakpointsForUISourceCode(this._uiSourceCode);
|
|
const source = this._uiSourceCode.workingCopy();
|
|
debuggerModel.setScriptSource(this._script.scriptId, source, scriptSourceWasSet.bind(this));
|
|
|
|
/**
|
|
* @param {?string} error
|
|
* @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails
|
|
* @this {Bindings.ResourceScriptFile}
|
|
*/
|
|
async function scriptSourceWasSet(error, exceptionDetails) {
|
|
if (!error && !exceptionDetails)
|
|
this._scriptSource = source;
|
|
this._update();
|
|
|
|
if (!error && !exceptionDetails) {
|
|
// Live edit can cause breakpoints to be in the wrong position, or to be lost altogether.
|
|
// If any breakpoints were in the pre-live edit script, they need to be re-added.
|
|
breakpoints.map(breakpoint => breakpoint.refreshInDebugger());
|
|
return;
|
|
}
|
|
if (!exceptionDetails) {
|
|
Common.console.addMessage(Common.UIString('LiveEdit failed: %s', error), Common.Console.MessageLevel.Warning);
|
|
return;
|
|
}
|
|
const messageText = Common.UIString('LiveEdit compile failed: %s', exceptionDetails.text);
|
|
this._uiSourceCode.addLineMessage(
|
|
Workspace.UISourceCode.Message.Level.Error, messageText, exceptionDetails.lineNumber,
|
|
exceptionDetails.columnNumber);
|
|
}
|
|
}
|
|
|
|
_update() {
|
|
if (this._isDiverged() && !this._hasDivergedFromVM)
|
|
this._divergeFromVM();
|
|
else if (!this._isDiverged() && this._hasDivergedFromVM)
|
|
this._mergeToVM();
|
|
}
|
|
|
|
_divergeFromVM() {
|
|
this._isDivergingFromVM = true;
|
|
this._resourceScriptMapping._debuggerWorkspaceBinding.updateLocations(this._script);
|
|
delete this._isDivergingFromVM;
|
|
this._hasDivergedFromVM = true;
|
|
this.dispatchEventToListeners(Bindings.ResourceScriptFile.Events.DidDivergeFromVM, this._uiSourceCode);
|
|
}
|
|
|
|
_mergeToVM() {
|
|
delete this._hasDivergedFromVM;
|
|
this._isMergingToVM = true;
|
|
this._resourceScriptMapping._debuggerWorkspaceBinding.updateLocations(this._script);
|
|
delete this._isMergingToVM;
|
|
this.dispatchEventToListeners(Bindings.ResourceScriptFile.Events.DidMergeToVM, this._uiSourceCode);
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
hasDivergedFromVM() {
|
|
return this._hasDivergedFromVM;
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isDivergingFromVM() {
|
|
return this._isDivergingFromVM;
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isMergingToVM() {
|
|
return this._isMergingToVM;
|
|
}
|
|
|
|
checkMapping() {
|
|
if (!this._script || typeof this._scriptSource !== 'undefined') {
|
|
this._mappingCheckedForTest();
|
|
return;
|
|
}
|
|
this._script.requestContent().then(callback.bind(this));
|
|
|
|
/**
|
|
* @param {?string} source
|
|
* @this {Bindings.ResourceScriptFile}
|
|
*/
|
|
function callback(source) {
|
|
this._scriptSource = source;
|
|
this._update();
|
|
this._mappingCheckedForTest();
|
|
}
|
|
}
|
|
|
|
_mappingCheckedForTest() {
|
|
}
|
|
|
|
dispose() {
|
|
this._uiSourceCode.removeEventListener(
|
|
Workspace.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
|
|
this._uiSourceCode.removeEventListener(
|
|
Workspace.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
|
|
}
|
|
|
|
/**
|
|
* @param {string} sourceMapURL
|
|
*/
|
|
addSourceMapURL(sourceMapURL) {
|
|
if (!this._script)
|
|
return;
|
|
this._script.debuggerModel.setSourceMapURL(this._script, sourceMapURL);
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
hasSourceMapURL() {
|
|
return this._script && !!this._script.sourceMapURL;
|
|
}
|
|
};
|
|
|
|
Bindings.ResourceScriptMapping._frameIdSymbol = Symbol('frameid');
|
|
|
|
/** @enum {symbol} */
|
|
Bindings.ResourceScriptFile.Events = {
|
|
DidMergeToVM: Symbol('DidMergeToVM'),
|
|
DidDivergeFromVM: Symbol('DidDivergeFromVM'),
|
|
};
|