1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * @implements {WebInspector.DebuggerSourceMapping}
8 * @param {!WebInspector.Target} target
9 * @param {!WebInspector.ScriptFormatterEditorAction} editorAction
11 WebInspector.FormatterScriptMapping = function(target, editorAction)
13 this._target = target;
14 this._editorAction = editorAction;
17 WebInspector.FormatterScriptMapping.prototype = {
19 * @param {!WebInspector.DebuggerModel.Location} rawLocation
20 * @return {?WebInspector.UILocation}
22 rawLocationToUILocation: function(rawLocation)
24 var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (rawLocation);
25 var script = debuggerModelLocation.script();
26 var uiSourceCode = this._editorAction._uiSourceCodes.get(script);
30 var formatData = this._editorAction._formatData.get(uiSourceCode);
33 var mapping = formatData.mapping;
34 var lineNumber = debuggerModelLocation.lineNumber;
35 var columnNumber = debuggerModelLocation.columnNumber || 0;
36 var formattedLocation = mapping.originalToFormatted(lineNumber, columnNumber);
37 return uiSourceCode.uiLocation(formattedLocation[0], formattedLocation[1]);
41 * @param {!WebInspector.UISourceCode} uiSourceCode
42 * @param {number} lineNumber
43 * @param {number} columnNumber
44 * @return {?WebInspector.DebuggerModel.Location}
46 uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
48 var formatData = this._editorAction._formatData.get(uiSourceCode);
51 var originalLocation = formatData.mapping.formattedToOriginal(lineNumber, columnNumber);
52 for (var i = 0; i < formatData.scripts.length; ++i) {
53 if (formatData.scripts[i].target() === this._target)
54 return this._target.debuggerModel.createRawLocation(formatData.scripts[i], originalLocation[0], originalLocation[1])
62 isIdentity: function()
68 * @param {!WebInspector.UISourceCode} uiSourceCode
69 * @param {number} lineNumber
72 uiLineHasMapping: function(uiSourceCode, lineNumber)
81 * @param {string} projectId
82 * @param {string} path
83 * @param {!WebInspector.FormatterSourceMapping} mapping
84 * @param {!Array.<!WebInspector.Script>} scripts
86 WebInspector.FormatterScriptMapping.FormatData = function(projectId, path, mapping, scripts)
88 this.projectId = projectId;
90 this.mapping = mapping;
91 this.scripts = scripts;
96 * @param {!WebInspector.Workspace} workspace
98 * @extends {WebInspector.ContentProviderBasedProjectDelegate}
100 WebInspector.FormatterProjectDelegate = function(workspace, id)
102 WebInspector.ContentProviderBasedProjectDelegate.call(this, workspace, id, WebInspector.projectTypes.Formatter);
105 WebInspector.FormatterProjectDelegate.prototype = {
109 displayName: function()
115 * @param {string} name
116 * @param {string} sourceURL
117 * @param {!WebInspector.ResourceType} contentType
118 * @param {string} content
121 _addFormatted: function(name, sourceURL, contentType, content)
123 var contentProvider = new WebInspector.StaticContentProvider(contentType, content);
124 return this.addContentProvider(sourceURL, name + ":formatted", "deobfuscated:" + sourceURL, contentProvider);
128 * @param {string} path
130 _removeFormatted: function(path)
132 this.removeFile(path);
135 __proto__: WebInspector.ContentProviderBasedProjectDelegate.prototype
140 * @implements {WebInspector.SourcesView.EditorAction}
141 * @implements {WebInspector.TargetManager.Observer}
143 WebInspector.ScriptFormatterEditorAction = function()
145 this._projectId = "formatter:";
146 this._projectDelegate = new WebInspector.FormatterProjectDelegate(WebInspector.workspace, this._projectId);
148 /** @type {!Map.<!WebInspector.Script, !WebInspector.UISourceCode>} */
149 this._uiSourceCodes = new Map();
150 /** @type {!StringMap.<string>} */
151 this._formattedPaths = new StringMap();
152 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.FormatterScriptMapping.FormatData>} */
153 this._formatData = new Map();
155 /** @type {!StringSet} */
156 this._pathsToFormatOnLoad = new StringSet();
158 /** @type {!Map.<!WebInspector.Target, !WebInspector.FormatterScriptMapping>} */
159 this._scriptMappingByTarget = new Map();
160 this._workspace = WebInspector.workspace;
161 WebInspector.targetManager.observeTargets(this);
164 WebInspector.ScriptFormatterEditorAction.prototype = {
166 * @param {!WebInspector.Target} target
168 targetAdded: function(target)
170 this._scriptMappingByTarget.set(target, new WebInspector.FormatterScriptMapping(target, this));
171 target.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
175 * @param {!WebInspector.Target} target
177 targetRemoved: function(target)
179 this._scriptMappingByTarget.remove(target);
180 this._cleanForTarget(target);
181 target.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
185 * @param {!WebInspector.Event} event
187 _editorSelected: function(event)
189 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
190 this._updateButton(uiSourceCode);
192 var path = uiSourceCode.project().id() + ":" + uiSourceCode.path();
193 if (this._isFormatableScript(uiSourceCode) && uiSourceCode.url && this._pathsToFormatOnLoad.contains(path) && !this._formattedPaths.get(path))
194 this._formatUISourceCodeScript(uiSourceCode);
198 * @param {!WebInspector.Event} event
200 _editorClosed: function(event)
202 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.uiSourceCode);
203 var wasSelected = /** @type {boolean} */ (event.data.wasSelected);
206 this._updateButton(null);
207 this._discardFormattedUISourceCodeScript(uiSourceCode);
211 * @param {?WebInspector.UISourceCode} uiSourceCode
213 _updateButton: function(uiSourceCode)
215 this._button.element.classList.toggle("hidden", !this._isFormatableScript(uiSourceCode));
219 * @param {!WebInspector.SourcesView} sourcesView
222 button: function(sourcesView)
225 return this._button.element;
227 this._sourcesView = sourcesView;
228 this._sourcesView.addEventListener(WebInspector.SourcesView.Events.EditorSelected, this._editorSelected.bind(this));
229 this._sourcesView.addEventListener(WebInspector.SourcesView.Events.EditorClosed, this._editorClosed.bind(this));
231 this._button = new WebInspector.StatusBarButton(WebInspector.UIString("Pretty print"), "sources-toggle-pretty-print-status-bar-item");
232 this._button.toggled = false;
233 this._button.addEventListener("click", this._toggleFormatScriptSource, this);
234 this._updateButton(null);
236 return this._button.element;
240 * @param {?WebInspector.UISourceCode} uiSourceCode
243 _isFormatableScript: function(uiSourceCode)
247 var supportedProjectTypes = [WebInspector.projectTypes.Network, WebInspector.projectTypes.Debugger, WebInspector.projectTypes.ContentScripts];
248 if (supportedProjectTypes.indexOf(uiSourceCode.project().type()) === -1)
250 var contentType = uiSourceCode.contentType();
251 return contentType === WebInspector.resourceTypes.Script || contentType === WebInspector.resourceTypes.Document;
254 _toggleFormatScriptSource: function()
256 var uiSourceCode = this._sourcesView.currentUISourceCode();
257 if (!this._isFormatableScript(uiSourceCode))
259 this._formatUISourceCodeScript(uiSourceCode);
261 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
262 action: WebInspector.UserMetrics.UserActionNames.TogglePrettyPrint,
264 url: uiSourceCode.originURL()
269 * @param {!WebInspector.UISourceCode} uiSourceCode
270 * @param {!WebInspector.UISourceCode} formattedUISourceCode
271 * @param {!WebInspector.FormatterSourceMapping} mapping
274 _showIfNeeded: function(uiSourceCode, formattedUISourceCode, mapping)
276 if (uiSourceCode !== this._sourcesView.currentUISourceCode())
278 var sourceFrame = this._sourcesView.viewForFile(uiSourceCode);
281 var selection = sourceFrame.selection();
282 start = mapping.originalToFormatted(selection.startLine, selection.startColumn);
284 this._sourcesView.showSourceLocation(formattedUISourceCode, start[0], start[1]);
285 this._updateButton(formattedUISourceCode);
289 * @param {!WebInspector.UISourceCode} formattedUISourceCode
291 _discardFormattedUISourceCodeScript: function(formattedUISourceCode)
293 var formatData = this._formatData.get(formattedUISourceCode);
297 this._formatData.remove(formattedUISourceCode);
298 var path = formatData.projectId + ":" + formatData.path;
299 this._formattedPaths.remove(path);
300 this._pathsToFormatOnLoad.remove(path);
301 for (var i = 0; i < formatData.scripts.length; ++i) {
302 this._uiSourceCodes.remove(formatData.scripts[i]);
303 WebInspector.debuggerWorkspaceBinding.popSourceMapping(formatData.scripts[i]);
305 this._projectDelegate._removeFormatted(formattedUISourceCode.path());
309 * @param {!WebInspector.Target} target
311 _cleanForTarget: function(target)
313 var uiSourceCodes = this._formatData.keys();
314 for (var i = 0; i < uiSourceCodes.length; ++i) {
315 WebInspector.debuggerWorkspaceBinding.setSourceMapping(target, uiSourceCodes[i], null);
316 var formatData = this._formatData.get(uiSourceCodes[i]);
318 for (var j = 0; j < formatData.scripts.length; ++j) {
319 if (formatData.scripts[j].target() === target)
320 this._uiSourceCodes.remove(formatData.scripts[j]);
322 scripts.push(formatData.scripts[j]);
326 formatData.scripts = scripts;
328 this._formattedPaths.remove(formatData.projectId + ":" + formatData.path);
329 this._formatData.remove(uiSourceCodes[i]);
330 this._projectDelegate._removeFormatted(uiSourceCodes[i].path());
336 * @param {!WebInspector.Event} event
338 _debuggerReset: function(event)
340 var debuggerModel = /** @type {!WebInspector.DebuggerModel} */ (event.target);
341 this._cleanForTarget(debuggerModel.target());
345 * @param {!WebInspector.UISourceCode} uiSourceCode
346 * @return {!Array.<!WebInspector.Script>}
348 _scriptsForUISourceCode: function(uiSourceCode)
351 * @param {!WebInspector.Script} script
354 function isInlineScript(script)
356 return script.isInlineScript() && !script.hasSourceURL;
359 if (uiSourceCode.contentType() === WebInspector.resourceTypes.Document) {
361 var targets = WebInspector.targetManager.targets();
362 for (var i = 0; i < targets.length; ++i)
363 scripts.pushAll(targets[i].debuggerModel.scriptsForSourceURL(uiSourceCode.url));
364 return scripts.filter(isInlineScript);
366 if (uiSourceCode.contentType() === WebInspector.resourceTypes.Script) {
367 var rawLocations = WebInspector.debuggerWorkspaceBinding.uiLocationToRawLocations(uiSourceCode, 0, 0);
368 return rawLocations.map(function(rawLocation) { return rawLocation.script()});
374 * @param {!WebInspector.UISourceCode} uiSourceCode
376 _formatUISourceCodeScript: function(uiSourceCode)
378 var formattedPath = this._formattedPaths.get(uiSourceCode.project().id() + ":" + uiSourceCode.path());
380 var uiSourceCodePath = formattedPath;
381 var formattedUISourceCode = this._workspace.uiSourceCode(this._projectId, uiSourceCodePath);
382 var formatData = formattedUISourceCode ? this._formatData.get(formattedUISourceCode) : null;
384 this._showIfNeeded(uiSourceCode, /** @type {!WebInspector.UISourceCode} */ (formattedUISourceCode), formatData.mapping);
388 uiSourceCode.requestContent(contentLoaded.bind(this));
391 * @this {WebInspector.ScriptFormatterEditorAction}
392 * @param {?string} content
394 function contentLoaded(content)
396 var formatter = WebInspector.Formatter.createFormatter(uiSourceCode.contentType());
397 formatter.formatContent(uiSourceCode.highlighterType(), content || "", innerCallback.bind(this));
401 * @this {WebInspector.ScriptFormatterEditorAction}
402 * @param {string} formattedContent
403 * @param {!WebInspector.FormatterSourceMapping} formatterMapping
405 function innerCallback(formattedContent, formatterMapping)
407 var scripts = this._scriptsForUISourceCode(uiSourceCode);
409 if (uiSourceCode.contentType() === WebInspector.resourceTypes.Document)
410 name = uiSourceCode.displayName();
412 name = uiSourceCode.name() || (scripts.length ? scripts[0].scriptId : "");
414 formattedPath = this._projectDelegate._addFormatted(name, uiSourceCode.url, uiSourceCode.contentType(), formattedContent);
415 var formattedUISourceCode = /** @type {!WebInspector.UISourceCode} */ (this._workspace.uiSourceCode(this._projectId, formattedPath));
416 var formatData = new WebInspector.FormatterScriptMapping.FormatData(uiSourceCode.project().id(), uiSourceCode.path(), formatterMapping, scripts);
417 this._formatData.set(formattedUISourceCode, formatData);
418 var path = uiSourceCode.project().id() + ":" + uiSourceCode.path();
419 this._formattedPaths.set(path, formattedPath);
420 this._pathsToFormatOnLoad.add(path);
421 for (var i = 0; i < scripts.length; ++i) {
422 this._uiSourceCodes.set(scripts[i], formattedUISourceCode);
423 var scriptMapping = /** @type {!WebInspector.FormatterScriptMapping} */(this._scriptMappingByTarget.get(scripts[i].target()));
424 WebInspector.debuggerWorkspaceBinding.pushSourceMapping(scripts[i], scriptMapping);
427 var targets = WebInspector.targetManager.targets();
428 for (var i = 0; i < targets.length; ++i) {
429 var scriptMapping = /** @type {!WebInspector.FormatterScriptMapping} */(this._scriptMappingByTarget.get(targets[i]));
430 WebInspector.debuggerWorkspaceBinding.setSourceMapping(targets[i], formattedUISourceCode, scriptMapping);
432 this._showIfNeeded(uiSourceCode, formattedUISourceCode, formatterMapping);