Home | History | Annotate | Download | only in sdk
      1 /*
      2  * Copyright (C) 2012 Google 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 are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @extends {WebInspector.Object}
     34  * @implements {WebInspector.TargetManager.Observer}
     35  * @param {!WebInspector.Workspace} workspace
     36  */
     37 WebInspector.ScriptSnippetModel = function(workspace)
     38 {
     39     this._workspace = workspace;
     40     /** @type {!Object.<string, !WebInspector.UISourceCode>} */
     41     this._uiSourceCodeForSnippetId = {};
     42     /** @type {!Map.<!WebInspector.UISourceCode, string>} */
     43     this._snippetIdForUISourceCode = new Map();
     44 
     45     /** @type {!Map.<!WebInspector.Target, !WebInspector.SnippetScriptMapping>} */
     46     this._mappingForTarget = new Map();
     47     this._snippetStorage = new WebInspector.SnippetStorage("script", "Script snippet #");
     48     this._lastSnippetEvaluationIndexSetting = WebInspector.settings.createSetting("lastSnippetEvaluationIndex", 0);
     49     this._projectId = WebInspector.projectTypes.Snippets + ":";
     50     this._projectDelegate = new WebInspector.SnippetsProjectDelegate(workspace, this, this._projectId);
     51     this._project = this._workspace.project(this._projectId);
     52     this._loadSnippets();
     53     WebInspector.targetManager.observeTargets(this);
     54 }
     55 
     56 WebInspector.ScriptSnippetModel.prototype = {
     57 
     58     /**
     59      * @param {!WebInspector.Target} target
     60      */
     61     targetAdded: function(target)
     62     {
     63         this._mappingForTarget.put(target, new WebInspector.SnippetScriptMapping(target, this));
     64     },
     65 
     66     /**
     67      * @param {!WebInspector.Target} target
     68      */
     69     targetRemoved: function(target)
     70     {
     71         this._mappingForTarget.remove(target);
     72     },
     73 
     74     /**
     75      * @param {!WebInspector.Target} target
     76      * @return {!WebInspector.SnippetScriptMapping|undefined}
     77      */
     78     snippetScriptMapping: function(target) {
     79         return this._mappingForTarget.get(target);
     80     },
     81 
     82     /**
     83      * @param {!WebInspector.Script} script
     84      */
     85     addScript: function(script)
     86     {
     87         this._mappingForTarget.get(script.target()).addScript(script);
     88     },
     89 
     90     /**
     91      * @param {!WebInspector.Target} target
     92      * @return {!WebInspector.SnippetScriptMapping}
     93      */
     94     createSnippetScriptMapping: function(target)
     95     {
     96         return new WebInspector.SnippetScriptMapping(target, this);
     97     },
     98 
     99     /**
    100      * @return {!WebInspector.Project}
    101      */
    102     project: function()
    103     {
    104         return this._project;
    105     },
    106 
    107     _loadSnippets: function()
    108     {
    109         var snippets = this._snippetStorage.snippets();
    110         for (var i = 0; i < snippets.length; ++i)
    111             this._addScriptSnippet(snippets[i]);
    112     },
    113 
    114     /**
    115      * @param {string} content
    116      * @return {string}
    117      */
    118     createScriptSnippet: function(content)
    119     {
    120         var snippet = this._snippetStorage.createSnippet();
    121         snippet.content = content;
    122         return this._addScriptSnippet(snippet);
    123     },
    124 
    125     /**
    126      * @param {!WebInspector.Snippet} snippet
    127      * @return {string}
    128      */
    129     _addScriptSnippet: function(snippet)
    130     {
    131         var path = this._projectDelegate.addSnippet(snippet.name, new WebInspector.SnippetContentProvider(snippet));
    132         var uiSourceCode = this._workspace.uiSourceCode(this._projectId, path);
    133         if (!uiSourceCode) {
    134             console.assert(uiSourceCode);
    135             return "";
    136         }
    137         uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
    138         this._snippetIdForUISourceCode.put(uiSourceCode, snippet.id);
    139         var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    140         this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    141         this._uiSourceCodeForSnippetId[snippet.id] = uiSourceCode;
    142         return path;
    143     },
    144 
    145     /**
    146      * @param {!WebInspector.Event} event
    147      */
    148     _workingCopyChanged: function(event)
    149     {
    150         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
    151         this._scriptSnippetEdited(uiSourceCode);
    152     },
    153 
    154     /**
    155      * @param {string} path
    156      */
    157     deleteScriptSnippet: function(path)
    158     {
    159         var uiSourceCode = this._workspace.uiSourceCode(this._projectId, path);
    160         if (!uiSourceCode)
    161             return;
    162         var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode) || "";
    163         var snippet = this._snippetStorage.snippetForId(snippetId);
    164         this._snippetStorage.deleteSnippet(snippet);
    165         this._removeBreakpoints(uiSourceCode);
    166         this._releaseSnippetScript(uiSourceCode);
    167         delete this._uiSourceCodeForSnippetId[snippet.id];
    168         this._snippetIdForUISourceCode.remove(uiSourceCode);
    169         this._projectDelegate.removeFile(snippet.name);
    170     },
    171 
    172     /**
    173      * @param {string} name
    174      * @param {string} newName
    175      * @param {function(boolean, string=)} callback
    176      */
    177     renameScriptSnippet: function(name, newName, callback)
    178     {
    179         newName = newName.trim();
    180         if (!newName || newName.indexOf("/") !== -1 || name === newName || this._snippetStorage.snippetForName(newName)) {
    181             callback(false);
    182             return;
    183         }
    184         var snippet = this._snippetStorage.snippetForName(name);
    185         console.assert(snippet, "Snippet '" + name + "' was not found.");
    186         var uiSourceCode = this._uiSourceCodeForSnippetId[snippet.id];
    187         console.assert(uiSourceCode, "No uiSourceCode was found for snippet '" + name + "'.");
    188 
    189         var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    190         snippet.name = newName;
    191         this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    192         callback(true, newName);
    193     },
    194 
    195     /**
    196      * @param {string} name
    197      * @param {string} newContent
    198      */
    199     _setScriptSnippetContent: function(name, newContent)
    200     {
    201         var snippet = this._snippetStorage.snippetForName(name);
    202         snippet.content = newContent;
    203     },
    204 
    205     /**
    206      * @param {!WebInspector.UISourceCode} uiSourceCode
    207      */
    208     _scriptSnippetEdited: function(uiSourceCode)
    209     {
    210         var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    211         this._releaseSnippetScript(uiSourceCode);
    212         this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    213         this._mappingForTarget.values().forEach(function(mapping) {mapping._restoreBreakpoints(uiSourceCode, breakpointLocations)});
    214     },
    215 
    216     /**
    217      * @return {number}
    218      */
    219     _nextEvaluationIndex: function()
    220     {
    221         var evaluationIndex = this._lastSnippetEvaluationIndexSetting.get() + 1;
    222         this._lastSnippetEvaluationIndexSetting.set(evaluationIndex);
    223         return evaluationIndex;
    224     },
    225 
    226     /**
    227      * @param {!WebInspector.ExecutionContext} executionContext
    228      * @param {!WebInspector.UISourceCode} uiSourceCode
    229      */
    230     evaluateScriptSnippet: function(executionContext, uiSourceCode)
    231     {
    232         var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    233         this._releaseSnippetScript(uiSourceCode);
    234         this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    235 
    236         var target = executionContext.target();
    237         var evaluationIndex = this._nextEvaluationIndex();
    238         var mapping = this._mappingForTarget.get(target);
    239         mapping._setEvaluationIndex(evaluationIndex, uiSourceCode);
    240         var evaluationUrl = mapping._evaluationSourceURL(uiSourceCode);
    241         var expression = uiSourceCode.workingCopy();
    242         target.consoleModel.show();
    243         target.debuggerAgent().compileScript(expression, evaluationUrl, executionContext.id, compileCallback.bind(this, target));
    244 
    245         /**
    246          * @param {!WebInspector.Target} target
    247          * @param {?string} error
    248          * @param {!DebuggerAgent.ScriptId=} scriptId
    249          * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
    250          * @this {WebInspector.ScriptSnippetModel}
    251          */
    252         function compileCallback(target, error, scriptId, exceptionDetails)
    253         {
    254             if (!uiSourceCode || this._mappingForTarget.get(target).evaluationIndex(uiSourceCode) !== evaluationIndex)
    255                 return;
    256 
    257             if (error) {
    258                 console.error(error);
    259                 return;
    260             }
    261 
    262             if (!scriptId) {
    263                 this._printRunOrCompileScriptResultFailure(target, exceptionDetails, evaluationUrl);
    264                 return;
    265             }
    266 
    267             var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    268             this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    269 
    270             this._runScript(scriptId, executionContext, evaluationUrl);
    271         }
    272     },
    273 
    274     /**
    275      * @param {!DebuggerAgent.ScriptId} scriptId
    276      * @param {!WebInspector.ExecutionContext} executionContext
    277      * @param {?string=} sourceURL
    278      */
    279     _runScript: function(scriptId, executionContext, sourceURL)
    280     {
    281         var target = executionContext.target();
    282         target.debuggerAgent().runScript(scriptId, executionContext.id, "console", false, runCallback.bind(this, target));
    283 
    284         /**
    285          * @param {!WebInspector.Target} target
    286          * @param {?string} error
    287          * @param {?RuntimeAgent.RemoteObject} result
    288          * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
    289          * @this {WebInspector.ScriptSnippetModel}
    290          */
    291         function runCallback(target, error, result, exceptionDetails)
    292         {
    293             if (error) {
    294                 console.error(error);
    295                 return;
    296             }
    297 
    298             if (!exceptionDetails)
    299                 this._printRunScriptResult(target, result, sourceURL);
    300             else
    301                 this._printRunOrCompileScriptResultFailure(target, exceptionDetails, sourceURL);
    302         }
    303     },
    304 
    305     /**
    306      * @param {!WebInspector.Target} target
    307      * @param {?RuntimeAgent.RemoteObject} result
    308      * @param {?string=} sourceURL
    309      */
    310     _printRunScriptResult: function(target, result, sourceURL)
    311     {
    312         var consoleMessage = new WebInspector.ConsoleMessage(
    313             target,
    314             WebInspector.ConsoleMessage.MessageSource.JS,
    315             WebInspector.ConsoleMessage.MessageLevel.Log,
    316             "",
    317             undefined,
    318             sourceURL,
    319             undefined,
    320             undefined,
    321             undefined,
    322             [result],
    323             undefined);
    324         target.consoleModel.addMessage(consoleMessage);
    325     },
    326 
    327     /**
    328      * @param {!WebInspector.Target} target
    329      * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
    330      * @param {?string=} sourceURL
    331      */
    332     _printRunOrCompileScriptResultFailure: function(target, exceptionDetails, sourceURL)
    333     {
    334         var consoleMessage = new WebInspector.ConsoleMessage(
    335             target,
    336             exceptionDetails.source,
    337             WebInspector.ConsoleMessage.MessageLevel.Error,
    338             exceptionDetails.text,
    339             undefined,
    340             sourceURL,
    341             exceptionDetails.line,
    342             exceptionDetails.column,
    343             undefined,
    344             undefined,
    345             exceptionDetails.stackTrace);
    346         target.consoleModel.addMessage(consoleMessage);
    347     },
    348 
    349     /**
    350      * @param {!WebInspector.UISourceCode} uiSourceCode
    351      * @return {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>}
    352      */
    353     _removeBreakpoints: function(uiSourceCode)
    354     {
    355         var breakpointLocations = WebInspector.breakpointManager.breakpointLocationsForUISourceCode(uiSourceCode);
    356         for (var i = 0; i < breakpointLocations.length; ++i)
    357             breakpointLocations[i].breakpoint.remove();
    358         return breakpointLocations;
    359     },
    360 
    361     /**
    362      * @param {!WebInspector.UISourceCode} uiSourceCode
    363      * @param {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>} breakpointLocations
    364      */
    365     _restoreBreakpoints: function(uiSourceCode, breakpointLocations)
    366     {
    367         for (var i = 0; i < breakpointLocations.length; ++i) {
    368             var uiLocation = breakpointLocations[i].uiLocation;
    369             var breakpoint = breakpointLocations[i].breakpoint;
    370             WebInspector.breakpointManager.setBreakpoint(uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber, breakpoint.condition(), breakpoint.enabled());
    371         }
    372     },
    373 
    374     /**
    375      * @param {!WebInspector.UISourceCode} uiSourceCode
    376      */
    377     _releaseSnippetScript: function(uiSourceCode)
    378     {
    379         this._mappingForTarget.values().forEach(function(mapping) {mapping._releaseSnippetScript(uiSourceCode)});
    380     },
    381 
    382     /**
    383      * @param {string} sourceURL
    384      * @return {?string}
    385      */
    386     _snippetIdForSourceURL: function(sourceURL)
    387     {
    388         var snippetPrefix = WebInspector.Script.snippetSourceURLPrefix;
    389         if (!sourceURL.startsWith(snippetPrefix))
    390             return null;
    391         var splitURL = sourceURL.substring(snippetPrefix.length).split("_");
    392         var snippetId = splitURL[0];
    393         return snippetId;
    394     },
    395 
    396     __proto__: WebInspector.Object.prototype
    397 }
    398 
    399 /**
    400  * @constructor
    401  * @implements {WebInspector.ScriptSourceMapping}
    402  * @param {!WebInspector.Target} target
    403  * @param {!WebInspector.ScriptSnippetModel} scriptSnippetModel
    404  */
    405 WebInspector.SnippetScriptMapping = function(target, scriptSnippetModel)
    406 {
    407     this._target = target;
    408     this._scriptSnippetModel = scriptSnippetModel;
    409     /** @type {!Object.<string, !WebInspector.UISourceCode>} */
    410     this._uiSourceCodeForScriptId = {};
    411     /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.Script>} */
    412     this._scriptForUISourceCode = new Map();
    413     /** @type {!Map.<!WebInspector.UISourceCode, number>} */
    414     this._evaluationIndexForUISourceCode = new Map();
    415     target.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._reset, this);
    416 }
    417 
    418 WebInspector.SnippetScriptMapping.prototype = {
    419     /**
    420      * @param {!WebInspector.UISourceCode} uiSourceCode
    421      */
    422     _releaseSnippetScript: function(uiSourceCode)
    423     {
    424         var script = this._scriptForUISourceCode.get(uiSourceCode);
    425         if (!script)
    426             return;
    427 
    428         delete this._uiSourceCodeForScriptId[script.scriptId];
    429         this._scriptForUISourceCode.remove(uiSourceCode);
    430         this._evaluationIndexForUISourceCode.remove(uiSourceCode);
    431     },
    432 
    433     /**
    434      +* @param {number} evaluationIndex
    435      * @param {!WebInspector.UISourceCode} uiSourceCode
    436      */
    437     _setEvaluationIndex: function(evaluationIndex, uiSourceCode)
    438     {
    439         this._evaluationIndexForUISourceCode.put(uiSourceCode, evaluationIndex);
    440     },
    441 
    442     /**
    443      * @param {!WebInspector.UISourceCode} uiSourceCode
    444      * @return {number|undefined}
    445      */
    446     evaluationIndex: function(uiSourceCode)
    447     {
    448         return this._evaluationIndexForUISourceCode.get(uiSourceCode);
    449     },
    450 
    451     /**
    452      * @param {!WebInspector.UISourceCode} uiSourceCode
    453      * @return {string}
    454      */
    455     _evaluationSourceURL: function(uiSourceCode)
    456     {
    457         var evaluationSuffix = "_" + this._evaluationIndexForUISourceCode.get(uiSourceCode);
    458         var snippetId = this._scriptSnippetModel._snippetIdForUISourceCode.get(uiSourceCode);
    459         return WebInspector.Script.snippetSourceURLPrefix + snippetId + evaluationSuffix;
    460     },
    461 
    462     _reset: function()
    463     {
    464         this._uiSourceCodeForScriptId = {};
    465         this._scriptForUISourceCode.clear();
    466         this._evaluationIndexForUISourceCode.clear();
    467     },
    468 
    469     /**
    470      * @param {!WebInspector.RawLocation} rawLocation
    471      * @return {?WebInspector.UILocation}
    472      */
    473     rawLocationToUILocation: function(rawLocation)
    474     {
    475         var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */(rawLocation);
    476         var uiSourceCode = this._uiSourceCodeForScriptId[debuggerModelLocation.scriptId];
    477         if (!uiSourceCode)
    478             return null;
    479 
    480         return uiSourceCode.uiLocation(debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber || 0);
    481     },
    482 
    483     /**
    484      * @param {!WebInspector.UISourceCode} uiSourceCode
    485      * @param {number} lineNumber
    486      * @param {number} columnNumber
    487      * @return {?WebInspector.DebuggerModel.Location}
    488      */
    489     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
    490     {
    491         var script = this._scriptForUISourceCode.get(uiSourceCode);
    492         if (!script)
    493             return null;
    494 
    495         return this._target.debuggerModel.createRawLocation(script, lineNumber, columnNumber);
    496     },
    497 
    498     /**
    499      * @param {string} sourceURL
    500      * @return {?string}
    501      */
    502     snippetIdForSourceURL: function(sourceURL)
    503     {
    504         return this._scriptSnippetModel._snippetIdForSourceURL(sourceURL);
    505     },
    506 
    507     /**
    508      * @param {!WebInspector.Script} script
    509      */
    510     addScript: function(script)
    511     {
    512         var snippetId = this.snippetIdForSourceURL(script.sourceURL);
    513         if (!snippetId)
    514             return;
    515         var uiSourceCode = this._scriptSnippetModel._uiSourceCodeForSnippetId[snippetId];
    516 
    517         if (!uiSourceCode || this._evaluationSourceURL(uiSourceCode) !== script.sourceURL)
    518             return;
    519 
    520         console.assert(!this._scriptForUISourceCode.get(uiSourceCode));
    521         uiSourceCode.setSourceMappingForTarget(this._target, this);
    522         this._uiSourceCodeForScriptId[script.scriptId] = uiSourceCode;
    523         this._scriptForUISourceCode.put(uiSourceCode, script);
    524         script.pushSourceMapping(this);
    525     },
    526 
    527     /**
    528      * @param {!WebInspector.UISourceCode} uiSourceCode
    529      * @param {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>} breakpointLocations
    530      */
    531     _restoreBreakpoints: function(uiSourceCode, breakpointLocations)
    532     {
    533         var script = this._scriptForUISourceCode.get(uiSourceCode);
    534         if (!script)
    535             return;
    536 
    537         var scriptUISourceCode = script.rawLocationToUILocation(0, 0).uiSourceCode;
    538         if (scriptUISourceCode)
    539             this._scriptSnippetModel._restoreBreakpoints(scriptUISourceCode, breakpointLocations);
    540     },
    541 
    542     /**
    543      * @return {boolean}
    544      */
    545     isIdentity: function()
    546     {
    547         return false;
    548     }
    549 }
    550 
    551 /**
    552  * @constructor
    553  * @implements {WebInspector.ContentProvider}
    554  * @param {!WebInspector.Snippet} snippet
    555  */
    556 WebInspector.SnippetContentProvider = function(snippet)
    557 {
    558     this._snippet = snippet;
    559 }
    560 
    561 WebInspector.SnippetContentProvider.prototype = {
    562     /**
    563      * @return {string}
    564      */
    565     contentURL: function()
    566     {
    567         return "";
    568     },
    569 
    570     /**
    571      * @return {!WebInspector.ResourceType}
    572      */
    573     contentType: function()
    574     {
    575         return WebInspector.resourceTypes.Script;
    576     },
    577 
    578     /**
    579      * @param {function(?string)} callback
    580      */
    581     requestContent: function(callback)
    582     {
    583         callback(this._snippet.content);
    584     },
    585 
    586     /**
    587      * @param {string} query
    588      * @param {boolean} caseSensitive
    589      * @param {boolean} isRegex
    590      * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
    591      */
    592     searchInContent: function(query, caseSensitive, isRegex, callback)
    593     {
    594         /**
    595          * @this {WebInspector.SnippetContentProvider}
    596          */
    597         function performSearch()
    598         {
    599             callback(WebInspector.ContentProvider.performSearchInContent(this._snippet.content, query, caseSensitive, isRegex));
    600         }
    601 
    602         // searchInContent should call back later.
    603         window.setTimeout(performSearch.bind(this), 0);
    604     }
    605 }
    606 
    607 /**
    608  * @constructor
    609  * @extends {WebInspector.ContentProviderBasedProjectDelegate}
    610  * @param {!WebInspector.Workspace} workspace
    611  * @param {!WebInspector.ScriptSnippetModel} model
    612  * @param {string} id
    613  */
    614 WebInspector.SnippetsProjectDelegate = function(workspace, model, id)
    615 {
    616     WebInspector.ContentProviderBasedProjectDelegate.call(this, workspace, id, WebInspector.projectTypes.Snippets);
    617     this._model = model;
    618 }
    619 
    620 WebInspector.SnippetsProjectDelegate.prototype = {
    621     /**
    622      * @param {string} name
    623      * @param {!WebInspector.ContentProvider} contentProvider
    624      * @return {string}
    625      */
    626     addSnippet: function(name, contentProvider)
    627     {
    628         return this.addContentProvider("", name, name, contentProvider);
    629     },
    630 
    631     /**
    632      * @return {boolean}
    633      */
    634     canSetFileContent: function()
    635     {
    636         return true;
    637     },
    638 
    639     /**
    640      * @param {string} path
    641      * @param {string} newContent
    642      * @param {function(?string)} callback
    643      */
    644     setFileContent: function(path, newContent, callback)
    645     {
    646         this._model._setScriptSnippetContent(path, newContent);
    647         callback("");
    648     },
    649 
    650     /**
    651      * @return {boolean}
    652      */
    653     canRename: function()
    654     {
    655         return true;
    656     },
    657 
    658     /**
    659      * @param {string} path
    660      * @param {string} newName
    661      * @param {function(boolean, string=)} callback
    662      */
    663     performRename: function(path, newName, callback)
    664     {
    665         this._model.renameScriptSnippet(path, newName, callback);
    666     },
    667 
    668     /**
    669      * @param {string} path
    670      * @param {?string} name
    671      * @param {string} content
    672      * @param {function(?string)} callback
    673      */
    674     createFile: function(path, name, content, callback)
    675     {
    676         var filePath = this._model.createScriptSnippet(content);
    677         callback(filePath);
    678     },
    679 
    680     /**
    681      * @param {string} path
    682      */
    683     deleteFile: function(path)
    684     {
    685         this._model.deleteScriptSnippet(path);
    686     },
    687 
    688     __proto__: WebInspector.ContentProviderBasedProjectDelegate.prototype
    689 }
    690 
    691 /**
    692  * @type {!WebInspector.ScriptSnippetModel}
    693  */
    694 WebInspector.scriptSnippetModel;
    695