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,boolean,string)} callback
     82      */
     83     requestContent: function(callback)
     84     {
     85         if (this._source) {
     86             callback(this._source, false, "text/javascript");
     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, false, "text/javascript");
     99         }
    100         if (this.scriptId) {
    101             // Script failed to parse.
    102             DebuggerAgent.getScriptSource(this.scriptId, didGetScriptSource.bind(this));
    103         } else
    104             callback("", false, "text/javascript");
    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>=)} 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          */
    152         function didEditScriptSource(error, errorData, callFrames, debugData)
    153         {
    154             // FIXME: support debugData.stack_update_needs_step_in flag by calling WebInspector.debugger_model.callStackModified
    155             if (!error)
    156                 this._source = newSource;
    157             callback(error, errorData, callFrames);
    158             if (!error)
    159                 this.dispatchEventToListeners(WebInspector.Script.Events.ScriptEdited, newSource);
    160         }
    161         if (this.scriptId) {
    162             // Script failed to parse.
    163             DebuggerAgent.setScriptSource(this.scriptId, newSource, undefined, didEditScriptSource.bind(this));
    164         } else
    165             callback("Script failed to parse");
    166     },
    167 
    168     /**
    169      * @return {boolean}
    170      */
    171     isInlineScript: function()
    172     {
    173         var startsAtZero = !this.lineOffset && !this.columnOffset;
    174         return !!this.sourceURL && !startsAtZero;
    175     },
    176 
    177     /**
    178      * @return {boolean}
    179      */
    180     isAnonymousScript: function()
    181     {
    182         return !this.sourceURL;
    183     },
    184 
    185     /**
    186      * @return {boolean}
    187      */
    188     isSnippet: function()
    189     {
    190         return !!this.sourceURL && this.sourceURL.startsWith(WebInspector.Script.snippetSourceURLPrefix);
    191     },
    192 
    193     /**
    194      * @param {number} lineNumber
    195      * @param {number=} columnNumber
    196      * @return {WebInspector.UILocation}
    197      */
    198     rawLocationToUILocation: function(lineNumber, columnNumber)
    199     {
    200         var uiLocation;
    201         var rawLocation = new WebInspector.DebuggerModel.Location(this.scriptId, lineNumber, columnNumber || 0);
    202         for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
    203             uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
    204         console.assert(uiLocation, "Script raw location can not be mapped to any ui location.");
    205         return uiLocation.uiSourceCode.overrideLocation(uiLocation);
    206     },
    207 
    208     /**
    209      * @param {!WebInspector.SourceMapping} sourceMapping
    210      */
    211     pushSourceMapping: function(sourceMapping)
    212     {
    213         this._sourceMappings.push(sourceMapping);
    214         this.updateLocations();
    215     },
    216 
    217     updateLocations: function()
    218     {
    219         var items = this._locations.items();
    220         for (var i = 0; i < items.length; ++i)
    221             items[i].update();
    222     },
    223 
    224     /**
    225      * @param {WebInspector.DebuggerModel.Location} rawLocation
    226      * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
    227      * @return {WebInspector.Script.Location}
    228      */
    229     createLiveLocation: function(rawLocation, updateDelegate)
    230     {
    231         console.assert(rawLocation.scriptId === this.scriptId);
    232         var location = new WebInspector.Script.Location(this, rawLocation, updateDelegate);
    233         this._locations.add(location);
    234         location.update();
    235         return location;
    236     },
    237 
    238     __proto__: WebInspector.Object.prototype
    239 }
    240 
    241 /**
    242  * @constructor
    243  * @extends {WebInspector.LiveLocation}
    244  * @param {WebInspector.Script} script
    245  * @param {WebInspector.DebuggerModel.Location} rawLocation
    246  * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
    247  */
    248 WebInspector.Script.Location = function(script, rawLocation, updateDelegate)
    249 {
    250     WebInspector.LiveLocation.call(this, rawLocation, updateDelegate);
    251     this._script = script;
    252 }
    253 
    254 WebInspector.Script.Location.prototype = {
    255     /**
    256      * @return {WebInspector.UILocation}
    257      */
    258     uiLocation: function()
    259     {
    260         var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this.rawLocation());
    261         return this._script.rawLocationToUILocation(debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber);
    262     },
    263 
    264     dispose: function()
    265     {
    266         WebInspector.LiveLocation.prototype.dispose.call(this);
    267         this._script._locations.remove(this);
    268     },
    269 
    270     __proto__: WebInspector.LiveLocation.prototype
    271 }
    272