Home | History | Annotate | Download | only in front_end
      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  * @param {WebInspector.Workspace} workspace
     35  */
     36 WebInspector.ScriptSnippetModel = function(workspace)
     37 {
     38     this._workspace = workspace;
     39     /** @type {!Object.<string, WebInspector.UISourceCode>} */
     40     this._uiSourceCodeForScriptId = {};
     41     /** @type {!Map.<WebInspector.UISourceCode, WebInspector.Script>} */
     42     this._scriptForUISourceCode = new Map();
     43     /** @type {!Object.<string, WebInspector.UISourceCode>} */
     44     this._uiSourceCodeForSnippetId = {};
     45     /** @type {!Map.<WebInspector.UISourceCode, string>} */
     46     this._snippetIdForUISourceCode = new Map();
     47 
     48     this._snippetStorage = new WebInspector.SnippetStorage("script", "Script snippet #");
     49     this._lastSnippetEvaluationIndexSetting = WebInspector.settings.createSetting("lastSnippetEvaluationIndex", 0);
     50     this._snippetScriptMapping = new WebInspector.SnippetScriptMapping(this);
     51     this._projectDelegate = new WebInspector.SnippetsProjectDelegate(this);
     52     this._project = this._workspace.addProject(this._projectDelegate);
     53     this.reset();
     54     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
     55 }
     56 
     57 WebInspector.ScriptSnippetModel.prototype = {
     58     /**
     59      * @return {WebInspector.SnippetScriptMapping}
     60      */
     61     get scriptMapping()
     62     {
     63         return this._snippetScriptMapping;
     64     },
     65 
     66     /**
     67      * @return {WebInspector.Project}
     68      */
     69     project: function()
     70     {
     71         return this._project;
     72     },
     73 
     74     _loadSnippets: function()
     75     {
     76         var snippets = this._snippetStorage.snippets();
     77         for (var i = 0; i < snippets.length; ++i)
     78             this._addScriptSnippet(snippets[i]);
     79     },
     80 
     81     /**
     82      * @return {string}
     83      */
     84     createScriptSnippet: function()
     85     {
     86         var snippet = this._snippetStorage.createSnippet();
     87         return this._addScriptSnippet(snippet);
     88     },
     89 
     90     /**
     91      * @param {WebInspector.Snippet} snippet
     92      * @return {string}
     93      */
     94     _addScriptSnippet: function(snippet)
     95     {
     96         var path = this._projectDelegate.addSnippet(snippet.name, new WebInspector.SnippetContentProvider(snippet));
     97         var uiSourceCode = this._workspace.uiSourceCode(this._projectDelegate.id(), path);
     98         var scriptFile = new WebInspector.SnippetScriptFile(this, uiSourceCode);
     99         uiSourceCode.setScriptFile(scriptFile);
    100         this._snippetIdForUISourceCode.put(uiSourceCode, snippet.id);
    101         uiSourceCode.setSourceMapping(this._snippetScriptMapping);
    102         this._uiSourceCodeForSnippetId[snippet.id] = uiSourceCode;
    103         return path;
    104     },
    105 
    106     /**
    107      * @param {string} path
    108      */
    109     deleteScriptSnippet: function(path)
    110     {
    111         var uiSourceCode = this._workspace.uiSourceCode(this._projectDelegate.id(), path);
    112         var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode) || "";
    113         var snippet = this._snippetStorage.snippetForId(snippetId);
    114         this._snippetStorage.deleteSnippet(snippet);
    115         this._removeBreakpoints(uiSourceCode);
    116         this._releaseSnippetScript(uiSourceCode);
    117         delete this._uiSourceCodeForSnippetId[snippet.id];
    118         this._snippetIdForUISourceCode.remove(uiSourceCode);
    119         this._projectDelegate.removeFile(snippet.name);
    120     },
    121 
    122     /**
    123      * @param {string} name
    124      * @param {string} newName
    125      * @param {function(boolean, string=)} callback
    126      */
    127     renameScriptSnippet: function(name, newName, callback)
    128     {
    129         newName = newName.trim();
    130         if (!newName || newName.indexOf("/") !== -1 || name === newName || this._snippetStorage.snippetForName(newName)) {
    131             callback(false);
    132             return;
    133         }
    134         var snippet = this._snippetStorage.snippetForName(name);
    135         console.assert(snippet, "Snippet '" + name + "' was not found.");
    136         var uiSourceCode = this._uiSourceCodeForSnippetId[snippet.id];
    137         console.assert(uiSourceCode, "No uiSourceCode was found for snippet '" + name + "'.");
    138 
    139         var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    140         snippet.name = newName;
    141         this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    142         callback(true, newName);
    143     },
    144 
    145     /**
    146      * @param {string} name
    147      * @param {string} newContent
    148      */
    149     _setScriptSnippetContent: function(name, newContent)
    150     {
    151         var snippet = this._snippetStorage.snippetForName(name);
    152         snippet.content = newContent;
    153     },
    154 
    155     /**
    156      * @param {WebInspector.UISourceCode} uiSourceCode
    157      */
    158     _scriptSnippetEdited: function(uiSourceCode)
    159     {
    160         var script = this._scriptForUISourceCode.get(uiSourceCode);
    161         if (!script)
    162             return;
    163 
    164         var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    165         this._releaseSnippetScript(uiSourceCode);
    166         this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    167         var scriptUISourceCode = script.rawLocationToUILocation(0, 0).uiSourceCode;
    168         if (scriptUISourceCode)
    169             this._restoreBreakpoints(scriptUISourceCode, breakpointLocations);
    170     },
    171 
    172     /**
    173      * @param {string} snippetId
    174      * @return {number}
    175      */
    176     _nextEvaluationIndex: function(snippetId)
    177     {
    178         var evaluationIndex = this._lastSnippetEvaluationIndexSetting.get() + 1;
    179         this._lastSnippetEvaluationIndexSetting.set(evaluationIndex);
    180         return evaluationIndex;
    181     },
    182 
    183     /**
    184      * @param {WebInspector.UISourceCode} uiSourceCode
    185      */
    186     evaluateScriptSnippet: function(uiSourceCode)
    187     {
    188         var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    189         this._releaseSnippetScript(uiSourceCode);
    190         this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    191         var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode) || "";
    192         var evaluationIndex = this._nextEvaluationIndex(snippetId);
    193         uiSourceCode._evaluationIndex = evaluationIndex;
    194         var evaluationUrl = this._evaluationSourceURL(uiSourceCode);
    195 
    196         var expression = uiSourceCode.workingCopy();
    197 
    198         // In order to stop on the breakpoints during the snippet evaluation we need to compile and run it separately.
    199         // If separate compilation and execution is not supported by the port we fall back to evaluation in console.
    200         // In case we don't need that since debugger is already paused.
    201         // We do the same when we are stopped on the call frame  since debugger is already paused and can not stop on breakpoint anymore.
    202         if (WebInspector.debuggerModel.selectedCallFrame()) {
    203             expression = uiSourceCode.workingCopy() + "\n//# sourceURL=" + evaluationUrl + "\n";
    204             WebInspector.evaluateInConsole(expression, true);
    205             return;
    206         }
    207 
    208         WebInspector.showConsole();
    209         DebuggerAgent.compileScript(expression, evaluationUrl, compileCallback.bind(this));
    210 
    211         /**
    212          * @param {?string} error
    213          * @param {string=} scriptId
    214          * @param {string=} syntaxErrorMessage
    215          */
    216         function compileCallback(error, scriptId, syntaxErrorMessage)
    217         {
    218             if (!uiSourceCode || uiSourceCode._evaluationIndex !== evaluationIndex)
    219                 return;
    220 
    221             if (error) {
    222                 console.error(error);
    223                 return;
    224             }
    225 
    226             if (!scriptId) {
    227                 var consoleMessage = WebInspector.ConsoleMessage.create(
    228                         WebInspector.ConsoleMessage.MessageSource.JS,
    229                         WebInspector.ConsoleMessage.MessageLevel.Error,
    230                         syntaxErrorMessage || "");
    231                 WebInspector.console.addMessage(consoleMessage);
    232                 return;
    233             }
    234 
    235             var breakpointLocations = this._removeBreakpoints(uiSourceCode);
    236             this._restoreBreakpoints(uiSourceCode, breakpointLocations);
    237 
    238             this._runScript(scriptId);
    239         }
    240     },
    241 
    242     /**
    243      * @param {DebuggerAgent.ScriptId} scriptId
    244      */
    245     _runScript: function(scriptId)
    246     {
    247         var currentExecutionContext = WebInspector.runtimeModel.currentExecutionContext();
    248         DebuggerAgent.runScript(scriptId, currentExecutionContext ? currentExecutionContext.id : undefined, "console", false, runCallback.bind(this));
    249 
    250         /**
    251          * @param {?string} error
    252          * @param {?RuntimeAgent.RemoteObject} result
    253          * @param {boolean=} wasThrown
    254          */
    255         function runCallback(error, result, wasThrown)
    256         {
    257             if (error) {
    258                 console.error(error);
    259                 return;
    260             }
    261 
    262             this._printRunScriptResult(result, wasThrown);
    263         }
    264     },
    265 
    266     /**
    267      * @param {?RuntimeAgent.RemoteObject} result
    268      * @param {boolean=} wasThrown
    269      */
    270     _printRunScriptResult: function(result, wasThrown)
    271     {
    272         var level = (wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log);
    273         var message = WebInspector.ConsoleMessage.create(WebInspector.ConsoleMessage.MessageSource.JS, level, "", undefined, undefined, undefined, undefined, undefined, [result]);
    274         WebInspector.console.addMessage(message)
    275     },
    276 
    277     /**
    278      * @param {WebInspector.DebuggerModel.Location} rawLocation
    279      * @return {WebInspector.UILocation}
    280      */
    281     _rawLocationToUILocation: function(rawLocation)
    282     {
    283         var uiSourceCode = this._uiSourceCodeForScriptId[rawLocation.scriptId];
    284         if (!uiSourceCode)
    285             return null;
    286         return new WebInspector.UILocation(uiSourceCode, rawLocation.lineNumber, rawLocation.columnNumber || 0);
    287     },
    288 
    289     /**
    290      * @param {WebInspector.UISourceCode} uiSourceCode
    291      * @param {number} lineNumber
    292      * @param {number} columnNumber
    293      * @return {WebInspector.DebuggerModel.Location}
    294      */
    295     _uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
    296     {
    297         var script = this._scriptForUISourceCode.get(uiSourceCode);
    298         if (!script)
    299             return null;
    300 
    301         return WebInspector.debuggerModel.createRawLocation(script, lineNumber, columnNumber);
    302     },
    303 
    304     /**
    305      * @param {WebInspector.Script} script
    306      */
    307     _addScript: function(script)
    308     {
    309         var snippetId = this._snippetIdForSourceURL(script.sourceURL);
    310         if (!snippetId)
    311             return;
    312         var uiSourceCode = this._uiSourceCodeForSnippetId[snippetId];
    313 
    314         if (!uiSourceCode || this._evaluationSourceURL(uiSourceCode) !== script.sourceURL)
    315             return;
    316 
    317         console.assert(!this._scriptForUISourceCode.get(uiSourceCode));
    318         this._uiSourceCodeForScriptId[script.scriptId] = uiSourceCode;
    319         this._scriptForUISourceCode.put(uiSourceCode, script);
    320         uiSourceCode.scriptFile().setHasDivergedFromVM(false);
    321         script.pushSourceMapping(this._snippetScriptMapping);
    322     },
    323 
    324     /**
    325      * @param {WebInspector.UISourceCode} uiSourceCode
    326      * @return {Array.<Object>}
    327      */
    328     _removeBreakpoints: function(uiSourceCode)
    329     {
    330         var breakpointLocations = WebInspector.breakpointManager.breakpointLocationsForUISourceCode(uiSourceCode);
    331         for (var i = 0; i < breakpointLocations.length; ++i)
    332             breakpointLocations[i].breakpoint.remove();
    333         return breakpointLocations;
    334     },
    335 
    336     /**
    337      * @param {WebInspector.UISourceCode} uiSourceCode
    338      * @param {Array.<Object>} breakpointLocations
    339      */
    340     _restoreBreakpoints: function(uiSourceCode, breakpointLocations)
    341     {
    342         for (var i = 0; i < breakpointLocations.length; ++i) {
    343             var uiLocation = breakpointLocations[i].uiLocation;
    344             var breakpoint = breakpointLocations[i].breakpoint;
    345             WebInspector.breakpointManager.setBreakpoint(uiSourceCode, uiLocation.lineNumber, breakpoint.condition(), breakpoint.enabled());
    346         }
    347     },
    348 
    349     /**
    350      * @param {WebInspector.UISourceCode} uiSourceCode
    351      */
    352     _releaseSnippetScript: function(uiSourceCode)
    353     {
    354         var script = this._scriptForUISourceCode.get(uiSourceCode);
    355         if (!script)
    356             return null;
    357 
    358         uiSourceCode.scriptFile().setIsDivergingFromVM(true);
    359         uiSourceCode.scriptFile().setHasDivergedFromVM(true);
    360         delete this._uiSourceCodeForScriptId[script.scriptId];
    361         this._scriptForUISourceCode.remove(uiSourceCode);
    362         delete uiSourceCode._evaluationIndex;
    363         uiSourceCode.scriptFile().setIsDivergingFromVM(false);
    364     },
    365 
    366     _debuggerReset: function()
    367     {
    368         for (var snippetId in this._uiSourceCodeForSnippetId) {
    369             var uiSourceCode = this._uiSourceCodeForSnippetId[snippetId];
    370             this._releaseSnippetScript(uiSourceCode);
    371         }
    372     },
    373 
    374     /**
    375      * @param {WebInspector.UISourceCode} uiSourceCode
    376      * @return {string}
    377      */
    378     _evaluationSourceURL: function(uiSourceCode)
    379     {
    380         var evaluationSuffix = "_" + uiSourceCode._evaluationIndex;
    381         var snippetId = this._snippetIdForUISourceCode.get(uiSourceCode);
    382         return WebInspector.Script.snippetSourceURLPrefix + snippetId + evaluationSuffix;
    383     },
    384 
    385     /**
    386      * @param {string} sourceURL
    387      * @return {string|null}
    388      */
    389     _snippetIdForSourceURL: function(sourceURL)
    390     {
    391         var snippetPrefix = WebInspector.Script.snippetSourceURLPrefix;
    392         if (!sourceURL.startsWith(snippetPrefix))
    393             return null;
    394         var splitURL = sourceURL.substring(snippetPrefix.length).split("_");
    395         var snippetId = splitURL[0];
    396         return snippetId;
    397     },
    398 
    399     reset: function()
    400     {
    401         /** @type {!Object.<string, WebInspector.UISourceCode>} */
    402         this._uiSourceCodeForScriptId = {};
    403         this._scriptForUISourceCode = new Map();
    404         /** @type {!Object.<string, WebInspector.UISourceCode>} */
    405         this._uiSourceCodeForSnippetId = {};
    406         this._snippetIdForUISourceCode = new Map();
    407         this._projectDelegate.reset();
    408         this._loadSnippets();
    409     },
    410 
    411     __proto__: WebInspector.Object.prototype
    412 }
    413 
    414 /**
    415  * @constructor
    416  * @implements {WebInspector.ScriptFile}
    417  * @extends {WebInspector.Object}
    418  * @param {WebInspector.ScriptSnippetModel} scriptSnippetModel
    419  * @param {WebInspector.UISourceCode} uiSourceCode
    420  */
    421 WebInspector.SnippetScriptFile = function(scriptSnippetModel, uiSourceCode)
    422 {
    423     WebInspector.ScriptFile.call(this);
    424     this._scriptSnippetModel = scriptSnippetModel;
    425     this._uiSourceCode = uiSourceCode;
    426     this._hasDivergedFromVM = true;
    427     this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
    428 }
    429 
    430 WebInspector.SnippetScriptFile.prototype = {
    431     /**
    432      * @return {boolean}
    433      */
    434     hasDivergedFromVM: function()
    435     {
    436         return this._hasDivergedFromVM;
    437     },
    438 
    439     /**
    440      * @param {boolean} hasDivergedFromVM
    441      */
    442     setHasDivergedFromVM: function(hasDivergedFromVM)
    443     {
    444         this._hasDivergedFromVM = hasDivergedFromVM;
    445     },
    446 
    447     /**
    448      * @return {boolean}
    449      */
    450     isDivergingFromVM: function()
    451     {
    452         return this._isDivergingFromVM;
    453     },
    454 
    455     checkMapping: function()
    456     {
    457     },
    458 
    459     /**
    460      * @return {boolean}
    461      */
    462     isMergingToVM: function()
    463     {
    464         return false;
    465     },
    466 
    467     /**
    468      * @param {boolean} isDivergingFromVM
    469      */
    470     setIsDivergingFromVM: function(isDivergingFromVM)
    471     {
    472         this._isDivergingFromVM = isDivergingFromVM;
    473     },
    474 
    475     _workingCopyChanged: function()
    476     {
    477         this._scriptSnippetModel._scriptSnippetEdited(this._uiSourceCode);
    478     },
    479 
    480     __proto__: WebInspector.Object.prototype
    481 }
    482 
    483 /**
    484  * @constructor
    485  * @implements {WebInspector.ScriptSourceMapping}
    486  * @param {WebInspector.ScriptSnippetModel} scriptSnippetModel
    487  */
    488 WebInspector.SnippetScriptMapping = function(scriptSnippetModel)
    489 {
    490     this._scriptSnippetModel = scriptSnippetModel;
    491 }
    492 
    493 WebInspector.SnippetScriptMapping.prototype = {
    494     /**
    495      * @param {WebInspector.RawLocation} rawLocation
    496      * @return {WebInspector.UILocation}
    497      */
    498     rawLocationToUILocation: function(rawLocation)
    499     {
    500         var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */(rawLocation);
    501         return this._scriptSnippetModel._rawLocationToUILocation(debuggerModelLocation);
    502     },
    503 
    504     /**
    505      * @param {WebInspector.UISourceCode} uiSourceCode
    506      * @param {number} lineNumber
    507      * @param {number} columnNumber
    508      * @return {WebInspector.DebuggerModel.Location}
    509      */
    510     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
    511     {
    512         return this._scriptSnippetModel._uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber);
    513     },
    514 
    515     /**
    516      * @return {boolean}
    517      */
    518     isIdentity: function()
    519     {
    520         return true;
    521     },
    522 
    523     /**
    524      * @param {string} sourceURL
    525      * @return {string|null}
    526      */
    527     snippetIdForSourceURL: function(sourceURL)
    528     {
    529         return this._scriptSnippetModel._snippetIdForSourceURL(sourceURL);
    530     },
    531 
    532     /**
    533      * @param {WebInspector.Script} script
    534      */
    535     addScript: function(script)
    536     {
    537         this._scriptSnippetModel._addScript(script);
    538     }
    539 }
    540 
    541 /**
    542  * @constructor
    543  * @implements {WebInspector.ContentProvider}
    544  * @param {WebInspector.Snippet} snippet
    545  */
    546 WebInspector.SnippetContentProvider = function(snippet)
    547 {
    548     this._snippet = snippet;
    549 }
    550 
    551 WebInspector.SnippetContentProvider.prototype = {
    552     /**
    553      * @return {string}
    554      */
    555     contentURL: function()
    556     {
    557         return "";
    558     },
    559 
    560     /**
    561      * @return {WebInspector.ResourceType}
    562      */
    563     contentType: function()
    564     {
    565         return WebInspector.resourceTypes.Script;
    566     },
    567 
    568     /**
    569      * @param {function(?string,boolean,string)} callback
    570      */
    571     requestContent: function(callback)
    572     {
    573         callback(this._snippet.content, false, WebInspector.resourceTypes.Script.canonicalMimeType());
    574     },
    575 
    576     /**
    577      * @param {string} query
    578      * @param {boolean} caseSensitive
    579      * @param {boolean} isRegex
    580      * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
    581      */
    582     searchInContent: function(query, caseSensitive, isRegex, callback)
    583     {
    584         function performSearch()
    585         {
    586             callback(WebInspector.ContentProvider.performSearchInContent(this._snippet.content, query, caseSensitive, isRegex));
    587         }
    588 
    589         // searchInContent should call back later.
    590         window.setTimeout(performSearch.bind(this), 0);
    591     },
    592 
    593     __proto__: WebInspector.ContentProvider.prototype
    594 }
    595 
    596 /**
    597  * @constructor
    598  * @extends {WebInspector.ContentProviderBasedProjectDelegate}
    599  * @param {WebInspector.ScriptSnippetModel} model
    600  */
    601 WebInspector.SnippetsProjectDelegate = function(model)
    602 {
    603     WebInspector.ContentProviderBasedProjectDelegate.call(this, WebInspector.projectTypes.Snippets);
    604     this._model = model;
    605 }
    606 
    607 WebInspector.SnippetsProjectDelegate.prototype = {
    608     /**
    609      * @override
    610      * @return {string}
    611      */
    612     id: function()
    613     {
    614         return WebInspector.projectTypes.Snippets + ":";
    615     },
    616 
    617     /**
    618      * @param {string} name
    619      * @param {WebInspector.ContentProvider} contentProvider
    620      * @return {string}
    621      */
    622     addSnippet: function(name, contentProvider)
    623     {
    624         return this.addContentProvider("", name, name, contentProvider, true, false);
    625     },
    626 
    627     /**
    628      * @return {boolean}
    629      */
    630     canSetFileContent: function()
    631     {
    632         return true;
    633     },
    634 
    635     /**
    636      * @param {string} path
    637      * @param {string} newContent
    638      * @param {function(?string)} callback
    639      */
    640     setFileContent: function(path, newContent, callback)
    641     {
    642         this._model._setScriptSnippetContent(path, newContent);
    643         callback("");
    644     },
    645 
    646     /**
    647      * @return {boolean}
    648      */
    649     canRename: function()
    650     {
    651         return true;
    652     },
    653 
    654     /**
    655      * @param {string} path
    656      * @param {string} newName
    657      * @param {function(boolean, string=)} callback
    658      */
    659     performRename: function(path, newName, callback)
    660     {
    661         this._model.renameScriptSnippet(path, newName, callback);
    662     },
    663 
    664     /**
    665      * @param {string} path
    666      * @param {?string} name
    667      * @param {function(?string)} callback
    668      */
    669     createFile: function(path, name, callback)
    670     {
    671         var filePath = this._model.createScriptSnippet();
    672         callback(filePath);
    673     },
    674 
    675     /**
    676      * @param {string} path
    677      */
    678     deleteFile: function(path)
    679     {
    680         this._model.deleteScriptSnippet(path);
    681     },
    682 
    683     __proto__: WebInspector.ContentProviderBasedProjectDelegate.prototype
    684 }
    685 
    686 /**
    687  * @type {?WebInspector.ScriptSnippetModel}
    688  */
    689 WebInspector.scriptSnippetModel = null;
    690