diff options
author | Ben Murdoch <benm@google.com> | 2011-05-16 16:25:10 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-05-23 18:54:14 +0100 |
commit | ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddb (patch) | |
tree | db769fadd053248f85db67434a5b275224defef7 /Source/WebCore/inspector/front-end | |
parent | 52e2557aeb8477967e97fd24f20f8f407a10fa15 (diff) | |
download | external_webkit-ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddb.zip external_webkit-ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddb.tar.gz external_webkit-ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddb.tar.bz2 |
Merge WebKit at r76408: Initial merge by git.
Change-Id: I5b91decbd693ccbf5c1b8354b37cd68cc9a1ea53
Diffstat (limited to 'Source/WebCore/inspector/front-end')
39 files changed, 4188 insertions, 642 deletions
diff --git a/Source/WebCore/inspector/front-end/AuditLauncherView.js b/Source/WebCore/inspector/front-end/AuditLauncherView.js index c140589..df16a41 100644 --- a/Source/WebCore/inspector/front-end/AuditLauncherView.js +++ b/Source/WebCore/inspector/front-end/AuditLauncherView.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -50,60 +50,38 @@ WebInspector.AuditLauncherView = function(runnerCallback) this._headerElement.className = "no-audits"; this._headerElement.textContent = WebInspector.UIString("No audits to run"); this._contentElement.appendChild(this._headerElement); + + WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this); + WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceFinished, this._onResourceFinished, this); } WebInspector.AuditLauncherView.prototype = { - get totalResources() + _resetResourceCount: function() { - return this._totalResources; + this._loadedResources = 0; + this._totalResources = 0; }, - set totalResources(x) + _onResourceStarted: function(event) { - if (this._totalResources === x) + var resource = event.data; + // Ignore long-living WebSockets for the sake of progress indicator, as we won't be waiting them anyway. + if (resource.type === WebInspector.Resource.Type.WebSocket) return; - this._totalResources = x; + ++this._totalResources; this._updateResourceProgress(); }, - get loadedResources() - { - return this._loadedResources; - }, - - set loadedResources(x) + _onResourceFinished: function(event) { - if (this._loadedResources === x) + var resource = event.data; + // See resorceStarted for details. + if (resource.type === WebInspector.Resource.Type.WebSocket) return; - this._loadedResources = x; + ++this._loadedResources; this._updateResourceProgress(); }, - _resetResourceCount: function() - { - this.loadedResources = 0; - this.totalResources = 0; - }, - - resourceStarted: function(resource) - { - // Ignore long-living WebSockets for the sake of progress indicator, as we won't be waiting them anyway. - if (resource.type !== WebInspector.Resource.Type.WebSocket) - ++this.totalResources; - }, - - resourceFinished: function(resource) - { - // See resorceStarted for details. - if (resource.type !== WebInspector.Resource.Type.WebSocket) - ++this.loadedResources; - }, - - reset: function() - { - this._resetResourceCount(); - }, - addCategory: function(category) { if (!this._sortedCategories.length) @@ -264,7 +242,7 @@ WebInspector.AuditLauncherView.prototype = { this._resourceProgressContainer.addStyleClass("hidden"); } else this._resourceProgressContainer.removeStyleClass("hidden"); - this._resourceProgressTextElement.textContent = WebInspector.UIString("Loading (%d of %d)", this.loadedResources, this.totalResources); + this._resourceProgressTextElement.textContent = WebInspector.UIString("Loading (%d of %d)", this._loadedResources, this._totalResources); }, _updateButton: function() diff --git a/Source/WebCore/inspector/front-end/AuditRules.js b/Source/WebCore/inspector/front-end/AuditRules.js index 7aa891f..c2bbcbb 100644 --- a/Source/WebCore/inspector/front-end/AuditRules.js +++ b/Source/WebCore/inspector/front-end/AuditRules.js @@ -65,7 +65,7 @@ WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, nee WebInspector.AuditRules.evaluateInTargetWindow = function(func, args, callback) { - InjectedScriptAccess.getDefault().evaluateOnSelf(func.toString(), args, callback); + InspectorBackend.evaluateOnSelf(func.toString(), args, callback); } @@ -721,7 +721,7 @@ WebInspector.AuditRules.ImageDimensionsRule.prototype = { return nodeIds; } - WebInspector.AuditRules.evaluateInTargetWindow(pushImageNodes, null, receivedImages); + WebInspector.AuditRules.evaluateInTargetWindow(pushImageNodes, [], receivedImages); } } @@ -797,7 +797,7 @@ WebInspector.AuditRules.CssInHeadRule.prototype = { return found ? urlToViolationsArray : null; } - WebInspector.AuditRules.evaluateInTargetWindow(routine, null, evalCallback); + WebInspector.AuditRules.evaluateInTargetWindow(routine, [], evalCallback); } } @@ -844,7 +844,7 @@ WebInspector.AuditRules.StylesScriptsOrderRule.prototype = { return [ lateStyleUrls, cssBeforeInlineCount ]; } - WebInspector.AuditRules.evaluateInTargetWindow(routine, null, evalCallback.bind(this)); + WebInspector.AuditRules.evaluateInTargetWindow(routine, [], evalCallback.bind(this)); } } diff --git a/Source/WebCore/inspector/front-end/AuditsPanel.js b/Source/WebCore/inspector/front-end/AuditsPanel.js index c639f47..f3cbfa7 100644 --- a/Source/WebCore/inspector/front-end/AuditsPanel.js +++ b/Source/WebCore/inspector/front-end/AuditsPanel.js @@ -96,16 +96,6 @@ WebInspector.AuditsPanel.prototype = { return this._auditCategoriesById; }, - resourceStarted: function(resource) - { - this._launcherView.resourceStarted(resource); - }, - - resourceFinished: function(resource) - { - this._launcherView.resourceFinished(resource); - }, - addCategory: function(category) { this.categoriesById[category.id] = category; @@ -246,11 +236,6 @@ WebInspector.AuditsPanel.prototype = { x.show(this.viewsContainerElement); }, - reset: function() - { - this._launcherView.reset(); - }, - attach: function() { WebInspector.Panel.prototype.attach.call(this); diff --git a/Source/WebCore/inspector/front-end/Breakpoint.js b/Source/WebCore/inspector/front-end/Breakpoint.js index e5e1768..aa600a7 100644 --- a/Source/WebCore/inspector/front-end/Breakpoint.js +++ b/Source/WebCore/inspector/front-end/Breakpoint.js @@ -37,7 +37,6 @@ WebInspector.Breakpoint = function(debuggerModel, breakpointId, sourceID, url, l this.sourceID = sourceID; this._enabled = enabled; this._condition = condition || ""; - this._hit = false; this._debuggerModel = debuggerModel; } @@ -60,52 +59,14 @@ WebInspector.Breakpoint.prototype = { return this._condition; }, - get hit() + get data() { - return this._hit; - }, - - set hit(hit) - { - this._hit = hit; - this.dispatchEventToListeners("hit-state-changed"); - }, - - click: function(event) - { - WebInspector.panels.scripts.showSourceLine(this.url, this.line); - }, - - compareTo: function(other) - { - if (this.url != other.url) - return this.url < other.url ? -1 : 1; - if (this.line != other.line) - return this.line < other.line ? -1 : 1; - return 0; - }, - - populateLabelElement: function(element) - { - function didGetSourceLine(text) - { - var displayName = this.url ? WebInspector.displayNameForURL(this.url) : WebInspector.UIString("(program)"); - var labelElement = document.createTextNode(displayName + ":" + this.line); - element.appendChild(labelElement); - - var sourceTextElement = document.createElement("div"); - sourceTextElement.textContent = text; - sourceTextElement.className = "source-text monospace"; - element.appendChild(sourceTextElement); - } - var script = this._debuggerModel.scriptForSourceID(this.sourceID); - script.sourceLine(this.line, didGetSourceLine.bind(this)); + return { id: this.id, url: this.url, sourceID: this.sourceID, lineNumber: this.line, condition: this.condition }; }, remove: function() { this._debuggerModel.removeBreakpoint(this.id); - this.dispatchEventToListeners("removed"); this.removeAllListeners(); delete this._debuggerModel; } diff --git a/Source/WebCore/inspector/front-end/BreakpointManager.js b/Source/WebCore/inspector/front-end/BreakpointManager.js index 67ef112..d943d5b 100644 --- a/Source/WebCore/inspector/front-end/BreakpointManager.js +++ b/Source/WebCore/inspector/front-end/BreakpointManager.js @@ -34,7 +34,7 @@ WebInspector.BreakpointManager = function() var breakpoints = WebInspector.settings.findSettingForAllProjects("nativeBreakpoints"); for (var projectId in breakpoints) this._stickyBreakpoints[projectId] = this._validateBreakpoints(breakpoints[projectId]); - InspectorBackend.setStickyBreakpoints(this._stickyBreakpoints); + InspectorBackend.setAllBrowserBreakpoints(this._stickyBreakpoints); this._breakpoints = {}; this._domBreakpointsRestored = false; @@ -303,7 +303,7 @@ WebInspector.BreakpointManager.prototype = { WebInspector.settings.nativeBreakpoints = breakpoints; this._stickyBreakpoints[WebInspector.settings.projectId] = breakpoints; - InspectorBackend.setStickyBreakpoints(this._stickyBreakpoints); + InspectorBackend.setAllBrowserBreakpoints(this._stickyBreakpoints); }, _validateBreakpoints: function(persistentBreakpoints) diff --git a/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js b/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js index 408c5ba..b237ca2 100644 --- a/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js +++ b/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js @@ -23,7 +23,213 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.BreakpointsSidebarPane = function(title) +WebInspector.JavaScriptBreakpointsSidebarPane = function(title) +{ + WebInspector.SidebarPane.call(this, WebInspector.UIString("Breakpoints")); + + this.listElement = document.createElement("ol"); + this.listElement.className = "breakpoint-list"; + + this.emptyElement = document.createElement("div"); + this.emptyElement.className = "info"; + this.emptyElement.textContent = WebInspector.UIString("No Breakpoints"); + + this.bodyElement.appendChild(this.emptyElement); + + this._items = {}; + + WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointAdded, this._breakpointAdded, this); + WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointRemoved, this._breakpointRemoved, this); + WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this); + WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this); + WebInspector.breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.ProjectChanged, this._projectChanged, this); +} + +WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { + _breakpointAdded: function(event) + { + var breakpoint = event.data; + var breakpointId = breakpoint.id; + var data = breakpoint.data; + + var element = document.createElement("li"); + + var checkbox = document.createElement("input"); + checkbox.className = "checkbox-elem"; + checkbox.type = "checkbox"; + checkbox.checked = breakpoint.enabled; + checkbox.addEventListener("click", this._breakpointItemCheckboxClicked.bind(this, breakpointId), false); + element.appendChild(checkbox); + + var label = document.createElement("span"); + element.appendChild(label); + + element._data = data; + var currentElement = this.listElement.firstChild; + while (currentElement) { + if (currentElement._data && this._compareBreakpoints(currentElement._data, element._data) > 0) + break; + currentElement = currentElement.nextSibling; + } + this._addListElement(element, currentElement); + + element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this, breakpointId), true); + + this._setupBreakpointElement(data, element); + + var breakpointItem = {}; + breakpointItem.data = data; + breakpointItem.element = element; + breakpointItem.checkbox = checkbox; + this._items[breakpointId] = breakpointItem; + + if (!this.expanded) + this.expanded = true; + }, + + _breakpointRemoved: function(event) + { + var breakpointId = event.data; + var breakpointItem = this._items[breakpointId]; + if (breakpointItem) { + delete this._items[breakpointId]; + this._removeListElement(breakpointItem.element); + } + }, + + _breakpointEnableChanged: function(enabled, event) + { + var breakpointId = event.data; + var breakpointItem = this._items[breakpointId]; + if (breakpointItem) + breakpointItem.checkbox.checked = enabled; + }, + + _breakpointItemCheckboxClicked: function(breakpointId, event) + { + this._setBreakpointEnabled(breakpointId, event.target.checked); + + // Breakpoint element may have it's own click handler. + event.stopPropagation(); + }, + + _contextMenuEventFired: function(breakpointId, event) + { + var contextMenu = new WebInspector.ContextMenu(); + contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), this._removeBreakpoint.bind(this, breakpointId)); + contextMenu.show(event); + }, + + _debuggerPaused: function(event) + { + var breakpointId = this._breakpointIdForDebuggerPausedEvent(event.data); + if (!breakpointId) + return; + var breakpointItem = this._items[breakpointId]; + if (!breakpointItem) + return; + breakpointItem.element.addStyleClass("breakpoint-hit"); + this._lastHitBreakpointItem = breakpointItem; + }, + + _debuggerResumed: function() + { + if (this._lastHitBreakpointItem) { + this._lastHitBreakpointItem.element.removeStyleClass("breakpoint-hit"); + delete this._lastHitBreakpointItem; + } + }, + + _addListElement: function(element, beforeElement) + { + if (beforeElement) + this.listElement.insertBefore(element, beforeElement); + else { + if (!this.listElement.firstChild) { + this.bodyElement.removeChild(this.emptyElement); + this.bodyElement.appendChild(this.listElement); + } + this.listElement.appendChild(element); + } + }, + + _removeListElement: function(element) + { + this.listElement.removeChild(element); + if (!this.listElement.firstChild) { + this.bodyElement.removeChild(this.listElement); + this.bodyElement.appendChild(this.emptyElement); + } + }, + + _projectChanged: function() + { + this.listElement.removeChildren(); + if (this.listElement.parentElement) { + this.bodyElement.removeChild(this.listElement); + this.bodyElement.appendChild(this.emptyElement); + } + this._items = {}; + }, + + _compare: function(x, y) + { + if (x !== y) + return x < y ? -1 : 1; + return 0; + }, + + _compareBreakpoints: function(b1, b2) + { + return this._compare(b1.url, b2.url) || this._compare(b1.lineNumber, b2.lineNumber); + }, + + _setupBreakpointElement: function(data, element) + { + var displayName = data.url ? WebInspector.displayNameForURL(data.url) : WebInspector.UIString("(program)"); + var labelElement = document.createTextNode(displayName + ":" + data.lineNumber); + element.appendChild(labelElement); + + var sourceTextElement = document.createElement("div"); + sourceTextElement.className = "source-text monospace"; + element.appendChild(sourceTextElement); + + function didGetSourceLine(text) + { + sourceTextElement.textContent = text; + } + var script = WebInspector.debuggerModel.scriptForSourceID(data.sourceID); + script.sourceLine(data.lineNumber, didGetSourceLine.bind(this)); + + element.addStyleClass("cursor-pointer"); + var clickHandler = WebInspector.panels.scripts.showSourceLine.bind(WebInspector.panels.scripts, data.url, data.lineNumber); + element.addEventListener("click", clickHandler, false); + }, + + _breakpointIdForDebuggerPausedEvent: function(details) + { + var callFrame = details.callFrames[0]; + var breakpoint = WebInspector.debuggerModel.findBreakpoint(callFrame.sourceID, callFrame.line); + if (breakpoint) + return breakpoint.id; + }, + + _setBreakpointEnabled: function(breakpointId, enabled) + { + var breakpoint = WebInspector.debuggerModel.breakpointForId(breakpointId); + WebInspector.debuggerModel.removeBreakpoint(breakpointId); + WebInspector.debuggerModel.setBreakpoint(breakpoint.sourceID, breakpoint.line, enabled, breakpoint.condition); + }, + + _removeBreakpoint: function(breakpointId) + { + WebInspector.debuggerModel.removeBreakpoint(breakpointId); + } +} + +WebInspector.JavaScriptBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; + +WebInspector.NativeBreakpointsSidebarPane = function(title) { WebInspector.SidebarPane.call(this, title); @@ -39,7 +245,7 @@ WebInspector.BreakpointsSidebarPane = function(title) WebInspector.breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.ProjectChanged, this._projectChanged, this); } -WebInspector.BreakpointsSidebarPane.prototype = { +WebInspector.NativeBreakpointsSidebarPane.prototype = { addBreakpointItem: function(breakpointItem) { var element = breakpointItem.element; @@ -102,11 +308,11 @@ WebInspector.BreakpointsSidebarPane.prototype = { } } -WebInspector.BreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; +WebInspector.NativeBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; WebInspector.XHRBreakpointsSidebarPane = function() { - WebInspector.BreakpointsSidebarPane.call(this, WebInspector.UIString("XHR Breakpoints")); + WebInspector.NativeBreakpointsSidebarPane.call(this, WebInspector.UIString("XHR Breakpoints")); function addButtonClicked(event) { @@ -123,7 +329,7 @@ WebInspector.XHRBreakpointsSidebarPane = function() WebInspector.XHRBreakpointsSidebarPane.prototype = { addBreakpointItem: function(breakpointItem) { - WebInspector.BreakpointsSidebarPane.prototype.addBreakpointItem.call(this, breakpointItem); + WebInspector.NativeBreakpointsSidebarPane.prototype.addBreakpointItem.call(this, breakpointItem); breakpointItem._labelElement.addEventListener("dblclick", this._startEditingBreakpoint.bind(this, breakpointItem), false); }, @@ -166,7 +372,7 @@ WebInspector.XHRBreakpointsSidebarPane.prototype = { } } -WebInspector.XHRBreakpointsSidebarPane.prototype.__proto__ = WebInspector.BreakpointsSidebarPane.prototype; +WebInspector.XHRBreakpointsSidebarPane.prototype.__proto__ = WebInspector.NativeBreakpointsSidebarPane.prototype; WebInspector.BreakpointItem = function(breakpoint) { diff --git a/Source/WebCore/inspector/front-end/CSSCompletions.js b/Source/WebCore/inspector/front-end/CSSCompletions.js index e8d7556..f60c297 100644 --- a/Source/WebCore/inspector/front-end/CSSCompletions.js +++ b/Source/WebCore/inspector/front-end/CSSCompletions.js @@ -44,7 +44,7 @@ WebInspector.CSSCompletions.prototype = { return []; var results = []; - while (this._values[firstIndex].indexOf(prefix) === 0) + while (firstIndex < this._values.length && this._values[firstIndex].indexOf(prefix) === 0) results.push(this._values[firstIndex++]); return results; }, diff --git a/Source/WebCore/inspector/front-end/CSSKeywordCompletions.js b/Source/WebCore/inspector/front-end/CSSKeywordCompletions.js new file mode 100755 index 0000000..ac62aff --- /dev/null +++ b/Source/WebCore/inspector/front-end/CSSKeywordCompletions.js @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2011 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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.CSSKeywordCompletions = { + forProperty: function(propertyName) + { + var acceptedKeywords = ["initial"]; + if (propertyName in this._propertyKeywordMap) + acceptedKeywords = acceptedKeywords.concat(this._propertyKeywordMap[propertyName]); + if (propertyName in this._colorAwareProperties) + acceptedKeywords = acceptedKeywords.concat(WebInspector.CSSKeywordCompletions._colors); + if (propertyName in WebInspector.StylesSidebarPane.InheritedProperties) + acceptedKeywords.push("inherit"); + return new WebInspector.CSSCompletions(acceptedKeywords); + } +}; + +WebInspector.CSSKeywordCompletions._colors = [ + "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "orange", "purple", "red", + "silver", "teal", "white", "yellow", "transparent", "currentcolor", "grey", "aliceblue", "antiquewhite", + "aquamarine", "azure", "beige", "bisque", "blanchedalmond", "blueviolet", "brown", "burlywood", "cadetblue", + "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", + "darkgoldenrod", "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", + "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey", + "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", + "floralwhite", "forestgreen", "gainsboro", "ghostwhite", "gold", "goldenrod", "greenyellow", "honeydew", "hotpink", + "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", + "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink", + "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue", "lightyellow", + "limegreen", "linen", "magenta", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", + "mediumslateblue", "mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", + "mistyrose", "moccasin", "navajowhite", "oldlace", "olivedrab", "orangered", "orchid", "palegoldenrod", "palegreen", + "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "rosybrown", + "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "skyblue", "slateblue", + "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan", "thistle", "tomato", "turquoise", "violet", + "wheat", "whitesmoke", "yellowgreen" +], + +WebInspector.CSSKeywordCompletions._colorAwareProperties = [ + "background", "background-color", "border", "border-color", "border-top", "border-right", "border-bottom", + "border-left", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", "color", + "outline", "outline-color", "text-line-through", "text-line-through-color", "text-overline", "text-overline-color", + "text-shadow", "text-underline", "text-underline-color", "-webkit-text-emphasis", "-webkit-text-emphasis-color" +].keySet(); + +WebInspector.CSSKeywordCompletions._propertyKeywordMap = { + "table-layout": [ + "auto", "fixed" + ], + "visibility": [ + "hidden", "visible", "collapse" + ], + "background-repeat": [ + "repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round" + ], + "text-underline": [ + "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave" + ], + "content": [ + "list-item", "close-quote", "no-close-quote", "no-open-quote", "open-quote" + ], + "list-style-image": [ + "none" + ], + "clear": [ + "none", "left", "right", "both" + ], + "text-underline-mode": [ + "continuous", "skip-white-space" + ], + "overflow-x": [ + "hidden", "auto", "visible", "overlay", "scroll" + ], + "stroke-linejoin": [ + "round", "miter", "bevel" + ], + "baseline-shift": [ + "baseline", "sub", "super" + ], + "border-bottom-width": [ + "medium", "thick", "thin" + ], + "marquee-speed": [ + "normal", "slow", "fast" + ], + "margin-top-collapse": [ + "collapse", "separate", "discard" + ], + "max-height": [ + "none" + ], + "box-orient": [ + "horizontal", "vertical", "inline-axis", "block-axis" + ], + "font-stretch": [ + "normal", "wider", "narrower", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", + "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" + ], + "-webkit-color-correction": [ + "default", "srgb" + ], + "text-underline-style": [ + "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave" + ], + "text-overline-mode": [ + "continuous", "skip-white-space" + ], + "-webkit-background-composite": [ + "highlight", "clear", "copy", "source-over", "source-in", "source-out", "source-atop", "destination-over", + "destination-in", "destination-out", "destination-atop", "xor", "plus-darker", "plus-lighter" + ], + "border-left-width": [ + "medium", "thick", "thin" + ], + "-webkit-writing-mode": [ + "lr", "rl", "tb", "lr-tb", "rl-tb", "tb-rl", "horizontal-tb", "vertical-rl", "vertical-lr", "horizontal-bt" + ], + "text-line-through-mode": [ + "continuous", "skip-white-space" + ], + "border-collapse": [ + "collapse", "separate" + ], + "page-break-inside": [ + "auto", "avoid" + ], + "border-top-width": [ + "medium", "thick", "thin" + ], + "outline-color": [ + "invert" + ], + "text-line-through-style": [ + "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave" + ], + "outline-style": [ + "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double" + ], + "cursor": [ + "none", "copy", "auto", "crosshair", "default", "pointer", "move", "vertical-text", "cell", "context-menu", + "alias", "progress", "no-drop", "not-allowed", "-webkit-zoom-in", "-webkit-zoom-out", "e-resize", "ne-resize", + "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize", "w-resize", "ew-resize", "ns-resize", + "nesw-resize", "nwse-resize", "col-resize", "row-resize", "text", "wait", "help", "all-scroll", "-webkit-grab", + "-webkit-grabbing" + ], + "border-width": [ + "medium", "thick", "thin" + ], + "size": [ + "a3", "a4", "a5", "b4", "b5", "landscape", "ledger", "legal", "letter", "portrait" + ], + "background-size": [ + "contain", "cover" + ], + "direction": [ + "ltr", "rtl" + ], + "marquee-direction": [ + "left", "right", "auto", "reverse", "forwards", "backwards", "ahead", "up", "down" + ], + "enable-background": [ + "accumulate", "new" + ], + "float": [ + "none", "left", "right" + ], + "overflow-y": [ + "hidden", "auto", "visible", "overlay", "scroll" + ], + "margin-bottom-collapse": [ + "collapse", "separate", "discard" + ], + "box-reflect": [ + "left", "right", "above", "below" + ], + "overflow": [ + "hidden", "auto", "visible", "overlay", "scroll" + ], + "text-rendering": [ + "auto", "optimizespeed", "optimizelegibility", "geometricprecision" + ], + "text-align": [ + "-webkit-auto", "left", "right", "center", "justify", "-webkit-left", "-webkit-right", "-webkit-center" + ], + "list-style-position": [ + "outside", "inside" + ], + "margin-bottom": [ + "auto" + ], + "color-interpolation": [ + "linearrgb" + ], + "background-origin": [ + "border-box", "content-box", "padding-box" + ], + "word-wrap": [ + "normal", "break-word" + ], + "font-weight": [ + "normal", "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900" + ], + "margin-before-collapse": [ + "collapse", "separate", "discard" + ], + "text-overline-width": [ + "normal", "medium", "auto", "thick", "thin" + ], + "text-transform": [ + "none", "capitalize", "uppercase", "lowercase" + ], + "border-right-style": [ + "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double" + ], + "border-left-style": [ + "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double" + ], + "-webkit-text-emphasis": [ + "circle", "filled", "open", "dot", "double-circle", "triangle", "sesame" + ], + "font-style": [ + "italic", "oblique", "normal" + ], + "speak": [ + "none", "normal", "spell-out", "digits", "literal-punctuation", "no-punctuation" + ], + "text-line-through": [ + "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave", "continuous", + "skip-white-space" + ], + "color-rendering": [ + "auto", "optimizespeed", "optimizequality" + ], + "list-style-type": [ + "none", "disc", "circle", "square", "decimal", "decimal-leading-zero", "arabic-indic", "binary", "bengali", + "cambodian", "khmer", "devanagari", "gujarati", "gurmukhi", "kannada", "lower-hexadecimal", "lao", "malayalam", + "mongolian", "myanmar", "octal", "oriya", "persian", "urdu", "telugu", "tibetan", "thai", "upper-hexadecimal", + "lower-roman", "upper-roman", "lower-greek", "lower-alpha", "lower-latin", "upper-alpha", "upper-latin", "afar", + "ethiopic-halehame-aa-et", "ethiopic-halehame-aa-er", "amharic", "ethiopic-halehame-am-et", "amharic-abegede", + "ethiopic-abegede-am-et", "cjk-earthly-branch", "cjk-heavenly-stem", "ethiopic", "ethiopic-halehame-gez", + "ethiopic-abegede", "ethiopic-abegede-gez", "hangul-consonant", "hangul", "lower-norwegian", "oromo", + "ethiopic-halehame-om-et", "sidama", "ethiopic-halehame-sid-et", "somali", "ethiopic-halehame-so-et", "tigre", + "ethiopic-halehame-tig", "tigrinya-er", "ethiopic-halehame-ti-er", "tigrinya-er-abegede", + "ethiopic-abegede-ti-er", "tigrinya-et", "ethiopic-halehame-ti-et", "tigrinya-et-abegede", + "ethiopic-abegede-ti-et", "upper-greek", "upper-norwegian", "asterisks", "footnotes", "hebrew", "armenian", + "lower-armenian", "upper-armenian", "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha", + "katakana-iroha" + ], + "-webkit-text-combine": [ + "none", "horizontal" + ], + "outline": [ + "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double" + ], + "font": [ + "caption", "icon", "menu", "message-box", "small-caption", "-webkit-mini-control", "-webkit-small-control", + "-webkit-control", "status-bar", "italic", "oblique", "small-caps", "normal", "bold", "bolder", "lighter", + "100", "200", "300", "400", "500", "600", "700", "800", "900", "xx-small", "x-small", "small", "medium", + "large", "x-large", "xx-large", "-webkit-xxx-large", "smaller", "larger", "serif", "sans-serif", "cursive", + "fantasy", "monospace", "-webkit-body" + ], + "dominant-baseline": [ + "middle", "auto", "central", "text-before-edge", "text-after-edge", "ideographic", "alphabetic", "hanging", + "mathematical", "use-script", "no-change", "reset-size" + ], + "display": [ + "none", "inline", "block", "list-item", "run-in", "compact", "inline-block", "table", "inline-table", + "table-row-group", "table-header-group", "table-footer-group", "table-row", "table-column-group", + "table-column", "table-cell", "table-caption", "-webkit-box", "-webkit-inline-box", "-wap-marquee" + ], + "-webkit-text-emphasis-position": [ + "over", "under" + ], + "image-rendering": [ + "auto", "optimizespeed", "optimizequality" + ], + "alignment-baseline": [ + "baseline", "middle", "auto", "before-edge", "after-edge", "central", "text-before-edge", "text-after-edge", + "ideographic", "alphabetic", "hanging", "mathematical" + ], + "outline-width": [ + "medium", "thick", "thin" + ], + "text-line-through-width": [ + "normal", "medium", "auto", "thick", "thin" + ], + "box-align": [ + "baseline", "center", "stretch", "start", "end" + ], + "border-right-width": [ + "medium", "thick", "thin" + ], + "border-top-style": [ + "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double" + ], + "line-height": [ + "normal" + ], + "text-overflow": [ + "clip", "ellipsis" + ], + "box-direction": [ + "normal", "reverse" + ], + "margin-after-collapse": [ + "collapse", "separate", "discard" + ], + "page-break-before": [ + "left", "right", "auto", "always", "avoid" + ], + "-webkit-hyphens": [ + "none", "auto", "manual" + ], + "border-image": [ + "repeat", "stretch" + ], + "text-decoration": [ + "blink", "line-through", "overline", "underline" + ], + "position": [ + "absolute", "fixed", "relative", "static" + ], + "font-family": [ + "serif", "sans-serif", "cursive", "fantasy", "monospace", "-webkit-body" + ], + "text-overflow-mode": [ + "clip", "ellipsis" + ], + "border-bottom-style": [ + "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double" + ], + "unicode-bidi": [ + "normal", "bidi-override", "embed" + ], + "clip-rule": [ + "nonzero", "evenodd" + ], + "margin-left": [ + "auto" + ], + "margin-top": [ + "auto" + ], + "zoom": [ + "document", "reset" + ], + "text-overline-style": [ + "none", "dotted", "dashed", "solid", "double", "dot-dash", "dot-dot-dash", "wave" + ], + "max-width": [ + "none" + ], + "empty-cells": [ + "hide", "show" + ], + "pointer-events": [ + "none", "all", "auto", "visible", "visiblepainted", "visiblefill", "visiblestroke", "painted", "fill", "stroke" + ], + "letter-spacing": [ + "normal" + ], + "background-clip": [ + "border-box", "content-box", "padding-box" + ], + "-webkit-font-smoothing": [ + "none", "auto", "antialiased", "subpixel-antialiased" + ], + "border": [ + "none", "hidden", "inset", "groove", "ridge", "outset", "dotted", "dashed", "solid", "double" + ], + "font-size": [ + "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "-webkit-xxx-large", "smaller", + "larger" + ], + "font-variant": [ + "small-caps", "normal" + ], + "vertical-align": [ + "baseline", "middle", "sub", "super", "text-top", "text-bottom", "top", "bottom", "-webkit-baseline-middle" + ], + "marquee-style": [ + "none", "scroll", "slide", "alternate" + ], + "white-space": [ + "normal", "nowrap", "pre", "pre-line", "pre-wrap" + ], + "text-underline-width": [ + "normal", "medium", "auto", "thick", "thin" + ], + "box-lines": [ + "single", "multiple" + ], + "page-break-after": [ + "left", "right", "auto", "always", "avoid" + ], + "clip-path": [ + "none" + ], + "margin": [ + "auto" + ], + "marquee-repetition": [ + "infinite" + ], + "margin-right": [ + "auto" + ], + "-webkit-text-emphasis-style": [ + "circle", "filled", "open", "dot", "double-circle", "triangle", "sesame" + ] +} diff --git a/Source/WebCore/inspector/front-end/ConsoleView.js b/Source/WebCore/inspector/front-end/ConsoleView.js index a40030e..bd08e60 100644 --- a/Source/WebCore/inspector/front-end/ConsoleView.js +++ b/Source/WebCore/inspector/front-end/ConsoleView.js @@ -149,6 +149,11 @@ WebInspector.ConsoleView.prototype = { consoleMessagesCleared: function() { console.clearMessages(); + }, + + monitoringXHRStateChanged: function(enabled) + { + console._monitoringXHREnabled = enabled; } } InspectorBackend.registerDomainDispatcher("Console", dispatcher); @@ -355,14 +360,11 @@ WebInspector.ConsoleView.prototype = { // Collect comma separated object properties for the completion. var includeInspectorCommandLineAPI = (!dotNotation && !bracketNotation); - var callFrameId = WebInspector.panels.scripts.selectedCallFrameId(); var injectedScriptAccess; - if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused) { - var selectedCallFrame = WebInspector.panels.scripts.sidebarPanes.callstack.selectedCallFrame; - injectedScriptAccess = InjectedScriptAccess.get(selectedCallFrame.worldId); - } else - injectedScriptAccess = InjectedScriptAccess.getDefault(); - injectedScriptAccess.getCompletions(expressionString, includeInspectorCommandLineAPI, callFrameId, reportCompletions); + if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused) + InspectorBackend.getCompletionsOnCallFrame(WebInspector.panels.scripts.selectedCallFrameId(), expressionString, includeInspectorCommandLineAPI, reportCompletions); + else + InspectorBackend.getCompletions(expressionString, includeInspectorCommandLineAPI, reportCompletions); }, _reportCompletions: function(bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix, result, isException) { @@ -416,14 +418,9 @@ WebInspector.ConsoleView.prototype = { return; } + var itemAction = InspectorBackend.setMonitoringXHREnabled.bind(InspectorBackend, !this._monitoringXHREnabled); var contextMenu = new WebInspector.ContextMenu(); - - function monitoringXHRWasChanged(newState) - { - WebInspector.monitoringXHREnabled = newState; - } - var itemAction = InspectorBackend.setMonitoringXHREnabled.bind(InspectorBackend, !WebInspector.monitoringXHREnabled, monitoringXHRWasChanged); - contextMenu.appendCheckboxItem(WebInspector.UIString("XMLHttpRequest logging"), itemAction, WebInspector.monitoringXHREnabled); + contextMenu.appendCheckboxItem(WebInspector.UIString("XMLHttpRequest logging"), itemAction, this._monitoringXHREnabled); contextMenu.appendItem(WebInspector.UIString("Clear Console"), this.requestClearMessages.bind(this)); contextMenu.show(event); }, @@ -532,8 +529,8 @@ WebInspector.ConsoleView.prototype = { function evalCallback(result) { callback(WebInspector.RemoteObject.fromPayload(result)); - }; - InjectedScriptAccess.getDefault().evaluate(expression, objectGroup, evalCallback); + } + InspectorBackend.evaluate(expression, objectGroup, evalCallback); }, _enterKeyPressed: function(event) @@ -681,6 +678,15 @@ WebInspector.ConsoleMessage = function(source, type, level, line, url, repeatCou this._parameters = parameters; this._stackTrace = stackTrace; this._requestId = requestId; + + if (stackTrace && stackTrace.length) { + var topCallFrame = stackTrace[0]; + if (!this.url) + this.url = topCallFrame.scriptName; + if (!this.line) + this.line = topCallFrame.lineNumber; + } + this._formatMessage(); } @@ -737,17 +743,8 @@ WebInspector.ConsoleMessage.prototype = { this._formattedMessage = document.createElement("span"); this._formattedMessage.className = "console-message-text source-code"; - if (stackTrace && stackTrace.length) { - var topCallFrame = stackTrace[0]; - var sourceName = topCallFrame.scriptName; - var sourceLine = topCallFrame.lineNumber; - } else { - var sourceName = this.url; - var sourceLine = this.line; - } - - if (sourceName && sourceName !== "undefined") { - var urlElement = WebInspector.linkifyResourceAsNode(sourceName, "scripts", sourceLine, "console-message-url"); + if (this.url && this.url !== "undefined") { + var urlElement = WebInspector.linkifyResourceAsNode(this.url, "scripts", this.line, "console-message-url"); this._formattedMessage.appendChild(urlElement); } diff --git a/Source/WebCore/inspector/front-end/DebuggerModel.js b/Source/WebCore/inspector/front-end/DebuggerModel.js index 8f5bcf7..717486c 100644 --- a/Source/WebCore/inspector/front-end/DebuggerModel.js +++ b/Source/WebCore/inspector/front-end/DebuggerModel.js @@ -31,6 +31,7 @@ WebInspector.DebuggerModel = function() { this._paused = false; + this._callFrames = []; this._breakpoints = {}; this._sourceIDAndLineToBreakpointId = {}; this._scripts = {}; @@ -43,6 +44,7 @@ WebInspector.DebuggerModel.Events = { DebuggerResumed: "debugger-resumed", ParsedScriptSource: "parsed-script-source", FailedToParseScriptSource: "failed-to-parse-script-source", + ScriptSourceChanged: "script-source-changed", BreakpointAdded: "breakpoint-added", BreakpointRemoved: "breakpoint-removed" } @@ -84,6 +86,7 @@ WebInspector.DebuggerModel.prototype = { delete this._breakpoints[breakpointId]; delete this._sourceIDAndLineToBreakpointId[this._encodeSourceIDAndLine(breakpoint.sourceID, breakpoint.line)]; this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointRemoved, breakpointId); + breakpoint.dispatchEventToListeners("removed"); }, _breakpointSetOnBackend: function(breakpointId, sourceID, lineNumber, condition, enabled, originalLineNumber, restored) @@ -103,6 +106,11 @@ WebInspector.DebuggerModel.prototype = { this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointAdded, breakpoint); }, + breakpointForId: function(breakpointId) + { + return this._breakpoints[breakpointId]; + }, + queryBreakpoints: function(filter) { var breakpoints = []; @@ -129,6 +137,7 @@ WebInspector.DebuggerModel.prototype = { reset: function() { this._paused = false; + this._callFrames = []; this._breakpoints = {}; delete this._oneTimeBreakpoint; this._sourceIDAndLineToBreakpointId = {}; @@ -156,39 +165,81 @@ WebInspector.DebuggerModel.prototype = { return scripts; }, + editScriptSource: function(sourceID, scriptSource) + { + function didEditScriptSource(success, newBodyOrErrorMessage, callFrames) + { + if (success) { + if (callFrames && callFrames.length) + this._callFrames = callFrames; + this._updateScriptSource(sourceID, newBodyOrErrorMessage); + } else + WebInspector.log(newBodyOrErrorMessage, WebInspector.ConsoleMessage.MessageLevel.Warning); + } + InspectorBackend.editScriptSource(sourceID, scriptSource, didEditScriptSource.bind(this)); + }, + + _updateScriptSource: function(sourceID, scriptSource) + { + var script = this._scripts[sourceID]; + var oldSource = script.source; + script.source = scriptSource; + + // Clear and re-create breakpoints according to text diff. + var diff = Array.diff(oldSource.split("\n"), script.source.split("\n")); + for (var id in this._breakpoints) { + var breakpoint = this._breakpoints[id]; + if (breakpoint.sourceID !== sourceID) + continue; + breakpoint.remove(); + var lineNumber = breakpoint.line - 1; + var newLineNumber = diff.left[lineNumber].row; + if (newLineNumber === undefined) { + for (var i = lineNumber - 1; i >= 0; --i) { + if (diff.left[i].row === undefined) + continue; + var shiftedLineNumber = diff.left[i].row + lineNumber - i; + if (shiftedLineNumber < diff.right.length) { + var originalLineNumber = diff.right[shiftedLineNumber].row; + if (originalLineNumber === lineNumber || originalLineNumber === undefined) + newLineNumber = shiftedLineNumber; + } + break; + } + } + if (newLineNumber !== undefined) + this.setBreakpoint(sourceID, newLineNumber + 1, breakpoint.enabled, breakpoint.condition); + } + + this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ScriptSourceChanged, { sourceID: sourceID, oldSource: oldSource }); + }, + + get callFrames() + { + return this._callFrames; + }, + _pausedScript: function(details) { this._paused = true; + this._callFrames = details.callFrames; if ("_continueToLineBreakpointId" in this) { InspectorBackend.removeBreakpoint(this._continueToLineBreakpointId); delete this._continueToLineBreakpointId; } this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, details); - - if (details.eventType === WebInspector.DebuggerEventTypes.JavaScriptPause || details.eventType === WebInspector.DebuggerEventTypes.NativeBreakpoint) - return; - - var breakpoint = this.findBreakpoint(details.callFrames[0].sourceID, details.callFrames[0].line); - if (!breakpoint) - return; - breakpoint.hit = true; - this._lastHitBreakpoint = breakpoint; }, _resumedScript: function() { this._paused = false; + this._callFrames = []; this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed); - - if (!this._lastHitBreakpoint) - return; - this._lastHitBreakpoint.hit = false; - delete this._lastHitBreakpoint; }, - _parsedScriptSource: function(sourceID, sourceURL, lineOffset, columnOffset, scriptWorldType) + _parsedScriptSource: function(sourceID, sourceURL, lineOffset, columnOffset, length, scriptWorldType) { - var script = new WebInspector.Script(sourceID, sourceURL, "", lineOffset, columnOffset, undefined, undefined, scriptWorldType); + var script = new WebInspector.Script(sourceID, sourceURL, "", lineOffset, columnOffset, length, undefined, undefined, scriptWorldType); this._scripts[sourceID] = script; this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ParsedScriptSource, sourceID); }, @@ -234,9 +285,9 @@ WebInspector.DebuggerDispatcher.prototype = { WebInspector.panels.scripts.debuggerWasDisabled(); }, - parsedScriptSource: function(sourceID, sourceURL, lineOffset, columnOffset, scriptWorldType) + parsedScriptSource: function(sourceID, sourceURL, lineOffset, columnOffset, length, scriptWorldType) { - this._debuggerModel._parsedScriptSource(sourceID, sourceURL, lineOffset, columnOffset, scriptWorldType); + this._debuggerModel._parsedScriptSource(sourceID, sourceURL, lineOffset, columnOffset, length, scriptWorldType); }, failedToParseScriptSource: function(sourceURL, source, startingLine, errorLine, errorMessage) diff --git a/Source/WebCore/inspector/front-end/ElementsTreeOutline.js b/Source/WebCore/inspector/front-end/ElementsTreeOutline.js index 67d34b2..722c028 100644 --- a/Source/WebCore/inspector/front-end/ElementsTreeOutline.js +++ b/Source/WebCore/inspector/front-end/ElementsTreeOutline.js @@ -411,8 +411,7 @@ WebInspector.ElementsTreeElement.prototype = { else this.tooltip = WebInspector.UIString("%d × %d pixels (Natural: %d × %d pixels)", properties.offsetWidth, properties.offsetHeight, properties.naturalWidth, properties.naturalHeight); } - - InjectedScriptAccess.getForNode(node).getNodeProperties(node.id, ["naturalHeight", "naturalWidth", "offsetHeight", "offsetWidth"], setTooltip.bind(this)); + InspectorBackend.getNodeProperties(node.id, ["naturalHeight", "naturalWidth", "offsetHeight", "offsetWidth"], setTooltip.bind(this)); }, updateSelection: function() diff --git a/Source/WebCore/inspector/front-end/ExtensionAPI.js b/Source/WebCore/inspector/front-end/ExtensionAPI.js index a9a2423..b10452d 100644 --- a/Source/WebCore/inspector/front-end/ExtensionAPI.js +++ b/Source/WebCore/inspector/front-end/ExtensionAPI.js @@ -450,8 +450,9 @@ ExtensionServerClient.prototype = { _onCallback: function(request) { if (request.requestId in this._callbacks) { - this._callbacks[request.requestId](request.result); + var callback = this._callbacks[request.requestId]; delete this._callbacks[request.requestId]; + callback(request.result); } }, diff --git a/Source/WebCore/inspector/front-end/ExtensionPanel.js b/Source/WebCore/inspector/front-end/ExtensionPanel.js index 4b42e68..fb98350 100644 --- a/Source/WebCore/inspector/front-end/ExtensionPanel.js +++ b/Source/WebCore/inspector/front-end/ExtensionPanel.js @@ -95,7 +95,7 @@ WebInspector.ExtensionWatchSidebarPane.prototype = { setExpression: function(expression, title) { - InjectedScriptAccess.getDefault().evaluate(expression, this._onEvaluate.bind(this, title)); + InspectorBackend.evaluate(expression, "extension-watch", this._onEvaluate.bind(this, title)); }, _onEvaluate: function(title, result) diff --git a/Source/WebCore/inspector/front-end/ExtensionServer.js b/Source/WebCore/inspector/front-end/ExtensionServer.js index 1320efb..0924106 100644 --- a/Source/WebCore/inspector/front-end/ExtensionServer.js +++ b/Source/WebCore/inspector/front-end/ExtensionServer.js @@ -53,7 +53,6 @@ WebInspector.ExtensionServer = function() this._registerHandler("subscribe", this._onSubscribe.bind(this)); this._registerHandler("unsubscribe", this._onUnsubscribe.bind(this)); - window.addEventListener("message", this._onWindowMessage.bind(this), false); } @@ -68,11 +67,6 @@ WebInspector.ExtensionServer.prototype = { this._postNotification("panel-objectSelected-" + panelId, objectId); }, - notifyResourceFinished: function(resource) - { - this._postNotification("resource-finished", resource.identifier, (new WebInspector.HAREntry(resource)).build()); - }, - notifySearchAction: function(panelId, action, searchString) { this._postNotification("panel-search-" + panelId, action, searchString); @@ -114,6 +108,12 @@ WebInspector.ExtensionServer.prototype = { delete this._clientObjects[auditRun.id]; }, + _notifyResourceFinished: function(event) + { + var resource = event.data; + this._postNotification("resource-finished", resource.identifier, (new WebInspector.HAREntry(resource)).build()); + }, + _postNotification: function(type, details) { var subscribers = this._subscribers[type]; @@ -272,7 +272,7 @@ WebInspector.ExtensionServer.prototype = { var evalExpression = "JSON.stringify(eval('" + "with (window.console._commandLineAPI) with (window) {' + unescape('" + escape(message.expression) + "') + '}'));"; - InjectedScriptAccess.getDefault().evaluate(evalExpression, callback.bind(this)); + InspectorBackend.evaluate(evalExpression, "none", callback.bind(this)); }, _onRevealAndSelect: function(message) @@ -356,6 +356,9 @@ WebInspector.ExtensionServer.prototype = { initExtensions: function() { + // The networkManager is normally created after the ExtensionServer is constructed, but before initExtensions() is called. + WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceFinished, this._notifyResourceFinished, this); + InspectorExtensionRegistry.getExtensionsAsync(); }, @@ -393,7 +396,6 @@ WebInspector.ExtensionServer.prototype = { "var apiPrivate = {};" + "(" + WebInspector.commonExtensionSymbols.toString() + ")(apiPrivate);" + "(" + WebInspector.injectedExtensionAPI.toString() + ").apply(this, arguments);" + - "webInspector.resources.Types = " + JSON.stringify(resourceTypes) + ";" + platformAPI + "})"; }, diff --git a/Source/WebCore/inspector/front-end/InjectedScriptAccess.js b/Source/WebCore/inspector/front-end/InjectedScriptAccess.js deleted file mode 100644 index cb3c2b8..0000000 --- a/Source/WebCore/inspector/front-end/InjectedScriptAccess.js +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. All rights reserved. - * Copyright (C) 2009 Joseph Pecoraro - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT - * OWNER OR 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. - */ - -function InjectedScriptAccess(worldId) { - this._worldId = worldId; -} - -InjectedScriptAccess.get = function(worldId) -{ - if (typeof worldId === "number") - return new InjectedScriptAccess(worldId); - - console.assert(false, "Access to injected script with no id"); -} - -InjectedScriptAccess.getForNode = function(node) -{ - // FIXME: do something. - return InjectedScriptAccess.get(-node.id); -} - -InjectedScriptAccess.getForObjectId = function(objectId) -{ - // FIXME: move to native layer. - var tokens = objectId.split(":"); - return InjectedScriptAccess.get(parseInt(tokens[0])); -} - -InjectedScriptAccess.getDefault = function() -{ - return InjectedScriptAccess.get(0); -} - -InjectedScriptAccess.prototype = {}; - -InjectedScriptAccess._installHandler = function(methodName, async) -{ - InjectedScriptAccess.prototype[methodName] = function() - { - var allArgs = Array.prototype.slice.call(arguments); - var callback = allArgs[allArgs.length - 1]; - var argsString = JSON.stringify(Array.prototype.slice.call(allArgs, 0, allArgs.length - 1)); - - function myCallback(result, isException) - { - if (!isException) - callback(result); - else - WebInspector.console.addMessage(WebInspector.ConsoleMessage.createTextMessage("Error dispatching: " + methodName)); - } - InspectorBackend.dispatchOnInjectedScript(this._worldId, methodName, argsString, myCallback); - }; -} - -// InjectedScriptAccess message forwarding puts some constraints on the way methods are implemented and called: -// - Make sure corresponding methods in InjectedScript return non-null and non-undefined values, -// - Make sure last parameter of all the InjectedSriptAccess.* calls is a callback function. -// We keep these sorted. -InjectedScriptAccess._installHandler("evaluate"); -InjectedScriptAccess._installHandler("evaluateInCallFrame"); -InjectedScriptAccess._installHandler("evaluateOnSelf"); -InjectedScriptAccess._installHandler("getCompletions"); -InjectedScriptAccess._installHandler("getProperties"); -InjectedScriptAccess._installHandler("getPrototypes"); -InjectedScriptAccess._installHandler("pushNodeToFrontend"); -InjectedScriptAccess._installHandler("resolveNode"); -InjectedScriptAccess._installHandler("getNodeProperties"); -InjectedScriptAccess._installHandler("setPropertyValue"); diff --git a/Source/WebCore/inspector/front-end/NetworkManager.js b/Source/WebCore/inspector/front-end/NetworkManager.js index a657377..ed4309e 100644 --- a/Source/WebCore/inspector/front-end/NetworkManager.js +++ b/Source/WebCore/inspector/front-end/NetworkManager.js @@ -30,6 +30,8 @@ WebInspector.NetworkManager = function(resourceTreeModel) { + WebInspector.Object.call(this); + this._inflightResources = {}; this._resourceTreeModel = resourceTreeModel; this._lastIdentifierForCachedResource = 0; @@ -86,6 +88,13 @@ WebInspector.NetworkManager.updateResourceWithCachedResource = function(resource WebInspector.NetworkManager.updateResourceWithResponse(resource, cachedResource.response); } +WebInspector.NetworkManager.EventTypes = { + ResourceStarted: "ResourceStarted", + ResourceUpdated: "ResourceUpdated", + ResourceFinished: "ResourceFinished", + MainResourceCommitLoad: "MainResourceCommitLoad" +} + WebInspector.NetworkManager.prototype = { reset: function() { @@ -119,7 +128,7 @@ WebInspector.NetworkManager.prototype = { if (isRedirect) this._startResource(resource); else - WebInspector.panels.network.refreshResource(resource); + this._updateResource(resource); }, markResourceAsCached: function(identifier) @@ -129,7 +138,7 @@ WebInspector.NetworkManager.prototype = { return; resource.cached = true; - WebInspector.panels.network.refreshResource(resource); + this._updateResource(resource); }, didReceiveResponse: function(identifier, time, resourceType, response) @@ -143,7 +152,7 @@ WebInspector.NetworkManager.prototype = { WebInspector.NetworkManager.updateResourceWithResponse(resource, response); - WebInspector.panels.network.refreshResource(resource); + this._updateResource(resource); this._resourceTreeModel.addResourceToFrame(resource.loader.frameId, resource); }, @@ -156,7 +165,7 @@ WebInspector.NetworkManager.prototype = { resource.resourceSize += lengthReceived; resource.endTime = time; - WebInspector.panels.network.refreshResource(resource); + this._updateResource(resource); }, didFinishLoading: function(identifier, finishTime) @@ -204,8 +213,7 @@ WebInspector.NetworkManager.prototype = { resource.type = WebInspector.Resource.Type[type]; resource.setInitialContent(sourceString); - WebInspector.panels.resources.refreshResource(resource); - WebInspector.panels.network.refreshResource(resource); + this._updateResource(resource); }, didCommitLoadForFrame: function(frame, loader) @@ -216,7 +224,7 @@ WebInspector.NetworkManager.prototype = { if (mainResource) { WebInspector.mainResource = mainResource; mainResource.isMainResource = true; - WebInspector.panels.network.mainResourceChanged(); + this.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.MainResourceCommitLoad, mainResource); } } }, @@ -239,7 +247,7 @@ WebInspector.NetworkManager.prototype = { resource.webSocketRequestKey3 = request.webSocketRequestKey3; resource.startTime = time; - WebInspector.panels.network.refreshResource(resource); + this._updateResource(resource); }, didReceiveWebSocketHandshakeResponse: function(identifier, time, response) @@ -254,7 +262,7 @@ WebInspector.NetworkManager.prototype = { resource.webSocketChallengeResponse = response.webSocketChallengeResponse; resource.responseReceivedTime = time; - WebInspector.panels.network.refreshResource(resource); + this._updateResource(resource); }, didCloseWebSocket: function(identifier, time) @@ -283,20 +291,24 @@ WebInspector.NetworkManager.prototype = { return newResource; }, - _startResource: function(resource, skipRefresh) + _startResource: function(resource) { this._inflightResources[resource.identifier] = resource; - WebInspector.panels.network.appendResource(resource, skipRefresh); - WebInspector.panels.audits.resourceStarted(resource); + this.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.ResourceStarted, resource); + }, + + _updateResource: function(resource) + { + this.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.ResourceUpdated, resource); }, _finishResource: function(resource, finishTime) { resource.endTime = finishTime; resource.finished = true; - WebInspector.panels.network.refreshResource(resource); - WebInspector.panels.audits.resourceFinished(resource); - WebInspector.extensionServer.notifyResourceFinished(resource); + this.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.ResourceFinished, resource); delete this._inflightResources[resource.identifier]; } } + +WebInspector.NetworkManager.prototype.__proto__ = WebInspector.Object.prototype; diff --git a/Source/WebCore/inspector/front-end/NetworkPanel.js b/Source/WebCore/inspector/front-end/NetworkPanel.js index 28cbd36..943ee7f 100644 --- a/Source/WebCore/inspector/front-end/NetworkPanel.js +++ b/Source/WebCore/inspector/front-end/NetworkPanel.js @@ -78,6 +78,11 @@ WebInspector.NetworkPanel = function() this._filter(this._filterAllElement, false); this._toggleGridMode(); + + WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this); + WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceUpdated, this._onResourceUpdated, this); + WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceFinished, this._onResourceUpdated, this); + WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.MainResourceCommitLoad, this._onMainResourceCommitLoad, this); } WebInspector.NetworkPanel.prototype = { @@ -772,7 +777,12 @@ WebInspector.NetworkPanel.prototype = { return this._resourcesById[id]; }, - appendResource: function(resource) + _onResourceStarted: function(event) + { + this._appendResource(event.data); + }, + + _appendResource: function(resource) { this._resources.push(resource); this._resourcesById[resource.identifier] = resource; @@ -781,13 +791,18 @@ WebInspector.NetworkPanel.prototype = { // Pull all the redirects of the main resource upon commit load. if (resource.redirects) { for (var i = 0; i < resource.redirects.length; ++i) - this.refreshResource(resource.redirects[i]); + this._refreshResource(resource.redirects[i]); } - this.refreshResource(resource); + this._refreshResource(resource); + }, + + _onResourceUpdated: function(event) + { + this._refreshResource(event.data); }, - refreshResource: function(resource) + _refreshResource: function(resource) { this._staleResources.push(resource); this._scheduleRefresh(); @@ -811,7 +826,7 @@ WebInspector.NetworkPanel.prototype = { this._reset(); }, - mainResourceChanged: function() + _onMainResourceCommitLoad: function() { if (this._preserveLogToggle.toggled) return; @@ -819,7 +834,7 @@ WebInspector.NetworkPanel.prototype = { this._reset(); // Now resurrect the main resource along with all redirects that lead to it. var resourcesToAppend = (WebInspector.mainResource.redirects || []).concat(WebInspector.mainResource); - resourcesToAppend.forEach(this.appendResource, this); + resourcesToAppend.forEach(this._appendResource, this); }, canShowSourceLine: function(url, line) diff --git a/Source/WebCore/inspector/front-end/PropertiesSidebarPane.js b/Source/WebCore/inspector/front-end/PropertiesSidebarPane.js index b9c212a..a1e37bc 100644 --- a/Source/WebCore/inspector/front-end/PropertiesSidebarPane.js +++ b/Source/WebCore/inspector/front-end/PropertiesSidebarPane.js @@ -42,11 +42,11 @@ WebInspector.PropertiesSidebarPane.prototype = { return; } - var self = this; - var callback = function(prototypes) { - var body = self.bodyElement; + function callback(prototypes) + { + var body = this.bodyElement; body.removeChildren(); - self.sections = []; + this.sections = []; // Get array of prototype user-friendly names. for (var i = 0; i < prototypes.length; ++i) { @@ -55,11 +55,11 @@ WebInspector.PropertiesSidebarPane.prototype = { if (title.match(/Prototype$/)) title = title.replace(/Prototype$/, ""); var section = new WebInspector.ObjectPropertiesSection(prototype, title); - self.sections.push(section); + this.sections.push(section); body.appendChild(section.element); } - }; - InjectedScriptAccess.getForNode(node).getPrototypes(node.id, callback); + } + InspectorBackend.getNodePrototypes(node.id, callback.bind(this)); } } diff --git a/Source/WebCore/inspector/front-end/RemoteObject.js b/Source/WebCore/inspector/front-end/RemoteObject.js index 4d6736c..10af2e3 100644 --- a/Source/WebCore/inspector/front-end/RemoteObject.js +++ b/Source/WebCore/inspector/front-end/RemoteObject.js @@ -52,7 +52,7 @@ WebInspector.RemoteObject.resolveNode = function(node, callback) { callback(object ? WebInspector.RemoteObject.fromPayload(object) : null); } - InjectedScriptAccess.getForNode(node).resolveNode(node.id, mycallback); + InspectorBackend.resolveNode(node.id, mycallback); } WebInspector.RemoteObject.fromPayload = function(payload) @@ -118,7 +118,7 @@ WebInspector.RemoteObject.prototype = { properties[i].value = WebInspector.RemoteObject.fromPayload(properties[i].value); callback(properties); } - InjectedScriptAccess.getForObjectId(this._objectId).getProperties(this._objectId, ignoreHasOwnProperty, abbreviate, remoteObjectBinder); + InspectorBackend.getProperties(this._objectId, !!ignoreHasOwnProperty, abbreviate, remoteObjectBinder); }, setPropertyValue: function(name, value, callback) @@ -127,12 +127,12 @@ WebInspector.RemoteObject.prototype = { callback(false); return; } - InjectedScriptAccess.getForObjectId(this._objectId).setPropertyValue(this._objectId, name, value, callback); + InspectorBackend.setPropertyValue(this._objectId, name, value, callback); }, pushNodeToFrontend: function(callback) { - InjectedScriptAccess.getForObjectId(this._objectId).pushNodeToFrontend(this._objectId, callback); + InspectorBackend.pushNodeToFrontend(this._objectId, callback); } } diff --git a/Source/WebCore/inspector/front-end/Resource.js b/Source/WebCore/inspector/front-end/Resource.js index 7340645..00c1fb9 100644 --- a/Source/WebCore/inspector/front-end/Resource.js +++ b/Source/WebCore/inspector/front-end/Resource.js @@ -44,7 +44,6 @@ WebInspector.Resource.Type = { Font: 3, Script: 4, XHR: 5, - Media: 6, WebSocket: 7, Other: 8, @@ -68,8 +67,6 @@ WebInspector.Resource.Type = { return WebInspector.UIString("Script"); case this.XHR: return WebInspector.UIString("XHR"); - case this.Media: - return WebInspector.UIString("Media"); case this.WebSocket: return WebInspector.UIString("WebSocket"); case this.Other: @@ -95,8 +92,6 @@ WebInspector.Resource.Type = { return "script"; case this.XHR: return "xhr"; - case this.Media: - return "media"; case this.WebSocket: return "websocket"; case this.Other: @@ -669,6 +664,13 @@ WebInspector.Resource.prototype = { requestContent: function(callback) { + // We do not support content retrieval for WebSockets at the moment. + // Since WebSockets are potentially long-living, fail requests immediately + // to prevent caller blocking until resource is marked as finished. + if (this.type === WebInspector.Resource.Type.WebSocket) { + callback(null, null); + return; + } if (this._content) { callback(this._content, this._contentEncoded); return; diff --git a/Source/WebCore/inspector/front-end/ResourcesPanel.js b/Source/WebCore/inspector/front-end/ResourcesPanel.js index d96989b..7e1fcc0 100644 --- a/Source/WebCore/inspector/front-end/ResourcesPanel.js +++ b/Source/WebCore/inspector/front-end/ResourcesPanel.js @@ -78,6 +78,8 @@ WebInspector.ResourcesPanel = function(database) this.sidebarElement.addEventListener("mousemove", this._onmousemove.bind(this), false); this.sidebarElement.addEventListener("mouseout", this._onmouseout.bind(this), false); + + WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceUpdated, this._refreshResource, this); } WebInspector.ResourcesPanel.prototype = { @@ -253,8 +255,9 @@ WebInspector.ResourcesPanel.prototype = { frameTreeElement.removeChildren(); }, - refreshResource: function(resource) + _refreshResource: function(event) { + var resource = event.data; // FIXME: do not add XHR in the first place based on the native instrumentation. if (resource.type === WebInspector.Resource.Type.XHR) { var resourceTreeElement = this._findTreeElementForResource(resource); diff --git a/Source/WebCore/inspector/front-end/Script.js b/Source/WebCore/inspector/front-end/Script.js index 89b2121..6e3b18d 100644 --- a/Source/WebCore/inspector/front-end/Script.js +++ b/Source/WebCore/inspector/front-end/Script.js @@ -23,13 +23,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.Script = function(sourceID, sourceURL, source, lineOffset, columnOffset, errorLine, errorMessage, worldType) +WebInspector.Script = function(sourceID, sourceURL, source, lineOffset, columnOffset, length, errorLine, errorMessage, worldType) { this.sourceID = sourceID; this.sourceURL = sourceURL; this._source = source; this.lineOffset = lineOffset; this.columnOffset = columnOffset; + this.length = length; this.errorLine = errorLine; this.errorMessage = errorMessage; this.worldType = worldType; @@ -96,12 +97,15 @@ WebInspector.Script.prototype = { get source() { + if (!this._source && this.resource) + this._source = this.resource.content; return this._source; }, set source(source) { this._source = source; + delete this._lineEndings; }, requestSource: function(callback) diff --git a/Source/WebCore/inspector/front-end/ScriptFormatter.js b/Source/WebCore/inspector/front-end/ScriptFormatter.js new file mode 100644 index 0000000..69ffb74 --- /dev/null +++ b/Source/WebCore/inspector/front-end/ScriptFormatter.js @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2011 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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.ScriptFormatter = function(source) +{ + this._originalSource = source; + this._originalLineEndings = source.findAll("\n"); + this._originalLineEndings.push(source.length); +} + +WebInspector.ScriptFormatter.locationToPosition = function(lineEndings, location) +{ + var position = location.line ? lineEndings[location.line - 1] + 1 : 0; + return position + location.column; +} + +WebInspector.ScriptFormatter.positionToLocation = function(lineEndings, position) +{ + var location = {}; + location.line = lineEndings.upperBound(position - 1); + if (!location.line) + location.column = position; + else + location.column = position - lineEndings[location.line - 1] - 1; + return location; +} + +WebInspector.ScriptFormatter.prototype = { + format: function(callback) + { + var worker = new Worker("scriptFormatterWorker.js"); + function messageHandler(event) + { + var formattedSource = event.data; + this._formatted = true; + this._formattedSource = formattedSource; + this._formattedLineEndings = formattedSource.findAll("\n"); + this._formattedLineEndings.push(formattedSource.length); + this._buildMapping(); + callback(formattedSource); + } + worker.onmessage = messageHandler.bind(this); + worker.postMessage(this._originalSource); + }, + + _buildMapping: function() + { + this._originalSymbolPositions = []; + this._formattedSymbolPositions = []; + var lastCodePosition = 0; + var regexp = /[\$\.\w]+|{|}|;/g; + while (true) { + var match = regexp.exec(this._formattedSource); + if (!match) + break; + var position = this._originalSource.indexOf(match[0], lastCodePosition); + if (position === -1) + continue; + this._originalSymbolPositions.push(position); + this._formattedSymbolPositions.push(match.index); + lastCodePosition = position + match[0].length; + } + this._originalSymbolPositions.push(this._originalSource.length); + this._formattedSymbolPositions.push(this._formattedSource.length); + }, + + originalLineNumberToFormattedLineNumber: function(originalLineNumber) + { + if (!this._formatted) + return originalLineNumber; + var originalPosition = WebInspector.ScriptFormatter.locationToPosition(this._originalLineEndings, { line: originalLineNumber, column: 0 }); + return this.originalPositionToFormattedLineNumber(originalPosition); + }, + + formattedLineNumberToOriginalLineNumber: function(formattedLineNumber) + { + if (!this._formatted) + return formattedLineNumber; + var originalPosition = this.formattedLineNumberToOriginalPosition(formattedLineNumber); + return WebInspector.ScriptFormatter.positionToLocation(this._originalLineEndings, originalPosition).line; + }, + + originalPositionToFormattedLineNumber: function(originalPosition) + { + var lineEndings = this._formatted ? this._formattedLineEndings : this._originalLineEndings; + if (this._formatted) + formattedPosition = this._convertPosition(this._originalSymbolPositions, this._formattedSymbolPositions, originalPosition); + return WebInspector.ScriptFormatter.positionToLocation(lineEndings, formattedPosition).line; + }, + + formattedLineNumberToOriginalPosition: function(formattedLineNumber) + { + var lineEndings = this._formatted ? this._formattedLineEndings : this._originalLineEndings; + var formattedPosition = WebInspector.ScriptFormatter.locationToPosition(lineEndings, { line: formattedLineNumber, column: 0 }); + if (!this._formatted) + return formattedPosition; + return this._convertPosition(this._formattedSymbolPositions, this._originalSymbolPositions, formattedPosition); + }, + + _convertPosition: function(symbolPositions1, symbolPositions2, position) + { + var index = symbolPositions1.upperBound(position); + if (index === symbolPositions2.length - 1) + return symbolPositions2[index] - 1; + return symbolPositions2[index]; + } +} diff --git a/Source/WebCore/inspector/front-end/ScriptFormatterWorker.js b/Source/WebCore/inspector/front-end/ScriptFormatterWorker.js new file mode 100644 index 0000000..e900317 --- /dev/null +++ b/Source/WebCore/inspector/front-end/ScriptFormatterWorker.js @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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. + */ + +var parse = loadModule("parse-js.js"); +var process = loadModule("process.js"); + +onmessage = function(event) { + postMessage(beautify(event.data)); +}; + +function beautify(source) +{ + var ast = parse.parse(source); + var beautifyOptions = { + indent_level: 4, + indent_start: 0, + quote_keys: false, + space_colon: false + }; + return process.gen_code(ast, beautifyOptions); +} + +function loadModule(src) +{ + var request = new XMLHttpRequest(); + request.open("GET", src, false); + request.send(); + + var exports = {}; + eval(request.responseText); + return exports; +} + +function require() +{ + return parse; +} diff --git a/Source/WebCore/inspector/front-end/ScriptView.js b/Source/WebCore/inspector/front-end/ScriptView.js index d6c1c59..f631fcc 100644 --- a/Source/WebCore/inspector/front-end/ScriptView.js +++ b/Source/WebCore/inspector/front-end/ScriptView.js @@ -30,7 +30,7 @@ WebInspector.ScriptView = function(script) this.element.addStyleClass("script-view"); var contentProvider = new WebInspector.SourceFrameContentProviderForScript(script); - this.sourceFrame = new WebInspector.SourceFrame(this.element, contentProvider, "", WebInspector.panels.scripts.canEditScripts()); + this.sourceFrame = new WebInspector.SourceFrame(this.element, contentProvider, "", true); } WebInspector.ScriptView.prototype = { diff --git a/Source/WebCore/inspector/front-end/ScriptsPanel.js b/Source/WebCore/inspector/front-end/ScriptsPanel.js index 32212d4..a74f80d 100644 --- a/Source/WebCore/inspector/front-end/ScriptsPanel.js +++ b/Source/WebCore/inspector/front-end/ScriptsPanel.js @@ -63,6 +63,15 @@ WebInspector.ScriptsPanel = function() // FIXME: append the functions select element to the top status bar when it is implemented. // this.topStatusBar.appendChild(this.functionsSelectElement); + this.formatButton = document.createElement("button"); + this.formatButton.className = "status-bar-item"; + this.formatButton.id = "format-script"; + this.formatButton.title = WebInspector.UIString("Format script."); + this.formatButton.appendChild(document.createElement("img")); + this.formatButton.addEventListener("click", this._formatScript.bind(this), false); + if (Preferences.debugMode) + this.topStatusBar.appendChild(this.formatButton); + this.sidebarButtonsElement = document.createElement("div"); this.sidebarButtonsElement.id = "scripts-sidebar-buttons"; this.topStatusBar.appendChild(this.sidebarButtonsElement); @@ -131,7 +140,7 @@ WebInspector.ScriptsPanel = function() this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane(); this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane(); this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane(); - this.sidebarPanes.jsBreakpoints = WebInspector.createJSBreakpointsSidebarPane(); + this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane(); if (Preferences.nativeInstrumentationEnabled) { this.sidebarPanes.domBreakpoints = WebInspector.createDOMBreakpointsSidebarPane(); this.sidebarPanes.xhrBreakpoints = WebInspector.createXHRBreakpointsSidebarPane(); @@ -168,8 +177,6 @@ WebInspector.ScriptsPanel = function() this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3); this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false); - this._pauseOnExceptionButton.state = WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions; - this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions."); this._registerShortcuts(); @@ -179,6 +186,7 @@ WebInspector.ScriptsPanel = function() WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this); WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this); + WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ScriptSourceChanged, this._scriptSourceChanged, this); WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this); WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this); } @@ -244,6 +252,27 @@ WebInspector.ScriptsPanel.prototype = { this._addScript(event.data); }, + _scriptSourceChanged: function(event) + { + var sourceID = event.data.sourceID; + var oldSource = event.data.oldSource; + + var script = WebInspector.debuggerModel.scriptForSourceID(sourceID); + var oldView = script._scriptView; + if (oldView) { + script._scriptView = new WebInspector.ScriptView(script); + this.viewRecreated(oldView, script._scriptView); + } + if (script.resource) { + var revertHandle = WebInspector.debuggerModel.editScriptSource.bind(WebInspector.debuggerModel, sourceID, oldSource); + script.resource.setContent(script.source, revertHandle); + } + + var callFrames = WebInspector.debuggerModel.callFrames; + if (callFrames.length) + this._debuggerPaused({ data: { callFrames: callFrames } }); + }, + _addScript: function(script) { var resource = WebInspector.resourceForURL(script.sourceURL); @@ -279,52 +308,6 @@ WebInspector.ScriptsPanel.prototype = { delete resource._scriptsPendingResourceLoad; }, - canEditScripts: function() - { - return Preferences.canEditScriptSource; - }, - - editScriptSource: function(editData, revertEditingCallback, cancelEditingCallback) - { - if (!this.canEditScripts()) - return; - - // Need to clear breakpoints and re-create them later when editing source. - var breakpoints = WebInspector.debuggerModel.queryBreakpoints(function(b) { return b.sourceID === editData.sourceID }); - for (var i = 0; i < breakpoints.length; ++i) - breakpoints[i].remove(); - - function mycallback(success, newBodyOrErrorMessage, callFrames) - { - if (success) { - var script = WebInspector.debuggerModel.scriptForSourceID(editData.sourceID); - script.source = newBodyOrErrorMessage; - var oldView = script._scriptView - if (oldView) { - script._scriptView = new WebInspector.ScriptView(script); - this.viewRecreated(oldView, script._scriptView); - } - if (script.resource) - script.resource.setContent(newBodyOrErrorMessage, revertEditingCallback); - - if (callFrames && callFrames.length) - this._debuggerPaused({ data: { callFrames: callFrames } }); - } else { - if (cancelEditingCallback) - cancelEditingCallback(); - WebInspector.log(newBodyOrErrorMessage, WebInspector.ConsoleMessage.MessageLevel.Warning); - } - for (var i = 0; i < breakpoints.length; ++i) { - var breakpoint = breakpoints[i]; - var newLine = breakpoint.line; - if (success && breakpoint.line >= editData.line) - newLine += editData.linesCountToShift; - WebInspector.debuggerModel.setBreakpoint(editData.sourceID, newLine, breakpoint.enabled, breakpoint.condition); - } - }; - InspectorBackend.editScriptSource(editData.sourceID, editData.content, mycallback.bind(this)); - }, - selectedCallFrameId: function() { var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame; @@ -359,7 +342,7 @@ WebInspector.ScriptsPanel.prototype = { if (result) callback(WebInspector.RemoteObject.fromPayload(result)); } - InjectedScriptAccess.get(callFrame.worldId).evaluateInCallFrame(callFrame.id, code, objectGroup, evalCallback); + InspectorBackend.evaluateOnCallFrame(callFrame.id, code, objectGroup, evalCallback); }, _debuggerPaused: function(event) @@ -392,9 +375,10 @@ WebInspector.ScriptsPanel.prototype = { debuggerWasEnabled: function() { + this._setPauseOnExceptions(WebInspector.settings.pauseOnExceptionState); + if (this._debuggerEnabled) return; - this._debuggerEnabled = true; this.reset(true); }, @@ -677,7 +661,7 @@ WebInspector.ScriptsPanel.prototype = { _clearCurrentExecutionLine: function() { if (this._executionSourceFrame) - this._executionSourceFrame.executionLine = 0; + this._executionSourceFrame.clearExecutionLine(); delete this._executionSourceFrame; }, @@ -699,7 +683,7 @@ WebInspector.ScriptsPanel.prototype = { this._executionSourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource); if (this._executionSourceFrame) - this._executionSourceFrame.executionLine = currentFrame.line; + this._executionSourceFrame.setExecutionLine(currentFrame.line); }, _changeVisibleFile: function(event) @@ -744,16 +728,21 @@ WebInspector.ScriptsPanel.prototype = { this.resize(); }, - updatePauseOnExceptionsState: function(pauseOnExceptionsState) + _setPauseOnExceptions: function(pauseOnExceptionsState) { - if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions) - this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions."); - else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnAllExceptions) - this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions."); - else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions) - this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions."); - - this._pauseOnExceptionButton.state = pauseOnExceptionsState; + function callback(pauseOnExceptionsState) + { + if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions) + this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions."); + else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnAllExceptions) + this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions."); + else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions) + this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions."); + + this._pauseOnExceptionButton.state = pauseOnExceptionsState; + WebInspector.settings.pauseOnExceptionState = pauseOnExceptionsState; + } + InspectorBackend.setPauseOnExceptionsState(pauseOnExceptionsState, callback.bind(this)); }, _updateDebuggerButtons: function() @@ -833,6 +822,12 @@ WebInspector.ScriptsPanel.prototype = { this._updateBackAndForwardButtons(); }, + _formatScript: function() + { + if (this.visibleView && this.visibleView.sourceFrame) + this.visibleView.sourceFrame.formatSource(); + }, + _enableDebugging: function() { if (this._debuggerEnabled) @@ -854,7 +849,7 @@ WebInspector.ScriptsPanel.prototype = { _togglePauseOnExceptions: function() { - InspectorBackend.setPauseOnExceptionsState((this._pauseOnExceptionButton.state + 1) % this._pauseOnExceptionButton.states, this.updatePauseOnExceptionsState.bind(this)); + this._setPauseOnExceptions((this._pauseOnExceptionButton.state + 1) % this._pauseOnExceptionButton.states); }, _togglePause: function() diff --git a/Source/WebCore/inspector/front-end/Settings.js b/Source/WebCore/inspector/front-end/Settings.js index bc50ce9..e26b1d7 100644 --- a/Source/WebCore/inspector/front-end/Settings.js +++ b/Source/WebCore/inspector/front-end/Settings.js @@ -65,6 +65,7 @@ WebInspector.Settings = function() this.installApplicationSetting("showUserAgentStyles", true); this.installApplicationSetting("watchExpressions", []); this.installApplicationSetting("lastActivePanel", "elements"); + this.installApplicationSetting("pauseOnExceptionState", WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions); this.installProjectSetting("breakpoints", {}); this.installProjectSetting("nativeBreakpoints", []); diff --git a/Source/WebCore/inspector/front-end/SourceFrame.js b/Source/WebCore/inspector/front-end/SourceFrame.js index af10f1e..eb89f24 100644 --- a/Source/WebCore/inspector/front-end/SourceFrame.js +++ b/Source/WebCore/inspector/front-end/SourceFrame.js @@ -28,12 +28,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.SourceFrame = function(parentElement, contentProvider, url, canEditScripts) +WebInspector.SourceFrame = function(parentElement, contentProvider, url, isScript) { this._parentElement = parentElement; this._contentProvider = contentProvider; this._url = url; - this._canEditScripts = canEditScripts; + this._isScript = isScript; + this._textModel = new WebInspector.TextEditorModel(); this._textModel.replaceTabsWithSpaces = true; @@ -71,23 +72,6 @@ WebInspector.SourceFrame.prototype = { } }, - get executionLine() - { - return this._executionLine; - }, - - set executionLine(x) - { - if (this._executionLine === x) - return; - - var previousLine = this._executionLine; - this._executionLine = x; - - if (this._textViewer) - this._updateExecutionLine(previousLine); - }, - markDiff: function(diffData) { if (this._diffLines && this._textViewer) @@ -172,6 +156,7 @@ WebInspector.SourceFrame.prototype = { { this._content = content; this._textModel.setText(null, content); + this._formatter = new WebInspector.ScriptFormatter(content); this._textViewer = new WebInspector.TextViewer(this._textModel, WebInspector.platform, this._url); var element = this._textViewer.element; @@ -179,15 +164,13 @@ WebInspector.SourceFrame.prototype = { element.addEventListener("mousedown", this._mouseDown.bind(this), true); element.addEventListener("mousemove", this._mouseMove.bind(this), true); element.addEventListener("scroll", this._scroll.bind(this), true); + element.addEventListener("dblclick", this._doubleClick.bind(this), true); this._parentElement.appendChild(element); this._textViewer.beginUpdates(); this._textViewer.mimeType = mimeType; - this._addExistingMessagesToSource(); - this._updateExecutionLine(); - this._updateDiffDecorations(); - this._textViewer.resize(); + this._setTextViewerDecorations(); if (this._lineNumberToReveal) { this.revealLine(this._lineNumberToReveal); @@ -210,15 +193,34 @@ WebInspector.SourceFrame.prototype = { delete this._delayedFindSearchMatches; } + this._textViewer.endUpdates(); + + WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointAdded, this._breakpointAdded, this); + + if (this._canEditScripts) + this._textViewer.editCallback = this._editLine.bind(this); + }, + + _setTextViewerDecorations: function() + { + this._rowMessages = {}; + this._messageBubbles = {}; + + this._textViewer.beginUpdates(); + + this._addExistingMessagesToSource(); + this._updateDiffDecorations(); + + if (this._executionLine) + this.setExecutionLine(this._executionLine); + var breakpoints = this._breakpoints(); for (var i = 0; i < breakpoints.length; ++i) this._addBreakpoint(breakpoints[i]); - WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointAdded, this._breakpointAdded, this); - this._textViewer.endUpdates(); + this._textViewer.resize(); - if (this._canEditScripts) - this._textViewer.editCallback = this._editLine.bind(this); + this._textViewer.endUpdates(); }, findSearchMatches: function(query, finishedCallback) @@ -300,18 +302,22 @@ WebInspector.SourceFrame.prototype = { msg._resourceMessageRepeatCountElement.textContent = WebInspector.UIString(" (repeated %d times)", msg.repeatCount); }, - _updateExecutionLine: function(previousLine) + setExecutionLine: function(lineNumber) { - if (previousLine) { - if (previousLine - 1 < this._textModel.linesCount) - this._textViewer.removeDecoration(previousLine - 1, "webkit-execution-line"); - } - - if (!this._executionLine) + this._executionLine = lineNumber; + if (!this._textViewer) return; + var textViewerLineNumber = this._formatter.originalLineNumberToFormattedLineNumber(this._executionLine - 1); + this._textViewer.addDecoration(textViewerLineNumber, "webkit-execution-line"); + }, - if (this._executionLine < this._textModel.linesCount) - this._textViewer.addDecoration(this._executionLine - 1, "webkit-execution-line"); + clearExecutionLine: function() + { + if (!this._textViewer) + return; + var textViewerLineNumber = this._formatter.originalLineNumberToFormattedLineNumber(this._executionLine - 1); + this._textViewer.removeDecoration(textViewerLineNumber, "webkit-execution-line"); + delete this._executionLine; }, _updateDiffDecorations: function() @@ -412,14 +418,15 @@ WebInspector.SourceFrame.prototype = { _addBreakpoint: function(breakpoint) { - if (breakpoint.line > this._textModel.linesCount) + var textViewerLineNumber = this._formatter.originalLineNumberToFormattedLineNumber(breakpoint.line - 1); + if (textViewerLineNumber >= this._textModel.linesCount) return; breakpoint.addEventListener("enable-changed", this._breakpointChanged, this); breakpoint.addEventListener("condition-changed", this._breakpointChanged, this); breakpoint.addEventListener("removed", this._breakpointRemoved, this); - this._setBreakpointDecoration(breakpoint.line, breakpoint.enabled, !!breakpoint.condition); + this._setBreakpointDecoration(textViewerLineNumber, breakpoint.enabled, !!breakpoint.condition); }, _breakpointRemoved: function(event) @@ -430,18 +437,19 @@ WebInspector.SourceFrame.prototype = { breakpoint.removeEventListener("condition-changed", null, this); breakpoint.removeEventListener("removed", null, this); - this._removeBreakpointDecoration(breakpoint.line); + var textViewerLineNumber = this._formatter.originalLineNumberToFormattedLineNumber(breakpoint.line - 1); + this._removeBreakpointDecoration(textViewerLineNumber); }, _breakpointChanged: function(event) { var breakpoint = event.target; - this._setBreakpointDecoration(breakpoint.line, breakpoint.enabled, !!breakpoint.condition); + var textViewerLineNumber = this._formatter.originalLineNumberToFormattedLineNumber(breakpoint.line - 1); + this._setBreakpointDecoration(textViewerLineNumber, breakpoint.enabled, !!breakpoint.condition); }, _setBreakpointDecoration: function(lineNumber, enabled, hasCondition) { - lineNumber -= 1; this._textViewer.beginUpdates(); this._textViewer.addDecoration(lineNumber, "webkit-breakpoint"); if (enabled) @@ -457,7 +465,6 @@ WebInspector.SourceFrame.prototype = { _removeBreakpointDecoration: function(lineNumber) { - lineNumber -= 1; this._textViewer.beginUpdates(); this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint"); this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint-disabled"); @@ -473,27 +480,28 @@ WebInspector.SourceFrame.prototype = { var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number"); if (!target) return; - var lineNumber = target.parentElement.lineNumber + 1; + var textViewerLineNumber = target.parentElement.lineNumber; + var originalLineNumber = this._formatter.formattedLineNumberToOriginalLineNumber(textViewerLineNumber); var contextMenu = new WebInspector.ContextMenu(); - contextMenu.appendItem(WebInspector.UIString("Continue to Here"), this._continueToLine.bind(this, lineNumber)); + contextMenu.appendItem(WebInspector.UIString("Continue to Here"), this._continueToLine.bind(this, originalLineNumber)); - var breakpoint = this._findBreakpoint(lineNumber); + var breakpoint = this._findBreakpoint(originalLineNumber); if (!breakpoint) { // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint. - contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), this._setBreakpoint.bind(this, lineNumber, "", true)); + contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), this._setBreakpoint.bind(this, originalLineNumber, "", true)); function addConditionalBreakpoint() { - this._setBreakpointDecoration(lineNumber, true, true); + this._setBreakpointDecoration(textViewerLineNumber, true, true); function didEditBreakpointCondition(committed, condition) { - this._removeBreakpointDecoration(lineNumber); + this._removeBreakpointDecoration(textViewerLineNumber); if (committed) - this._setBreakpoint(lineNumber, true, condition); + this._setBreakpoint(originalLineNumber, true, condition); } - this._editBreakpointCondition(lineNumber, "", didEditBreakpointCondition.bind(this)); + this._editBreakpointCondition(textViewerLineNumber, "", didEditBreakpointCondition.bind(this)); } contextMenu.appendItem(WebInspector.UIString("Add Conditional Breakpoint…"), addConditionalBreakpoint.bind(this)); } else { @@ -505,16 +513,16 @@ WebInspector.SourceFrame.prototype = { { if (committed) { breakpoint.remove(); - this._setBreakpoint(breakpoint.line, breakpoint.enabled, condition); + this._setBreakpoint(originalLineNumber, breakpoint.enabled, condition); } } - this._editBreakpointCondition(lineNumber, breakpoint.condition, didEditBreakpointCondition.bind(this)); + this._editBreakpointCondition(textViewerLineNumber, breakpoint.condition, didEditBreakpointCondition.bind(this)); } contextMenu.appendItem(WebInspector.UIString("Edit Breakpoint…"), editBreakpointCondition.bind(this)); function setBreakpointEnabled(enabled) { breakpoint.remove(); - this._setBreakpoint(breakpoint.line, enabled, breakpoint.condition); + this._setBreakpoint(originalLineNumber, enabled, breakpoint.condition); } if (breakpoint.enabled) contextMenu.appendItem(WebInspector.UIString("Disable Breakpoint"), setBreakpointEnabled.bind(this, false)); @@ -538,15 +546,15 @@ WebInspector.SourceFrame.prototype = { var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number"); if (!target) return; - var lineNumber = target.parentElement.lineNumber + 1; + var originalLineNumber = this._formatter.formattedLineNumberToOriginalLineNumber(target.parentElement.lineNumber); - var breakpoint = this._findBreakpoint(lineNumber); + var breakpoint = this._findBreakpoint(originalLineNumber); if (breakpoint) { breakpoint.remove(); if (event.shiftKey) - this._setBreakpoint(breakpoint.line, !breakpoint.enabled, breakpoint.condition); + this._setBreakpoint(originalLineNumber, !breakpoint.enabled, breakpoint.condition); } else - this._setBreakpoint(lineNumber, true, ""); + this._setBreakpoint(originalLineNumber, true, ""); event.preventDefault(); }, @@ -710,7 +718,6 @@ WebInspector.SourceFrame.prototype = { _editBreakpointCondition: function(lineNumber, condition, callback) { - lineNumber -= 1; this._conditionElement = this._createConditionElement(lineNumber); this._textViewer.addDecoration(lineNumber, this._conditionElement); @@ -776,49 +783,55 @@ WebInspector.SourceFrame.prototype = { this._textViewer.resize(); }, + formatSource: function() + { + if (!this._formatter) + return; + + function didFormat(source) + { + this._textModel.setText(null, source); + this._setTextViewerDecorations(); + } + this._formatter.format(didFormat.bind(this)); + }, + _continueToLine: function(lineNumber) { var sourceID = this._sourceIDForLine(lineNumber); if (!sourceID) return; - WebInspector.debuggerModel.continueToLine(sourceID, lineNumber); + WebInspector.debuggerModel.continueToLine(sourceID, lineNumber + 1); }, - _editLine: function(lineNumber, newContent, cancelEditingCallback) + _doubleClick: function(event) { - lineNumber += 1; - - var lines = []; - var oldLines = this._content.split('\n'); - for (var i = 0; i < oldLines.length; ++i) { - if (i === lineNumber - 1) - lines.push(newContent); - else - lines.push(oldLines[i]); - } + if (!Preferences.canEditScriptSource || !this._isScript) + return; - var editData = {}; - editData.sourceID = this._sourceIDForLine(lineNumber); - editData.content = lines.join("\n"); - editData.line = lineNumber; - editData.linesCountToShift = newContent.split("\n").length - 1; - this._doEditLine(editData, cancelEditingCallback); - }, + var target = event.target.enclosingNodeOrSelfWithNodeName("TD"); + if (!target || target.parentElement.firstChild === target) + return; // Do not trigger editing from line numbers. - _revertEditLine: function(editData, contentToRevertTo) - { - var newEditData = {}; - newEditData.sourceID = editData.sourceID; - newEditData.content = contentToRevertTo; - newEditData.line = editData.line; - newEditData.linesCountToShift = -editData.linesCountToShift; - this._doEditLine(newEditData); - }, + var lineRow = target.parentElement; + var lineNumber = lineRow.lineNumber; + var sourceID = this._sourceIDForLine(lineNumber); + if (!sourceID) + return; - _doEditLine: function(editData, cancelEditingCallback) - { - var revertEditingCallback = this._revertEditLine.bind(this, editData); - WebInspector.panels.scripts.editScriptSource(editData, revertEditingCallback, cancelEditingCallback); + function didEditLine(newContent) + { + var lines = []; + var oldLines = this._content.split('\n'); + for (var i = 0; i < oldLines.length; ++i) { + if (i === lineNumber) + lines.push(newContent); + else + lines.push(oldLines[i]); + } + WebInspector.debuggerModel.editScriptSource(sourceID, lines.join("\n")); + } + this._textViewer.editLine(lineRow, didEditLine.bind(this)); }, _setBreakpoint: function(lineNumber, enabled, condition) @@ -826,7 +839,7 @@ WebInspector.SourceFrame.prototype = { var sourceID = this._sourceIDForLine(lineNumber); if (!sourceID) return; - WebInspector.debuggerModel.setBreakpoint(sourceID, lineNumber, enabled, condition); + WebInspector.debuggerModel.setBreakpoint(sourceID, lineNumber + 1, enabled, condition); if (!WebInspector.panels.scripts.breakpointsActivated) WebInspector.panels.scripts.toggleBreakpointsClicked(); }, @@ -840,7 +853,7 @@ WebInspector.SourceFrame.prototype = { _findBreakpoint: function(lineNumber) { var sourceID = this._sourceIDForLine(lineNumber); - return WebInspector.debuggerModel.findBreakpoint(sourceID, lineNumber); + return WebInspector.debuggerModel.findBreakpoint(sourceID, lineNumber + 1); }, _sourceIDForLine: function(lineNumber) @@ -849,9 +862,9 @@ WebInspector.SourceFrame.prototype = { var closestStartingLine = 0; var scripts = this._contentProvider.scripts(); for (var i = 0; i < scripts.length; ++i) { - var startingLine = scripts[i].startingLine; - if (startingLine <= lineNumber && startingLine >= closestStartingLine) { - closestStartingLine = startingLine; + var lineOffset = scripts[i].lineOffset; + if (lineOffset <= lineNumber && lineOffset >= closestStartingLine) { + closestStartingLine = lineOffset; sourceIDForLine = scripts[i].sourceID; } } diff --git a/Source/WebCore/inspector/front-end/SourceView.js b/Source/WebCore/inspector/front-end/SourceView.js index e78ff94..37caabb 100644 --- a/Source/WebCore/inspector/front-end/SourceView.js +++ b/Source/WebCore/inspector/front-end/SourceView.js @@ -33,8 +33,8 @@ WebInspector.SourceView = function(resource) this.element.addStyleClass("source"); var contentProvider = new WebInspector.SourceFrameContentProviderForResource(resource); - var canEditScripts = WebInspector.panels.scripts.canEditScripts() && resource.type === WebInspector.Resource.Type.Script; - this.sourceFrame = new WebInspector.SourceFrame(this.element, contentProvider, resource.url, canEditScripts); + var isScript = resource.type === WebInspector.Resource.Type.Script; + this.sourceFrame = new WebInspector.SourceFrame(this.element, contentProvider, resource.url, isScript); } WebInspector.SourceView.prototype = { diff --git a/Source/WebCore/inspector/front-end/StylesSidebarPane.js b/Source/WebCore/inspector/front-end/StylesSidebarPane.js index d646829..9880adc 100644 --- a/Source/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/Source/WebCore/inspector/front-end/StylesSidebarPane.js @@ -81,6 +81,8 @@ WebInspector.StylesSidebarPane = function(computedStylePane) this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true); } +WebInspector.StylesSidebarPane.StyleValueDelimiters = " \t\n\"':;,/()"; + // Taken from http://www.w3.org/TR/CSS21/propidx.html. WebInspector.StylesSidebarPane.InheritedProperties = [ "azimuth", "border-collapse", "border-spacing", "caption-side", "color", "cursor", "direction", "elevation", @@ -1379,6 +1381,8 @@ WebInspector.StylePropertyTreeElement.prototype = { } this.listItemElement.removeChildren(); + nameElement.normalize(); + valueElement.normalize(); if (!this.treeOutline) return; @@ -1529,16 +1533,15 @@ WebInspector.StylePropertyTreeElement.prototype = { var context = { expanded: this.expanded, hasChildren: this.hasChildren, - keyDownListener: isEditingName ? this.editingNameKeyDown.bind(this) : this.editingValueKeyDown.bind(this), - keyPressListener: isEditingName ? this.editingNameKeyPress.bind(this) : this.editingValueKeyPress.bind(this), + keyDownListener: isEditingName ? null : this.editingValueKeyDown.bind(this), isEditingName: isEditingName, }; // Lie about our children to prevent expanding on double click and to collapse shorthands. this.hasChildren = false; - selectElement.addEventListener("keydown", context.keyDownListener, false); - selectElement.addEventListener("keypress", context.keyPressListener, false); + if (!isEditingName) + selectElement.addEventListener("keydown", context.keyDownListener, false); if (selectElement.parentElement) selectElement.parentElement.addStyleClass("child-editing"); selectElement.textContent = selectElement.textContent; // remove color swatch and the like @@ -1613,76 +1616,15 @@ WebInspector.StylePropertyTreeElement.prototype = { customFinishHandler: nameValueFinishHandler.bind(this, context, isEditingName), pasteHandler: isEditingName ? pasteHandler.bind(this, context) : null }); - window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1); - }, - - editingNameKeyPress: function(event) - { - // Complete property names. - var character = event.data.toLowerCase(); - if (character && /[a-z-]/.test(character)) { - var selection = window.getSelection(); - var prefix = selection.anchorNode.textContent.substring(0, selection.anchorOffset); - var property = WebInspector.cssNameCompletions.firstStartsWith(prefix + character); - if (!selection.isCollapsed) - selection.deleteFromDocument(); - - this.restoreNameElement(); - - if (property) { - if (property !== this.nameElement.textContent) - this.nameElement.textContent = property; - this.nameElement.firstChild.select(prefix.length + 1); - event.preventDefault(); - } - } - }, - - editingValueKeyPress: function(event) - { - // FIXME: This should complete property values. - }, - - editingNameKeyDown: function(event) - { - var showNext; - if (event.keyIdentifier === "Up") - showNext = false; - else if (event.keyIdentifier === "Down") - showNext = true; - else - return; - - var selection = window.getSelection(); - if (!selection.rangeCount) - return; - - var selectionRange = selection.getRangeAt(0); - if (selectionRange.commonAncestorContainer !== this.nameElement && !selectionRange.commonAncestorContainer.isDescendant(this.nameElement)) - return; - - const styleValueDelimeters = " \t\n\"':;,/()"; - var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, styleValueDelimeters, this.nameElement); - var wordString = wordRange.toString(); - var cursorPosition = selectionRange.startOffset != selectionRange.endOffset ? selectionRange.startOffset : 0; - var prefix = selectionRange.startContainer.textContent.substring(0, cursorPosition); - var property; - - if (showNext) - property = WebInspector.cssNameCompletions.next(wordString, prefix); - else - property = WebInspector.cssNameCompletions.previous(wordString, prefix); - - if (property) { - this.nameElement.textContent = property; - this.nameElement.firstChild.select(cursorPosition); - } - event.preventDefault(); + this._prompt = new WebInspector.StylesSidebarPane.CSSPropertyPrompt(selectElement, isEditingName ? WebInspector.cssNameCompletions : WebInspector.CSSKeywordCompletions.forProperty(this.nameElement.textContent)); + window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1); }, editingValueKeyDown: function(event) { + if (event.handled) + return; var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down"); var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdentifier === "PageDown"); if (!arrowKeyPressed && !pageKeyPressed) @@ -1696,8 +1638,7 @@ WebInspector.StylePropertyTreeElement.prototype = { if (selectionRange.commonAncestorContainer !== this.valueElement && !selectionRange.commonAncestorContainer.isDescendant(this.valueElement)) return; - const styleValueDelimeters = " \t\n\"':;,/()"; - var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, styleValueDelimeters, this.valueElement); + var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, WebInspector.StylesSidebarPane.StyleValueDelimiters, this.valueElement); var wordString = wordRange.toString(); var replacementString = wordString; @@ -1739,42 +1680,45 @@ WebInspector.StylePropertyTreeElement.prototype = { } replacementString = prefix + number + suffix; - } else { - // FIXME: this should cycle through known keywords for the current property value. - } - var replacementTextNode = document.createTextNode(replacementString); + var replacementTextNode = document.createTextNode(replacementString); - wordRange.deleteContents(); - wordRange.insertNode(replacementTextNode); + wordRange.deleteContents(); + wordRange.insertNode(replacementTextNode); - var finalSelectionRange = document.createRange(); - finalSelectionRange.setStart(replacementTextNode, 0); - finalSelectionRange.setEnd(replacementTextNode, replacementString.length); + var finalSelectionRange = document.createRange(); + finalSelectionRange.setStart(replacementTextNode, 0); + finalSelectionRange.setEnd(replacementTextNode, replacementString.length); - selection.removeAllRanges(); - selection.addRange(finalSelectionRange); + selection.removeAllRanges(); + selection.addRange(finalSelectionRange); - event.preventDefault(); + event.handled = true; + event.preventDefault(); - if (!("originalPropertyText" in this)) { - // Remember the rule's original CSS text on [Page](Up|Down), so it can be restored - // if the editing is canceled. - this.originalPropertyText = this.property.propertyText; - } + if (!("originalPropertyText" in this)) { + // Remember the rule's original CSS text on [Page](Up|Down), so it can be restored + // if the editing is canceled. + this.originalPropertyText = this.property.propertyText; + } - // Synthesize property text disregarding any comments, custom whitespace etc. - this.applyStyleText(this.nameElement.textContent + ": " + this.valueElement.textContent); + // Synthesize property text disregarding any comments, custom whitespace etc. + this.applyStyleText(this.nameElement.textContent + ": " + this.valueElement.textContent); + } }, editingEnded: function(context) { + if (this._prompt) { + this._prompt.removeFromElement(); + delete this._prompt; + } this.hasChildren = context.hasChildren; if (context.expanded) this.expand(); var editedElement = context.isEditingName ? this.nameElement : this.valueElement; - editedElement.removeEventListener("keydown", context.keyDownListener, false); - editedElement.removeEventListener("keypress", context.keyPressListener, false); + if (!context.isEditingName) + editedElement.removeEventListener("keydown", context.keyDownListener, false); if (editedElement.parentElement) editedElement.parentElement.removeStyleClass("child-editing"); @@ -1791,6 +1735,8 @@ WebInspector.StylePropertyTreeElement.prototype = { else this.updateTitle(); } + + // This should happen last, as it clears the info necessary to restore the property value after [Page]Up/Down changes. this.editingEnded(context); }, @@ -1948,3 +1894,57 @@ WebInspector.StylePropertyTreeElement.prototype = { } WebInspector.StylePropertyTreeElement.prototype.__proto__ = TreeElement.prototype; + +WebInspector.StylesSidebarPane.CSSPropertyPrompt = function(element, cssCompletions) +{ + WebInspector.TextPrompt.call(this, element, this._buildPropertyCompletions.bind(this), WebInspector.StylesSidebarPane.StyleValueDelimiters, true); + this._cssCompletions = cssCompletions; +} + +WebInspector.StylesSidebarPane.CSSPropertyPrompt.prototype = { + upKeyPressed: function(event) + { + this._handleNameOrValueUpDown(event); + }, + + downKeyPressed: function(event) + { + this._handleNameOrValueUpDown(event); + }, + + tabKeyPressed: function(event) + { + this.acceptAutoComplete(); + }, + + _handleNameOrValueUpDown: function(event) + { + var reverse = event.keyIdentifier === "Up"; + if (this.autoCompleteElement) + this.complete(false, reverse); // Accept the current suggestion, if any. + this.complete(false, reverse); // Actually increment/decrement the suggestion. + event.handled = true; + }, + + _buildPropertyCompletions: function(wordRange, bestMatchOnly, completionsReadyCallback) + { + var prefix = wordRange.toString().toLowerCase(); + if (!prefix.length) + return; + + var results; + if (bestMatchOnly) { + results = []; + var firstMatch = this._cssCompletions.firstStartsWith(prefix); + if (firstMatch) + results.push(firstMatch); + return completionsReadyCallback(results); + } + + results = this._cssCompletions.startsWith(prefix); + if (results) + completionsReadyCallback(results); + } +} + +WebInspector.StylesSidebarPane.CSSPropertyPrompt.prototype.__proto__ = WebInspector.TextPrompt.prototype; diff --git a/Source/WebCore/inspector/front-end/TextPrompt.js b/Source/WebCore/inspector/front-end/TextPrompt.js index 21a5bde..ac54d8c 100644 --- a/Source/WebCore/inspector/front-end/TextPrompt.js +++ b/Source/WebCore/inspector/front-end/TextPrompt.js @@ -26,15 +26,18 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.TextPrompt = function(element, completions, stopCharacters) +WebInspector.TextPrompt = function(element, completions, stopCharacters, omitHistory) { this.element = element; this.element.addStyleClass("text-prompt"); this.completions = completions; this.completionStopCharacters = stopCharacters; - this.history = []; - this.historyOffset = 0; - this.element.addEventListener("keydown", this._onKeyDown.bind(this), true); + if (!omitHistory) { + this.history = []; + this.historyOffset = 0; + } + this._boundOnKeyDown = this._onKeyDown.bind(this); + this.element.addEventListener("keydown", this._boundOnKeyDown, true); } WebInspector.TextPrompt.prototype = { @@ -55,6 +58,12 @@ WebInspector.TextPrompt.prototype = { this.moveCaretToEndOfPrompt(); }, + removeFromElement: function() + { + this.clearAutoComplete(true); + this.element.removeEventListener("keydown", this._boundOnKeyDown, true); + }, + _onKeyDown: function(event) { function defaultAction() @@ -63,16 +72,20 @@ WebInspector.TextPrompt.prototype = { this.autoCompleteSoon(); } + if (event.handled) + return; + var handled = false; + switch (event.keyIdentifier) { case "Up": - this._upKeyPressed(event); + this.upKeyPressed(event); break; case "Down": - this._downKeyPressed(event); + this.downKeyPressed(event); break; case "U+0009": // Tab - this._tabKeyPressed(event); + this.tabKeyPressed(event); break; case "Right": case "End": @@ -85,7 +98,7 @@ WebInspector.TextPrompt.prototype = { case "Control": break; case "U+0050": // Ctrl+P = Previous - if (WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) { + if (this.history && WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) { handled = true; this._moveBackInHistory(); break; @@ -93,7 +106,7 @@ WebInspector.TextPrompt.prototype = { defaultAction.call(this); break; case "U+004E": // Ctrl+N = Next - if (WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) { + if (this.history && WebInspector.isMac() && event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) { handled = true; this._moveForwardInHistory(); break; @@ -105,7 +118,9 @@ WebInspector.TextPrompt.prototype = { break; } + handled |= event.handled; if (handled) { + event.handled = true; event.preventDefault(); event.stopPropagation(); } @@ -376,39 +391,33 @@ WebInspector.TextPrompt.prototype = { selection.addRange(selectionRange); }, - _tabKeyPressed: function(event) + tabKeyPressed: function(event) { - event.preventDefault(); - event.stopPropagation(); - + event.handled = true; this.complete(false, event.shiftKey); }, - _upKeyPressed: function(event) + upKeyPressed: function(event) { if (!this.isCaretOnFirstLine()) return; - event.preventDefault(); - event.stopPropagation(); - + event.handled = true; this._moveBackInHistory(); }, - _downKeyPressed: function(event) + downKeyPressed: function(event) { if (!this.isCaretOnLastLine()) return; - event.preventDefault(); - event.stopPropagation(); - + event.handled = true; this._moveForwardInHistory(); }, _moveBackInHistory: function() { - if (this.historyOffset == this.history.length) + if (!this.history || this.historyOffset == this.history.length) return; this.clearAutoComplete(true); @@ -437,7 +446,7 @@ WebInspector.TextPrompt.prototype = { _moveForwardInHistory: function() { - if (this.historyOffset === 0) + if (!this.history || this.historyOffset === 0) return; this.clearAutoComplete(true); diff --git a/Source/WebCore/inspector/front-end/TextViewer.js b/Source/WebCore/inspector/front-end/TextViewer.js index f116dea..ea36513 100644 --- a/Source/WebCore/inspector/front-end/TextViewer.js +++ b/Source/WebCore/inspector/front-end/TextViewer.js @@ -43,7 +43,6 @@ WebInspector.TextViewer = function(textModel, platform, url) this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false); this.element.addEventListener("beforecopy", this._beforeCopy.bind(this), false); this.element.addEventListener("copy", this._copy.bind(this), false); - this.element.addEventListener("dblclick", this._handleDoubleClick.bind(this), false); this._url = url; @@ -80,11 +79,6 @@ WebInspector.TextViewer.prototype = { chunk.element.scrollIntoViewIfNeeded(); }, - set editCallback(editCallback) - { - this._editCallback = editCallback; - }, - addDecoration: function(lineNumber, decoration) { var chunk = this._makeLineAChunk(lineNumber); @@ -231,20 +225,20 @@ WebInspector.TextViewer.prototype = { scrollValue = -1; else if (event.keyCode == WebInspector.KeyboardShortcut.Keys.Down.code) scrollValue = 1; - + if (scrollValue) { event.preventDefault(); event.stopPropagation(); this.element.scrollByLines(scrollValue); return; } - + scrollValue = 0; if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Left.code) scrollValue = -40; else if (event.keyCode == WebInspector.KeyboardShortcut.Keys.Right.code) scrollValue = 40; - + if (scrollValue) { event.preventDefault(); event.stopPropagation(); @@ -252,42 +246,25 @@ WebInspector.TextViewer.prototype = { } }, - _handleDoubleClick: function(e) + editLine: function(lineRow, callback) { - if (!this._editCallback) - return; - - var cell = e.target.enclosingNodeOrSelfWithNodeName("TD"); - if (!cell) - return; - - var lineRow = cell.parentElement; - if (lineRow.firstChild === cell) - return; // Do not trigger editing from line numbers. - - var oldContent = lineRow.lastChild.innerHTML; - var cancelEditingCallback = this._cancelEditingLine.bind(this, lineRow.lastChild, oldContent); - var commitEditingCallback = this._commitEditingLine.bind(this, lineRow.lineNumber, lineRow.lastChild, cancelEditingCallback); - this._editingLine = WebInspector.startEditing(lineRow.lastChild, { + var element = lineRow.lastChild; + var oldContent = element.innerHTML; + function finishEditing(committed, e, newContent) + { + if (committed) + callback(newContent); + element.innerHTML = oldContent; + delete this._editingLine; + } + this._editingLine = WebInspector.startEditing(element, { context: null, - commitHandler: commitEditingCallback, - cancelHandler: cancelEditingCallback, + commitHandler: finishEditing.bind(this, true), + cancelHandler: finishEditing.bind(this, false), multiline: true }); }, - _commitEditingLine: function(lineNumber, element, cancelEditingCallback) - { - this._editCallback(lineNumber, element.textContent, cancelEditingCallback); - delete this._editingLine; - }, - - _cancelEditingLine: function(element, oldContent, e) - { - element.innerHTML = oldContent; - delete this._editingLine; - }, - _beforeCopy: function(e) { e.preventDefault(); @@ -786,7 +763,7 @@ WebInspector.TextChunk.prototype = { var lineContentElement = document.createElement("td"); lineContentElement.className = "webkit-line-content"; - lineRow.appendChild(lineContentElement); + lineRow.appendChild(lineContentElement); } lineRow.lineNumber = lineNumber; lineNumberElement.textContent = lineNumber + 1; diff --git a/Source/WebCore/inspector/front-end/UglifyJS/parse-js.js b/Source/WebCore/inspector/front-end/UglifyJS/parse-js.js new file mode 100644 index 0000000..a218c01 --- /dev/null +++ b/Source/WebCore/inspector/front-end/UglifyJS/parse-js.js @@ -0,0 +1,1233 @@ +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + + This version is suitable for Node.js. With minimal changes (the + exports stuff) it should work on any JS platform. + + This file contains the tokenizer/parser. It is a port to JavaScript + of parse-js [1], a JavaScript parser library written in Common Lisp + by Marijn Haverbeke. Thank you Marijn! + + [1] http://marijn.haverbeke.nl/parse-js/ + + Exported functions: + + - tokenizer(code) -- returns a function. Call the returned + function to fetch the next token. + + - parse(code) -- returns an AST of the given JavaScript code. + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + <mihai.bazon@gmail.com> + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com> + Based on parse-js (http://marijn.haverbeke.nl/parse-js/). + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * 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 THE COPYRIGHT HOLDER “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 THE COPYRIGHT HOLDER 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. + + ***********************************************************************/ + +/* -----[ Tokenizer (constants) ]----- */ + +var KEYWORDS = array_to_hash([ + "break", + "case", + "catch", + "const", + "continue", + "default", + "delete", + "do", + "else", + "finally", + "for", + "function", + "if", + "in", + "instanceof", + "new", + "return", + "switch", + "throw", + "try", + "typeof", + "var", + "void", + "while", + "with" +]); + +var RESERVED_WORDS = array_to_hash([ + "abstract", + "boolean", + "byte", + "char", + "class", + "debugger", + "double", + "enum", + "export", + "extends", + "final", + "float", + "goto", + "implements", + "import", + "int", + "interface", + "long", + "native", + "package", + "private", + "protected", + "public", + "short", + "static", + "super", + "synchronized", + "throws", + "transient", + "volatile" +]); + +var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([ + "return", + "new", + "delete", + "throw", + "else", + "case" +]); + +var KEYWORDS_ATOM = array_to_hash([ + "false", + "null", + "true", + "undefined" +]); + +var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^")); + +var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; +var RE_OCT_NUMBER = /^0[0-7]+$/; +var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i; + +var OPERATORS = array_to_hash([ + "in", + "instanceof", + "typeof", + "new", + "void", + "delete", + "++", + "--", + "+", + "-", + "!", + "~", + "&", + "|", + "^", + "*", + "/", + "%", + ">>", + "<<", + ">>>", + "<", + ">", + "<=", + ">=", + "==", + "===", + "!=", + "!==", + "?", + "=", + "+=", + "-=", + "/=", + "*=", + "%=", + ">>=", + "<<=", + ">>>=", + "~=", + "%=", + "|=", + "^=", + "&=", + "&&", + "||" +]); + +var WHITESPACE_CHARS = array_to_hash(characters(" \n\r\t")); + +var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:")); + +var PUNC_CHARS = array_to_hash(characters("[]{}(),;:")); + +var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy")); + +/* -----[ Tokenizer ]----- */ + +function is_alphanumeric_char(ch) { + ch = ch.charCodeAt(0); + return (ch >= 48 && ch <= 57) || + (ch >= 65 && ch <= 90) || + (ch >= 97 && ch <= 122); +}; + +function is_identifier_char(ch) { + return is_alphanumeric_char(ch) || ch == "$" || ch == "_"; +}; + +function is_digit(ch) { + ch = ch.charCodeAt(0); + return ch >= 48 && ch <= 57; +}; + +function parse_js_number(num) { + if (RE_HEX_NUMBER.test(num)) { + return parseInt(num.substr(2), 16); + } else if (RE_OCT_NUMBER.test(num)) { + return parseInt(num.substr(1), 8); + } else if (RE_DEC_NUMBER.test(num)) { + return parseFloat(num); + } +}; + +function JS_Parse_Error(message, line, col, pos) { + this.message = message; + this.line = line; + this.col = col; + this.pos = pos; + try { + ({})(); + } catch(ex) { + this.stack = ex.stack; + }; +}; + +JS_Parse_Error.prototype.toString = function() { + return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack; +}; + +function js_error(message, line, col, pos) { + throw new JS_Parse_Error(message, line, col, pos); +}; + +function is_token(token, type, val) { + return token.type == type && (val == null || token.value == val); +}; + +var EX_EOF = {}; + +function tokenizer($TEXT, skip_comments) { + + var S = { + text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''), + pos : 0, + tokpos : 0, + line : 0, + tokline : 0, + col : 0, + tokcol : 0, + newline_before : false, + regex_allowed : false + }; + + function peek() { return S.text.charAt(S.pos); }; + + function next(signal_eof) { + var ch = S.text.charAt(S.pos++); + if (signal_eof && !ch) + throw EX_EOF; + if (ch == "\n") { + S.newline_before = true; + ++S.line; + S.col = 0; + } else { + ++S.col; + } + return ch; + }; + + function eof() { + return !S.peek(); + }; + + function find(what, signal_eof) { + var pos = S.text.indexOf(what, S.pos); + if (signal_eof && pos == -1) throw EX_EOF; + return pos; + }; + + function start_token() { + S.tokline = S.line; + S.tokcol = S.col; + S.tokpos = S.pos; + }; + + function token(type, value) { + S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) || + (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) || + (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value))); + var ret = { + type : type, + value : value, + line : S.tokline, + col : S.tokcol, + pos : S.tokpos, + nlb : S.newline_before + }; + S.newline_before = false; + return ret; + }; + + function skip_whitespace() { + while (HOP(WHITESPACE_CHARS, peek())) + next(); + }; + + function read_while(pred) { + var ret = "", ch = peek(), i = 0; + while (ch && pred(ch, i++)) { + ret += next(); + ch = peek(); + } + return ret; + }; + + function parse_error(err) { + js_error(err, S.tokline, S.tokcol, S.tokpos); + }; + + function read_num(prefix) { + var has_e = false, after_e = false, has_x = false; + var num = read_while(function(ch, i){ + if (ch == "x" || ch == "X") { + if (has_x) return false; + return has_x = true; + } + if (!has_x && (ch == "E" || ch == "e")) { + if (has_e) return false; + return has_e = after_e = true; + } + if (ch == "-") { + if (after_e || (i == 0 && !prefix)) return true; + return false; + } + if (ch == "+") return after_e; + after_e = false; + return is_alphanumeric_char(ch) || ch == "."; + }); + if (prefix) + num = prefix + num; + var valid = parse_js_number(num); + if (!isNaN(valid)) { + return token("num", valid); + } else { + parse_error("Invalid syntax: " + num); + } + }; + + function read_escaped_char() { + var ch = next(true); + switch (ch) { + case "n" : return "\n"; + case "r" : return "\r"; + case "t" : return "\t"; + case "b" : return "\b"; + case "v" : return "\v"; + case "f" : return "\f"; + case "0" : return "\0"; + case "x" : return String.fromCharCode(hex_bytes(2)); + case "u" : return String.fromCharCode(hex_bytes(4)); + default : return ch; + } + }; + + function hex_bytes(n) { + var num = 0; + for (; n > 0; --n) { + var digit = parseInt(next(true), 16); + if (isNaN(digit)) + parse_error("Invalid hex-character pattern in string"); + num = (num << 4) | digit; + } + return num; + }; + + function read_string() { + return with_eof_error("Unterminated string constant", function(){ + var quote = next(), ret = ""; + for (;;) { + var ch = next(true); + if (ch == "\\") ch = read_escaped_char(); + else if (ch == quote) break; + ret += ch; + } + return token("string", ret); + }); + }; + + function read_line_comment() { + next(); + var i = find("\n"), ret; + if (i == -1) { + ret = S.text.substr(S.pos); + S.pos = S.text.length; + } else { + ret = S.text.substring(S.pos, i); + S.pos = i; + } + return token("comment1", ret); + }; + + function read_multiline_comment() { + next(); + return with_eof_error("Unterminated multiline comment", function(){ + var i = find("*/", true), + text = S.text.substring(S.pos, i), + tok = token("comment2", text); + S.pos = i + 2; + S.newline_before = text.indexOf("\n") >= 0; + return tok; + }); + }; + + function read_regexp() { + return with_eof_error("Unterminated regular expression", function(){ + var prev_backslash = false, regexp = "", ch, in_class = false; + while ((ch = next(true))) if (prev_backslash) { + regexp += "\\" + ch; + prev_backslash = false; + } else if (ch == "[") { + in_class = true; + regexp += ch; + } else if (ch == "]" && in_class) { + in_class = false; + regexp += ch; + } else if (ch == "/" && !in_class) { + break; + } else if (ch == "\\") { + prev_backslash = true; + } else { + regexp += ch; + } + var mods = read_while(function(ch){ + return HOP(REGEXP_MODIFIERS, ch); + }); + return token("regexp", [ regexp, mods ]); + }); + }; + + function read_operator(prefix) { + function grow(op) { + var bigger = op + peek(); + if (HOP(OPERATORS, bigger)) { + next(); + return grow(bigger); + } else { + return op; + } + }; + return token("operator", grow(prefix || next())); + }; + + var handle_slash = skip_comments ? function() { + next(); + var regex_allowed = S.regex_allowed; + switch (peek()) { + case "/": read_line_comment(); S.regex_allowed = regex_allowed; return next_token(); + case "*": read_multiline_comment(); S.regex_allowed = regex_allowed; return next_token(); + } + return S.regex_allowed ? read_regexp() : read_operator("/"); + } : function() { + next(); + switch (peek()) { + case "/": return read_line_comment(); + case "*": return read_multiline_comment(); + } + return S.regex_allowed ? read_regexp() : read_operator("/"); + }; + + function handle_dot() { + next(); + return is_digit(peek()) + ? read_num(".") + : token("punc", "."); + }; + + function read_word() { + var word = read_while(is_identifier_char); + return !HOP(KEYWORDS, word) + ? token("name", word) + : HOP(OPERATORS, word) + ? token("operator", word) + : HOP(KEYWORDS_ATOM, word) + ? token("atom", word) + : token("keyword", word); + }; + + function with_eof_error(eof_error, cont) { + try { + return cont(); + } catch(ex) { + if (ex === EX_EOF) parse_error(eof_error); + else throw ex; + } + }; + + function next_token(force_regexp) { + if (force_regexp) + return read_regexp(); + skip_whitespace(); + start_token(); + var ch = peek(); + if (!ch) return token("eof"); + if (is_digit(ch)) return read_num(); + if (ch == '"' || ch == "'") return read_string(); + if (HOP(PUNC_CHARS, ch)) return token("punc", next()); + if (ch == ".") return handle_dot(); + if (ch == "/") return handle_slash(); + if (HOP(OPERATOR_CHARS, ch)) return read_operator(); + if (is_identifier_char(ch)) return read_word(); + parse_error("Unexpected character '" + ch + "'"); + }; + + next_token.context = function(nc) { + if (nc) S = nc; + return S; + }; + + return next_token; + +}; + +/* -----[ Parser (constants) ]----- */ + +var UNARY_PREFIX = array_to_hash([ + "typeof", + "void", + "delete", + "--", + "++", + "!", + "~", + "-", + "+" +]); + +var UNARY_POSTFIX = array_to_hash([ "--", "++" ]); + +var ASSIGNMENT = (function(a, ret, i){ + while (i < a.length) { + ret[a[i]] = a[i].substr(0, a[i].length - 1); + i++; + } + return ret; +})( + ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "~=", "%=", "|=", "^=", "&="], + { "=": true }, + 0 +); + +var PRECEDENCE = (function(a, ret){ + for (var i = 0, n = 1; i < a.length; ++i, ++n) { + var b = a[i]; + for (var j = 0; j < b.length; ++j) { + ret[b[j]] = n; + } + } + return ret; +})( + [ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "===", "!=", "!=="], + ["<", ">", "<=", ">=", "in", "instanceof"], + [">>", "<<", ">>>"], + ["+", "-"], + ["*", "/", "%"] + ], + {} +); + +var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); + +var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); + +/* -----[ Parser ]----- */ + +function NodeWithToken(str, start, end) { + this.name = str; + this.start = start; + this.end = end; +}; + +NodeWithToken.prototype.toString = function() { return this.name; }; + +function parse($TEXT, strict_mode, embed_tokens) { + + var S = { + input: tokenizer($TEXT, true), + token: null, + prev: null, + peeked: null, + in_function: 0, + in_loop: 0, + labels: [] + }; + + S.token = next(); + + function is(type, value) { + return is_token(S.token, type, value); + }; + + function peek() { return S.peeked || (S.peeked = S.input()); }; + + function next() { + S.prev = S.token; + if (S.peeked) { + S.token = S.peeked; + S.peeked = null; + } else { + S.token = S.input(); + } + return S.token; + }; + + function prev() { + return S.prev; + }; + + function croak(msg, line, col, pos) { + var ctx = S.input.context(); + js_error(msg, + line != null ? line : ctx.tokline, + col != null ? col : ctx.tokcol, + pos != null ? pos : ctx.tokpos); + }; + + function token_error(token, msg) { + croak(msg, token.line, token.col); + }; + + function unexpected(token) { + if (token == null) + token = S.token; + token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); + }; + + function expect_token(type, val) { + if (is(type, val)) { + return next(); + } + token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type); + }; + + function expect(punc) { return expect_token("punc", punc); }; + + function can_insert_semicolon() { + return !strict_mode && ( + S.token.nlb || is("eof") || is("punc", "}") + ); + }; + + function semicolon() { + if (is("punc", ";")) next(); + else if (!can_insert_semicolon()) unexpected(); + }; + + function as() { + return slice(arguments); + }; + + function parenthesised() { + expect("("); + var ex = expression(); + expect(")"); + return ex; + }; + + function add_tokens(str, start, end) { + return new NodeWithToken(str, start, end); + }; + + var statement = embed_tokens ? function() { + var start = S.token; + var stmt = $statement(); + stmt[0] = add_tokens(stmt[0], start, prev()); + return stmt; + } : $statement; + + function $statement() { + if (is("operator", "/")) { + S.peeked = null; + S.token = S.input(true); // force regexp + } + switch (S.token.type) { + case "num": + case "string": + case "regexp": + case "operator": + case "atom": + return simple_statement(); + + case "name": + return is_token(peek(), "punc", ":") + ? labeled_statement(prog1(S.token.value, next, next)) + : simple_statement(); + + case "punc": + switch (S.token.value) { + case "{": + return as("block", block_()); + case "[": + case "(": + return simple_statement(); + case ";": + next(); + return as("block"); + default: + unexpected(); + } + + case "keyword": + switch (prog1(S.token.value, next)) { + case "break": + return break_cont("break"); + + case "continue": + return break_cont("continue"); + + case "debugger": + semicolon(); + return as("debugger"); + + case "do": + return (function(body){ + expect_token("keyword", "while"); + return as("do", prog1(parenthesised, semicolon), body); + })(in_loop(statement)); + + case "for": + return for_(); + + case "function": + return function_(true); + + case "if": + return if_(); + + case "return": + if (S.in_function == 0) + croak("'return' outside of function"); + return as("return", + is("punc", ";") + ? (next(), null) + : can_insert_semicolon() + ? null + : prog1(expression, semicolon)); + + case "switch": + return as("switch", parenthesised(), switch_block_()); + + case "throw": + return as("throw", prog1(expression, semicolon)); + + case "try": + return try_(); + + case "var": + return prog1(var_, semicolon); + + case "const": + return prog1(const_, semicolon); + + case "while": + return as("while", parenthesised(), in_loop(statement)); + + case "with": + return as("with", parenthesised(), statement()); + + default: + unexpected(); + } + } + }; + + function labeled_statement(label) { + S.labels.push(label); + var start = S.token, stat = statement(); + if (strict_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0])) + unexpected(start); + S.labels.pop(); + return as("label", label, stat); + }; + + function simple_statement() { + return as("stat", prog1(expression, semicolon)); + }; + + function break_cont(type) { + var name = is("name") ? S.token.value : null; + if (name != null) { + next(); + if (!member(name, S.labels)) + croak("Label " + name + " without matching loop or statement"); + } + else if (S.in_loop == 0) + croak(type + " not inside a loop or switch"); + semicolon(); + return as(type, name); + }; + + function for_() { + expect("("); + var has_var = is("keyword", "var"); + if (has_var) + next(); + if (is("name") && is_token(peek(), "operator", "in")) { + // for (i in foo) + var name = S.token.value; + next(); next(); + var obj = expression(); + expect(")"); + return as("for-in", has_var, name, obj, in_loop(statement)); + } else { + // classic for + var init = is("punc", ";") ? null : has_var ? var_() : expression(); + expect(";"); + var test = is("punc", ";") ? null : expression(); + expect(";"); + var step = is("punc", ")") ? null : expression(); + expect(")"); + return as("for", init, test, step, in_loop(statement)); + } + }; + + function function_(in_statement) { + var name = is("name") ? prog1(S.token.value, next) : null; + if (in_statement && !name) + unexpected(); + expect("("); + return as(in_statement ? "defun" : "function", + name, + // arguments + (function(first, a){ + while (!is("punc", ")")) { + if (first) first = false; else expect(","); + if (!is("name")) unexpected(); + a.push(S.token.value); + next(); + } + next(); + return a; + })(true, []), + // body + (function(){ + ++S.in_function; + var loop = S.in_loop; + S.in_loop = 0; + var a = block_(); + --S.in_function; + S.in_loop = loop; + return a; + })()); + }; + + function if_() { + var cond = parenthesised(), body = statement(), belse; + if (is("keyword", "else")) { + next(); + belse = statement(); + } + return as("if", cond, body, belse); + }; + + function block_() { + expect("{"); + var a = []; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + a.push(statement()); + } + next(); + return a; + }; + + var switch_block_ = curry(in_loop, function(){ + expect("{"); + var a = [], cur = null; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + if (is("keyword", "case")) { + next(); + cur = []; + a.push([ expression(), cur ]); + expect(":"); + } + else if (is("keyword", "default")) { + next(); + expect(":"); + cur = []; + a.push([ null, cur ]); + } + else { + if (!cur) unexpected(); + cur.push(statement()); + } + } + next(); + return a; + }); + + function try_() { + var body = block_(), bcatch, bfinally; + if (is("keyword", "catch")) { + next(); + expect("("); + if (!is("name")) + croak("Name expected"); + var name = S.token.value; + next(); + expect(")"); + bcatch = [ name, block_() ]; + } + if (is("keyword", "finally")) { + next(); + bfinally = block_(); + } + if (!bcatch && !bfinally) + croak("Missing catch/finally blocks"); + return as("try", body, bcatch, bfinally); + }; + + function vardefs() { + var a = []; + for (;;) { + if (!is("name")) + unexpected(); + var name = S.token.value; + next(); + if (is("operator", "=")) { + next(); + a.push([ name, expression(false) ]); + } else { + a.push([ name ]); + } + if (!is("punc", ",")) + break; + next(); + } + return a; + }; + + function var_() { + return as("var", vardefs()); + }; + + function const_() { + return as("const", vardefs()); + }; + + function new_() { + var newexp = expr_atom(false), args; + if (is("punc", "(")) { + next(); + args = expr_list(")"); + } else { + args = []; + } + return subscripts(as("new", newexp, args), true); + }; + + function expr_atom(allow_calls) { + if (is("operator", "new")) { + next(); + return new_(); + } + if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) { + return make_unary("unary-prefix", + prog1(S.token.value, next), + expr_atom(allow_calls)); + } + if (is("punc")) { + switch (S.token.value) { + case "(": + next(); + return subscripts(prog1(expression, curry(expect, ")")), allow_calls); + case "[": + next(); + return subscripts(array_(), allow_calls); + case "{": + next(); + return subscripts(object_(), allow_calls); + } + unexpected(); + } + if (is("keyword", "function")) { + next(); + return subscripts(function_(false), allow_calls); + } + if (HOP(ATOMIC_START_TOKEN, S.token.type)) { + var atom = S.token.type == "regexp" + ? as("regexp", S.token.value[0], S.token.value[1]) + : as(S.token.type, S.token.value); + return subscripts(prog1(atom, next), allow_calls); + } + unexpected(); + }; + + function expr_list(closing, allow_trailing_comma) { + var first = true, a = []; + while (!is("punc", closing)) { + if (first) first = false; else expect(","); + if (allow_trailing_comma && is("punc", closing)) + break; + a.push(expression(false)); + } + next(); + return a; + }; + + function array_() { + return as("array", expr_list("]", !strict_mode)); + }; + + function object_() { + var first = true, a = []; + while (!is("punc", "}")) { + if (first) first = false; else expect(","); + if (!strict_mode && is("punc", "}")) + // allow trailing comma + break; + var type = S.token.type; + var name = as_property_name(); + if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) { + a.push([ as_name(), function_(false), name ]); + } else { + expect(":"); + a.push([ name, expression(false) ]); + } + } + next(); + return as("object", a); + }; + + function as_property_name() { + switch (S.token.type) { + case "num": + case "string": + return prog1(S.token.value, next); + } + return as_name(); + }; + + function as_name() { + switch (S.token.type) { + case "name": + case "operator": + case "keyword": + case "atom": + return prog1(S.token.value, next); + default: + unexpected(); + } + }; + + function subscripts(expr, allow_calls) { + if (is("punc", ".")) { + next(); + return subscripts(as("dot", expr, as_name()), allow_calls); + } + if (is("punc", "[")) { + next(); + return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls); + } + if (allow_calls && is("punc", "(")) { + next(); + return subscripts(as("call", expr, expr_list(")")), true); + } + if (allow_calls && is("operator") && HOP(UNARY_POSTFIX, S.token.value)) { + return prog1(curry(make_unary, "unary-postfix", S.token.value, expr), + next); + } + return expr; + }; + + function make_unary(tag, op, expr) { + if ((op == "++" || op == "--") && !is_assignable(expr)) + croak("Invalid use of " + op + " operator"); + return as(tag, op, expr); + }; + + function expr_op(left, min_prec) { + var op = is("operator") ? S.token.value : null; + var prec = op != null ? PRECEDENCE[op] : null; + if (prec != null && prec > min_prec) { + next(); + var right = expr_op(expr_atom(true), prec); + return expr_op(as("binary", op, left, right), min_prec); + } + return left; + }; + + function expr_ops() { + return expr_op(expr_atom(true), 0); + }; + + function maybe_conditional() { + var expr = expr_ops(); + if (is("operator", "?")) { + next(); + var yes = expression(false); + expect(":"); + return as("conditional", expr, yes, expression(false)); + } + return expr; + }; + + function is_assignable(expr) { + switch (expr[0]) { + case "dot": + case "sub": + return true; + case "name": + return expr[1] != "this"; + } + }; + + function maybe_assign() { + var left = maybe_conditional(), val = S.token.value; + if (is("operator") && HOP(ASSIGNMENT, val)) { + if (is_assignable(left)) { + next(); + return as("assign", ASSIGNMENT[val], left, maybe_assign()); + } + croak("Invalid assignment"); + } + return left; + }; + + function expression(commas) { + if (arguments.length == 0) + commas = true; + var expr = maybe_assign(); + if (commas && is("punc", ",")) { + next(); + return as("seq", expr, expression()); + } + return expr; + }; + + function in_loop(cont) { + try { + ++S.in_loop; + return cont(); + } finally { + --S.in_loop; + } + }; + + return as("toplevel", (function(a){ + while (!is("eof")) + a.push(statement()); + return a; + })([])); + +}; + +/* -----[ Utilities ]----- */ + +function curry(f) { + var args = slice(arguments, 1); + return function() { return f.apply(this, args.concat(slice(arguments))); }; +}; + +function prog1(ret) { + if (ret instanceof Function) + ret = ret(); + for (var i = 1, n = arguments.length; --n > 0; ++i) + arguments[i](); + return ret; +}; + +function array_to_hash(a) { + var ret = {}; + for (var i = 0; i < a.length; ++i) + ret[a[i]] = true; + return ret; +}; + +function slice(a, start) { + return Array.prototype.slice.call(a, start == null ? 0 : start); +}; + +function characters(str) { + return str.split(""); +}; + +function member(name, array) { + for (var i = array.length; --i >= 0;) + if (array[i] === name) + return true; + return false; +}; + +function HOP(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +}; + +/* -----[ Exports ]----- */ + +exports.tokenizer = tokenizer; +exports.parse = parse; +exports.slice = slice; +exports.curry = curry; +exports.member = member; +exports.array_to_hash = array_to_hash; +exports.PRECEDENCE = PRECEDENCE; +exports.KEYWORDS_ATOM = KEYWORDS_ATOM; +exports.RESERVED_WORDS = RESERVED_WORDS; +exports.KEYWORDS = KEYWORDS; +exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN; +exports.OPERATORS = OPERATORS; +exports.is_alphanumeric_char = is_alphanumeric_char; diff --git a/Source/WebCore/inspector/front-end/UglifyJS/process.js b/Source/WebCore/inspector/front-end/UglifyJS/process.js new file mode 100755 index 0000000..65dbc0e --- /dev/null +++ b/Source/WebCore/inspector/front-end/UglifyJS/process.js @@ -0,0 +1,1560 @@ +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + + This version is suitable for Node.js. With minimal changes (the + exports stuff) it should work on any JS platform. + + This file implements some AST processors. They work on data built + by parse-js. + + Exported functions: + + - ast_mangle(ast, include_toplevel) -- mangles the + variable/function names in the AST. Returns an AST. Pass true + as second argument to mangle toplevel names too. + + - ast_squeeze(ast) -- employs various optimizations to make the + final generated code even smaller. Returns an AST. + + - gen_code(ast, beautify) -- generates JS code from the AST. Pass + true (or an object, see the code for some options) as second + argument to get "pretty" (indented) code. + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + <mihai.bazon@gmail.com> + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * 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 THE COPYRIGHT HOLDER “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 THE COPYRIGHT HOLDER 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. + + ***********************************************************************/ + +var jsp = require("./parse-js"), + slice = jsp.slice, + member = jsp.member, + PRECEDENCE = jsp.PRECEDENCE, + OPERATORS = jsp.OPERATORS; + +/* -----[ helper for AST traversal ]----- */ + +function ast_walker(ast) { + function _vardefs(defs) { + return MAP(defs, function(def){ + var a = [ def[0] ]; + if (def.length > 1) + a[1] = walk(def[1]); + return a; + }); + }; + var walkers = { + "string": function(str) { + return [ "string", str ]; + }, + "num": function(num) { + return [ "num", num ]; + }, + "name": function(name) { + return [ "name", name ]; + }, + "toplevel": function(statements) { + return [ "toplevel", MAP(statements, walk) ]; + }, + "block": function(statements) { + var out = [ "block" ]; + if (statements != null) + out.push(MAP(statements, walk)); + return out; + }, + "var": function(defs) { + return [ "var", _vardefs(defs) ]; + }, + "const": function(defs) { + return [ "const", _vardefs(defs) ]; + }, + "try": function(t, c, f) { + return [ + "try", + MAP(t, walk), + c != null ? [ c[0], MAP(c[1], walk) ] : null, + f != null ? MAP(f, walk) : null + ]; + }, + "throw": function(expr) { + return [ "throw", walk(expr) ]; + }, + "new": function(ctor, args) { + return [ "new", walk(ctor), MAP(args, walk) ]; + }, + "switch": function(expr, body) { + return [ "switch", walk(expr), MAP(body, function(branch){ + return [ branch[0] ? walk(branch[0]) : null, + MAP(branch[1], walk) ]; + }) ]; + }, + "break": function(label) { + return [ "break", label ]; + }, + "continue": function(label) { + return [ "continue", label ]; + }, + "conditional": function(cond, t, e) { + return [ "conditional", walk(cond), walk(t), walk(e) ]; + }, + "assign": function(op, lvalue, rvalue) { + return [ "assign", op, walk(lvalue), walk(rvalue) ]; + }, + "dot": function(expr) { + return [ "dot", walk(expr) ].concat(slice(arguments, 1)); + }, + "call": function(expr, args) { + return [ "call", walk(expr), MAP(args, walk) ]; + }, + "function": function(name, args, body) { + return [ "function", name, args.slice(), MAP(body, walk) ]; + }, + "defun": function(name, args, body) { + return [ "defun", name, args.slice(), MAP(body, walk) ]; + }, + "if": function(conditional, t, e) { + return [ "if", walk(conditional), walk(t), walk(e) ]; + }, + "for": function(init, cond, step, block) { + return [ "for", walk(init), walk(cond), walk(step), walk(block) ]; + }, + "for-in": function(has_var, key, hash, block) { + return [ "for-in", has_var, key, walk(hash), walk(block) ]; + }, + "while": function(cond, block) { + return [ "while", walk(cond), walk(block) ]; + }, + "do": function(cond, block) { + return [ "do", walk(cond), walk(block) ]; + }, + "return": function(expr) { + return [ "return", walk(expr) ]; + }, + "binary": function(op, left, right) { + return [ "binary", op, walk(left), walk(right) ]; + }, + "unary-prefix": function(op, expr) { + return [ "unary-prefix", op, walk(expr) ]; + }, + "unary-postfix": function(op, expr) { + return [ "unary-postfix", op, walk(expr) ]; + }, + "sub": function(expr, subscript) { + return [ "sub", walk(expr), walk(subscript) ]; + }, + "object": function(props) { + return [ "object", MAP(props, function(p){ + return p.length == 2 + ? [ p[0], walk(p[1]) ] + : [ p[0], walk(p[1]), p[2] ]; // get/set-ter + }) ]; + }, + "regexp": function(rx, mods) { + return [ "regexp", rx, mods ]; + }, + "array": function(elements) { + return [ "array", MAP(elements, walk) ]; + }, + "stat": function(stat) { + return [ "stat", walk(stat) ]; + }, + "seq": function() { + return [ "seq" ].concat(MAP(slice(arguments), walk)); + }, + "label": function(name, block) { + return [ "label", name, walk(block) ]; + }, + "with": function(expr, block) { + return [ "with", walk(expr), walk(block) ]; + }, + "atom": function(name) { + return [ "atom", name ]; + } + }; + + var user = {}; + var stack = []; + function walk(ast) { + if (ast == null) + return null; + try { + stack.push(ast); + var type = ast[0]; + var gen = user[type]; + if (gen) { + var ret = gen.apply(ast, ast.slice(1)); + if (ret != null) + return ret; + } + gen = walkers[type]; + return gen.apply(ast, ast.slice(1)); + } finally { + stack.pop(); + } + }; + + function with_walkers(walkers, cont){ + var save = {}, i; + for (i in walkers) if (HOP(walkers, i)) { + save[i] = user[i]; + user[i] = walkers[i]; + } + var ret = cont(); + for (i in save) if (HOP(save, i)) { + if (!save[i]) delete user[i]; + else user[i] = save[i]; + } + return ret; + }; + + return { + walk: walk, + with_walkers: with_walkers, + parent: function() { + return stack[stack.length - 2]; // last one is current node + }, + stack: function() { + return stack; + } + }; +}; + +/* -----[ Scope and mangling ]----- */ + +function Scope(parent) { + this.names = {}; // names defined in this scope + this.mangled = {}; // mangled names (orig.name => mangled) + this.rev_mangled = {}; // reverse lookup (mangled => orig.name) + this.cname = -1; // current mangled name + this.refs = {}; // names referenced from this scope + this.uses_with = false; // will become TRUE if eval() is detected in this or any subscopes + this.uses_eval = false; // will become TRUE if with() is detected in this or any subscopes + this.parent = parent; // parent scope + this.children = []; // sub-scopes + if (parent) { + this.level = parent.level + 1; + parent.children.push(this); + } else { + this.level = 0; + } +}; + +var base54 = (function(){ + var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_"; + return function(num) { + var ret = ""; + do { + ret = DIGITS.charAt(num % 54) + ret; + num = Math.floor(num / 54); + } while (num > 0); + return ret; + }; +})(); + +Scope.prototype = { + has: function(name) { + for (var s = this; s; s = s.parent) + if (HOP(s.names, name)) + return s; + }, + has_mangled: function(mname) { + for (var s = this; s; s = s.parent) + if (HOP(s.rev_mangled, mname)) + return s; + }, + toJSON: function() { + return { + names: this.names, + uses_eval: this.uses_eval, + uses_with: this.uses_with + }; + }, + + next_mangled: function() { + // we must be careful that the new mangled name: + // + // 1. doesn't shadow a mangled name from a parent + // scope, unless we don't reference the original + // name from this scope OR from any sub-scopes! + // This will get slow. + // + // 2. doesn't shadow an original name from a parent + // scope, in the event that the name is not mangled + // in the parent scope and we reference that name + // here OR IN ANY SUBSCOPES! + // + // 3. doesn't shadow a name that is referenced but not + // defined (possibly global defined elsewhere). + for (;;) { + var m = base54(++this.cname), prior; + + // case 1. + prior = this.has_mangled(m); + if (prior && this.refs[prior.rev_mangled[m]] === prior) + continue; + + // case 2. + prior = this.has(m); + if (prior && prior !== this && this.refs[m] === prior && !prior.has_mangled(m)) + continue; + + // case 3. + if (HOP(this.refs, m) && this.refs[m] == null) + continue; + + // I got "do" once. :-/ + if (!is_identifier(m)) + continue; + + return m; + } + }, + get_mangled: function(name, newMangle) { + if (this.uses_eval || this.uses_with) return name; // no mangle if eval or with is in use + var s = this.has(name); + if (!s) return name; // not in visible scope, no mangle + if (HOP(s.mangled, name)) return s.mangled[name]; // already mangled in this scope + if (!newMangle) return name; // not found and no mangling requested + + var m = s.next_mangled(); + s.rev_mangled[m] = name; + return s.mangled[name] = m; + }, + define: function(name) { + if (name != null) + return this.names[name] = name; + } +}; + +function ast_add_scope(ast) { + + var current_scope = null; + var w = ast_walker(), walk = w.walk; + var having_eval = []; + + function with_new_scope(cont) { + current_scope = new Scope(current_scope); + var ret = current_scope.body = cont(); + ret.scope = current_scope; + current_scope = current_scope.parent; + return ret; + }; + + function define(name) { + return current_scope.define(name); + }; + + function reference(name) { + current_scope.refs[name] = true; + }; + + function _lambda(name, args, body) { + return [ this[0], define(name), args, with_new_scope(function(){ + MAP(args, define); + return MAP(body, walk); + })]; + }; + + return with_new_scope(function(){ + // process AST + var ret = w.with_walkers({ + "function": _lambda, + "defun": _lambda, + "with": function(expr, block) { + for (var s = current_scope; s; s = s.parent) + s.uses_with = true; + }, + "var": function(defs) { + MAP(defs, function(d){ define(d[0]) }); + }, + "const": function(defs) { + MAP(defs, function(d){ define(d[0]) }); + }, + "try": function(t, c, f) { + if (c != null) return [ + "try", + MAP(t, walk), + [ define(c[0]), MAP(c[1], walk) ], + f != null ? MAP(f, walk) : null + ]; + }, + "name": function(name) { + if (name == "eval") + having_eval.push(current_scope); + reference(name); + }, + "for-in": function(has_var, name) { + if (has_var) define(name); + else reference(name); + } + }, function(){ + return walk(ast); + }); + + // the reason why we need an additional pass here is + // that names can be used prior to their definition. + + // scopes where eval was detected and their parents + // are marked with uses_eval, unless they define the + // "eval" name. + MAP(having_eval, function(scope){ + if (!scope.has("eval")) while (scope) { + scope.uses_eval = true; + scope = scope.parent; + } + }); + + // for referenced names it might be useful to know + // their origin scope. current_scope here is the + // toplevel one. + function fixrefs(scope, i) { + // do children first; order shouldn't matter + for (i = scope.children.length; --i >= 0;) + fixrefs(scope.children[i]); + for (i in scope.refs) if (HOP(scope.refs, i)) { + // find origin scope and propagate the reference to origin + for (var origin = scope.has(i), s = scope; s; s = s.parent) { + s.refs[i] = origin; + if (s === origin) break; + } + } + }; + fixrefs(current_scope); + + return ret; + }); + +}; + +/* -----[ mangle names ]----- */ + +function ast_mangle(ast, do_toplevel) { + var w = ast_walker(), walk = w.walk, scope; + + function get_mangled(name, newMangle) { + if (!do_toplevel && !scope.parent) return name; // don't mangle toplevel + return scope.get_mangled(name, newMangle); + }; + + function _lambda(name, args, body) { + if (name) name = get_mangled(name); + body = with_scope(body.scope, function(){ + args = MAP(args, function(name){ return get_mangled(name) }); + return MAP(body, walk); + }); + return [ this[0], name, args, body ]; + }; + + function with_scope(s, cont) { + var _scope = scope; + scope = s; + for (var i in s.names) if (HOP(s.names, i)) { + get_mangled(i, true); + } + var ret = cont(); + ret.scope = s; + scope = _scope; + return ret; + }; + + function _vardefs(defs) { + return MAP(defs, function(d){ + return [ get_mangled(d[0]), walk(d[1]) ]; + }); + }; + + return w.with_walkers({ + "function": _lambda, + "defun": function() { + // move function declarations to the top when + // they are not in some block. + var ast = _lambda.apply(this, arguments); + switch (w.parent()[0]) { + case "toplevel": + case "function": + case "defun": + return MAP.at_top(ast); + } + return ast; + }, + "var": function(defs) { + return [ "var", _vardefs(defs) ]; + }, + "const": function(defs) { + return [ "const", _vardefs(defs) ]; + }, + "name": function(name) { + return [ "name", get_mangled(name) ]; + }, + "try": function(t, c, f) { + return [ "try", + MAP(t, walk), + c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null, + f != null ? MAP(f, walk) : null ]; + }, + "toplevel": function(body) { + return with_scope(this.scope, function(){ + return [ "toplevel", MAP(body, walk) ]; + }); + }, + "for-in": function(has_var, name, obj, stat) { + return [ "for-in", has_var, get_mangled(name), walk(obj), walk(stat) ]; + } + }, function() { + return walk(ast_add_scope(ast)); + }); +}; + +/* -----[ + - compress foo["bar"] into foo.bar, + - remove block brackets {} where possible + - join consecutive var declarations + - various optimizations for IFs: + - if (cond) foo(); else bar(); ==> cond?foo():bar(); + - if (cond) foo(); ==> cond&&foo(); + - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); // also for throw + - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()} + ]----- */ + +var warn = function(){}; + +function best_of(ast1, ast2) { + return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1; +}; + +function last_stat(b) { + if (b[0] == "block" && b[1] && b[1].length > 0) + return b[1][b[1].length - 1]; + return b; +} + +function aborts(t) { + if (t) { + t = last_stat(t); + if (t[0] == "return" || t[0] == "break" || t[0] == "continue" || t[0] == "throw") + return true; + } +}; + +function negate(c) { + var not_c = [ "unary-prefix", "!", c ]; + switch (c[0]) { + case "unary-prefix": + return c[1] == "!" ? c[2] : not_c; + case "binary": + var op = c[1], left = c[2], right = c[3]; + switch (op) { + case "<=": return [ "binary", ">", left, right ]; + case "<": return [ "binary", ">=", left, right ]; + case ">=": return [ "binary", "<", left, right ]; + case ">": return [ "binary", "<=", left, right ]; + case "==": return [ "binary", "!=", left, right ]; + case "!=": return [ "binary", "==", left, right ]; + case "===": return [ "binary", "!==", left, right ]; + case "!==": return [ "binary", "===", left, right ]; + case "&&": return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]); + case "||": return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]); + } + break; + } + return not_c; +}; + +function make_conditional(c, t, e) { + if (c[0] == "unary-prefix" && c[1] == "!") { + return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ]; + } else { + return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ]; + } +}; + +function empty(b) { + return !b || (b[0] == "block" && (!b[1] || b[1].length == 0)); +}; + +function ast_squeeze(ast, options) { + options = defaults(options, { + make_seqs : true, + dead_code : true, + no_warnings : false, + extra : false + }); + + var w = ast_walker(), walk = w.walk, scope; + + function with_scope(s, cont) { + var _scope = scope; + scope = s; + var ret = cont(); + ret.scope = s; + scope = _scope; + return ret; + }; + + function is_constant(node) { + return node[0] == "string" || node[0] == "num"; + }; + + function find_first_execute(node) { + if (!node) + return false; + + switch (node[0]) { + case "num": + case "string": + case "name": + return node; + case "call": + case "conditional": + case "for": + case "if": + case "new": + case "return": + case "stat": + case "switch": + case "throw": + return find_first_execute(node[1]); + case "binary": + return find_first_execute(node[2]); + case "assign": + if (node[1] === true) + return find_first_execute(node[3]); + break; + case "var": + if (node[1][0].length > 1) + return find_first_execute(node[1][0][1]); + break; + } + return null; + } + + function find_assign_recursive(p, v) { + if (p[0] == "assign" && p[1] != true || p[0] == "unary-prefix") { + if (p[2][0] == "name" && v[0] == "name" && p[2][1] == v[1]) + return true; + return false; + } + + if (p[0] != "assign" || p[1] !== true) + return false; + + if ((is_constant(p[3]) && p[3][0] == v[0] && p[3][1] == v[1]) || + (p[3][0] == "name" && v[0] == "name" && p[3][1] == v[1]) || + (p[2][0] == "name" && v[0] == "name" && p[2][1] == v[1])) + return true; + + return find_assign_recursive(p[3], v); + }; + + function rmblock(block) { + if (block != null && block[0] == "block" && block[1] && block[1].length == 1) + block = block[1][0]; + return block; + }; + + function clone(obj) { + if (obj && obj.constructor == Array) + return MAP(obj, clone); + return obj; + }; + + function make_seq_to_statements(node) { + if (node[0] != "seq") { + switch (node[0]) { + case "var": + case "const": + return [ node ]; + default: + return [ [ "stat", node ] ]; + } + } + + var ret = []; + for (var i = 1; i < node.length; i++) + ret.push.apply(ret, make_seq_to_statements(node[i])); + + return ret; + }; + + function _lambda(name, args, body) { + return [ this[0], name, args, with_scope(body.scope, function(){ + return tighten(MAP(body, walk), "lambda"); + }) ]; + }; + + // we get here for blocks that have been already transformed. + // this function does a few things: + // 1. discard useless blocks + // 2. join consecutive var declarations + // 3. remove obviously dead code + // 4. transform consecutive statements using the comma operator + // 5. if block_type == "lambda" and it detects constructs like if(foo) return ... - rewrite like if (!foo) { ... } + function tighten(statements, block_type) { + statements = statements.reduce(function(a, stat){ + if (stat[0] == "block") { + if (stat[1]) { + a.push.apply(a, stat[1]); + } + } else { + a.push(stat); + } + return a; + }, []); + + if (options.extra) { + // Detightening things. We do this because then we can assume that the + // statements are structured in a specific way. + statements = (function(a, prev) { + statements.forEach(function(cur) { + switch (cur[0]) { + case "for": + if (cur[1] != null) { + a.push.apply(a, make_seq_to_statements(cur[1])); + cur[1] = null; + } + a.push(cur); + break; + case "stat": + var stats = make_seq_to_statements(cur[1]); + stats.forEach(function(s) { + if (s[1][0] == "unary-postfix") + s[1][0] = "unary-prefix"; + }); + a.push.apply(a, stats); + break; + default: + a.push(cur); + } + }); + return a; + })([]); + + statements = (function(a, prev) { + statements.forEach(function(cur) { + if (!(prev && prev[0] == "stat")) { + a.push(cur); + prev = cur; + return; + } + + var p = prev[1]; + var c = find_first_execute(cur); + if (c && find_assign_recursive(p, c)) { + var old_cur = clone(cur); + c.splice(0, c.length); + c.push.apply(c, p); + var tmp_cur = best_of(cur, [ "toplevel", [ prev, old_cur ] ]); + if (tmp_cur == cur) { + a[a.length -1] = cur; + } else { + cur = old_cur; + a.push(cur); + } + } else { + a.push(cur); + } + prev = cur; + }); + return a; + })([]); + } + + statements = (function(a, prev){ + statements.forEach(function(cur){ + if (prev && ((cur[0] == "var" && prev[0] == "var") || + (cur[0] == "const" && prev[0] == "const"))) { + prev[1] = prev[1].concat(cur[1]); + } else { + a.push(cur); + prev = cur; + } + }); + return a; + })([]); + + if (options.dead_code) statements = (function(a, has_quit){ + statements.forEach(function(st){ + if (has_quit) { + if (member(st[0], [ "function", "defun" , "var", "const" ])) { + a.push(st); + } + else if (!options.no_warnings) + warn("Removing unreachable code: " + gen_code(st, true)); + } + else { + a.push(st); + if (member(st[0], [ "return", "throw", "break", "continue" ])) + has_quit = true; + } + }); + return a; + })([]); + + if (options.make_seqs) statements = (function(a, prev) { + statements.forEach(function(cur){ + if (prev && prev[0] == "stat" && cur[0] == "stat") { + prev[1] = [ "seq", prev[1], cur[1] ]; + } else { + a.push(cur); + prev = cur; + } + }); + return a; + })([]); + + if (options.extra) { + statements = (function(a, prev){ + statements.forEach(function(cur){ + var replaced = false; + if (prev && cur[0] == "for" && cur[1] == null && (prev[0] == "var" || prev[0] == "const" || prev[0] == "stat")) { + cur[1] = prev; + a[a.length - 1] = cur; + } else { + a.push(cur); + } + prev = cur; + }); + return a; + })([]); + } + + if (block_type == "lambda") statements = (function(i, a, stat){ + while (i < statements.length) { + stat = statements[i++]; + if (stat[0] == "if" && !stat[3]) { + if (stat[2][0] == "return" && stat[2][1] == null) { + a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ])); + break; + } + var last = last_stat(stat[2]); + if (last[0] == "return" && last[1] == null) { + a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ])); + break; + } + } + a.push(stat); + } + return a; + })(0, []); + + return statements; + }; + + function make_if(c, t, e) { + c = walk(c); + t = walk(t); + e = walk(e); + + if (empty(t)) { + c = negate(c); + t = e; + e = null; + } else if (empty(e)) { + e = null; + } else { + // if we have both else and then, maybe it makes sense to switch them? + (function(){ + var a = gen_code(c); + var n = negate(c); + var b = gen_code(n); + if (b.length < a.length) { + var tmp = t; + t = e; + e = tmp; + c = n; + } + })(); + } + if (empty(e) && empty(t)) + return [ "stat", c ]; + var ret = [ "if", c, t, e ]; + if (t[0] == "stat") { + if (e) { + if (e[0] == "stat") { + ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]); + } + } + else { + ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]); + } + } + else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw")) { + ret = best_of(ret, [ t[0], make_conditional(c, t[1], e[1] ) ]); + } + else if (e && aborts(t)) { + ret = [ [ "if", c, t ] ]; + if (e[0] == "block") { + if (e[1]) ret = ret.concat(e[1]); + } + else { + ret.push(e); + } + ret = walk([ "block", ret ]); + } + else if (t && aborts(e)) { + ret = [ [ "if", negate(c), e ] ]; + if (t[0] == "block") { + if (t[1]) ret = ret.concat(t[1]); + } else { + ret.push(t); + } + ret = walk([ "block", ret ]); + } + return ret; + }; + + return w.with_walkers({ + "sub": function(expr, subscript) { + if (subscript[0] == "string") { + var name = subscript[1]; + if (is_identifier(name)) { + return [ "dot", walk(expr), name ]; + } + } + }, + "if": make_if, + "toplevel": function(body) { + return [ "toplevel", with_scope(this.scope, function(){ + return tighten(MAP(body, walk)); + }) ]; + }, + "switch": function(expr, body) { + var last = body.length - 1; + return [ "switch", walk(expr), MAP(body, function(branch, i){ + var block = tighten(MAP(branch[1], walk)); + if (i == last && block.length > 0) { + var node = block[block.length - 1]; + if (node[0] == "break" && !node[1]) + block.pop(); + } + return [ branch[0] ? walk(branch[0]) : null, block ]; + }) ]; + }, + "function": _lambda, + "defun": _lambda, + "block": function(body) { + if (body) return rmblock([ "block", tighten(MAP(body, walk)) ]); + }, + "binary": function(op, left, right) { + left = walk(left); + right = walk(right); + var best = [ "binary", op, left, right ]; + if (is_constant(right)) { + if (is_constant(left)) { + var val = null; + switch (op) { + case "+": val = left[1] + right[1]; break; + case "*": val = left[1] * right[1]; break; + case "/": val = left[1] / right[1]; break; + case "-": val = left[1] - right[1]; break; + case "<<": val = left[1] << right[1]; break; + case ">>": val = left[1] >> right[1]; break; + case ">>>": val = left[1] >>> right[1]; break; + } + if (val != null) { + best = best_of(best, [ typeof val == "string" ? "string" : "num", val ]); + } + } else if (left[0] == "binary" && left[1] == "+" && left[3][0] == "string") { + best = best_of(best, [ "binary", "+", left[2], [ "string", left[3][1] + right[1] ] ]); + } + } + return best; + }, + "conditional": function(c, t, e) { + return make_conditional(walk(c), walk(t), walk(e)); + }, + "try": function(t, c, f) { + return [ + "try", + tighten(MAP(t, walk)), + c != null ? [ c[0], tighten(MAP(c[1], walk)) ] : null, + f != null ? tighten(MAP(f, walk)) : null + ]; + }, + "unary-prefix": function(op, cond) { + if (op == "!") { + cond = walk(cond); + if (cond[0] == "unary-prefix" && cond[1] == "!") { + var p = w.parent(); + if (p[0] == "unary-prefix" && p[1] == "!") + return cond[2]; + return [ "unary-prefix", "!", cond ]; + } + return best_of(this, negate(cond)); + } + }, + "name": function(name) { + switch (name) { + case "true": return [ "unary-prefix", "!", [ "num", 0 ]]; + case "false": return [ "unary-prefix", "!", [ "num", 1 ]]; + } + }, + "new": function(ctor, args) { + if (ctor[0] == "name" && ctor[1] == "Array" && !scope.has("Array")) { + if (args.length != 1) { + return [ "array", args ]; + } else { + return [ "call", [ "name", "Array" ], args ]; + } + } + }, + "call": function(expr, args) { + if (expr[0] == "name" && expr[1] == "Array" && args.length != 1 && !scope.has("Array")) { + return [ "array", args ]; + } + } + }, function() { + return walk(ast_add_scope(ast)); + }); +}; + +/* -----[ re-generate code from the AST ]----- */ + +var DOT_CALL_NO_PARENS = jsp.array_to_hash([ + "name", + "array", + "string", + "dot", + "sub", + "call", + "regexp" +]); + +function make_string(str) { + var dq = 0, sq = 0; + str = str.replace(/[\\\b\f\n\r\t\x22\x27]/g, function(s){ + switch (s) { + case "\\": return "\\\\"; + case "\b": return "\\b"; + case "\f": return "\\f"; + case "\n": return "\\n"; + case "\r": return "\\r"; + case "\t": return "\\t"; + case '"': ++dq; return '"'; + case "'": ++sq; return "'"; + } + return s; + }); + if (dq > sq) { + return "'" + str.replace(/\x27/g, "\\'") + "'"; + } else { + return '"' + str.replace(/\x22/g, '\\"') + '"'; + } +}; + +function gen_code(ast, beautify) { + if (beautify) beautify = defaults(beautify, { + indent_start : 0, + indent_level : 4, + quote_keys : false, + space_colon : false + }); + var indentation = 0, + newline = beautify ? "\n" : "", + space = beautify ? " " : ""; + + function indent(line) { + if (line == null) + line = ""; + if (beautify) + line = repeat_string(" ", beautify.indent_start + indentation * beautify.indent_level) + line; + return line; + }; + + function with_indent(cont, incr) { + if (incr == null) incr = 1; + indentation += incr; + try { return cont.apply(null, slice(arguments, 1)); } + finally { indentation -= incr; } + }; + + function add_spaces(a) { + if (beautify) + return a.join(" "); + var b = []; + for (var i = 0; i < a.length; ++i) { + var next = a[i + 1]; + b.push(a[i]); + if (next && + ((/[a-z0-9_\x24]$/i.test(a[i].toString()) && /^[a-z0-9_\x24]/i.test(next.toString())) || + (/[\+\-]$/.test(a[i].toString()) && /^[\+\-]/.test(next.toString())))) { + b.push(" "); + } + } + return b.join(""); + }; + + function add_commas(a) { + return a.join("," + space); + }; + + function parenthesize(expr) { + var gen = make(expr); + for (var i = 1; i < arguments.length; ++i) { + var el = arguments[i]; + if ((el instanceof Function && el(expr)) || expr[0] == el) + return "(" + gen + ")"; + } + return gen; + }; + + function best_of(a) { + if (a.length == 1) { + return a[0]; + } + if (a.length == 2) { + var b = a[1]; + a = a[0]; + return a.length <= b.length ? a : b; + } + return best_of([ a[0], best_of(a.slice(1)) ]); + }; + + function needs_parens(expr) { + if (expr[0] == "function") { + // dot/call on a literal function requires the + // function literal itself to be parenthesized + // only if it's the first "thing" in a + // statement. This means that the parent is + // "stat", but it could also be a "seq" and + // we're the first in this "seq" and the + // parent is "stat", and so on. Messy stuff, + // but it worths the trouble. + var a = slice($stack), self = a.pop(), p = a.pop(); + while (p) { + if (p[0] == "stat") return true; + if ((p[0] == "seq" && p[1] === self) || + (p[0] == "call" && p[1] === self) || + (p[0] == "binary" && p[2] === self)) { + self = p; + p = a.pop(); + } else { + return false; + } + } + } + return !HOP(DOT_CALL_NO_PARENS, expr[0]); + }; + + function make_num(num) { + var str = num.toString(10), a = [ str.replace(/^0\./, ".") ], m; + if (Math.floor(num) === num) { + a.push("0x" + num.toString(16).toLowerCase(), // probably pointless + "0" + num.toString(8)); // same. + if ((m = /^(.*?)(0+)$/.exec(num))) { + a.push(m[1] + "e" + m[2].length); + } + } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) { + a.push(m[2] + "e-" + (m[1].length + m[2].length), + str.substr(str.indexOf("."))); + } + return best_of(a); + }; + + var generators = { + "string": make_string, + "num": make_num, + "name": make_name, + "toplevel": function(statements) { + return make_block_statements(statements) + .join(newline + newline); + }, + "block": make_block, + "var": function(defs) { + return "var " + add_commas(MAP(defs, make_1vardef)) + ";"; + }, + "const": function(defs) { + return "const " + add_commas(MAP(defs, make_1vardef)) + ";"; + }, + "try": function(tr, ca, fi) { + var out = [ "try", make_block(tr) ]; + if (ca) out.push("catch", "(" + ca[0] + ")", make_block(ca[1])); + if (fi) out.push("finally", make_block(fi)); + return add_spaces(out); + }, + "throw": function(expr) { + return add_spaces([ "throw", make(expr) ]) + ";"; + }, + "new": function(ctor, args) { + args = args.length > 0 ? "(" + add_commas(MAP(args, make)) + ")" : ""; + return add_spaces([ "new", parenthesize(ctor, "seq", "binary", "conditional", "assign", function(expr){ + var w = ast_walker(), has_call = {}; + try { + w.with_walkers({ + "call": function() { throw has_call }, + "function": function() { return this } + }, function(){ + w.walk(expr); + }); + } catch(ex) { + if (ex === has_call) + return true; + throw ex; + } + }) + args ]); + }, + "switch": function(expr, body) { + return add_spaces([ "switch", "(" + make(expr) + ")", make_switch_block(body) ]); + }, + "break": function(label) { + var out = "break"; + if (label != null) + out += " " + make_name(label); + return out + ";"; + }, + "continue": function(label) { + var out = "continue"; + if (label != null) + out += " " + make_name(label); + return out + ";"; + }, + "conditional": function(co, th, el) { + return add_spaces([ parenthesize(co, "assign", "seq", "conditional"), "?", + parenthesize(th, "seq"), ":", + parenthesize(el, "seq") ]); + }, + "assign": function(op, lvalue, rvalue) { + if (op && op !== true) op += "="; + else op = "="; + return add_spaces([ make(lvalue), op, parenthesize(rvalue, "seq") ]); + }, + "dot": function(expr) { + var out = make(expr), i = 1; + if (needs_parens(expr)) + out = "(" + out + ")"; + while (i < arguments.length) + out += "." + make_name(arguments[i++]); + return out; + }, + "call": function(func, args) { + var f = make(func); + if (needs_parens(func)) + f = "(" + f + ")"; + return f + "(" + add_commas(MAP(args, function(expr){ + return parenthesize(expr, "seq"); + })) + ")"; + }, + "function": make_function, + "defun": make_function, + "if": function(co, th, el) { + var out = [ "if", "(" + make(co) + ")", el ? make_then(th) : make(th) ]; + if (el) { + out.push("else", make(el)); + } + return add_spaces(out); + }, + "for": function(init, cond, step, block) { + var out = [ "for" ]; + init = (init != null ? make(init) : "").replace(/;*\s*$/, ";" + space); + cond = (cond != null ? make(cond) : "").replace(/;*\s*$/, ";" + space); + step = (step != null ? make(step) : "").replace(/;*\s*$/, ""); + var args = init + cond + step; + if (args == "; ; ") args = ";;"; + out.push("(" + args + ")", make(block)); + return add_spaces(out); + }, + "for-in": function(has_var, key, hash, block) { + var out = add_spaces([ "for", "(" ]); + if (has_var) + out += "var "; + out += add_spaces([ make_name(key) + " in " + make(hash) + ")", make(block) ]); + return out; + }, + "while": function(condition, block) { + return add_spaces([ "while", "(" + make(condition) + ")", make(block) ]); + }, + "do": function(condition, block) { + return add_spaces([ "do", make(block), "while", "(" + make(condition) + ")" ]) + ";"; + }, + "return": function(expr) { + var out = [ "return" ]; + if (expr != null) out.push(make(expr)); + return add_spaces(out) + ";"; + }, + "binary": function(operator, lvalue, rvalue) { + var left = make(lvalue), right = make(rvalue); + // XXX: I'm pretty sure other cases will bite here. + // we need to be smarter. + // adding parens all the time is the safest bet. + if (member(lvalue[0], [ "assign", "conditional", "seq" ]) || + lvalue[0] == "binary" && PRECEDENCE[operator] > PRECEDENCE[lvalue[1]]) { + left = "(" + left + ")"; + } + if (member(rvalue[0], [ "assign", "conditional", "seq" ]) || + rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]]) { + right = "(" + right + ")"; + } + return add_spaces([ left, operator, right ]); + }, + "unary-prefix": function(operator, expr) { + var val = make(expr); + if (!(expr[0] == "num" || (expr[0] == "unary-prefix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr))) + val = "(" + val + ")"; + return operator + (jsp.is_alphanumeric_char(operator.charAt(0)) ? " " : "") + val; + }, + "unary-postfix": function(operator, expr) { + var val = make(expr); + if (!(expr[0] == "num" || (expr[0] == "unary-postfix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr))) + val = "(" + val + ")"; + return val + operator; + }, + "sub": function(expr, subscript) { + var hash = make(expr); + if (needs_parens(expr)) + hash = "(" + hash + ")"; + return hash + "[" + make(subscript) + "]"; + }, + "object": function(props) { + if (props.length == 0) + return "{}"; + return "{" + newline + with_indent(function(){ + return MAP(props, function(p){ + if (p.length == 3) { + // getter/setter. The name is in p[0], the arg.list in p[1][2], the + // body in p[1][3] and type ("get" / "set") in p[2]. + return indent(make_function(p[0], p[1][2], p[1][3], p[2])); + } + var key = p[0], val = make(p[1]); + if (beautify && beautify.quote_keys) { + key = make_string(key); + } else if (typeof key == "number" || !beautify && +key + "" == key) { + key = make_num(+key); + } else if (!is_identifier(key)) { + key = make_string(key); + } + return indent(add_spaces(beautify && beautify.space_colon + ? [ key, ":", val ] + : [ key + ":", val ])); + }).join("," + newline); + }) + newline + indent("}"); + }, + "regexp": function(rx, mods) { + return "/" + rx + "/" + mods; + }, + "array": function(elements) { + if (elements.length == 0) return "[]"; + return add_spaces([ "[", add_commas(MAP(elements, function(el){ + return parenthesize(el, "seq"); + })), "]" ]); + }, + "stat": function(stmt) { + return make(stmt).replace(/;*\s*$/, ";"); + }, + "seq": function() { + return add_commas(MAP(slice(arguments), make)); + }, + "label": function(name, block) { + return add_spaces([ make_name(name), ":", make(block) ]); + }, + "with": function(expr, block) { + return add_spaces([ "with", "(" + make(expr) + ")", make(block) ]); + }, + "atom": function(name) { + return make_name(name); + }, + "comment1": function(text) { + return "//" + text + "\n"; + }, + "comment2": function(text) { + return "/*" + text + "*/"; + } + }; + + // The squeezer replaces "block"-s that contain only a single + // statement with the statement itself; technically, the AST + // is correct, but this can create problems when we output an + // IF having an ELSE clause where the THEN clause ends in an + // IF *without* an ELSE block (then the outer ELSE would refer + // to the inner IF). This function checks for this case and + // adds the block brackets if needed. + function make_then(th) { + if (th[0] == "do") { + // https://github.com/mishoo/UglifyJS/issues/#issue/57 + // IE croaks with "syntax error" on code like this: + // if (foo) do ... while(cond); else ... + // we need block brackets around do/while + return make([ "block", [ th ]]); + } + var b = th; + while (true) { + var type = b[0]; + if (type == "if") { + if (!b[3]) + // no else, we must add the block + return make([ "block", [ th ]]); + b = b[3]; + } + else if (type == "while" || type == "do") b = b[2]; + else if (type == "for" || type == "for-in") b = b[4]; + else break; + } + return make(th); + }; + + function make_function(name, args, body, keyword) { + var out = keyword || "function"; + if (name) { + out += " " + make_name(name); + } + out += "(" + add_commas(MAP(args, make_name)) + ")"; + return add_spaces([ out, make_block(body) ]); + }; + + function make_name(name) { + return name.toString(); + }; + + function make_block_statements(statements) { + for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) { + var stat = statements[i]; + var code = make(stat); + if (code != ";") { + if (!beautify && i == last) + code = code.replace(/;+\s*$/, ""); + a.push(code); + } + } + return MAP(a, indent); + }; + + function make_switch_block(body) { + var n = body.length; + if (n == 0) return "{}"; + return "{" + newline + MAP(body, function(branch, i){ + var has_body = branch[1].length > 0, code = with_indent(function(){ + return indent(branch[0] + ? add_spaces([ "case", make(branch[0]) + ":" ]) + : "default:"); + }, 0.5) + (has_body ? newline + with_indent(function(){ + return make_block_statements(branch[1]).join(newline); + }) : ""); + if (!beautify && has_body && i < n - 1) + code += ";"; + return code; + }).join(newline) + newline + indent("}"); + }; + + function make_block(statements) { + if (!statements) return ";"; + if (statements.length == 0) return "{}"; + return "{" + newline + with_indent(function(){ + return make_block_statements(statements).join(newline); + }) + newline + indent("}"); + }; + + function make_1vardef(def) { + var name = def[0], val = def[1]; + if (val != null) + name = add_spaces([ name, "=", make(val) ]); + return name; + }; + + var $stack = []; + + function make(node) { + var type = node[0]; + var gen = generators[type]; + if (!gen) + throw new Error("Can't find generator for \"" + type + "\""); + $stack.push(node); + var ret = gen.apply(type, node.slice(1)); + $stack.pop(); + return ret; + }; + + return make(ast); +}; + +/* -----[ Utilities ]----- */ + +function repeat_string(str, i) { + if (i <= 0) return ""; + if (i == 1) return str; + var d = repeat_string(str, i >> 1); + d += d; + if (i & 1) d += str; + return d; +}; + +function defaults(args, defs) { + var ret = {}; + if (args === true) + args = {}; + for (var i in defs) if (HOP(defs, i)) { + ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; + } + return ret; +}; + +function is_identifier(name) { + return /^[a-z_$][a-z0-9_$]*$/i.test(name) + && name != "this" + && !HOP(jsp.KEYWORDS_ATOM, name) + && !HOP(jsp.RESERVED_WORDS, name) + && !HOP(jsp.KEYWORDS, name); +}; + +function HOP(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +}; + +// some utilities + +var MAP; + +(function(){ + MAP = function(a, f, o) { + var ret = []; + for (var i = 0; i < a.length; ++i) { + var val = f.call(o, a[i], i); + if (val instanceof AtTop) ret.unshift(val.v); + else ret.push(val); + } + return ret; + }; + MAP.at_top = function(val) { return new AtTop(val) }; + function AtTop(val) { this.v = val }; +})(); + +/* -----[ Exports ]----- */ + +exports.ast_walker = ast_walker; +exports.ast_mangle = ast_mangle; +exports.ast_squeeze = ast_squeeze; +exports.gen_code = gen_code; +exports.ast_add_scope = ast_add_scope; +exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more; +exports.set_logger = function(logger) { warn = logger }; diff --git a/Source/WebCore/inspector/front-end/WebKit.qrc b/Source/WebCore/inspector/front-end/WebKit.qrc index 80a6533..edc1861 100644 --- a/Source/WebCore/inspector/front-end/WebKit.qrc +++ b/Source/WebCore/inspector/front-end/WebKit.qrc @@ -23,6 +23,7 @@ <file>CookieParser.js</file> <file>CookiesTable.js</file> <file>CSSCompletions.js</file> + <file>CSSKeywordCompletions.js</file> <file>CSSStyleModel.js</file> <file>Database.js</file> <file>DatabaseQueryView.js</file> @@ -51,7 +52,6 @@ <file>HelpScreen.js</file> <file>ImageView.js</file> <file>InjectedFakeWorker.js</file> - <file>InjectedScriptAccess.js</file> <file>inspector.js</file> <file>InspectorFrontendHostStub.js</file> <file>KeyboardShortcut.js</file> @@ -81,6 +81,8 @@ <file>ResourcesPanel.js</file> <file>ScopeChainSidebarPane.js</file> <file>Script.js</file> + <file>ScriptFormatter.js</file> + <file>ScriptFormatterWorker.js</file> <file>ScriptsPanel.js</file> <file>ScriptView.js</file> <file>Section.js</file> @@ -114,6 +116,8 @@ <file>WatchExpressionsSidebarPane.js</file> <file>WelcomeView.js</file> <file>WorkersSidebarPane.js</file> + <file>UglifyJS/parse-js.js</file> + <file>UglifyJS/process.js</file> <file>audits.css</file> <file>goToLineDialog.css</file> <file>heapProfiler.css</file> diff --git a/Source/WebCore/inspector/front-end/inspector.css b/Source/WebCore/inspector/front-end/inspector.css index c908427..f629d12 100644 --- a/Source/WebCore/inspector/front-end/inspector.css +++ b/Source/WebCore/inspector/front-end/inspector.css @@ -778,8 +778,8 @@ body.platform-linux .monospace, body.platform-linux .source-code { color: red; } -.auto-complete-text { - color: rgb(128, 128, 128); +.auto-complete-text, .editing .auto-complete-text { + color: rgb(128, 128, 128) !important; -webkit-user-select: none; -webkit-user-modify: read-only; } diff --git a/Source/WebCore/inspector/front-end/inspector.html b/Source/WebCore/inspector/front-end/inspector.html index 44f096f..0e0b9e9 100644 --- a/Source/WebCore/inspector/front-end/inspector.html +++ b/Source/WebCore/inspector/front-end/inspector.html @@ -92,6 +92,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="EventListenersSidebarPane.js"></script> <script type="text/javascript" src="Color.js"></script> <script type="text/javascript" src="CSSCompletions.js"></script> + <script type="text/javascript" src="CSSKeywordCompletions.js"></script> <script type="text/javascript" src="StylesSidebarPane.js"></script> <script type="text/javascript" src="PanelEnablerView.js"></script> <script type="text/javascript" src="WelcomeView.js"></script> @@ -121,6 +122,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="NetworkItemView.js"></script> <script type="text/javascript" src="ResourceView.js"></script> <script type="text/javascript" src="SourceFrame.js"></script> + <script type="text/javascript" src="ScriptFormatter.js"></script> <script type="text/javascript" src="DOMSyntaxHighlighter.js"></script> <script type="text/javascript" src="TextEditorModel.js"></script> <script type="text/javascript" src="TextEditorHighlighter.js"></script> @@ -142,7 +144,6 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="HeapSnapshotView.js"></script> <script type="text/javascript" src="DebuggerModel.js"></script> <script type="text/javascript" src="DOMAgent.js"></script> - <script type="text/javascript" src="InjectedScriptAccess.js"></script> <script type="text/javascript" src="TimelineAgent.js"></script> <script type="text/javascript" src="TimelinePanel.js"></script> <script type="text/javascript" src="TimelineOverviewPane.js"></script> diff --git a/Source/WebCore/inspector/front-end/inspector.js b/Source/WebCore/inspector/front-end/inspector.js index d8a93b1..77abe78 100644 --- a/Source/WebCore/inspector/front-end/inspector.js +++ b/Source/WebCore/inspector/front-end/inspector.js @@ -185,20 +185,9 @@ var WebInspector = { } }, - createJSBreakpointsSidebarPane: function() - { - var pane = new WebInspector.BreakpointsSidebarPane(WebInspector.UIString("Breakpoints")); - function breakpointAdded(event) - { - pane.addBreakpointItem(new WebInspector.BreakpointItem(event.data)); - } - WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointAdded, breakpointAdded); - return pane; - }, - createDOMBreakpointsSidebarPane: function() { - var pane = new WebInspector.BreakpointsSidebarPane(WebInspector.UIString("DOM Breakpoints")); + var pane = new WebInspector.NativeBreakpointsSidebarPane(WebInspector.UIString("DOM Breakpoints")); function breakpointAdded(event) { pane.addBreakpointItem(new WebInspector.BreakpointItem(event.data)); @@ -534,7 +523,7 @@ WebInspector.doLoadedDone = function() scripts: new WebInspector.ResourceCategory("scripts", WebInspector.UIString("Scripts"), "rgb(255,121,0)"), xhr: new WebInspector.ResourceCategory("xhr", WebInspector.UIString("XHR"), "rgb(231,231,10)"), fonts: new WebInspector.ResourceCategory("fonts", WebInspector.UIString("Fonts"), "rgb(255,82,62)"), - websockets: new WebInspector.ResourceCategory("websockets", WebInspector.UIString("WebSocket"), "rgb(186,186,186)"), // FIXME: Decide the color. + websockets: new WebInspector.ResourceCategory("websockets", WebInspector.UIString("WebSockets"), "rgb(186,186,186)"), // FIXME: Decide the color. other: new WebInspector.ResourceCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)") }; @@ -601,14 +590,6 @@ WebInspector.doLoadedDone = function() this.extensionServer.initExtensions(); - function populateInspectorState(inspectorState) - { - WebInspector.monitoringXHREnabled = inspectorState.monitoringXHREnabled; - if ("pauseOnExceptionsState" in inspectorState) - WebInspector.panels.scripts.updatePauseOnExceptionsState(inspectorState.pauseOnExceptionsState); - } - InspectorBackend.getInspectorState(populateInspectorState); - function onPopulateScriptObjects() { if (!WebInspector.currentPanel) diff --git a/Source/WebCore/inspector/front-end/utilities.js b/Source/WebCore/inspector/front-end/utilities.js index 4320ba8..5ed9a8c 100644 --- a/Source/WebCore/inspector/front-end/utilities.js +++ b/Source/WebCore/inspector/front-end/utilities.js @@ -745,6 +745,22 @@ Object.defineProperty(Array.prototype, "keySet", { value: function() return keys; }}); +Object.defineProperty(Array.prototype, "upperBound", { value: function(value) +{ + var first = 0; + var count = this.length; + while (count > 0) { + var step = count >> 1; + var middle = first + step; + if (value >= this[middle]) { + first = middle + 1; + count -= step + 1; + } else + count = step; + } + return first; +}}); + Array.diff = function(left, right) { var o = left; |