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 * @implements {WebInspector.DebuggerSourceMapping}
34 * @param {!WebInspector.DebuggerModel} debuggerModel
35 * @param {!WebInspector.Workspace} workspace
36 * @param {!WebInspector.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
38 WebInspector.ResourceScriptMapping = function(debuggerModel, workspace, debuggerWorkspaceBinding)
40 this._target = debuggerModel.target();
41 this._debuggerModel = debuggerModel;
42 this._workspace = workspace;
43 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
44 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
45 this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
46 /** @type {!Set.<string>} */
47 this._boundURLs = new Set();
49 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.ResourceScriptFile>} */
50 this._uiSourceCodeToScriptFile = new Map();
52 debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
55 WebInspector.ResourceScriptMapping.prototype = {
57 * @param {!WebInspector.DebuggerModel.Location} rawLocation
58 * @return {?WebInspector.UILocation}
60 rawLocationToUILocation: function(rawLocation)
62 var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (rawLocation);
63 var script = debuggerModelLocation.script();
64 var uiSourceCode = this._workspaceUISourceCodeForScript(script);
67 var scriptFile = this.scriptFile(uiSourceCode);
68 if (scriptFile && ((scriptFile.hasDivergedFromVM() && !scriptFile.isMergingToVM()) || scriptFile.isDivergingFromVM()))
70 var lineNumber = debuggerModelLocation.lineNumber - (script.isInlineScriptWithSourceURL() ? script.lineOffset : 0);
71 var columnNumber = debuggerModelLocation.columnNumber || 0;
72 if (script.isInlineScriptWithSourceURL() && !lineNumber && columnNumber)
73 columnNumber -= script.columnOffset;
74 return uiSourceCode.uiLocation(lineNumber, columnNumber);
78 * @param {!WebInspector.UISourceCode} uiSourceCode
79 * @param {number} lineNumber
80 * @param {number} columnNumber
81 * @return {?WebInspector.DebuggerModel.Location}
83 uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
85 var scripts = this._scriptsForUISourceCode(uiSourceCode);
86 console.assert(scripts.length);
87 var script = scripts[0];
88 if (script.isInlineScriptWithSourceURL())
89 return this._debuggerModel.createRawLocation(script, lineNumber + script.lineOffset, lineNumber ? columnNumber : columnNumber + script.columnOffset);
90 return this._debuggerModel.createRawLocation(script, lineNumber, columnNumber);
94 * @param {!WebInspector.Script} script
96 addScript: function(script)
98 if (script.isAnonymousScript())
100 this._debuggerWorkspaceBinding.pushSourceMapping(script, this);
102 var uiSourceCode = this._workspaceUISourceCodeForScript(script);
106 this._bindUISourceCodeToScripts(uiSourceCode, [script]);
112 isIdentity: function()
118 * @param {!WebInspector.UISourceCode} uiSourceCode
119 * @param {number} lineNumber
122 uiLineHasMapping: function(uiSourceCode, lineNumber)
128 * @param {!WebInspector.UISourceCode} uiSourceCode
129 * @return {?WebInspector.ResourceScriptFile}
131 scriptFile: function(uiSourceCode)
133 return this._uiSourceCodeToScriptFile.get(uiSourceCode) || null;
137 * @param {!WebInspector.UISourceCode} uiSourceCode
138 * @param {?WebInspector.ResourceScriptFile} scriptFile
140 _setScriptFile: function(uiSourceCode, scriptFile)
143 this._uiSourceCodeToScriptFile.set(uiSourceCode, scriptFile);
145 this._uiSourceCodeToScriptFile.remove(uiSourceCode);
149 * @param {!WebInspector.Event} event
151 _uiSourceCodeAdded: function(event)
153 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
154 if (!uiSourceCode.url)
156 if (uiSourceCode.project().isServiceProject())
159 var scripts = this._scriptsForUISourceCode(uiSourceCode);
163 this._bindUISourceCodeToScripts(uiSourceCode, scripts);
167 * @param {!WebInspector.Event} event
169 _uiSourceCodeRemoved: function(event)
171 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
172 if (!uiSourceCode.url)
174 if (uiSourceCode.project().isServiceProject())
177 this._unbindUISourceCode(uiSourceCode);
181 * @param {!WebInspector.UISourceCode} uiSourceCode
183 _hasMergedToVM: function(uiSourceCode)
185 var scripts = this._scriptsForUISourceCode(uiSourceCode);
188 for (var i = 0; i < scripts.length; ++i)
189 this._debuggerWorkspaceBinding.updateLocations(scripts[i]);
193 * @param {!WebInspector.UISourceCode} uiSourceCode
195 _hasDivergedFromVM: function(uiSourceCode)
197 var scripts = this._scriptsForUISourceCode(uiSourceCode);
200 for (var i = 0; i < scripts.length; ++i)
201 this._debuggerWorkspaceBinding.updateLocations(scripts[i]);
205 * @param {!WebInspector.Script} script
206 * @return {?WebInspector.UISourceCode}
208 _workspaceUISourceCodeForScript: function(script)
210 if (script.isAnonymousScript())
212 return this._workspace.uiSourceCodeForURL(script.sourceURL);
216 * @param {!WebInspector.UISourceCode} uiSourceCode
217 * @return {!Array.<!WebInspector.Script>}
219 _scriptsForUISourceCode: function(uiSourceCode)
221 if (!uiSourceCode.url)
223 return this._debuggerModel.scriptsForSourceURL(uiSourceCode.url);
227 * @param {!WebInspector.UISourceCode} uiSourceCode
228 * @param {!Array.<!WebInspector.Script>} scripts
230 _bindUISourceCodeToScripts: function(uiSourceCode, scripts)
232 console.assert(scripts.length);
233 var scriptFile = new WebInspector.ResourceScriptFile(this, uiSourceCode, scripts);
234 this._setScriptFile(uiSourceCode, scriptFile);
235 for (var i = 0; i < scripts.length; ++i)
236 this._debuggerWorkspaceBinding.updateLocations(scripts[i]);
237 this._debuggerWorkspaceBinding.setSourceMapping(this._target, uiSourceCode, this);
238 this._boundURLs.add(uiSourceCode.url);
242 * @param {!WebInspector.UISourceCode} uiSourceCode
244 _unbindUISourceCode: function(uiSourceCode)
246 var scriptFile = this.scriptFile(uiSourceCode);
248 scriptFile.dispose();
249 this._setScriptFile(uiSourceCode, null);
251 this._debuggerWorkspaceBinding.setSourceMapping(this._target, uiSourceCode, null);
254 _debuggerReset: function()
256 var boundURLs = this._boundURLs.valuesArray();
257 for (var i = 0; i < boundURLs.length; ++i)
259 var uiSourceCode = this._workspace.uiSourceCodeForURL(boundURLs[i]);
262 this._unbindUISourceCode(uiSourceCode);
264 this._boundURLs.clear();
269 this._debuggerReset();
270 this._workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
271 this._workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
278 * @extends {WebInspector.Object}
279 * @param {!WebInspector.ResourceScriptMapping} resourceScriptMapping
280 * @param {!WebInspector.UISourceCode} uiSourceCode
281 * @param {!Array.<!WebInspector.Script>} scripts
283 WebInspector.ResourceScriptFile = function(resourceScriptMapping, uiSourceCode, scripts)
285 console.assert(scripts.length);
287 this._resourceScriptMapping = resourceScriptMapping;
288 this._uiSourceCode = uiSourceCode;
290 if (this._uiSourceCode.contentType() === WebInspector.resourceTypes.Script)
291 this._script = scripts[0];
293 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
297 WebInspector.ResourceScriptFile.Events = {
298 DidMergeToVM: "DidMergeToVM",
299 DidDivergeFromVM: "DidDivergeFromVM",
302 WebInspector.ResourceScriptFile.prototype = {
304 * @param {function(?string,!DebuggerAgent.SetScriptSourceError=,!WebInspector.Script=)=} callback
306 commitLiveEdit: function(callback)
308 var target = this._resourceScriptMapping._target;
310 * @param {?string} error
311 * @param {!DebuggerAgent.SetScriptSourceError=} errorData
312 * @this {WebInspector.ResourceScriptFile}
314 function innerCallback(error, errorData)
317 this._scriptSource = source;
320 callback(error, errorData, this._script);
324 var source = this._uiSourceCode.workingCopy();
325 target.debuggerModel.setScriptSource(this._script.scriptId, source, innerCallback.bind(this));
331 _isDiverged: function()
333 if (this._uiSourceCode.isDirty())
337 if (typeof this._scriptSource === "undefined")
339 if (!this._uiSourceCode.workingCopy().startsWith(this._scriptSource))
341 var suffix = this._uiSourceCode.workingCopy().substr(this._scriptSource.length);
342 return !!suffix.length && !suffix.match(WebInspector.Script.sourceURLRegex);
346 * @param {!WebInspector.Event} event
348 _workingCopyChanged: function(event)
355 if (this._isDiverged() && !this._hasDivergedFromVM)
356 this._divergeFromVM();
357 else if (!this._isDiverged() && this._hasDivergedFromVM)
361 _divergeFromVM: function()
363 this._isDivergingFromVM = true;
364 this._resourceScriptMapping._hasDivergedFromVM(this._uiSourceCode);
365 delete this._isDivergingFromVM;
366 this._hasDivergedFromVM = true;
367 this.dispatchEventToListeners(WebInspector.ResourceScriptFile.Events.DidDivergeFromVM, this._uiSourceCode);
370 _mergeToVM: function()
372 delete this._hasDivergedFromVM;
373 this._isMergingToVM = true;
374 this._resourceScriptMapping._hasMergedToVM(this._uiSourceCode);
375 delete this._isMergingToVM;
376 this.dispatchEventToListeners(WebInspector.ResourceScriptFile.Events.DidMergeToVM, this._uiSourceCode);
382 hasDivergedFromVM: function()
384 return this._hasDivergedFromVM;
390 isDivergingFromVM: function()
392 return this._isDivergingFromVM;
398 isMergingToVM: function()
400 return this._isMergingToVM;
403 checkMapping: function()
407 if (typeof this._scriptSource !== "undefined")
409 this._script.requestContent(callback.bind(this));
412 * @param {?string} source
413 * @this {WebInspector.ResourceScriptFile}
415 function callback(source)
417 this._scriptSource = source;
423 * @return {?WebInspector.Target}
429 return this._script.target();
434 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
438 * @param {string} sourceMapURL
440 addSourceMapURL: function(sourceMapURL)
444 this._script.addSourceMapURL(sourceMapURL);
447 __proto__: WebInspector.Object.prototype