Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / bindings / CompilerScriptMapping.js
blob1174ee19b32d0785c0ac9ebc08a56ff478ddb70c
1 /*
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
6 * met:
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.
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.
31 /**
32 * @constructor
33 * @implements {WebInspector.DebuggerSourceMapping}
34 * @param {!WebInspector.DebuggerModel} debuggerModel
35 * @param {!WebInspector.Workspace} workspace
36 * @param {!WebInspector.NetworkMapping} networkMapping
37 * @param {!WebInspector.NetworkProject} networkProject
38 * @param {!WebInspector.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
40 WebInspector.CompilerScriptMapping = function(debuggerModel, workspace, networkMapping, networkProject, debuggerWorkspaceBinding)
42 this._target = debuggerModel.target();
43 this._debuggerModel = debuggerModel;
44 this._workspace = workspace;
45 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);
46 this._networkMapping = networkMapping;
47 this._networkProject = networkProject;
48 this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
50 /** @type {!Object.<string, !WebInspector.SourceMap>} */
51 this._sourceMapForSourceMapURL = {};
52 /** @type {!Object.<string, !Array.<function(?WebInspector.SourceMap)>>} */
53 this._pendingSourceMapLoadingCallbacks = {};
54 /** @type {!Object.<string, !WebInspector.SourceMap>} */
55 this._sourceMapForScriptId = {};
56 /** @type {!Map.<!WebInspector.SourceMap, !WebInspector.Script>} */
57 this._scriptForSourceMap = new Map();
58 /** @type {!Map.<string, !WebInspector.SourceMap>} */
59 this._sourceMapForURL = new Map();
60 /** @type {!Map.<string, !WebInspector.UISourceCode>} */
61 this._stubUISourceCodes = new Map();
63 this._stubProjectID = "compiler-script-project";
64 this._stubProjectDelegate = new WebInspector.ContentProviderBasedProjectDelegate(this._workspace, this._stubProjectID, WebInspector.projectTypes.Service);
65 debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
68 WebInspector.CompilerScriptMapping.StubProjectID = "compiler-script-project";
70 WebInspector.CompilerScriptMapping.prototype = {
71 /**
72 * @param {!WebInspector.DebuggerModel.Location} rawLocation
73 * @return {boolean}
75 mapsToSourceCode: function (rawLocation) {
76 var sourceMap = this._sourceMapForScriptId[rawLocation.scriptId];
77 if (!sourceMap) {
78 return true;
80 return !!sourceMap.findEntry(rawLocation.lineNumber, rawLocation.columnNumber);
83 /**
84 * @override
85 * @param {!WebInspector.DebuggerModel.Location} rawLocation
86 * @return {?WebInspector.UILocation}
88 rawLocationToUILocation: function(rawLocation)
90 var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (rawLocation);
92 var stubUISourceCode = this._stubUISourceCodes.get(debuggerModelLocation.scriptId);
93 if (stubUISourceCode)
94 return new WebInspector.UILocation(stubUISourceCode, rawLocation.lineNumber, rawLocation.columnNumber);
96 var sourceMap = this._sourceMapForScriptId[debuggerModelLocation.scriptId];
97 if (!sourceMap)
98 return null;
99 var lineNumber = debuggerModelLocation.lineNumber;
100 var columnNumber = debuggerModelLocation.columnNumber || 0;
101 var entry = sourceMap.findEntry(lineNumber, columnNumber);
102 if (!entry || !entry.sourceURL)
103 return null;
104 var uiSourceCode = this._networkMapping.uiSourceCodeForURL(/** @type {string} */ (entry.sourceURL), this._target);
105 if (!uiSourceCode)
106 return null;
107 return uiSourceCode.uiLocation(/** @type {number} */ (entry.sourceLineNumber), /** @type {number} */ (entry.sourceColumnNumber));
111 * @override
112 * @param {!WebInspector.UISourceCode} uiSourceCode
113 * @param {number} lineNumber
114 * @param {number} columnNumber
115 * @return {?WebInspector.DebuggerModel.Location}
117 uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
119 if (uiSourceCode.project().type() === WebInspector.projectTypes.Service)
120 return null;
121 var networkURL = this._networkMapping.networkURL(uiSourceCode);
122 if (!networkURL)
123 return null;
124 var sourceMap = this._sourceMapForURL.get(networkURL);
125 if (!sourceMap)
126 return null;
127 var script = /** @type {!WebInspector.Script} */ (this._scriptForSourceMap.get(sourceMap));
128 console.assert(script);
129 var entry = sourceMap.firstSourceLineMapping(networkURL, lineNumber);
130 if (!entry)
131 return null;
132 return this._debuggerModel.createRawLocation(script, entry.lineNumber, entry.columnNumber);
136 * @param {!WebInspector.Script} script
138 addScript: function(script)
140 if (!script.sourceMapURL) {
141 script.addEventListener(WebInspector.Script.Events.SourceMapURLAdded, this._sourceMapURLAdded.bind(this));
142 return;
145 this._processScript(script);
149 * @param {!WebInspector.Event} event
151 _sourceMapURLAdded: function(event)
153 var script = /** @type {!WebInspector.Script} */ (event.target);
154 if (!script.sourceMapURL)
155 return;
156 this._processScript(script);
160 * @param {!WebInspector.Script} script
162 _processScript: function(script)
164 // Create stub UISourceCode for the time source mapping is being loaded.
165 var url = script.sourceURL;
166 var splitURL = WebInspector.ParsedURL.splitURLIntoPathComponents(url);
167 var parentPath = splitURL.slice(1, -1).join("/");
168 var name = splitURL.peekLast() || "";
169 var uiSourceCodePath = this._stubProjectDelegate.addContentProvider(parentPath, name, url, url, new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Script, "\n\n\n\n\n// Please wait a bit.\n// Compiled script is not shown while source map is being loaded!", url));
170 var stubUISourceCode = /** @type {!WebInspector.UISourceCode} */ (this._workspace.uiSourceCode(this._stubProjectID, uiSourceCodePath));
171 this._stubUISourceCodes.set(script.scriptId, stubUISourceCode);
173 this._debuggerWorkspaceBinding.pushSourceMapping(script, this);
174 this._loadSourceMapForScript(script, this._sourceMapLoaded.bind(this, script, uiSourceCodePath));
178 * @param {!WebInspector.Script} script
179 * @param {string} uiSourceCodePath
180 * @param {?WebInspector.SourceMap} sourceMap
182 _sourceMapLoaded: function(script, uiSourceCodePath, sourceMap)
184 this._stubUISourceCodes.delete(script.scriptId);
185 this._stubProjectDelegate.removeFile(uiSourceCodePath);
187 if (!sourceMap) {
188 this._debuggerWorkspaceBinding.updateLocations(script);
189 return;
192 if (this._scriptForSourceMap.get(sourceMap)) {
193 this._sourceMapForScriptId[script.scriptId] = sourceMap;
194 this._debuggerWorkspaceBinding.updateLocations(script);
195 return;
198 this._sourceMapForScriptId[script.scriptId] = sourceMap;
199 this._scriptForSourceMap.set(sourceMap, script);
201 var sourceURLs = sourceMap.sources();
202 var missingSources = [];
203 for (var i = 0; i < sourceURLs.length; ++i) {
204 var sourceURL = sourceURLs[i];
205 if (this._sourceMapForURL.get(sourceURL))
206 continue;
207 this._sourceMapForURL.set(sourceURL, sourceMap);
208 if (!this._networkMapping.hasMappingForURL(sourceURL) && !this._networkMapping.uiSourceCodeForURL(sourceURL, script.target())) {
209 var contentProvider = sourceMap.sourceContentProvider(sourceURL, WebInspector.resourceTypes.Script);
210 this._networkProject.addFileForURL(sourceURL, contentProvider, script.isContentScript());
212 var uiSourceCode = this._networkMapping.uiSourceCodeForURL(sourceURL, this._target);
213 if (uiSourceCode) {
214 this._bindUISourceCode(uiSourceCode);
215 } else {
216 if (missingSources.length < 3)
217 missingSources.push(sourceURL);
218 else if (missingSources.peekLast() !== "\u2026")
219 missingSources.push("\u2026");
222 if (missingSources.length) {
223 WebInspector.console.warn(
224 WebInspector.UIString("Source map %s points to the files missing from the workspace: [%s]",
225 sourceMap.url(), missingSources.join(", ")));
228 this._debuggerWorkspaceBinding.updateLocations(script);
232 * @override
233 * @return {boolean}
235 isIdentity: function()
237 return false;
241 * @override
242 * @param {!WebInspector.UISourceCode} uiSourceCode
243 * @param {number} lineNumber
244 * @return {boolean}
246 uiLineHasMapping: function(uiSourceCode, lineNumber)
248 var networkURL = this._networkMapping.networkURL(uiSourceCode);
249 if (!networkURL)
250 return true;
251 var sourceMap = this._sourceMapForURL.get(networkURL);
252 if (!sourceMap)
253 return true;
254 return !!sourceMap.firstSourceLineMapping(networkURL, lineNumber);
258 * @param {!WebInspector.UISourceCode} uiSourceCode
260 _bindUISourceCode: function(uiSourceCode)
262 this._debuggerWorkspaceBinding.setSourceMapping(this._target, uiSourceCode, this);
266 * @param {!WebInspector.UISourceCode} uiSourceCode
268 _unbindUISourceCode: function(uiSourceCode)
270 this._debuggerWorkspaceBinding.setSourceMapping(this._target, uiSourceCode, null);
274 * @param {!WebInspector.Event} event
276 _uiSourceCodeAddedToWorkspace: function(event)
278 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
279 var networkURL = this._networkMapping.networkURL(uiSourceCode);
280 if (!networkURL || !this._sourceMapForURL.get(networkURL))
281 return;
282 this._bindUISourceCode(uiSourceCode);
286 * @param {!WebInspector.Script} script
287 * @param {function(?WebInspector.SourceMap)} callback
289 _loadSourceMapForScript: function(script, callback)
291 // script.sourceURL can be a random string, but is generally an absolute path -> complete it to inspected page url for
292 // relative links.
293 var scriptURL = WebInspector.ParsedURL.completeURL(this._target.resourceTreeModel.inspectedPageURL(), script.sourceURL);
294 if (!scriptURL) {
295 callback(null);
296 return;
299 console.assert(script.sourceMapURL);
300 var scriptSourceMapURL = /** @type {string} */ (script.sourceMapURL);
302 var sourceMapURL = WebInspector.ParsedURL.completeURL(scriptURL, scriptSourceMapURL);
303 if (!sourceMapURL) {
304 callback(null);
305 return;
308 var sourceMap = this._sourceMapForSourceMapURL[sourceMapURL];
309 if (sourceMap) {
310 callback(sourceMap);
311 return;
314 var pendingCallbacks = this._pendingSourceMapLoadingCallbacks[sourceMapURL];
315 if (pendingCallbacks) {
316 pendingCallbacks.push(callback);
317 return;
320 pendingCallbacks = [callback];
321 this._pendingSourceMapLoadingCallbacks[sourceMapURL] = pendingCallbacks;
323 WebInspector.SourceMap.load(sourceMapURL, scriptURL, sourceMapLoaded.bind(this));
326 * @param {?WebInspector.SourceMap} sourceMap
327 * @this {WebInspector.CompilerScriptMapping}
329 function sourceMapLoaded(sourceMap)
331 var url = /** @type {string} */ (sourceMapURL);
332 var callbacks = this._pendingSourceMapLoadingCallbacks[url];
333 delete this._pendingSourceMapLoadingCallbacks[url];
334 if (!callbacks)
335 return;
336 if (sourceMap)
337 this._sourceMapForSourceMapURL[url] = sourceMap;
338 for (var i = 0; i < callbacks.length; ++i)
339 callbacks[i](sourceMap);
343 _debuggerReset: function()
346 * @param {string} sourceURL
347 * @this {WebInspector.CompilerScriptMapping}
349 function unbindUISourceCodeForURL(sourceURL)
351 var uiSourceCode = this._networkMapping.uiSourceCodeForURL(sourceURL, this._target);
352 if (!uiSourceCode)
353 return;
354 this._unbindUISourceCode(uiSourceCode);
357 this._sourceMapForURL.keysArray().forEach(unbindUISourceCodeForURL.bind(this));
359 this._sourceMapForSourceMapURL = {};
360 this._pendingSourceMapLoadingCallbacks = {};
361 this._sourceMapForScriptId = {};
362 this._scriptForSourceMap.clear();
363 this._sourceMapForURL.clear();
366 dispose: function()
368 this._workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);