2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.Object}
34 * @implements {WebInspector.TargetManager.Observer}
35 * @param {!WebInspector.Workspace} workspace
37 WebInspector
.ScriptSnippetModel = function(workspace
)
39 this._workspace
= workspace
;
40 /** @type {!Object.<string, !WebInspector.UISourceCode>} */
41 this._uiSourceCodeForSnippetId
= {};
42 /** @type {!Map.<!WebInspector.UISourceCode, string>} */
43 this._snippetIdForUISourceCode
= new Map();
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
);
53 WebInspector
.targetManager
.observeTargets(this);
56 WebInspector
.ScriptSnippetModel
.snippetSourceURLPrefix
= "snippets:///";
59 WebInspector
.ScriptSnippetModel
.prototype = {
63 * @param {!WebInspector.Target} target
65 targetAdded: function(target
)
67 var debuggerModel
= WebInspector
.DebuggerModel
.fromTarget(target
);
69 this._mappingForTarget
.set(target
, new WebInspector
.SnippetScriptMapping(debuggerModel
, this));
74 * @param {!WebInspector.Target} target
76 targetRemoved: function(target
)
78 if (WebInspector
.DebuggerModel
.fromTarget(target
))
79 this._mappingForTarget
.remove(target
);
83 * @param {!WebInspector.Target} target
84 * @return {!WebInspector.SnippetScriptMapping|undefined}
86 snippetScriptMapping: function(target
)
88 return this._mappingForTarget
.get(target
);
92 * @return {!WebInspector.Project}
99 _loadSnippets: function()
101 var snippets
= this._snippetStorage
.snippets();
102 for (var i
= 0; i
< snippets
.length
; ++i
)
103 this._addScriptSnippet(snippets
[i
]);
107 * @param {string} content
110 createScriptSnippet: function(content
)
112 var snippet
= this._snippetStorage
.createSnippet();
113 snippet
.content
= content
;
114 return this._addScriptSnippet(snippet
);
118 * @param {!WebInspector.Snippet} snippet
121 _addScriptSnippet: function(snippet
)
123 var path
= this._projectDelegate
.addSnippet(snippet
.name
, new WebInspector
.SnippetContentProvider(snippet
));
124 var uiSourceCode
= this._workspace
.uiSourceCode(this._projectId
, path
);
126 console
.assert(uiSourceCode
);
129 uiSourceCode
.addEventListener(WebInspector
.UISourceCode
.Events
.WorkingCopyChanged
, this._workingCopyChanged
, this);
130 this._snippetIdForUISourceCode
.set(uiSourceCode
, snippet
.id
);
131 var breakpointLocations
= this._removeBreakpoints(uiSourceCode
);
132 this._restoreBreakpoints(uiSourceCode
, breakpointLocations
);
133 this._uiSourceCodeForSnippetId
[snippet
.id
] = uiSourceCode
;
138 * @param {!WebInspector.Event} event
140 _workingCopyChanged: function(event
)
142 var uiSourceCode
= /** @type {!WebInspector.UISourceCode} */ (event
.target
);
143 this._scriptSnippetEdited(uiSourceCode
);
147 * @param {string} path
149 deleteScriptSnippet: function(path
)
151 var uiSourceCode
= this._workspace
.uiSourceCode(this._projectId
, path
);
154 var snippetId
= this._snippetIdForUISourceCode
.get(uiSourceCode
) || "";
155 var snippet
= this._snippetStorage
.snippetForId(snippetId
);
156 this._snippetStorage
.deleteSnippet(snippet
);
157 this._removeBreakpoints(uiSourceCode
);
158 this._releaseSnippetScript(uiSourceCode
);
159 delete this._uiSourceCodeForSnippetId
[snippet
.id
];
160 this._snippetIdForUISourceCode
.remove(uiSourceCode
);
161 this._projectDelegate
.removeFile(snippet
.name
);
165 * @param {string} name
166 * @param {string} newName
167 * @param {function(boolean, string=)} callback
169 renameScriptSnippet: function(name
, newName
, callback
)
171 newName
= newName
.trim();
172 if (!newName
|| newName
.indexOf("/") !== -1 || name
=== newName
|| this._snippetStorage
.snippetForName(newName
)) {
176 var snippet
= this._snippetStorage
.snippetForName(name
);
177 console
.assert(snippet
, "Snippet '" + name
+ "' was not found.");
178 var uiSourceCode
= this._uiSourceCodeForSnippetId
[snippet
.id
];
179 console
.assert(uiSourceCode
, "No uiSourceCode was found for snippet '" + name
+ "'.");
181 var breakpointLocations
= this._removeBreakpoints(uiSourceCode
);
182 snippet
.name
= newName
;
183 this._restoreBreakpoints(uiSourceCode
, breakpointLocations
);
184 callback(true, newName
);
188 * @param {string} name
189 * @param {string} newContent
191 _setScriptSnippetContent: function(name
, newContent
)
193 var snippet
= this._snippetStorage
.snippetForName(name
);
194 snippet
.content
= newContent
;
198 * @param {!WebInspector.UISourceCode} uiSourceCode
200 _scriptSnippetEdited: function(uiSourceCode
)
202 var breakpointLocations
= this._removeBreakpoints(uiSourceCode
);
203 this._releaseSnippetScript(uiSourceCode
);
204 this._restoreBreakpoints(uiSourceCode
, breakpointLocations
);
205 this._mappingForTarget
.valuesArray().forEach(function(mapping
) {mapping
._restoreBreakpoints(uiSourceCode
, breakpointLocations
);});
211 _nextEvaluationIndex: function()
213 var evaluationIndex
= this._lastSnippetEvaluationIndexSetting
.get() + 1;
214 this._lastSnippetEvaluationIndexSetting
.set(evaluationIndex
);
215 return evaluationIndex
;
219 * @param {!WebInspector.ExecutionContext} executionContext
220 * @param {!WebInspector.UISourceCode} uiSourceCode
222 evaluateScriptSnippet: function(executionContext
, uiSourceCode
)
224 var breakpointLocations
= this._removeBreakpoints(uiSourceCode
);
225 this._releaseSnippetScript(uiSourceCode
);
226 this._restoreBreakpoints(uiSourceCode
, breakpointLocations
);
228 var target
= executionContext
.target();
229 var debuggerModel
= executionContext
.debuggerModel
;
230 var evaluationIndex
= this._nextEvaluationIndex();
231 var mapping
= this._mappingForTarget
.get(target
);
232 mapping
._setEvaluationIndex(evaluationIndex
, uiSourceCode
);
233 var evaluationUrl
= mapping
._evaluationSourceURL(uiSourceCode
);
234 var expression
= uiSourceCode
.workingCopy();
235 WebInspector
.console
.show();
236 debuggerModel
.compileScript(expression
, "", true, executionContext
.id
, compileCallback
.bind(this));
239 * @param {!DebuggerAgent.ScriptId=} scriptId
240 * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
241 * @this {WebInspector.ScriptSnippetModel}
243 function compileCallback(scriptId
, exceptionDetails
)
245 var mapping
= this._mappingForTarget
.get(target
);
246 if (mapping
.evaluationIndex(uiSourceCode
) !== evaluationIndex
)
250 this._printRunOrCompileScriptResultFailure(target
, exceptionDetails
, evaluationUrl
);
254 mapping
._addScript(debuggerModel
.scriptForId(scriptId
), uiSourceCode
);
255 var breakpointLocations
= this._removeBreakpoints(uiSourceCode
);
256 this._restoreBreakpoints(uiSourceCode
, breakpointLocations
);
258 this._runScript(scriptId
, executionContext
, evaluationUrl
);
263 * @param {!DebuggerAgent.ScriptId} scriptId
264 * @param {!WebInspector.ExecutionContext} executionContext
265 * @param {?string=} sourceURL
267 _runScript: function(scriptId
, executionContext
, sourceURL
)
269 var target
= executionContext
.target();
270 executionContext
.debuggerModel
.runScript(scriptId
, executionContext
.id
, "console", false, runCallback
.bind(this, target
));
273 * @param {!WebInspector.Target} target
274 * @param {?RuntimeAgent.RemoteObject} result
275 * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
276 * @this {WebInspector.ScriptSnippetModel}
278 function runCallback(target
, result
, exceptionDetails
)
280 if (!exceptionDetails
)
281 this._printRunScriptResult(target
, result
, sourceURL
);
283 this._printRunOrCompileScriptResultFailure(target
, exceptionDetails
, sourceURL
);
288 * @param {!WebInspector.Target} target
289 * @param {?RuntimeAgent.RemoteObject} result
290 * @param {?string=} sourceURL
292 _printRunScriptResult: function(target
, result
, sourceURL
)
294 var consoleMessage
= new WebInspector
.ConsoleMessage(
296 WebInspector
.ConsoleMessage
.MessageSource
.JS
,
297 WebInspector
.ConsoleMessage
.MessageLevel
.Log
,
306 target
.consoleModel
.addMessage(consoleMessage
);
310 * @param {!WebInspector.Target} target
311 * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
312 * @param {?string=} sourceURL
314 _printRunOrCompileScriptResultFailure: function(target
, exceptionDetails
, sourceURL
)
316 var consoleMessage
= new WebInspector
.ConsoleMessage(
318 exceptionDetails
.source
,
319 WebInspector
.ConsoleMessage
.MessageLevel
.Error
,
320 exceptionDetails
.text
,
323 exceptionDetails
.line
,
324 exceptionDetails
.column
,
327 exceptionDetails
.stackTrace
);
328 target
.consoleModel
.addMessage(consoleMessage
);
332 * @param {!WebInspector.UISourceCode} uiSourceCode
333 * @return {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>}
335 _removeBreakpoints: function(uiSourceCode
)
337 var breakpointLocations
= WebInspector
.breakpointManager
.breakpointLocationsForUISourceCode(uiSourceCode
);
338 for (var i
= 0; i
< breakpointLocations
.length
; ++i
)
339 breakpointLocations
[i
].breakpoint
.remove();
340 return breakpointLocations
;
344 * @param {!WebInspector.UISourceCode} uiSourceCode
345 * @param {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>} breakpointLocations
347 _restoreBreakpoints: function(uiSourceCode
, breakpointLocations
)
349 for (var i
= 0; i
< breakpointLocations
.length
; ++i
) {
350 var uiLocation
= breakpointLocations
[i
].uiLocation
;
351 var breakpoint
= breakpointLocations
[i
].breakpoint
;
352 WebInspector
.breakpointManager
.setBreakpoint(uiSourceCode
, uiLocation
.lineNumber
, uiLocation
.columnNumber
, breakpoint
.condition(), breakpoint
.enabled());
357 * @param {!WebInspector.UISourceCode} uiSourceCode
359 _releaseSnippetScript: function(uiSourceCode
)
361 this._mappingForTarget
.valuesArray().forEach(function(mapping
) {mapping
._releaseSnippetScript(uiSourceCode
);});
365 * @param {string} sourceURL
368 _snippetIdForSourceURL: function(sourceURL
)
370 var snippetPrefix
= WebInspector
.ScriptSnippetModel
.snippetSourceURLPrefix
;
371 if (!sourceURL
.startsWith(snippetPrefix
))
373 var splitURL
= sourceURL
.substring(snippetPrefix
.length
).split("_");
374 var snippetId
= splitURL
[0];
378 __proto__
: WebInspector
.Object
.prototype
383 * @implements {WebInspector.DebuggerSourceMapping}
384 * @param {!WebInspector.DebuggerModel} debuggerModel
385 * @param {!WebInspector.ScriptSnippetModel} scriptSnippetModel
387 WebInspector
.SnippetScriptMapping = function(debuggerModel
, scriptSnippetModel
)
389 this._target
= debuggerModel
.target();
390 this._debuggerModel
= debuggerModel
;
391 this._scriptSnippetModel
= scriptSnippetModel
;
392 /** @type {!Object.<string, !WebInspector.UISourceCode>} */
393 this._uiSourceCodeForScriptId
= {};
394 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.Script>} */
395 this._scriptForUISourceCode
= new Map();
396 /** @type {!Map.<!WebInspector.UISourceCode, number>} */
397 this._evaluationIndexForUISourceCode
= new Map();
398 debuggerModel
.addEventListener(WebInspector
.DebuggerModel
.Events
.GlobalObjectCleared
, this._reset
, this);
401 WebInspector
.SnippetScriptMapping
.prototype = {
403 * @param {!WebInspector.UISourceCode} uiSourceCode
405 _releaseSnippetScript: function(uiSourceCode
)
407 var script
= this._scriptForUISourceCode
.get(uiSourceCode
);
411 delete this._uiSourceCodeForScriptId
[script
.scriptId
];
412 this._scriptForUISourceCode
.remove(uiSourceCode
);
413 this._evaluationIndexForUISourceCode
.remove(uiSourceCode
);
417 +* @param {number} evaluationIndex
418 * @param {!WebInspector.UISourceCode} uiSourceCode
420 _setEvaluationIndex: function(evaluationIndex
, uiSourceCode
)
422 this._evaluationIndexForUISourceCode
.set(uiSourceCode
, evaluationIndex
);
426 * @param {!WebInspector.UISourceCode} uiSourceCode
427 * @return {number|undefined}
429 evaluationIndex: function(uiSourceCode
)
431 return this._evaluationIndexForUISourceCode
.get(uiSourceCode
);
435 * @param {!WebInspector.UISourceCode} uiSourceCode
438 _evaluationSourceURL: function(uiSourceCode
)
440 var evaluationSuffix
= "_" + this._evaluationIndexForUISourceCode
.get(uiSourceCode
);
441 var snippetId
= this._scriptSnippetModel
._snippetIdForUISourceCode
.get(uiSourceCode
);
442 return WebInspector
.ScriptSnippetModel
.snippetSourceURLPrefix
+ snippetId
+ evaluationSuffix
;
447 this._uiSourceCodeForScriptId
= {};
448 this._scriptForUISourceCode
.clear();
449 this._evaluationIndexForUISourceCode
.clear();
454 * @param {!WebInspector.DebuggerModel.Location} rawLocation
455 * @return {?WebInspector.UILocation}
457 rawLocationToUILocation: function(rawLocation
)
459 var debuggerModelLocation
= /** @type {!WebInspector.DebuggerModel.Location} */(rawLocation
);
460 var uiSourceCode
= this._uiSourceCodeForScriptId
[debuggerModelLocation
.scriptId
];
464 return uiSourceCode
.uiLocation(debuggerModelLocation
.lineNumber
, debuggerModelLocation
.columnNumber
|| 0);
469 * @param {!WebInspector.UISourceCode} uiSourceCode
470 * @param {number} lineNumber
471 * @param {number} columnNumber
472 * @return {?WebInspector.DebuggerModel.Location}
474 uiLocationToRawLocation: function(uiSourceCode
, lineNumber
, columnNumber
)
476 var script
= this._scriptForUISourceCode
.get(uiSourceCode
);
480 return this._debuggerModel
.createRawLocation(script
, lineNumber
, columnNumber
);
484 * @param {!WebInspector.Script} script
485 * @param {!WebInspector.UISourceCode} uiSourceCode
487 _addScript: function(script
, uiSourceCode
)
489 console
.assert(!this._scriptForUISourceCode
.get(uiSourceCode
));
490 WebInspector
.debuggerWorkspaceBinding
.setSourceMapping(this._target
, uiSourceCode
, this);
491 this._uiSourceCodeForScriptId
[script
.scriptId
] = uiSourceCode
;
492 this._scriptForUISourceCode
.set(uiSourceCode
, script
);
493 WebInspector
.debuggerWorkspaceBinding
.pushSourceMapping(script
, this);
497 * @param {!WebInspector.UISourceCode} uiSourceCode
498 * @param {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>} breakpointLocations
500 _restoreBreakpoints: function(uiSourceCode
, breakpointLocations
)
502 var script
= this._scriptForUISourceCode
.get(uiSourceCode
);
505 var rawLocation
= /** @type {!WebInspector.DebuggerModel.Location} */ (this._debuggerModel
.createRawLocation(script
, 0, 0));
506 var scriptUISourceCode
= WebInspector
.debuggerWorkspaceBinding
.rawLocationToUILocation(rawLocation
).uiSourceCode
;
507 if (scriptUISourceCode
)
508 this._scriptSnippetModel
._restoreBreakpoints(scriptUISourceCode
, breakpointLocations
);
515 isIdentity: function()
522 * @param {!WebInspector.UISourceCode} uiSourceCode
523 * @param {number} lineNumber
526 uiLineHasMapping: function(uiSourceCode
, lineNumber
)
534 * @implements {WebInspector.ContentProvider}
535 * @param {!WebInspector.Snippet} snippet
537 WebInspector
.SnippetContentProvider = function(snippet
)
539 this._snippet
= snippet
;
542 WebInspector
.SnippetContentProvider
.prototype = {
547 contentURL: function()
554 * @return {!WebInspector.ResourceType}
556 contentType: function()
558 return WebInspector
.resourceTypes
.Script
;
563 * @param {function(?string)} callback
565 requestContent: function(callback
)
567 callback(this._snippet
.content
);
572 * @param {string} query
573 * @param {boolean} caseSensitive
574 * @param {boolean} isRegex
575 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
577 searchInContent: function(query
, caseSensitive
, isRegex
, callback
)
580 * @this {WebInspector.SnippetContentProvider}
582 function performSearch()
584 callback(WebInspector
.ContentProvider
.performSearchInContent(this._snippet
.content
, query
, caseSensitive
, isRegex
));
587 // searchInContent should call back later.
588 window
.setTimeout(performSearch
.bind(this), 0);
594 * @extends {WebInspector.ContentProviderBasedProjectDelegate}
595 * @param {!WebInspector.Workspace} workspace
596 * @param {!WebInspector.ScriptSnippetModel} model
599 WebInspector
.SnippetsProjectDelegate = function(workspace
, model
, id
)
601 WebInspector
.ContentProviderBasedProjectDelegate
.call(this, workspace
, id
, WebInspector
.projectTypes
.Snippets
);
605 WebInspector
.SnippetsProjectDelegate
.prototype = {
607 * @param {string} name
608 * @param {!WebInspector.ContentProvider} contentProvider
611 addSnippet: function(name
, contentProvider
)
613 return this.addContentProvider("", name
, name
, name
, contentProvider
);
620 canSetFileContent: function()
627 * @param {string} path
628 * @param {string} newContent
629 * @param {function(?string)} callback
631 setFileContent: function(path
, newContent
, callback
)
633 this._model
._setScriptSnippetContent(path
, newContent
);
641 canRename: function()
648 * @param {string} path
649 * @param {string} newName
650 * @param {function(boolean, string=)} callback
652 performRename: function(path
, newName
, callback
)
654 this._model
.renameScriptSnippet(path
, newName
, callback
);
659 * @param {string} path
660 * @param {?string} name
661 * @param {string} content
662 * @param {function(?string)} callback
664 createFile: function(path
, name
, content
, callback
)
666 var filePath
= this._model
.createScriptSnippet(content
);
672 * @param {string} path
674 deleteFile: function(path
)
676 this._model
.deleteScriptSnippet(path
);
679 __proto__
: WebInspector
.ContentProviderBasedProjectDelegate
.prototype
683 * @type {!WebInspector.ScriptSnippetModel}
685 WebInspector
.scriptSnippetModel
= new WebInspector
.ScriptSnippetModel(WebInspector
.workspace
);