blob: 8a6509466d7d1eb1e19394c4a46c3bccd1fd56f7 [file] [log] [blame]
/*
* Copyright (C) 2008 Apple 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:
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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.
*/
/**
* @constructor
* @extends {WebInspector.Object}
* @implements {WebInspector.ContentProvider}
* @param {string} scriptId
* @param {string} sourceURL
* @param {number} startLine
* @param {number} startColumn
* @param {number} endLine
* @param {number} endColumn
* @param {boolean} isContentScript
* @param {string=} sourceMapURL
* @param {boolean=} hasSourceURL
*/
WebInspector.Script = function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
{
this.scriptId = scriptId;
this.sourceURL = sourceURL;
this.lineOffset = startLine;
this.columnOffset = startColumn;
this.endLine = endLine;
this.endColumn = endColumn;
this.isContentScript = isContentScript;
this.sourceMapURL = sourceMapURL;
this.hasSourceURL = hasSourceURL;
/** @type {!Set.<!WebInspector.Script.Location>} */
this._locations = new Set();
/** @type {!Array.<!WebInspector.SourceMapping>} */
this._sourceMappings = [];
}
WebInspector.Script.Events = {
ScriptEdited: "ScriptEdited",
}
WebInspector.Script.snippetSourceURLPrefix = "snippets:///";
WebInspector.Script.prototype = {
/**
* @return {string}
*/
contentURL: function()
{
return this.sourceURL;
},
/**
* @return {WebInspector.ResourceType}
*/
contentType: function()
{
return WebInspector.resourceTypes.Script;
},
/**
* @param {function(?string,boolean,string)} callback
*/
requestContent: function(callback)
{
if (this._source) {
callback(this._source, false, "text/javascript");
return;
}
/**
* @this {WebInspector.Script}
* @param {?Protocol.Error} error
* @param {string} source
*/
function didGetScriptSource(error, source)
{
this._source = error ? "" : source;
callback(this._source, false, "text/javascript");
}
if (this.scriptId) {
// Script failed to parse.
DebuggerAgent.getScriptSource(this.scriptId, didGetScriptSource.bind(this));
} else
callback("", false, "text/javascript");
},
/**
* @param {string} query
* @param {boolean} caseSensitive
* @param {boolean} isRegex
* @param {function(Array.<PageAgent.SearchMatch>)} callback
*/
searchInContent: function(query, caseSensitive, isRegex, callback)
{
/**
* @this {WebInspector.Script}
* @param {?Protocol.Error} error
* @param {Array.<PageAgent.SearchMatch>} searchMatches
*/
function innerCallback(error, searchMatches)
{
if (error)
console.error(error);
var result = [];
for (var i = 0; i < searchMatches.length; ++i) {
var searchMatch = new WebInspector.ContentProvider.SearchMatch(searchMatches[i].lineNumber, searchMatches[i].lineContent);
result.push(searchMatch);
}
callback(result || []);
}
if (this.scriptId) {
// Script failed to parse.
DebuggerAgent.searchInContent(this.scriptId, query, caseSensitive, isRegex, innerCallback.bind(this));
} else
callback([]);
},
/**
* @param {string} newSource
* @param {function(?Protocol.Error, DebuggerAgent.SetScriptSourceError=, Array.<DebuggerAgent.CallFrame>=, boolean=)} callback
*/
editSource: function(newSource, callback)
{
/**
* @this {WebInspector.Script}
* @param {?Protocol.Error} error
* @param {DebuggerAgent.SetScriptSourceError=} errorData
* @param {Array.<DebuggerAgent.CallFrame>=} callFrames
* @param {Object=} debugData
*/
function didEditScriptSource(error, errorData, callFrames, debugData)
{
// FIXME: support debugData.stack_update_needs_step_in flag by calling WebInspector.debugger_model.callStackModified
if (!error)
this._source = newSource;
var needsStepIn = !!debugData && debugData["stack_update_needs_step_in"] === true;
callback(error, errorData, callFrames, needsStepIn);
if (!error)
this.dispatchEventToListeners(WebInspector.Script.Events.ScriptEdited, newSource);
}
if (this.scriptId) {
// Script failed to parse.
DebuggerAgent.setScriptSource(this.scriptId, newSource, undefined, didEditScriptSource.bind(this));
} else
callback("Script failed to parse");
},
/**
* @return {boolean}
*/
isInlineScript: function()
{
var startsAtZero = !this.lineOffset && !this.columnOffset;
return !!this.sourceURL && !startsAtZero;
},
/**
* @return {boolean}
*/
isAnonymousScript: function()
{
return !this.sourceURL;
},
/**
* @return {boolean}
*/
isSnippet: function()
{
return !!this.sourceURL && this.sourceURL.startsWith(WebInspector.Script.snippetSourceURLPrefix);
},
/**
* @param {number} lineNumber
* @param {number=} columnNumber
* @return {WebInspector.UILocation}
*/
rawLocationToUILocation: function(lineNumber, columnNumber)
{
var uiLocation;
var rawLocation = new WebInspector.DebuggerModel.Location(this.scriptId, lineNumber, columnNumber || 0);
for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
console.assert(uiLocation, "Script raw location can not be mapped to any ui location.");
return uiLocation.uiSourceCode.overrideLocation(uiLocation);
},
/**
* @param {!WebInspector.SourceMapping} sourceMapping
*/
pushSourceMapping: function(sourceMapping)
{
this._sourceMappings.push(sourceMapping);
this.updateLocations();
},
updateLocations: function()
{
var items = this._locations.items();
for (var i = 0; i < items.length; ++i)
items[i].update();
},
/**
* @param {WebInspector.DebuggerModel.Location} rawLocation
* @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
* @return {WebInspector.Script.Location}
*/
createLiveLocation: function(rawLocation, updateDelegate)
{
console.assert(rawLocation.scriptId === this.scriptId);
var location = new WebInspector.Script.Location(this, rawLocation, updateDelegate);
this._locations.add(location);
location.update();
return location;
},
__proto__: WebInspector.Object.prototype
}
/**
* @constructor
* @extends {WebInspector.LiveLocation}
* @param {WebInspector.Script} script
* @param {WebInspector.DebuggerModel.Location} rawLocation
* @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
*/
WebInspector.Script.Location = function(script, rawLocation, updateDelegate)
{
WebInspector.LiveLocation.call(this, rawLocation, updateDelegate);
this._script = script;
}
WebInspector.Script.Location.prototype = {
/**
* @return {WebInspector.UILocation}
*/
uiLocation: function()
{
var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this.rawLocation());
return this._script.rawLocationToUILocation(debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber);
},
dispose: function()
{
WebInspector.LiveLocation.prototype.dispose.call(this);
this._script._locations.remove(this);
},
__proto__: WebInspector.LiveLocation.prototype
}