Home | History | Annotate | Download | only in bindings
      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.set(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.set(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         WebInspector.console.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.DebuggerSourceMapping}
    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.set(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.DebuggerModel.Location} 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         WebInspector.debuggerWorkspaceBinding.setSourceMapping(this._target, uiSourceCode, this);
    522         this._uiSourceCodeForScriptId[script.scriptId] = uiSourceCode;
    523         this._scriptForUISourceCode.set(uiSourceCode, script);
    524         WebInspector.debuggerWorkspaceBinding.pushSourceMapping(script, 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 rawLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (script.target().debuggerModel.createRawLocation(script, 0, 0));
    538         var scriptUISourceCode = WebInspector.debuggerWorkspaceBinding.rawLocationToUILocation(rawLocation).uiSourceCode;
    539         if (scriptUISourceCode)
    540             this._scriptSnippetModel._restoreBreakpoints(scriptUISourceCode, breakpointLocations);
    541     },
    542 
    543     /**
    544      * @return {boolean}
    545      */
    546     isIdentity: function()
    547     {
    548         return false;
    549     },
    550 
    551     /**
    552      * @param {!WebInspector.UISourceCode} uiSourceCode
    553      * @param {number} lineNumber
    554      * @return {boolean}
    555      */
    556     uiLineHasMapping: function(uiSourceCode, lineNumber)
    557     {
    558         return true;
    559     }
    560 }
    561 
    562 /**
    563  * @constructor
    564  * @implements {WebInspector.ContentProvider}
    565  * @param {!WebInspector.Snippet} snippet
    566  */
    567 WebInspector.SnippetContentProvider = function(snippet)
    568 {
    569     this._snippet = snippet;
    570 }
    571 
    572 WebInspector.SnippetContentProvider.prototype = {
    573     /**
    574      * @return {string}
    575      */
    576     contentURL: function()
    577     {
    578         return "";
    579     },
    580 
    581     /**
    582      * @return {!WebInspector.ResourceType}
    583      */
    584     contentType: function()
    585     {
    586         return WebInspector.resourceTypes.Script;
    587     },
    588 
    589     /**
    590      * @param {function(?string)} callback
    591      */
    592     requestContent: function(callback)
    593     {
    594         callback(this._snippet.content);
    595     },
    596 
    597     /**
    598      * @param {string} query
    599      * @param {boolean} caseSensitive
    600      * @param {boolean} isRegex
    601      * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
    602      */
    603     searchInContent: function(query, caseSensitive, isRegex, callback)
    604     {
    605         /**
    606          * @this {WebInspector.SnippetContentProvider}
    607          */
    608         function performSearch()
    609         {
    610             callback(WebInspector.ContentProvider.performSearchInContent(this._snippet.content, query, caseSensitive, isRegex));
    611         }
    612 
    613         // searchInContent should call back later.
    614         window.setTimeout(performSearch.bind(this), 0);
    615     }
    616 }
    617 
    618 /**
    619  * @constructor
    620  * @extends {WebInspector.ContentProviderBasedProjectDelegate}
    621  * @param {!WebInspector.Workspace} workspace
    622  * @param {!WebInspector.ScriptSnippetModel} model
    623  * @param {string} id
    624  */
    625 WebInspector.SnippetsProjectDelegate = function(workspace, model, id)
    626 {
    627     WebInspector.ContentProviderBasedProjectDelegate.call(this, workspace, id, WebInspector.projectTypes.Snippets);
    628     this._model = model;
    629 }
    630 
    631 WebInspector.SnippetsProjectDelegate.prototype = {
    632     /**
    633      * @param {string} name
    634      * @param {!WebInspector.ContentProvider} contentProvider
    635      * @return {string}
    636      */
    637     addSnippet: function(name, contentProvider)
    638     {
    639         return this.addContentProvider("", name, name, contentProvider);
    640     },
    641 
    642     /**
    643      * @return {boolean}
    644      */
    645     canSetFileContent: function()
    646     {
    647         return true;
    648     },
    649 
    650     /**
    651      * @param {string} path
    652      * @param {string} newContent
    653      * @param {function(?string)} callback
    654      */
    655     setFileContent: function(path, newContent, callback)
    656     {
    657         this._model._setScriptSnippetContent(path, newContent);
    658         callback("");
    659     },
    660 
    661     /**
    662      * @return {boolean}
    663      */
    664     canRename: function()
    665     {
    666         return true;
    667     },
    668 
    669     /**
    670      * @param {string} path
    671      * @param {string} newName
    672      * @param {function(boolean, string=)} callback
    673      */
    674     performRename: function(path, newName, callback)
    675     {
    676         this._model.renameScriptSnippet(path, newName, callback);
    677     },
    678 
    679     /**
    680      * @param {string} path
    681      * @param {?string} name
    682      * @param {string} content
    683      * @param {function(?string)} callback
    684      */
    685     createFile: function(path, name, content, callback)
    686     {
    687         var filePath = this._model.createScriptSnippet(content);
    688         callback(filePath);
    689     },
    690 
    691     /**
    692      * @param {string} path
    693      */
    694     deleteFile: function(path)
    695     {
    696         this._model.deleteScriptSnippet(path);
    697     },
    698 
    699     __proto__: WebInspector.ContentProviderBasedProjectDelegate.prototype
    700 }
    701 
    702 /**
    703  * @type {!WebInspector.ScriptSnippetModel}
    704  */
    705 WebInspector.scriptSnippetModel;
    706