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.NetworkMapping} networkMapping
37 * @param {!WebInspector.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
39 WebInspector.ResourceScriptMapping = function(debuggerModel, workspace, networkMapping, debuggerWorkspaceBinding)
41 this._target = debuggerModel.target();
42 this._debuggerModel = debuggerModel;
43 this._workspace = workspace;
44 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
45 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
46 this._networkMapping = networkMapping;
47 this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
48 /** @type {!Set.<string>} */
49 this._boundURLs = new Set();
51 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.ResourceScriptFile>} */
52 this._uiSourceCodeToScriptFile = new Map();
54 debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
57 WebInspector.ResourceScriptMapping.prototype = {
60 * @param {!WebInspector.DebuggerModel.Location} rawLocation
61 * @return {?WebInspector.UILocation}
63 rawLocationToUILocation: function(rawLocation)
65 var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (rawLocation);
66 var script = debuggerModelLocation.script();
67 var uiSourceCode = this._workspaceUISourceCodeForScript(script);
70 var scriptFile = this.scriptFile(uiSourceCode);
71 if (scriptFile && ((scriptFile.hasDivergedFromVM() && !scriptFile.isMergingToVM()) || scriptFile.isDivergingFromVM()))
73 var lineNumber = debuggerModelLocation.lineNumber - (script.isInlineScriptWithSourceURL() ? script.lineOffset : 0);
74 var columnNumber = debuggerModelLocation.columnNumber || 0;
75 if (script.isInlineScriptWithSourceURL() && !lineNumber && columnNumber)
76 columnNumber -= script.columnOffset;
77 return uiSourceCode.uiLocation(lineNumber, columnNumber);
82 * @param {!WebInspector.UISourceCode} uiSourceCode
83 * @param {number} lineNumber
84 * @param {number} columnNumber
85 * @return {?WebInspector.DebuggerModel.Location}
87 uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
89 var scripts = this._scriptsForUISourceCode(uiSourceCode);
90 console.assert(scripts.length);
91 var script = scripts[0];
92 if (script.isInlineScriptWithSourceURL())
93 return this._debuggerModel.createRawLocation(script, lineNumber + script.lineOffset, lineNumber ? columnNumber : columnNumber + script.columnOffset);
94 return this._debuggerModel.createRawLocation(script, lineNumber, columnNumber);
98 * @param {!WebInspector.Script} script
100 addScript: function(script)
102 if (script.isAnonymousScript())
104 this._debuggerWorkspaceBinding.pushSourceMapping(script, this);
106 var uiSourceCode = this._workspaceUISourceCodeForScript(script);
110 this._bindUISourceCodeToScripts(uiSourceCode, [script]);
117 isIdentity: function()
124 * @param {!WebInspector.UISourceCode} uiSourceCode
125 * @param {number} lineNumber
128 uiLineHasMapping: function(uiSourceCode, lineNumber)
134 * @param {!WebInspector.UISourceCode} uiSourceCode
135 * @return {?WebInspector.ResourceScriptFile}
137 scriptFile: function(uiSourceCode)
139 return this._uiSourceCodeToScriptFile.get(uiSourceCode) || null;
143 * @param {!WebInspector.UISourceCode} uiSourceCode
144 * @param {?WebInspector.ResourceScriptFile} scriptFile
146 _setScriptFile: function(uiSourceCode, scriptFile)
149 this._uiSourceCodeToScriptFile.set(uiSourceCode, scriptFile);
151 this._uiSourceCodeToScriptFile.remove(uiSourceCode);
155 * @param {!WebInspector.Event} event
157 _uiSourceCodeAdded: function(event)
159 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
160 if (!this._networkMapping.networkURL(uiSourceCode))
162 if (uiSourceCode.project().isServiceProject())
165 var scripts = this._scriptsForUISourceCode(uiSourceCode);
169 this._bindUISourceCodeToScripts(uiSourceCode, scripts);
173 * @param {!WebInspector.Event} event
175 _uiSourceCodeRemoved: function(event)
177 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
178 if (!this._networkMapping.networkURL(uiSourceCode))
180 if (uiSourceCode.project().isServiceProject())
183 this._unbindUISourceCode(uiSourceCode);
187 * @param {!WebInspector.UISourceCode} uiSourceCode
189 _hasMergedToVM: function(uiSourceCode)
191 var scripts = this._scriptsForUISourceCode(uiSourceCode);
194 for (var i = 0; i < scripts.length; ++i)
195 this._debuggerWorkspaceBinding.updateLocations(scripts[i]);
199 * @param {!WebInspector.UISourceCode} uiSourceCode
201 _hasDivergedFromVM: function(uiSourceCode)
203 var scripts = this._scriptsForUISourceCode(uiSourceCode);
206 for (var i = 0; i < scripts.length; ++i)
207 this._debuggerWorkspaceBinding.updateLocations(scripts[i]);
211 * @param {!WebInspector.Script} script
212 * @return {?WebInspector.UISourceCode}
214 _workspaceUISourceCodeForScript: function(script)
216 if (script.isAnonymousScript())
218 return this._networkMapping.uiSourceCodeForURL(script.sourceURL, this._target);
222 * @param {!WebInspector.UISourceCode} uiSourceCode
223 * @return {!Array.<!WebInspector.Script>}
225 _scriptsForUISourceCode: function(uiSourceCode)
227 var target = WebInspector.NetworkProject.targetForUISourceCode(uiSourceCode);
228 if (target && target !== this._debuggerModel.target())
230 if (!this._networkMapping.networkURL(uiSourceCode))
232 return this._debuggerModel.scriptsForSourceURL(this._networkMapping.networkURL(uiSourceCode));
236 * @param {!WebInspector.UISourceCode} uiSourceCode
237 * @param {!Array.<!WebInspector.Script>} scripts
239 _bindUISourceCodeToScripts: function(uiSourceCode, scripts)
241 console.assert(scripts.length);
242 // Due to different listeners order, a script file could be created just before uiSourceCode
243 // for the corresponding script was created. Check that we don't create scriptFile twice.
244 var boundScriptFile = this.scriptFile(uiSourceCode);
245 if (boundScriptFile && boundScriptFile.hasScripts(scripts))
248 var scriptFile = new WebInspector.ResourceScriptFile(this, uiSourceCode, scripts);
249 this._setScriptFile(uiSourceCode, scriptFile);
250 for (var i = 0; i < scripts.length; ++i)
251 this._debuggerWorkspaceBinding.updateLocations(scripts[i]);
252 this._debuggerWorkspaceBinding.setSourceMapping(this._target, uiSourceCode, this);
253 this._boundURLs.add(this._networkMapping.networkURL(uiSourceCode));
257 * @param {!WebInspector.UISourceCode} uiSourceCode
259 _unbindUISourceCode: function(uiSourceCode)
261 var scriptFile = this.scriptFile(uiSourceCode);
263 scriptFile.dispose();
264 this._setScriptFile(uiSourceCode, null);
266 this._debuggerWorkspaceBinding.setSourceMapping(this._target, uiSourceCode, null);
269 _debuggerReset: function()
271 var boundURLs = this._boundURLs.valuesArray();
272 for (var i = 0; i < boundURLs.length; ++i)
274 var uiSourceCode = this._networkMapping.uiSourceCodeForURL(boundURLs[i], this._target);
277 this._unbindUISourceCode(uiSourceCode);
279 this._boundURLs.clear();
284 this._debuggerReset();
285 this._workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
286 this._workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
293 * @extends {WebInspector.Object}
294 * @param {!WebInspector.ResourceScriptMapping} resourceScriptMapping
295 * @param {!WebInspector.UISourceCode} uiSourceCode
296 * @param {!Array.<!WebInspector.Script>} scripts
298 WebInspector.ResourceScriptFile = function(resourceScriptMapping, uiSourceCode, scripts)
300 console.assert(scripts.length);
302 this._resourceScriptMapping = resourceScriptMapping;
303 this._uiSourceCode = uiSourceCode;
305 if (this._uiSourceCode.contentType() === WebInspector.resourceTypes.Script)
306 this._script = scripts[0];
308 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
311 WebInspector.ResourceScriptFile.Events = {
312 DidMergeToVM: "DidMergeToVM",
313 DidDivergeFromVM: "DidDivergeFromVM",
316 WebInspector.ResourceScriptFile.prototype = {
318 * @param {!Array.<!WebInspector.Script>} scripts
321 hasScripts: function(scripts)
323 return this._script && this._script === scripts[0];
327 * @param {function(?string,!DebuggerAgent.SetScriptSourceError=,!WebInspector.Script=)=} callback
329 commitLiveEdit: function(callback)
332 * @param {?string} error
333 * @param {!DebuggerAgent.SetScriptSourceError=} errorData
334 * @this {WebInspector.ResourceScriptFile}
336 function innerCallback(error, errorData)
339 this._scriptSource = source;
342 callback(error, errorData, this._script);
346 var debuggerModel = this._resourceScriptMapping._debuggerModel;
347 var source = this._uiSourceCode.workingCopy();
348 debuggerModel.setScriptSource(this._script.scriptId, source, innerCallback.bind(this));
354 _isDiverged: function()
356 if (this._uiSourceCode.isDirty())
360 if (typeof this._scriptSource === "undefined")
362 if (!this._uiSourceCode.workingCopy().startsWith(this._scriptSource))
364 var suffix = this._uiSourceCode.workingCopy().substr(this._scriptSource.length);
365 return !!suffix.length && !suffix.match(WebInspector.Script.sourceURLRegex);
369 * @param {!WebInspector.Event} event
371 _workingCopyChanged: function(event)
378 if (this._isDiverged() && !this._hasDivergedFromVM)
379 this._divergeFromVM();
380 else if (!this._isDiverged() && this._hasDivergedFromVM)
384 _divergeFromVM: function()
386 this._isDivergingFromVM = true;
387 this._resourceScriptMapping._hasDivergedFromVM(this._uiSourceCode);
388 delete this._isDivergingFromVM;
389 this._hasDivergedFromVM = true;
390 this.dispatchEventToListeners(WebInspector.ResourceScriptFile.Events.DidDivergeFromVM, this._uiSourceCode);
393 _mergeToVM: function()
395 delete this._hasDivergedFromVM;
396 this._isMergingToVM = true;
397 this._resourceScriptMapping._hasMergedToVM(this._uiSourceCode);
398 delete this._isMergingToVM;
399 this.dispatchEventToListeners(WebInspector.ResourceScriptFile.Events.DidMergeToVM, this._uiSourceCode);
405 hasDivergedFromVM: function()
407 return this._hasDivergedFromVM;
413 isDivergingFromVM: function()
415 return this._isDivergingFromVM;
421 isMergingToVM: function()
423 return this._isMergingToVM;
426 checkMapping: function()
430 if (typeof this._scriptSource !== "undefined")
432 this._script.requestContent(callback.bind(this));
435 * @param {?string} source
436 * @this {WebInspector.ResourceScriptFile}
438 function callback(source)
440 this._scriptSource = source;
446 * @return {?WebInspector.Target}
452 return this._script.target();
457 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
461 * @param {string} sourceMapURL
463 addSourceMapURL: function(sourceMapURL)
467 this._script.addSourceMapURL(sourceMapURL);
470 __proto__: WebInspector.Object.prototype