summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js')
-rw-r--r--Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js464
1 files changed, 464 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js b/Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js
new file mode 100644
index 000000000..79691700c
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCodeLocation.js
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceCodeLocation = class SourceCodeLocation extends WebInspector.Object
+{
+ constructor(sourceCode, lineNumber, columnNumber)
+ {
+ super();
+
+ console.assert(sourceCode === null || sourceCode instanceof WebInspector.SourceCode);
+ console.assert(!(sourceCode instanceof WebInspector.SourceMapResource));
+ console.assert(typeof lineNumber === "number" && !isNaN(lineNumber) && lineNumber >= 0);
+ console.assert(typeof columnNumber === "number" && !isNaN(columnNumber) && columnNumber >= 0);
+
+ this._sourceCode = sourceCode || null;
+ this._lineNumber = lineNumber;
+ this._columnNumber = columnNumber;
+ this._resolveFormattedLocation();
+
+ if (this._sourceCode) {
+ this._sourceCode.addEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
+ this._sourceCode.addEventListener(WebInspector.SourceCode.Event.FormatterDidChange, this._sourceCodeFormatterDidChange, this);
+ }
+
+ this._resetMappedLocation();
+ }
+
+ // Public
+
+ isEqual(other)
+ {
+ if (!other)
+ return false;
+ return this._sourceCode === other._sourceCode && this._lineNumber === other._lineNumber && this._columnNumber === other._columnNumber;
+ }
+
+ get sourceCode()
+ {
+ return this._sourceCode;
+ }
+
+ set sourceCode(sourceCode)
+ {
+ this.setSourceCode(sourceCode);
+ }
+
+ // Raw line and column in the original source code.
+
+ get lineNumber()
+ {
+ return this._lineNumber;
+ }
+
+ get columnNumber()
+ {
+ return this._columnNumber;
+ }
+
+ position()
+ {
+ return new WebInspector.SourceCodePosition(this.lineNumber, this.columnNumber);
+ }
+
+ // Formatted line and column if the original source code is pretty printed.
+ // This is the same as the raw location if there is no formatter.
+
+ get formattedLineNumber()
+ {
+ return this._formattedLineNumber;
+ }
+
+ get formattedColumnNumber()
+ {
+ return this._formattedColumnNumber;
+ }
+
+ formattedPosition()
+ {
+ return new WebInspector.SourceCodePosition(this.formattedLineNumber, this.formattedColumnNumber);
+ }
+
+ // Display line and column:
+ // - Mapped line and column if the original source code has a source map.
+ // - Otherwise this is the formatted / raw line and column.
+
+ get displaySourceCode()
+ {
+ this.resolveMappedLocation();
+ return this._mappedResource || this._sourceCode;
+ }
+
+ get displayLineNumber()
+ {
+ this.resolveMappedLocation();
+ return isNaN(this._mappedLineNumber) ? this._formattedLineNumber : this._mappedLineNumber;
+ }
+
+ get displayColumnNumber()
+ {
+ this.resolveMappedLocation();
+ return isNaN(this._mappedColumnNumber) ? this._formattedColumnNumber : this._mappedColumnNumber;
+ }
+
+ displayPosition()
+ {
+ return new WebInspector.SourceCodePosition(this.displayLineNumber, this.displayColumnNumber);
+ }
+
+ // User presentable location strings: "file:lineNumber:columnNumber".
+
+ originalLocationString(columnStyle, nameStyle, prefix)
+ {
+ return this._locationString(this.sourceCode, this.lineNumber, this.columnNumber, columnStyle, nameStyle, prefix);
+ }
+
+ formattedLocationString(columnStyle, nameStyle, prefix)
+ {
+ return this._locationString(this.sourceCode, this.formattedLineNumber, this.formattedColumn, columnStyle, nameStyle, prefix);
+ }
+
+ displayLocationString(columnStyle, nameStyle, prefix)
+ {
+ return this._locationString(this.displaySourceCode, this.displayLineNumber, this.displayColumnNumber, columnStyle, nameStyle, prefix);
+ }
+
+ tooltipString()
+ {
+ if (!this.hasDifferentDisplayLocation())
+ return this.originalLocationString(WebInspector.SourceCodeLocation.ColumnStyle.Shown, WebInspector.SourceCodeLocation.NameStyle.Full);
+
+ var tooltip = WebInspector.UIString("Located at %s").format(this.displayLocationString(WebInspector.SourceCodeLocation.ColumnStyle.Shown, WebInspector.SourceCodeLocation.NameStyle.Full));
+ tooltip += "\n" + WebInspector.UIString("Originally %s").format(this.originalLocationString(WebInspector.SourceCodeLocation.ColumnStyle.Shown, WebInspector.SourceCodeLocation.NameStyle.Full));
+ return tooltip;
+ }
+
+ hasMappedLocation()
+ {
+ this.resolveMappedLocation();
+ return this._mappedResource !== null;
+ }
+
+ hasFormattedLocation()
+ {
+ return this._formattedLineNumber !== this._lineNumber || this._formattedColumnNumber !== this._columnNumber;
+ }
+
+ hasDifferentDisplayLocation()
+ {
+ return this.hasMappedLocation() || this.hasFormattedLocation();
+ }
+
+ update(sourceCode, lineNumber, columnNumber)
+ {
+ console.assert(sourceCode === this._sourceCode || (this._mappedResource && sourceCode === this._mappedResource));
+ console.assert(typeof lineNumber === "number" && !isNaN(lineNumber) && lineNumber >= 0);
+ console.assert(typeof columnNumber === "number" && !isNaN(columnNumber) && columnNumber >= 0);
+
+ if (sourceCode === this._sourceCode && lineNumber === this._lineNumber && columnNumber === this._columnNumber)
+ return;
+ if (this._mappedResource && sourceCode === this._mappedResource && lineNumber === this._mappedLineNumber && columnNumber === this._mappedColumnNumber)
+ return;
+
+ var newSourceCodeLocation = sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
+ console.assert(newSourceCodeLocation.sourceCode === this._sourceCode);
+
+ this._makeChangeAndDispatchChangeEventIfNeeded(function() {
+ this._lineNumber = newSourceCodeLocation._lineNumber;
+ this._columnNumber = newSourceCodeLocation._columnNumber;
+ if (newSourceCodeLocation._mappedLocationIsResolved) {
+ this._mappedLocationIsResolved = true;
+ this._mappedResource = newSourceCodeLocation._mappedResource;
+ this._mappedLineNumber = newSourceCodeLocation._mappedLineNumber;
+ this._mappedColumnNumber = newSourceCodeLocation._mappedColumnNumber;
+ }
+ });
+ }
+
+ populateLiveDisplayLocationTooltip(element, prefix)
+ {
+ prefix = prefix || "";
+
+ element.title = prefix + this.tooltipString();
+
+ this.addEventListener(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, function(event) {
+ if (this.sourceCode)
+ element.title = prefix + this.tooltipString();
+ }, this);
+ }
+
+ populateLiveDisplayLocationString(element, propertyName, columnStyle, nameStyle, prefix)
+ {
+ var currentDisplay;
+
+ function updateDisplayString(showAlternativeLocation, forceUpdate)
+ {
+ if (!forceUpdate && currentDisplay === showAlternativeLocation)
+ return;
+
+ currentDisplay = showAlternativeLocation;
+
+ if (!showAlternativeLocation) {
+ element[propertyName] = this.displayLocationString(columnStyle, nameStyle, prefix);
+ element.classList.toggle(WebInspector.SourceCodeLocation.DisplayLocationClassName, this.hasDifferentDisplayLocation());
+ } else if (this.hasDifferentDisplayLocation()) {
+ element[propertyName] = this.originalLocationString(columnStyle, nameStyle, prefix);
+ element.classList.remove(WebInspector.SourceCodeLocation.DisplayLocationClassName);
+ }
+ }
+
+ function mouseOverOrMove(event)
+ {
+ updateDisplayString.call(this, event.metaKey && !event.altKey && !event.shiftKey);
+ }
+
+ updateDisplayString.call(this, false);
+
+ this.addEventListener(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, function(event) {
+ if (this.sourceCode)
+ updateDisplayString.call(this, currentDisplay, true);
+ }, this);
+
+ var boundMouseOverOrMove = mouseOverOrMove.bind(this);
+ element.addEventListener("mouseover", boundMouseOverOrMove);
+ element.addEventListener("mousemove", boundMouseOverOrMove);
+ element.addEventListener("mouseout", (event) => { updateDisplayString.call(this, false); });
+ }
+
+ // Protected
+
+ setSourceCode(sourceCode)
+ {
+ console.assert((this._sourceCode === null && sourceCode instanceof WebInspector.SourceCode) || (this._sourceCode instanceof WebInspector.SourceCode && sourceCode === null));
+
+ if (sourceCode === this._sourceCode)
+ return;
+
+ this._makeChangeAndDispatchChangeEventIfNeeded(function() {
+ if (this._sourceCode) {
+ this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
+ this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.FormatterDidChange, this._sourceCodeFormatterDidChange, this);
+ }
+
+ this._sourceCode = sourceCode;
+
+ if (this._sourceCode) {
+ this._sourceCode.addEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
+ this._sourceCode.addEventListener(WebInspector.SourceCode.Event.FormatterDidChange, this._sourceCodeFormatterDidChange, this);
+ }
+ });
+ }
+
+ resolveMappedLocation()
+ {
+ if (this._mappedLocationIsResolved)
+ return;
+
+ console.assert(this._mappedResource === null);
+ console.assert(isNaN(this._mappedLineNumber));
+ console.assert(isNaN(this._mappedColumnNumber));
+
+ this._mappedLocationIsResolved = true;
+
+ if (!this._sourceCode)
+ return;
+
+ var sourceMaps = this._sourceCode.sourceMaps;
+ if (!sourceMaps.length)
+ return;
+
+ for (var i = 0; i < sourceMaps.length; ++i) {
+ var sourceMap = sourceMaps[i];
+ var entry = sourceMap.findEntry(this._lineNumber, this._columnNumber);
+ if (!entry || entry.length === 2)
+ continue;
+ console.assert(entry.length === 5);
+ var url = entry[2];
+ var sourceMapResource = sourceMap.resourceForURL(url);
+ if (!sourceMapResource)
+ return;
+ this._mappedResource = sourceMapResource;
+ this._mappedLineNumber = entry[3];
+ this._mappedColumnNumber = entry[4];
+ return;
+ }
+ }
+
+ // Private
+
+ _locationString(sourceCode, lineNumber, columnNumber, columnStyle, nameStyle, prefix)
+ {
+ console.assert(sourceCode);
+ if (!sourceCode)
+ return "";
+
+ columnStyle = columnStyle || WebInspector.SourceCodeLocation.ColumnStyle.OnlyIfLarge;
+ nameStyle = nameStyle || WebInspector.SourceCodeLocation.NameStyle.Short;
+ prefix = prefix || "";
+
+ let lineString = lineNumber + 1; // The user visible line number is 1-based.
+ if (columnStyle === WebInspector.SourceCodeLocation.ColumnStyle.Shown && columnNumber > 0)
+ lineString += ":" + (columnNumber + 1); // The user visible column number is 1-based.
+ else if (columnStyle === WebInspector.SourceCodeLocation.ColumnStyle.OnlyIfLarge && columnNumber > WebInspector.SourceCodeLocation.LargeColumnNumber)
+ lineString += ":" + (columnNumber + 1); // The user visible column number is 1-based.
+ else if (columnStyle === WebInspector.SourceCodeLocation.ColumnStyle.Hidden)
+ lineString = "";
+
+ switch (nameStyle) {
+ case WebInspector.SourceCodeLocation.NameStyle.None:
+ return prefix + lineString;
+
+ case WebInspector.SourceCodeLocation.NameStyle.Short:
+ case WebInspector.SourceCodeLocation.NameStyle.Full:
+ var displayURL = sourceCode.displayURL;
+ var name = nameStyle === WebInspector.SourceCodeLocation.NameStyle.Full && displayURL ? displayURL : sourceCode.displayName;
+ if (columnStyle === WebInspector.SourceCodeLocation.ColumnStyle.Hidden)
+ return prefix + name;
+ var lineSuffix = displayURL ? ":" + lineString : WebInspector.UIString(" (line %s)").format(lineString);
+ return prefix + name + lineSuffix;
+
+ default:
+ console.error("Unknown nameStyle: " + nameStyle);
+ return prefix + lineString;
+ }
+ }
+
+ _resetMappedLocation()
+ {
+ this._mappedLocationIsResolved = false;
+ this._mappedResource = null;
+ this._mappedLineNumber = NaN;
+ this._mappedColumnNumber = NaN;
+ }
+
+ _setMappedLocation(mappedResource, mappedLineNumber, mappedColumnNumber)
+ {
+ // Called by SourceMapResource when it creates a SourceCodeLocation and already knows the resolved location.
+ this._mappedLocationIsResolved = true;
+ this._mappedResource = mappedResource;
+ this._mappedLineNumber = mappedLineNumber;
+ this._mappedColumnNumber = mappedColumnNumber;
+ }
+
+ _resolveFormattedLocation()
+ {
+ if (this._sourceCode && this._sourceCode.formatterSourceMap) {
+ var formattedLocation = this._sourceCode.formatterSourceMap.originalToFormatted(this._lineNumber, this._columnNumber);
+ this._formattedLineNumber = formattedLocation.lineNumber;
+ this._formattedColumnNumber = formattedLocation.columnNumber;
+ } else {
+ this._formattedLineNumber = this._lineNumber;
+ this._formattedColumnNumber = this._columnNumber;
+ }
+ }
+
+ _makeChangeAndDispatchChangeEventIfNeeded(changeFunction)
+ {
+ var oldSourceCode = this._sourceCode;
+ var oldLineNumber = this._lineNumber;
+ var oldColumnNumber = this._columnNumber;
+
+ var oldFormattedLineNumber = this._formattedLineNumber;
+ var oldFormattedColumnNumber = this._formattedColumnNumber;
+
+ var oldDisplaySourceCode = this.displaySourceCode;
+ var oldDisplayLineNumber = this.displayLineNumber;
+ var oldDisplayColumnNumber = this.displayColumnNumber;
+
+ this._resetMappedLocation();
+
+ if (changeFunction)
+ changeFunction.call(this);
+
+ this.resolveMappedLocation();
+ this._resolveFormattedLocation();
+
+ // If the display source code is non-null then the addresses are not NaN and can be compared.
+ var displayLocationChanged = false;
+ var newDisplaySourceCode = this.displaySourceCode;
+ if (oldDisplaySourceCode !== newDisplaySourceCode)
+ displayLocationChanged = true;
+ else if (newDisplaySourceCode && (oldDisplayLineNumber !== this.displayLineNumber || oldDisplayColumnNumber !== this.displayColumnNumber))
+ displayLocationChanged = true;
+
+ var anyLocationChanged = false;
+ if (displayLocationChanged)
+ anyLocationChanged = true;
+ else if (oldSourceCode !== this._sourceCode)
+ anyLocationChanged = true;
+ else if (this._sourceCode && (oldLineNumber !== this._lineNumber || oldColumnNumber !== this._columnNumber))
+ anyLocationChanged = true;
+ else if (this._sourceCode && (oldFormattedLineNumber !== this._formattedLineNumber || oldFormattedColumnNumber !== this._formattedColumnNumber))
+ anyLocationChanged = true;
+
+ if (displayLocationChanged || anyLocationChanged) {
+ var oldData = {
+ oldSourceCode,
+ oldLineNumber,
+ oldColumnNumber,
+ oldFormattedLineNumber,
+ oldFormattedColumnNumber,
+ oldDisplaySourceCode,
+ oldDisplayLineNumber,
+ oldDisplayColumnNumber
+ };
+ if (displayLocationChanged)
+ this.dispatchEventToListeners(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, oldData);
+ if (anyLocationChanged)
+ this.dispatchEventToListeners(WebInspector.SourceCodeLocation.Event.LocationChanged, oldData);
+ }
+ }
+
+ _sourceCodeSourceMapAdded()
+ {
+ this._makeChangeAndDispatchChangeEventIfNeeded(null);
+ }
+
+ _sourceCodeFormatterDidChange()
+ {
+ this._makeChangeAndDispatchChangeEventIfNeeded(null);
+ }
+};
+
+WebInspector.SourceCodeLocation.DisplayLocationClassName = "display-location";
+
+WebInspector.SourceCodeLocation.LargeColumnNumber = 80;
+
+WebInspector.SourceCodeLocation.NameStyle = {
+ None: "none", // File name not included.
+ Short: "short", // Only the file name.
+ Full: "full" // Full URL is used.
+};
+
+WebInspector.SourceCodeLocation.ColumnStyle = {
+ Hidden: "hidden", // line and column numbers are not included.
+ OnlyIfLarge: "only-if-large", // column numbers greater than 80 are shown.
+ Shown: "shown" // non-zero column numbers are shown.
+};
+
+WebInspector.SourceCodeLocation.Event = {
+ LocationChanged: "source-code-location-location-changed",
+ DisplayLocationChanged: "source-code-location-display-location-changed"
+};