blob: f151676f328359d5fdff91e0aabecca6e1489f87 [file] [log] [blame]
// 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.
/**
* @constructor
* @implements {WebInspector.TargetManager.Observer}
* @param {!WebInspector.TargetManager} targetManager
* @param {!WebInspector.Workspace} workspace
* @param {!WebInspector.NetworkMapping} networkMapping
*/
WebInspector.DebuggerWorkspaceBinding = function(targetManager, workspace, networkMapping)
{
this._workspace = workspace;
this._networkMapping = networkMapping;
// FIXME: Migrate from _targetToData to _debuggerModelToData.
/** @type {!Map.<!WebInspector.Target, !WebInspector.DebuggerWorkspaceBinding.TargetData>} */
this._targetToData = new Map();
targetManager.observeTargets(this);
targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._globalObjectCleared, this);
targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.BeforeDebuggerPaused, this._beforeDebuggerPaused, this);
targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
}
WebInspector.DebuggerWorkspaceBinding.prototype = {
/**
* @override
* @param {!WebInspector.Target} target
*/
targetAdded: function(target)
{
var debuggerModel = WebInspector.DebuggerModel.fromTarget(target);
if (debuggerModel)
this._targetToData.set(target, new WebInspector.DebuggerWorkspaceBinding.TargetData(debuggerModel, this));
},
/**
* @override
* @param {!WebInspector.Target} target
*/
targetRemoved: function(target)
{
if (!WebInspector.DebuggerModel.fromTarget(target))
return;
var targetData = this._targetToData.get(target);
targetData._dispose();
this._targetToData.remove(target);
},
/**
* @param {!WebInspector.Event} event
*/
_uiSourceCodeRemoved: function(event)
{
var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
var targetDatas = this._targetToData.valuesArray();
for (var i = 0; i < targetDatas.length; ++i)
targetDatas[i]._uiSourceCodeRemoved(uiSourceCode);
},
/**
* @param {!WebInspector.Event} event
*/
_projectRemoved: function(event)
{
var project = /** @type {!WebInspector.Project} */ (event.data);
var targetDatas = this._targetToData.valuesArray();
var uiSourceCodes = project.uiSourceCodes();
for (var i = 0; i < targetDatas.length; ++i) {
for (var j = 0; j < uiSourceCodes.length; ++j)
targetDatas[i]._uiSourceCodeRemoved(uiSourceCodes[j]);
}
},
/**
* @param {!WebInspector.Script} script
* @param {!WebInspector.DebuggerSourceMapping} sourceMapping
*/
pushSourceMapping: function(script, sourceMapping)
{
var info = this._ensureInfoForScript(script);
info._pushSourceMapping(sourceMapping);
},
/**
* @param {!WebInspector.Script} script
* @return {!WebInspector.DebuggerSourceMapping}
*/
popSourceMapping: function(script)
{
var info = this._infoForScript(script.target(), script.scriptId);
console.assert(info);
return info._popSourceMapping();
},
/**
* @param {!WebInspector.Target} target
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {?WebInspector.DebuggerSourceMapping} sourceMapping
*/
setSourceMapping: function(target, uiSourceCode, sourceMapping)
{
var data = this._targetToData.get(target);
if (data)
data._setSourceMapping(uiSourceCode, sourceMapping);
},
/**
* @param {!WebInspector.Script} script
*/
updateLocations: function(script)
{
var info = this._infoForScript(script.target(), script.scriptId);
if (info)
info._updateLocations();
},
/**
* @param {!WebInspector.DebuggerModel.Location} rawLocation
* @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
* @return {!WebInspector.DebuggerWorkspaceBinding.Location}
*/
createLiveLocation: function(rawLocation, updateDelegate)
{
var info = this._infoForScript(rawLocation.target(), rawLocation.scriptId);
console.assert(info);
var location = new WebInspector.DebuggerWorkspaceBinding.Location(info._script, rawLocation, this, updateDelegate);
info._addLocation(location);
return location;
},
/**
* @param {!WebInspector.DebuggerModel.CallFrame} callFrame
* @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
* @return {!WebInspector.DebuggerWorkspaceBinding.Location}
*/
createCallFrameLiveLocation: function(callFrame, updateDelegate)
{
var target = callFrame.target();
this._ensureInfoForScript(callFrame.script);
var location = this.createLiveLocation(callFrame.location(), updateDelegate);
this._registerCallFrameLiveLocation(target, location);
return location;
},
/**
* @param {!WebInspector.DebuggerModel.Location} rawLocation
* @return {!WebInspector.UILocation}
*/
rawLocationToUILocation: function(rawLocation)
{
var info = this._infoForScript(rawLocation.target(), rawLocation.scriptId);
console.assert(info);
return info._rawLocationToUILocation(rawLocation);
},
/**
* @param {!WebInspector.Target} target
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @param {number} columnNumber
* @return {?WebInspector.DebuggerModel.Location}
*/
uiLocationToRawLocation: function(target, uiSourceCode, lineNumber, columnNumber)
{
var targetData = this._targetToData.get(target);
return targetData ? /** @type {?WebInspector.DebuggerModel.Location} */ (targetData._uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber)) : null;
},
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @param {number} columnNumber
* @return {!Array.<!WebInspector.DebuggerModel.Location>}
*/
uiLocationToRawLocations: function(uiSourceCode, lineNumber, columnNumber)
{
var result = [];
var targetDatas = this._targetToData.valuesArray();
for (var i = 0; i < targetDatas.length; ++i) {
var rawLocation = targetDatas[i]._uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber);
if (rawLocation)
result.push(rawLocation);
}
return result;
},
/**
* @param {!WebInspector.UILocation} uiLocation
* @return {!WebInspector.UILocation}
*/
normalizeUILocation: function(uiLocation)
{
var target = WebInspector.NetworkProject.targetForUISourceCode(uiLocation.uiSourceCode);
if (target) {
var rawLocation = this.uiLocationToRawLocation(target, uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber);
if (rawLocation)
return this.rawLocationToUILocation(rawLocation);
}
return uiLocation;
},
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @return {boolean}
*/
uiLineHasMapping: function(uiSourceCode, lineNumber)
{
var targetDatas = this._targetToData.valuesArray();
for (var i = 0; i < targetDatas.length; ++i) {
if (!targetDatas[i]._uiLineHasMapping(uiSourceCode, lineNumber))
return false;
}
return true;
},
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {!WebInspector.Target} target
* @return {?WebInspector.ResourceScriptFile}
*/
scriptFile: function(uiSourceCode, target)
{
var targetData = this._targetToData.get(target);
return targetData ? targetData._resourceMapping.scriptFile(uiSourceCode) : null;
},
/**
* @param {!WebInspector.Event} event
*/
_globalObjectCleared: function(event)
{
var debuggerModel = /** @type {!WebInspector.DebuggerModel} */ (event.target);
this._reset(debuggerModel.target());
},
/**
* @param {!WebInspector.Target} target
*/
_reset: function(target)
{
var targetData = this._targetToData.get(target);
targetData.callFrameLocations.valuesArray().forEach(function(location) { location.dispose(); });
targetData.callFrameLocations.clear();
},
/**
* @param {!WebInspector.Script} script
* @return {!WebInspector.DebuggerWorkspaceBinding.ScriptInfo}
*/
_ensureInfoForScript: function(script)
{
var scriptDataMap = this._targetToData.get(script.target()).scriptDataMap;
var info = scriptDataMap.get(script.scriptId);
if (!info) {
info = new WebInspector.DebuggerWorkspaceBinding.ScriptInfo(script);
scriptDataMap.set(script.scriptId, info);
}
return info;
},
/**
* @param {!WebInspector.Target} target
* @param {string} scriptId
* @return {?WebInspector.DebuggerWorkspaceBinding.ScriptInfo}
*/
_infoForScript: function(target, scriptId)
{
var data = this._targetToData.get(target);
if (!data)
return null;
return data.scriptDataMap.get(scriptId) || null;
},
/**
* @param {!WebInspector.Target} target
* @param {!WebInspector.DebuggerWorkspaceBinding.Location} location
*/
_registerCallFrameLiveLocation: function(target, location)
{
var locations = this._targetToData.get(target).callFrameLocations;
locations.add(location);
},
/**
* @param {!WebInspector.DebuggerWorkspaceBinding.Location} location
*/
_removeLiveLocation: function(location)
{
var info = this._infoForScript(location._script.target(), location._script.scriptId);
if (info)
info._removeLocation(location);
},
/**
* @param {!WebInspector.Event} event
*/
_debuggerResumed: function(event)
{
var debuggerModel = /** @type {!WebInspector.DebuggerModel} */ (event.target);
this._reset(debuggerModel.target());
},
/**
* @param {!WebInspector.Event} event
*/
_beforeDebuggerPaused: function(event)
{
var rawLocation = event.data.callFrames[0].location();
var targetData = this._targetToData.get(rawLocation.target());
if (!targetData._compilerMapping.mapsToSourceCode(rawLocation)) {
event.stopPropagation();
event.preventDefault();
}
}
}
/**
* @constructor
* @param {!WebInspector.DebuggerModel} debuggerModel
* @param {!WebInspector.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
*/
WebInspector.DebuggerWorkspaceBinding.TargetData = function(debuggerModel, debuggerWorkspaceBinding)
{
this._target = debuggerModel.target();
/** @type {!Map.<string, !WebInspector.DebuggerWorkspaceBinding.ScriptInfo>} */
this.scriptDataMap = new Map();
/** @type {!Set.<!WebInspector.DebuggerWorkspaceBinding.Location>} */
this.callFrameLocations = new Set();
var workspace = debuggerWorkspaceBinding._workspace;
var networkMapping = debuggerWorkspaceBinding._networkMapping;
this._defaultMapping = new WebInspector.DefaultScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);
this._resourceMapping = new WebInspector.ResourceScriptMapping(debuggerModel, workspace, networkMapping, debuggerWorkspaceBinding);
this._compilerMapping = new WebInspector.CompilerScriptMapping(debuggerModel, workspace, networkMapping, WebInspector.NetworkProject.forTarget(this._target), debuggerWorkspaceBinding);
/** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.DebuggerSourceMapping>} */
this._uiSourceCodeToSourceMapping = new Map();
debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._parsedScriptSource, this);
}
WebInspector.DebuggerWorkspaceBinding.TargetData.prototype = {
/**
* @param {!WebInspector.Event} event
*/
_parsedScriptSource: function(event)
{
var script = /** @type {!WebInspector.Script} */ (event.data);
this._defaultMapping.addScript(script);
this._resourceMapping.addScript(script);
if (WebInspector.moduleSetting("jsSourceMapsEnabled").get())
this._compilerMapping.addScript(script);
},
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {?WebInspector.DebuggerSourceMapping} sourceMapping
*/
_setSourceMapping: function(uiSourceCode, sourceMapping)
{
if (this._uiSourceCodeToSourceMapping.get(uiSourceCode) === sourceMapping)
return;
if (sourceMapping)
this._uiSourceCodeToSourceMapping.set(uiSourceCode, sourceMapping);
else
this._uiSourceCodeToSourceMapping.remove(uiSourceCode);
uiSourceCode.dispatchEventToListeners(WebInspector.UISourceCode.Events.SourceMappingChanged, {target: this._target, isIdentity: sourceMapping ? sourceMapping.isIdentity() : false});
},
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @param {number} columnNumber
* @return {?WebInspector.DebuggerModel.Location}
*/
_uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
{
var sourceMapping = this._uiSourceCodeToSourceMapping.get(uiSourceCode);
return sourceMapping ? sourceMapping.uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber) : null;
},
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @return {boolean}
*/
_uiLineHasMapping: function(uiSourceCode, lineNumber)
{
var sourceMapping = this._uiSourceCodeToSourceMapping.get(uiSourceCode);
return sourceMapping ? sourceMapping.uiLineHasMapping(uiSourceCode, lineNumber) : true;
},
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
*/
_uiSourceCodeRemoved: function(uiSourceCode)
{
this._uiSourceCodeToSourceMapping.remove(uiSourceCode);
},
_dispose: function()
{
this._compilerMapping.dispose();
this._resourceMapping.dispose();
this._defaultMapping.dispose();
this._uiSourceCodeToSourceMapping.clear();
}
}
/**
* @constructor
* @param {!WebInspector.Script} script
*/
WebInspector.DebuggerWorkspaceBinding.ScriptInfo = function(script)
{
this._script = script;
/** @type {!Array.<!WebInspector.DebuggerSourceMapping>} */
this._sourceMappings = [];
/** @type {!Set.<!WebInspector.LiveLocation>} */
this._locations = new Set();
}
WebInspector.DebuggerWorkspaceBinding.ScriptInfo.prototype = {
/**
* @param {!WebInspector.DebuggerSourceMapping} sourceMapping
*/
_pushSourceMapping: function(sourceMapping)
{
this._sourceMappings.push(sourceMapping);
this._updateLocations();
},
/**
* @return {!WebInspector.DebuggerSourceMapping}
*/
_popSourceMapping: function()
{
var sourceMapping = this._sourceMappings.pop();
this._updateLocations();
return sourceMapping;
},
/**
* @param {!WebInspector.LiveLocation} location
*/
_addLocation: function(location)
{
this._locations.add(location);
location.update();
},
/**
* @param {!WebInspector.LiveLocation} location
*/
_removeLocation: function(location)
{
this._locations.delete(location);
},
_updateLocations: function()
{
for (var location of this._locations)
location.update();
},
/**
* @param {!WebInspector.DebuggerModel.Location} rawLocation
* @return {!WebInspector.UILocation}
*/
_rawLocationToUILocation: function(rawLocation)
{
var uiLocation;
for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
console.assert(uiLocation, "Script raw location cannot be mapped to any UI location.");
return /** @type {!WebInspector.UILocation} */ (uiLocation);
}
}
/**
* @constructor
* @extends {WebInspector.LiveLocation}
* @param {!WebInspector.Script} script
* @param {!WebInspector.DebuggerModel.Location} rawLocation
* @param {!WebInspector.DebuggerWorkspaceBinding} binding
* @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
*/
WebInspector.DebuggerWorkspaceBinding.Location = function(script, rawLocation, binding, updateDelegate)
{
WebInspector.LiveLocation.call(this, updateDelegate);
this._script = script;
this._rawLocation = rawLocation;
this._binding = binding;
}
WebInspector.DebuggerWorkspaceBinding.Location.prototype = {
/**
* @override
* @return {!WebInspector.UILocation}
*/
uiLocation: function()
{
var debuggerModelLocation = this._rawLocation;
return this._binding.rawLocationToUILocation(debuggerModelLocation);
},
dispose: function()
{
WebInspector.LiveLocation.prototype.dispose.call(this);
this._binding._removeLiveLocation(this);
},
__proto__: WebInspector.LiveLocation.prototype
}
/**
* @interface
*/
WebInspector.DebuggerSourceMapping = function()
{
}
WebInspector.DebuggerSourceMapping.prototype = {
/**
* @param {!WebInspector.DebuggerModel.Location} rawLocation
* @return {?WebInspector.UILocation}
*/
rawLocationToUILocation: function(rawLocation) { },
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @param {number} columnNumber
* @return {?WebInspector.DebuggerModel.Location}
*/
uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber) { },
/**
* @return {boolean}
*/
isIdentity: function() { },
/**
* @param {!WebInspector.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @return {boolean}
*/
uiLineHasMapping: function(uiSourceCode, lineNumber) { }
}
/**
* @type {!WebInspector.DebuggerWorkspaceBinding}
*/
WebInspector.debuggerWorkspaceBinding;