Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 /**
     27  * @constructor
     28  * @extends {WebInspector.Object}
     29  * @implements {WebInspector.ContentProvider}
     30  * @param {string} scriptId
     31  * @param {string} sourceURL
     32  * @param {number} startLine
     33  * @param {number} startColumn
     34  * @param {number} endLine
     35  * @param {number} endColumn
     36  * @param {boolean} isContentScript
     37  * @param {string=} sourceMapURL
     38  * @param {boolean=} hasSourceURL
     39  */
     40 WebInspector.Script = function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
     41 {
     42     this.scriptId = scriptId;
     43     this.sourceURL = sourceURL;
     44     this.lineOffset = startLine;
     45     this.columnOffset = startColumn;
     46     this.endLine = endLine;
     47     this.endColumn = endColumn;
     48     this.isContentScript = isContentScript;
     49     this.sourceMapURL = sourceMapURL;
     50     this.hasSourceURL = hasSourceURL;
     51     /** @type {!Set.<!WebInspector.Script.Location>} */
     52     this._locations = new Set();
     53     /** @type {!Array.<!WebInspector.SourceMapping>} */
     54     this._sourceMappings = [];
     55 }
     56 
     57 WebInspector.Script.Events = {
     58     ScriptEdited: "ScriptEdited",
     59 }
     60 
     61 WebInspector.Script.snippetSourceURLPrefix = "snippets:///";
     62 
     63 WebInspector.Script.prototype = {
     64     /**
     65      * @return {string}
     66      */
     67     contentURL: function()
     68     {
     69         return this.sourceURL;
     70     },
     71 
     72     /**
     73      * @return {!WebInspector.ResourceType}
     74      */
     75     contentType: function()
     76     {
     77         return WebInspector.resourceTypes.Script;
     78     },
     79 
     80     /**
     81      * @param {function(?string)} callback
     82      */
     83     requestContent: function(callback)
     84     {
     85         if (this._source) {
     86             callback(this._source);
     87             return;
     88         }
     89 
     90         /**
     91          * @this {WebInspector.Script}
     92          * @param {?Protocol.Error} error
     93          * @param {string} source
     94          */
     95         function didGetScriptSource(error, source)
     96         {
     97             this._source = error ? "" : source;
     98             callback(this._source);
     99         }
    100         if (this.scriptId) {
    101             // Script failed to parse.
    102             DebuggerAgent.getScriptSource(this.scriptId, didGetScriptSource.bind(this));
    103         } else
    104             callback("");
    105     },
    106 
    107     /**
    108      * @param {string} query
    109      * @param {boolean} caseSensitive
    110      * @param {boolean} isRegex
    111      * @param {function(!Array.<!PageAgent.SearchMatch>)} callback
    112      */
    113     searchInContent: function(query, caseSensitive, isRegex, callback)
    114     {
    115         /**
    116          * @this {WebInspector.Script}
    117          * @param {?Protocol.Error} error
    118          * @param {!Array.<!PageAgent.SearchMatch>} searchMatches
    119          */
    120         function innerCallback(error, searchMatches)
    121         {
    122             if (error)
    123                 console.error(error);
    124             var result = [];
    125             for (var i = 0; i < searchMatches.length; ++i) {
    126                 var searchMatch = new WebInspector.ContentProvider.SearchMatch(searchMatches[i].lineNumber, searchMatches[i].lineContent);
    127                 result.push(searchMatch);
    128             }
    129             callback(result || []);
    130         }
    131 
    132         if (this.scriptId) {
    133             // Script failed to parse.
    134             DebuggerAgent.searchInContent(this.scriptId, query, caseSensitive, isRegex, innerCallback.bind(this));
    135         } else
    136             callback([]);
    137     },
    138 
    139     /**
    140      * @param {string} newSource
    141      * @param {function(?Protocol.Error, !DebuggerAgent.SetScriptSourceError=, !Array.<!DebuggerAgent.CallFrame>=, !DebuggerAgent.StackTrace=, boolean=)} callback
    142      */
    143     editSource: function(newSource, callback)
    144     {
    145         /**
    146          * @this {WebInspector.Script}
    147          * @param {?Protocol.Error} error
    148          * @param {!DebuggerAgent.SetScriptSourceError=} errorData
    149          * @param {!Array.<!DebuggerAgent.CallFrame>=} callFrames
    150          * @param {!Object=} debugData
    151          * @param {!DebuggerAgent.StackTrace=} asyncStackTrace
    152          */
    153         function didEditScriptSource(error, errorData, callFrames, debugData, asyncStackTrace)
    154         {
    155             // FIXME: support debugData.stack_update_needs_step_in flag by calling WebInspector.debugger_model.callStackModified
    156             if (!error)
    157                 this._source = newSource;
    158             var needsStepIn = !!debugData && debugData["stack_update_needs_step_in"] === true;
    159             callback(error, errorData, callFrames, asyncStackTrace, needsStepIn);
    160             if (!error)
    161                 this.dispatchEventToListeners(WebInspector.Script.Events.ScriptEdited, newSource);
    162         }
    163 
    164         if (this.scriptId)
    165             DebuggerAgent.setScriptSource(this.scriptId, newSource, undefined, didEditScriptSource.bind(this));
    166         else
    167             callback("Script failed to parse");
    168     },
    169 
    170     /**
    171      * @return {boolean}
    172      */
    173     isInlineScript: function()
    174     {
    175         var startsAtZero = !this.lineOffset && !this.columnOffset;
    176         return !!this.sourceURL && !startsAtZero;
    177     },
    178 
    179     /**
    180      * @return {boolean}
    181      */
    182     isAnonymousScript: function()
    183     {
    184         return !this.sourceURL;
    185     },
    186 
    187     /**
    188      * @return {boolean}
    189      */
    190     isSnippet: function()
    191     {
    192         return !!this.sourceURL && this.sourceURL.startsWith(WebInspector.Script.snippetSourceURLPrefix);
    193     },
    194 
    195     /**
    196      * @param {number} lineNumber
    197      * @param {number=} columnNumber
    198      * @return {!WebInspector.UILocation}
    199      */
    200     rawLocationToUILocation: function(lineNumber, columnNumber)
    201     {
    202         var uiLocation;
    203         var rawLocation = new WebInspector.DebuggerModel.Location(this.scriptId, lineNumber, columnNumber || 0);
    204         for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
    205             uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
    206         console.assert(uiLocation, "Script raw location can not be mapped to any ui location.");
    207         return uiLocation.uiSourceCode.overrideLocation(uiLocation);
    208     },
    209 
    210     /**
    211      * @param {!WebInspector.SourceMapping} sourceMapping
    212      */
    213     pushSourceMapping: function(sourceMapping)
    214     {
    215         this._sourceMappings.push(sourceMapping);
    216         this.updateLocations();
    217     },
    218 
    219     updateLocations: function()
    220     {
    221         var items = this._locations.items();
    222         for (var i = 0; i < items.length; ++i)
    223             items[i].update();
    224     },
    225 
    226     /**
    227      * @param {!WebInspector.DebuggerModel.Location} rawLocation
    228      * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
    229      * @return {!WebInspector.Script.Location}
    230      */
    231     createLiveLocation: function(rawLocation, updateDelegate)
    232     {
    233         console.assert(rawLocation.scriptId === this.scriptId);
    234         var location = new WebInspector.Script.Location(this, rawLocation, updateDelegate);
    235         this._locations.add(location);
    236         location.update();
    237         return location;
    238     },
    239 
    240     __proto__: WebInspector.Object.prototype
    241 }
    242 
    243 /**
    244  * @constructor
    245  * @extends {WebInspector.LiveLocation}
    246  * @param {!WebInspector.Script} script
    247  * @param {!WebInspector.DebuggerModel.Location} rawLocation
    248  * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
    249  */
    250 WebInspector.Script.Location = function(script, rawLocation, updateDelegate)
    251 {
    252     WebInspector.LiveLocation.call(this, rawLocation, updateDelegate);
    253     this._script = script;
    254 }
    255 
    256 WebInspector.Script.Location.prototype = {
    257     /**
    258      * @return {!WebInspector.UILocation}
    259      */
    260     uiLocation: function()
    261     {
    262         var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (this.rawLocation());
    263         return this._script.rawLocationToUILocation(debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber);
    264     },
    265 
    266     dispose: function()
    267     {
    268         WebInspector.LiveLocation.prototype.dispose.call(this);
    269         this._script._locations.remove(this);
    270     },
    271 
    272     __proto__: WebInspector.LiveLocation.prototype
    273 }
    274