diff options
| author | Ben Murdoch <benm@google.com> | 2011-05-24 11:24:40 +0100 |
|---|---|---|
| committer | Ben Murdoch <benm@google.com> | 2011-06-02 09:53:15 +0100 |
| commit | 81bc750723a18f21cd17d1b173cd2a4dda9cea6e (patch) | |
| tree | 7a9e5ed86ff429fd347a25153107221543909b19 /Source/WebCore/inspector/front-end | |
| parent | 94088a6d336c1dd80a1e734af51e96abcbb689a7 (diff) | |
| download | external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.zip external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.gz external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.bz2 | |
Merge WebKit at r80534: Intial merge by Git
Change-Id: Ia7a83357124c9e1cdb1debf55d9661ec0bd09a61
Diffstat (limited to 'Source/WebCore/inspector/front-end')
61 files changed, 4735 insertions, 1630 deletions
diff --git a/Source/WebCore/inspector/front-end/AuditRules.js b/Source/WebCore/inspector/front-end/AuditRules.js index c122ba4..803e9e0 100644 --- a/Source/WebCore/inspector/front-end/AuditRules.js +++ b/Source/WebCore/inspector/front-end/AuditRules.js @@ -65,10 +65,16 @@ WebInspector.AuditRules.getDomainToResourcesMap = function(resources, types, nee WebInspector.AuditRules.evaluateInTargetWindow = function(func, args, callback) { - InspectorBackend.evaluateOnSelf(func.toString(), args, callback); + function mycallback(result) + { + if (result) + callback(JSON.parse(result.description)); + else + callback(null); + } + RuntimeAgent.evaluate("JSON.stringify((" + func + ")(" + JSON.stringify(args) + "))", "", false, mycallback); } - WebInspector.AuditRules.GzipRule = function() { WebInspector.AuditRule.call(this, "network-gzip", "Enable gzip compression"); @@ -83,6 +89,8 @@ WebInspector.AuditRules.GzipRule.prototype = { var summary = result.addChild("", true); for (var i = 0, length = resources.length; i < length; ++i) { var resource = resources[i]; + if (resource.statusCode === 304) + continue; // Do not test 304 Not Modified resources as their contents are always empty. if (this._shouldCompress(resource)) { var size = resource.resourceSize; candidateSize += size; @@ -104,8 +112,11 @@ WebInspector.AuditRules.GzipRule.prototype = { _isCompressed: function(resource) { - var encoding = resource.responseHeaders["Content-Encoding"]; - return encoding === "gzip" || encoding === "deflate"; + var encodingHeader = resource.responseHeaders["Content-Encoding"]; + if (!encodingHeader) + return false; + + return /\b(?:gzip|deflate)\b/.test(encodingHeader); }, _shouldCompress: function(resource) @@ -383,7 +394,7 @@ WebInspector.AuditRules.UnusedCssRule.prototype = { WebInspector.CSSStyleSheet.createForId(styleSheetIds[i], styleSheetCallback.bind(null, styleSheets, i == styleSheetIds.length - 1 ? evalCallback : null)); } - InspectorBackend.getAllStyles(allStylesCallback); + CSSAgent.getAllStyles(allStylesCallback); } } @@ -639,24 +650,23 @@ WebInspector.AuditRules.ImageDimensionsRule = function() WebInspector.AuditRules.ImageDimensionsRule.prototype = { doRun: function(resources, result, callback) { - function doneCallback(context) + var urlToNoDimensionCount = {}; + + function doneCallback() { - var map = context.urlToNoDimensionCount; - for (var url in map) { + for (var url in urlToNoDimensionCount) { var entry = entry || result.addChild("A width and height should be specified for all images in order to speed up page display. The following image(s) are missing a width and/or height:", true); var value = WebInspector.AuditRuleResult.linkifyDisplayName(url); - if (map[url] > 1) - value += String.sprintf(" (%d uses)", map[url]); + if (urlToNoDimensionCount[url] > 1) + value += String.sprintf(" (%d uses)", urlToNoDimensionCount[url]); entry.addChild(value); result.violationCount++; } callback(entry ? result : null); } - function imageStylesReady(imageId, context, styles) + function imageStylesReady(imageId, lastCall, styles) { - --context.imagesLeft; - const node = WebInspector.domAgent.nodeForId(imageId); var src = node.getAttribute("src"); if (!src.asParsedURL()) { @@ -672,14 +682,22 @@ WebInspector.AuditRules.ImageDimensionsRule.prototype = { const computedStyle = styles.computedStyle; if (computedStyle.getPropertyValue("position") === "absolute") { - if (!context.imagesLeft) - doneCallback(context); + if (lastCall) + doneCallback(); return; } var widthFound = "width" in styles.styleAttributes; var heightFound = "height" in styles.styleAttributes; + var inlineStyle = styles.inlineStyle; + if (inlineStyle) { + if (inlineStyle.getPropertyValue("width") !== "") + widthFound = true; + if (inlineStyle.getPropertyValue("height") !== "") + heightFound = true; + } + for (var i = styles.matchedCSSRules.length - 1; i >= 0 && !(widthFound && heightFound); --i) { var style = styles.matchedCSSRules[i].style; if (style.getPropertyValue("width") !== "") @@ -687,41 +705,25 @@ WebInspector.AuditRules.ImageDimensionsRule.prototype = { if (style.getPropertyValue("height") !== "") heightFound = true; } - + if (!widthFound || !heightFound) { - if (src in context.urlToNoDimensionCount) - ++context.urlToNoDimensionCount[src]; + if (src in urlToNoDimensionCount) + ++urlToNoDimensionCount[src]; else - context.urlToNoDimensionCount[src] = 1; + urlToNoDimensionCount[src] = 1; } - if (!context.imagesLeft) - doneCallback(context); + if (lastCall) + doneCallback(); } - function receivedImages(imageIds) + function getStyles(nodeIds) { - if (!imageIds || !imageIds.length) - return callback(null); - var context = {imagesLeft: imageIds.length, urlToNoDimensionCount: {}}; - for (var i = imageIds.length - 1; i >= 0; --i) - WebInspector.cssModel.getStylesAsync(imageIds[i], imageStylesReady.bind(this, imageIds[i], context)); - } - - function pushImageNodes() - { - const nodeIds = []; - var nodes = document.getElementsByTagName("img"); - for (var i = 0; i < nodes.length; ++i) { - if (!nodes[i].src) - continue; - var nodeId = this.getNodeId(nodes[i]); - nodeIds.push(nodeId); - } - return nodeIds; + for (var i = 0; i < nodeIds.length; ++i) + WebInspector.cssModel.getStylesAsync(nodeIds[i], imageStylesReady.bind(this, nodeIds[i], i === nodeIds.length - 1)); } - WebInspector.AuditRules.evaluateInTargetWindow(pushImageNodes, [], receivedImages); + DOMAgent.querySelectorAll(0, "img[src]", true, getStyles); } } diff --git a/Source/WebCore/inspector/front-end/AuditsPanel.js b/Source/WebCore/inspector/front-end/AuditsPanel.js index 47c0b30..3144c78 100644 --- a/Source/WebCore/inspector/front-end/AuditsPanel.js +++ b/Source/WebCore/inspector/front-end/AuditsPanel.js @@ -192,7 +192,7 @@ WebInspector.AuditsPanel.prototype = { _reloadResources: function(callback) { this._pageReloadCallback = callback; - InspectorBackend.reloadPage(false); + InspectorAgent.reloadPage(false); }, _didMainResourceLoad: function() diff --git a/Source/WebCore/inspector/front-end/BreakpointManager.js b/Source/WebCore/inspector/front-end/BreakpointManager.js index 94345d5..b62820e 100644 --- a/Source/WebCore/inspector/front-end/BreakpointManager.js +++ b/Source/WebCore/inspector/front-end/BreakpointManager.js @@ -207,7 +207,7 @@ WebInspector.BreakpointManager.prototype = { } if (!this._breakpointsPushedToFrontend) { - InspectorBackend.setAllBrowserBreakpoints(this._stickyBreakpoints); + BrowserDebuggerAgent.setAllBrowserBreakpoints(this._stickyBreakpoints); this._breakpointsPushedToFrontend = true; } }, @@ -216,6 +216,9 @@ WebInspector.BreakpointManager.prototype = { { function didPushNodeByPathToFrontend(path, nodeId) { + if (!nodeId) + return; + pathToNodeId[path] = nodeId; pendingCalls -= 1; if (pendingCalls) @@ -243,7 +246,7 @@ WebInspector.BreakpointManager.prototype = { continue; pathToNodeId[path] = 0; pendingCalls += 1; - InspectorBackend.pushNodeByPathToFrontend(path, didPushNodeByPathToFrontend.bind(this, path)); + WebInspector.domAgent.pushNodeByPathToFrontend(path, didPushNodeByPathToFrontend.bind(this, path)); } if (!pendingCalls) this._domBreakpointsRestored = true; @@ -268,7 +271,7 @@ WebInspector.BreakpointManager.prototype = { WebInspector.settings.nativeBreakpoints = breakpoints; this._stickyBreakpoints[WebInspector.settings.projectId] = breakpoints; - InspectorBackend.setAllBrowserBreakpoints(this._stickyBreakpoints); + BrowserDebuggerAgent.setAllBrowserBreakpoints(this._stickyBreakpoints); }, _validateBreakpoints: function(persistentBreakpoints) @@ -331,12 +334,12 @@ WebInspector.DOMBreakpoint = function(node, type) WebInspector.DOMBreakpoint.prototype = { _enable: function() { - InspectorBackend.setDOMBreakpoint(this._nodeId, this._type); + BrowserDebuggerAgent.setDOMBreakpoint(this._nodeId, this._type); }, _disable: function() { - InspectorBackend.removeDOMBreakpoint(this._nodeId, this._type); + BrowserDebuggerAgent.removeDOMBreakpoint(this._nodeId, this._type); }, _serializeToJSON: function() @@ -354,12 +357,12 @@ WebInspector.EventListenerBreakpoint = function(eventName) WebInspector.EventListenerBreakpoint.prototype = { _enable: function() { - InspectorBackend.setEventListenerBreakpoint(this._eventName); + BrowserDebuggerAgent.setEventListenerBreakpoint(this._eventName); }, _disable: function() { - InspectorBackend.removeEventListenerBreakpoint(this._eventName); + BrowserDebuggerAgent.removeEventListenerBreakpoint(this._eventName); }, _serializeToJSON: function() @@ -377,12 +380,12 @@ WebInspector.XHRBreakpoint = function(url) WebInspector.XHRBreakpoint.prototype = { _enable: function() { - InspectorBackend.setXHRBreakpoint(this._url); + BrowserDebuggerAgent.setXHRBreakpoint(this._url); }, _disable: function() { - InspectorBackend.removeXHRBreakpoint(this._url); + BrowserDebuggerAgent.removeXHRBreakpoint(this._url); }, _serializeToJSON: function() @@ -476,7 +479,34 @@ WebInspector.DOMBreakpointView.prototype = { populateStatusMessageElement: function(element, eventData) { + if (this._type === WebInspector.DOMBreakpointTypes.SubtreeModified) { + var targetNodeObject = WebInspector.RemoteObject.fromPayload(eventData.targetNode); + targetNodeObject.pushNodeToFrontend(decorateNode.bind(this)); + function decorateNode(targetNodeId) + { + if (!targetNodeId) + return; + + RuntimeAgent.releaseObject(eventData.targetNode); + var targetNode = WebInspector.panels.elements.linkifyNodeById(targetNodeId); + if (eventData.insertion) { + if (targetNodeId !== this._nodeId) + this._format(element, "Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s.", targetNode); + else + this._format(element, "Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node."); + } else + this._format(element, "Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.", targetNode); + } + } else + this._format(element, "Paused on a \"%s\" breakpoint set on %s."); + }, + + _format: function(element, message, extraSubstitution) + { var substitutions = [WebInspector.domBreakpointTypeLabel(this._type), WebInspector.panels.elements.linkifyNodeById(this._nodeId)]; + if (extraSubstitution) + substitutions.push(extraSubstitution); + var formatters = { s: function(substitution) { @@ -489,17 +519,7 @@ WebInspector.DOMBreakpointView.prototype = { b = document.createTextNode(b); element.appendChild(b); } - if (this._type === WebInspector.DOMBreakpointTypes.SubtreeModified) { - var targetNode = WebInspector.panels.elements.linkifyNodeById(eventData.targetNodeId); - if (eventData.insertion) { - if (eventData.targetNodeId !== this._nodeId) - WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s.", substitutions.concat(targetNode), formatters, "", append); - else - WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node.", substitutions, formatters, "", append); - } else - WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.", substitutions.concat(targetNode), formatters, "", append); - } else - WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s.", substitutions, formatters, "", append); + WebInspector.formatLocalized(message, substitutions, formatters, "", append); }, _onRemove: function() diff --git a/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js b/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js index 0a47bf8..0c46463 100644 --- a/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js +++ b/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js @@ -143,10 +143,10 @@ WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { _debuggerPaused: function(event) { - var breakpointId = this._breakpointIdForDebuggerPausedEvent(event.data); - if (!breakpointId) + var breakpoint = event.data.breakpoint; + if (!breakpoint) return; - var breakpointItem = this._items[breakpointId]; + var breakpointItem = this._items[breakpoint.id]; if (!breakpointItem) return; breakpointItem.element.addStyleClass("breakpoint-hit"); @@ -236,14 +236,6 @@ WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { 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; - }, - _removeBreakpoint: function(breakpointId) { WebInspector.debuggerModel.removeBreakpoint(breakpointId); diff --git a/Source/WebCore/inspector/front-end/CSSStyleModel.js b/Source/WebCore/inspector/front-end/CSSStyleModel.js index 69bd7a9..700417e 100644 --- a/Source/WebCore/inspector/front-end/CSSStyleModel.js +++ b/Source/WebCore/inspector/front-end/CSSStyleModel.js @@ -83,7 +83,7 @@ WebInspector.CSSStyleModel.prototype = { userCallback(result); } - InspectorBackend.getStylesForNode(nodeId, callback.bind(null, userCallback)); + CSSAgent.getStylesForNode(nodeId, callback.bind(null, userCallback)); }, getComputedStyleAsync: function(nodeId, userCallback) @@ -96,7 +96,7 @@ WebInspector.CSSStyleModel.prototype = { userCallback(WebInspector.CSSStyleDeclaration.parsePayload(stylePayload)); } - InspectorBackend.getComputedStyleForNode(nodeId, callback.bind(null, userCallback)); + CSSAgent.getComputedStyleForNode(nodeId, callback.bind(null, userCallback)); }, getInlineStyleAsync: function(nodeId, userCallback) @@ -109,7 +109,7 @@ WebInspector.CSSStyleModel.prototype = { userCallback(WebInspector.CSSStyleDeclaration.parsePayload(stylePayload)); } - InspectorBackend.getInlineStyleForNode(nodeId, callback.bind(null, userCallback)); + CSSAgent.getInlineStyleForNode(nodeId, callback.bind(null, userCallback)); }, setRuleSelector: function(ruleId, nodeId, newSelector, successCallback, failureCallback) @@ -127,10 +127,10 @@ WebInspector.CSSStyleModel.prototype = { if (!rulePayload) failureCallback(); else - InspectorBackend.querySelectorAll(nodeId, newSelector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload)); + DOMAgent.querySelectorAll(nodeId, newSelector, true, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload)); } - InspectorBackend.setRuleSelector(ruleId, newSelector, callback.bind(this, nodeId, successCallback, failureCallback)); + CSSAgent.setRuleSelector(ruleId, newSelector, callback.bind(this, nodeId, successCallback, failureCallback)); }, addRule: function(nodeId, selector, successCallback, failureCallback) @@ -149,10 +149,10 @@ WebInspector.CSSStyleModel.prototype = { // Invalid syntax for a selector failureCallback(); } else - InspectorBackend.querySelectorAll(nodeId, selector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload)); + DOMAgent.querySelectorAll(nodeId, selector, true, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload)); } - InspectorBackend.addRule(nodeId, selector, callback.bind(this, successCallback, failureCallback, selector)); + CSSAgent.addRule(nodeId, selector, callback.bind(this, successCallback, failureCallback, selector)); }, _styleSheetChanged: function(styleSheetId, majorChange) @@ -166,7 +166,7 @@ WebInspector.CSSStyleModel.prototype = { if (resource && resource.type === WebInspector.Resource.Type.Stylesheet) resource.setContent(content, this._onRevert.bind(this, styleSheetId)); } - InspectorBackend.getStyleSheetText(styleSheetId, callback.bind(this)); + CSSAgent.getStyleSheetText(styleSheetId, callback.bind(this)); }, _onRevert: function(styleSheetId, contentToRevertTo) @@ -176,7 +176,7 @@ WebInspector.CSSStyleModel.prototype = { this._styleSheetChanged(styleSheetId, true); this.dispatchEventToListeners("stylesheet changed"); } - InspectorBackend.setStyleSheetText(styleSheetId, contentToRevertTo, callback.bind(this)); + CSSAgent.setStyleSheetText(styleSheetId, contentToRevertTo, callback.bind(this)); } } @@ -348,7 +348,7 @@ WebInspector.CSSStyleDeclaration.prototype = { } } - InspectorBackend.setPropertyText(this.id, index, name + ": " + value + ";", false, callback.bind(null, userCallback)); + CSSAgent.setPropertyText(this.id, index, name + ": " + value + ";", false, callback.bind(null, userCallback)); }, appendProperty: function(name, value, userCallback) @@ -488,7 +488,7 @@ WebInspector.CSSProperty.prototype = { throw "No ownerStyle for property"; // An index past all the properties adds a new property to the style. - InspectorBackend.setPropertyText(this.ownerStyle.id, this.index, propertyText, this.index < this.ownerStyle.pastLastSourcePropertyIndex(), callback.bind(this)); + CSSAgent.setPropertyText(this.ownerStyle.id, this.index, propertyText, this.index < this.ownerStyle.pastLastSourcePropertyIndex(), callback.bind(this)); }, setValue: function(newValue, userCallback) @@ -517,7 +517,7 @@ WebInspector.CSSProperty.prototype = { } } - InspectorBackend.toggleProperty(this.ownerStyle.id, this.index, disabled, callback.bind(this)); + CSSAgent.toggleProperty(this.ownerStyle.id, this.index, disabled, callback.bind(this)); } } @@ -548,7 +548,7 @@ WebInspector.CSSStyleSheet.createForId = function(styleSheetId, userCallback) else userCallback(new WebInspector.CSSStyleSheet(styleSheetPayload)); } - InspectorBackend.getStyleSheet(styleSheetId, callback.bind(this)); + CSSAgent.getStyleSheet(styleSheetId, callback.bind(this)); } WebInspector.CSSStyleSheet.prototype = { @@ -569,6 +569,6 @@ WebInspector.CSSStyleSheet.prototype = { } } - InspectorBackend.setStyleSheetText(this.id, newText, callback.bind(this)); + CSSAgent.setStyleSheetText(this.id, newText, callback.bind(this)); } } diff --git a/Source/WebCore/inspector/front-end/CallStackSidebarPane.js b/Source/WebCore/inspector/front-end/CallStackSidebarPane.js index 503e5f4..e1618b2 100644 --- a/Source/WebCore/inspector/front-end/CallStackSidebarPane.js +++ b/Source/WebCore/inspector/front-end/CallStackSidebarPane.js @@ -23,20 +23,20 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.CallStackSidebarPane = function() +WebInspector.CallStackSidebarPane = function(model) { WebInspector.SidebarPane.call(this, WebInspector.UIString("Call Stack")); + this._model = model; } WebInspector.CallStackSidebarPane.prototype = { - update: function(callFrames, eventType, eventData) + update: function(details) { this.bodyElement.removeChildren(); this.placards = []; - delete this._selectedCallFrame; - if (!callFrames) { + if (!details) { var infoElement = document.createElement("div"); infoElement.className = "info"; infoElement.textContent = WebInspector.UIString("Not Paused"); @@ -44,6 +44,7 @@ WebInspector.CallStackSidebarPane.prototype = { return; } + var callFrames = details.callFrames; var title; var subtitle; var script; @@ -65,12 +66,10 @@ WebInspector.CallStackSidebarPane.prototype = { else subtitle = WebInspector.UIString("(internal script)"); - if (callFrame.line > 0) { - if (subtitle) - subtitle += ":" + callFrame.line; - else - subtitle = WebInspector.UIString("line %d", callFrame.line); - } + if (subtitle) + subtitle += ":" + (callFrame.line + 1); + else + subtitle = WebInspector.UIString("line %d", callFrame.line + 1); var placard = new WebInspector.Placard(title, subtitle); placard.callFrame = callFrame; @@ -81,30 +80,20 @@ WebInspector.CallStackSidebarPane.prototype = { this.bodyElement.appendChild(placard.element); } - if (WebInspector.debuggerModel.findBreakpoint(callFrames[0].sourceID, callFrames[0].line)) + if (details.breakpoint) this._scriptBreakpointHit(); - else if (eventType === WebInspector.DebuggerEventTypes.NativeBreakpoint) - this._nativeBreakpointHit(eventData); - }, - - get selectedCallFrame() - { - return this._selectedCallFrame; + else if (details.eventType === WebInspector.DebuggerEventTypes.NativeBreakpoint) + this._nativeBreakpointHit(details.eventData); }, set selectedCallFrame(x) { - if (this._selectedCallFrame === x) - return; - - this._selectedCallFrame = x; + this._model.selectedCallFrame = x; for (var i = 0; i < this.placards.length; ++i) { var placard = this.placards[i]; - placard.selected = (placard.callFrame === this._selectedCallFrame); + placard.selected = (placard.callFrame === x); } - - this.dispatchEventToListeners("call frame selected"); }, handleShortcut: function(event) @@ -143,11 +132,11 @@ WebInspector.CallStackSidebarPane.prototype = { _selectedCallFrameIndex: function() { - if (!this._selectedCallFrame) + if (!this._model.selectedCallFrame) return -1; for (var i = 0; i < this.placards.length; ++i) { var placard = this.placards[i]; - if (placard.callFrame === this._selectedCallFrame) + if (placard.callFrame === this._model.selectedCallFrame) return i; } return -1; diff --git a/Source/WebCore/inspector/front-end/ConsoleView.js b/Source/WebCore/inspector/front-end/ConsoleView.js index 35d1ebf..f59d87f 100644 --- a/Source/WebCore/inspector/front-end/ConsoleView.js +++ b/Source/WebCore/inspector/front-end/ConsoleView.js @@ -316,7 +316,7 @@ WebInspector.ConsoleView.prototype = { requestClearMessages: function() { - InspectorBackend.clearConsoleMessages(); + ConsoleAgent.clearConsoleMessages(); }, clearMessages: function() @@ -353,21 +353,30 @@ WebInspector.ConsoleView.prototype = { if (!expressionString && !prefix) return; - var reportCompletions = this._reportCompletions.bind(this, bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix); - // Collect comma separated object properties for the completion. + this.evalInInspectedWindow(expressionString, "completion", true, evaluated.bind(this)); - var includeCommandLineAPI = (!dotNotation && !bracketNotation); - var injectedScriptAccess; - if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused) - InspectorBackend.getCompletionsOnCallFrame(WebInspector.panels.scripts.selectedCallFrameId(), expressionString, includeCommandLineAPI, reportCompletions); - else - InspectorBackend.getCompletions(expressionString, includeCommandLineAPI, reportCompletions); - }, + function evaluated(result) + { + if (!result) + return; + result.getProperties(true, false, evaluatedProperties.bind(this)); + } - _reportCompletions: function(bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix, result, isException) { - if (isException) - return; + function evaluatedProperties(properties) + { + RuntimeAgent.releaseObjectGroup(0, "completion"); + var propertyNames = []; + for (var i = 0; properties && i < properties.length; ++i) + propertyNames.push(properties[i].name); + + var includeCommandLineAPI = (!dotNotation && !bracketNotation); + if (includeCommandLineAPI) + propertyNames.splice(0, 0, "dir", "dirxml", "keys", "values", "profile", "profileEnd", "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear"); + this._reportCompletions(bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix, propertyNames); + } + }, + _reportCompletions: function(bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix, properties) { if (bracketNotation) { if (prefix.length && prefix[0] === "'") var quoteUsed = "'"; @@ -376,7 +385,7 @@ WebInspector.ConsoleView.prototype = { } var results = []; - var properties = Object.keys(result).sort(); + properties.sort(); for (var i = 0; i < properties.length; ++i) { var property = properties[i]; @@ -417,7 +426,7 @@ WebInspector.ConsoleView.prototype = { var itemAction = function () { WebInspector.settings.monitoringXHREnabled = !WebInspector.settings.monitoringXHREnabled; - InspectorBackend.setMonitoringXHREnabled(WebInspector.settings.monitoringXHREnabled); + ConsoleAgent.setMonitoringXHREnabled(WebInspector.settings.monitoringXHREnabled); }.bind(this); var contextMenu = new WebInspector.ContextMenu(); contextMenu.appendCheckboxItem(WebInspector.UIString("XMLHttpRequest logging"), itemAction, WebInspector.settings.monitoringXHREnabled) @@ -513,7 +522,7 @@ WebInspector.ConsoleView.prototype = { evalInInspectedWindow: function(expression, objectGroup, includeCommandLineAPI, callback) { if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused) { - WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, objectGroup, includeCommandLineAPI, callback); + WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, objectGroup, includeCommandLineAPI, callback); return; } @@ -526,7 +535,7 @@ WebInspector.ConsoleView.prototype = { { callback(WebInspector.RemoteObject.fromPayload(result)); } - InspectorBackend.evaluate(expression, objectGroup, includeCommandLineAPI, evalCallback); + RuntimeAgent.evaluate(expression, objectGroup, includeCommandLineAPI, evalCallback); }, _enterKeyPressed: function(event) diff --git a/Source/WebCore/inspector/front-end/CookieItemsView.js b/Source/WebCore/inspector/front-end/CookieItemsView.js index dc76b39..c8187bb 100644 --- a/Source/WebCore/inspector/front-end/CookieItemsView.js +++ b/Source/WebCore/inspector/front-end/CookieItemsView.js @@ -140,7 +140,7 @@ WebInspector.CookieItemsView.prototype = { _deleteCookie: function(cookie) { - InspectorBackend.deleteCookie(cookie.name, this._cookieDomain); + InspectorAgent.deleteCookie(cookie.name, this._cookieDomain); this._update(); }, diff --git a/Source/WebCore/inspector/front-end/DOMAgent.js b/Source/WebCore/inspector/front-end/DOMAgent.js index 3645bb9..cb28aba 100644 --- a/Source/WebCore/inspector/front-end/DOMAgent.js +++ b/Source/WebCore/inspector/front-end/DOMAgent.js @@ -223,12 +223,11 @@ WebInspector.DOMNode.prototype = { } } -WebInspector.DOMDocument = function(domAgent, defaultView, payload) +WebInspector.DOMDocument = function(domAgent, payload) { WebInspector.DOMNode.call(this, this, payload); this._listeners = {}; this._domAgent = domAgent; - this.defaultView = defaultView; } WebInspector.DOMDocument.prototype = { @@ -269,44 +268,48 @@ WebInspector.DOMDocument.prototype = { WebInspector.DOMDocument.prototype.__proto__ = WebInspector.DOMNode.prototype; - -WebInspector.DOMWindow = function(domAgent) -{ - this._domAgent = domAgent; +WebInspector.DOMAgent = function() { + this._idToDOMNode = null; + this._document = null; + InspectorBackend.registerDomainDispatcher("DOM", new WebInspector.DOMDispatcher(this)); } -WebInspector.DOMWindow.prototype = { - get document() - { - return this._domAgent.document; - }, - - get Node() +WebInspector.DOMAgent.prototype = { + requestDocument: function(callback) { - return WebInspector.DOMNode; + if (this._document) { + if (callback) + callback(this._document); + } else + this._documentUpdated(callback); }, - get Element() + pushNodeToFrontend: function(objectId, callback) { - return WebInspector.DOMNode; + function mycallback() + { + if (this._document) + DOMAgent.pushNodeToFrontend(objectId, callback); + else { + if (callback) + callback(0); + } + } + this.requestDocument(mycallback.bind(this)); }, - Object: function() - { - } -} - -WebInspector.DOMAgent = function() { - this._window = new WebInspector.DOMWindow(this); - this._idToDOMNode = null; - this.document = null; - InspectorBackend.registerDomainDispatcher("DOM", new WebInspector.DOMDispatcher(this)); -} - -WebInspector.DOMAgent.prototype = { - get domWindow() + pushNodeByPathToFrontend: function(path, callback) { - return this._window; + function mycallback() + { + if (this._document) + DOMAgent.pushNodeByPathToFrontend(path, callback); + else { + if (callback) + callback(0); + } + } + this.requestDocument(mycallback.bind(this)); }, getChildNodesAsync: function(parent, callback) @@ -319,25 +322,25 @@ WebInspector.DOMAgent.prototype = { function mycallback() { callback(parent.children); } - InspectorBackend.getChildNodes(parent.id, mycallback); + DOMAgent.getChildNodes(parent.id, mycallback); }, setAttributeAsync: function(node, name, value, callback) { var mycallback = this._didApplyDomChange.bind(this, node, callback); - InspectorBackend.setAttribute(node.id, name, value, mycallback); + DOMAgent.setAttribute(node.id, name, value, mycallback); }, removeAttributeAsync: function(node, name, callback) { var mycallback = this._didApplyDomChange.bind(this, node, callback); - InspectorBackend.removeAttribute(node.id, name, mycallback); + DOMAgent.removeAttribute(node.id, name, mycallback); }, setTextNodeValueAsync: function(node, text, callback) { var mycallback = this._didApplyDomChange.bind(this, node, callback); - InspectorBackend.setTextNodeValue(node.id, text, mycallback); + DOMAgent.setTextNodeValue(node.id, text, mycallback); }, _didApplyDomChange: function(node, callback, success) @@ -356,7 +359,7 @@ WebInspector.DOMAgent.prototype = { var node = this._idToDOMNode[nodeId]; node._setAttributesPayload(attrsArray); var event = {target: node}; - this.document._fireDomEvent("DOMAttrModified", event); + this._document._fireDomEvent("DOMAttrModified", event); }, _characterDataModified: function(nodeId, newValue) @@ -365,7 +368,7 @@ WebInspector.DOMAgent.prototype = { node._nodeValue = newValue; node.textContent = newValue; var event = { target : node }; - this.document._fireDomEvent("DOMCharacterDataModified", event); + this._document._fireDomEvent("DOMCharacterDataModified", event); }, nodeForId: function(nodeId) @@ -373,27 +376,43 @@ WebInspector.DOMAgent.prototype = { return this._idToDOMNode[nodeId]; }, + _documentUpdated: function(callback) + { + function mycallback(root) + { + this._setDocument(root); + if (callback) + callback(this._document); + } + DOMAgent.getDocument(mycallback.bind(this)); + }, + _setDocument: function(payload) { this._idToDOMNode = {}; if (payload && "id" in payload) { - this.document = new WebInspector.DOMDocument(this, this._window, payload); - this._idToDOMNode[payload.id] = this.document; - this._bindNodes(this.document.children); + this._document = new WebInspector.DOMDocument(this, payload); + this._idToDOMNode[payload.id] = this._document; + this._bindNodes(this._document.children); WebInspector.breakpointManager.restoreDOMBreakpoints(); } else - this.document = null; - WebInspector.panels.elements.setDocument(this.document); + this._document = null; + WebInspector.panels.elements.setDocument(this._document); }, _setDetachedRoot: function(payload) { - var root = new WebInspector.DOMNode(this.document, payload); + var root = new WebInspector.DOMNode(this._document, payload); this._idToDOMNode[payload.id] = root; }, _setChildNodes: function(parentId, payloads) { + if (!parentId && payloads.length) { + this._setDetachedRoot(payloads[0]); + return; + } + var parent = this._idToDOMNode[parentId]; parent._setChildrenPayload(payloads); this._bindNodes(parent.children); @@ -426,7 +445,7 @@ WebInspector.DOMAgent.prototype = { var node = parent._insertChild(prev, payload); this._idToDOMNode[node.id] = node; var event = { target : node, relatedNode : parent }; - this.document._fireDomEvent("DOMNodeInserted", event); + this._document._fireDomEvent("DOMNodeInserted", event); }, _childNodeRemoved: function(parentId, nodeId) @@ -435,7 +454,7 @@ WebInspector.DOMAgent.prototype = { var node = this._idToDOMNode[nodeId]; parent.removeChild_(node); var event = { target : node, relatedNode : parent }; - this.document._fireDomEvent("DOMNodeRemoved", event); + this._document._fireDomEvent("DOMNodeRemoved", event); delete this._idToDOMNode[nodeId]; this._removeBreakpoints(node); }, @@ -457,9 +476,9 @@ WebInspector.DOMDispatcher = function(domAgent) } WebInspector.DOMDispatcher.prototype = { - setDocument: function(payload) + documentUpdated: function() { - this._domAgent._setDocument(payload); + this._domAgent._documentUpdated(); }, attributesUpdated: function(nodeId, attrsArray) @@ -477,11 +496,6 @@ WebInspector.DOMDispatcher.prototype = { this._domAgent._setChildNodes(parentId, payloads); }, - setDetachedRoot: function(payload) - { - this._domAgent._setDetachedRoot(payload); - }, - childNodeCountUpdated: function(nodeId, newValue) { this._domAgent._childNodeCountUpdated(nodeId, newValue); @@ -495,6 +509,16 @@ WebInspector.DOMDispatcher.prototype = { childNodeRemoved: function(parentId, nodeId) { this._domAgent._childNodeRemoved(parentId, nodeId); + }, + + inspectElementRequested: function(nodeId) + { + WebInspector.updateFocusedNode(nodeId); + }, + + addNodesToSearchResult: function(nodeIds) + { + WebInspector.panels.elements.addNodesToSearchResult(nodeIds); } } @@ -511,7 +535,7 @@ WebInspector.ApplicationCacheDispatcher.getApplicationCachesAsync = function(cal callback(applicationCaches); } - InspectorBackend.getApplicationCaches(mycallback); + ApplicationCacheAgent.getApplicationCaches(mycallback); } WebInspector.ApplicationCacheDispatcher.prototype = { @@ -540,7 +564,7 @@ WebInspector.Cookies.getCookiesAsync = function(callback) callback(cookies, true); } - InspectorBackend.getCookies(mycallback); + InspectorAgent.getCookies(mycallback); } WebInspector.Cookies.buildCookiesFromString = function(rawCookieString) @@ -585,5 +609,5 @@ WebInspector.EventListeners.getEventListenersForNodeAsync = function(node, callb { if (!node) return; - InspectorBackend.getEventListenersForNode(node.id, callback); + DOMAgent.getEventListenersForNode(node.id, callback); } diff --git a/Source/WebCore/inspector/front-end/DOMStorage.js b/Source/WebCore/inspector/front-end/DOMStorage.js index d3d2226..ea24921 100644 --- a/Source/WebCore/inspector/front-end/DOMStorage.js +++ b/Source/WebCore/inspector/front-end/DOMStorage.js @@ -39,11 +39,6 @@ WebInspector.DOMStorage.prototype = { return this._id; }, - get domStorage() - { - return this._domStorage; - }, - get domain() { return this._domain; @@ -56,17 +51,17 @@ WebInspector.DOMStorage.prototype = { getEntries: function(callback) { - InspectorBackend.getDOMStorageEntries(this._id, callback); + DOMStorageAgent.getDOMStorageEntries(this._id, callback); }, setItem: function(key, value, callback) { - InspectorBackend.setDOMStorageItem(this._id, key, value, callback); + DOMStorageAgent.setDOMStorageItem(this._id, key, value, callback); }, removeItem: function(key, callback) { - InspectorBackend.removeDOMStorageItem(this._id, key, callback); + DOMStorageAgent.removeDOMStorageItem(this._id, key, callback); } } @@ -87,12 +82,6 @@ WebInspector.DOMStorageDispatcher.prototype = { WebInspector.panels.resources.addDOMStorage(domStorage); }, - selectDOMStorage: function(o) - { - WebInspector.showPanel("resources"); - WebInspector.panels.resources.selectDOMStorage(o); - }, - updateDOMStorage: function(storageId) { WebInspector.panels.resources.updateDOMStorage(storageId); diff --git a/Source/WebCore/inspector/front-end/DataGrid.js b/Source/WebCore/inspector/front-end/DataGrid.js index 45f0b55..6d54941 100644 --- a/Source/WebCore/inspector/front-end/DataGrid.js +++ b/Source/WebCore/inspector/front-end/DataGrid.js @@ -809,14 +809,14 @@ WebInspector.DataGrid.prototype = { dataGridNodeFromNode: function(target) { var rowElement = target.enclosingNodeOrSelfWithNodeName("tr"); - return rowElement._dataGridNode; + return rowElement && rowElement._dataGridNode; }, dataGridNodeFromPoint: function(x, y) { var node = this._dataTable.ownerDocument.elementFromPoint(x, y); var rowElement = node.enclosingNodeOrSelfWithNodeName("tr"); - return rowElement._dataGridNode; + return rowElement && rowElement._dataGridNode; }, _clickInHeaderCell: function(event) diff --git a/Source/WebCore/inspector/front-end/Database.js b/Source/WebCore/inspector/front-end/Database.js index faa17fa..e4bafea 100644 --- a/Source/WebCore/inspector/front-end/Database.js +++ b/Source/WebCore/inspector/front-end/Database.js @@ -81,7 +81,7 @@ WebInspector.Database.prototype = { { callback(names.sort()); } - InspectorBackend.getDatabaseTableNames(this._id, sortingCallback); + DatabaseAgent.getDatabaseTableNames(this._id, sortingCallback); }, executeSql: function(query, onSuccess, onError) @@ -94,7 +94,7 @@ WebInspector.Database.prototype = { } WebInspector.DatabaseDispatcher._callbacks[transactionId] = {"onSuccess": onSuccess, "onError": onError}; } - InspectorBackend.executeSQL(this._id, query, callback); + DatabaseAgent.executeSQL(this._id, query, callback); } } @@ -107,8 +107,6 @@ WebInspector.DatabaseDispatcher._callbacks = {}; WebInspector.DatabaseDispatcher.prototype = { addDatabase: function(payload) { - if (!WebInspector.panels.resources) - return; var database = new WebInspector.Database( payload.id, payload.domain, @@ -117,12 +115,6 @@ WebInspector.DatabaseDispatcher.prototype = { WebInspector.panels.resources.addDatabase(database); }, - selectDatabase: function(o) - { - WebInspector.showPanel("resources"); - WebInspector.panels.resources.selectDatabase(o); - }, - sqlTransactionSucceeded: function(transactionId, columnNames, values) { if (!WebInspector.DatabaseDispatcher._callbacks[transactionId]) diff --git a/Source/WebCore/inspector/front-end/DebuggerModel.js b/Source/WebCore/inspector/front-end/DebuggerModel.js index 1bf1e47..d31ff24 100644 --- a/Source/WebCore/inspector/front-end/DebuggerModel.js +++ b/Source/WebCore/inspector/front-end/DebuggerModel.js @@ -52,7 +52,7 @@ WebInspector.DebuggerModel.Events = { WebInspector.DebuggerModel.prototype = { enableDebugger: function() { - InspectorBackend.enableDebugger(); + DebuggerAgent.enable(); if (this._breakpointsPushedToBackend) return; var breakpoints = WebInspector.settings.breakpoints; @@ -68,12 +68,12 @@ WebInspector.DebuggerModel.prototype = { disableDebugger: function() { - InspectorBackend.disableDebugger(); + DebuggerAgent.disable(); }, - continueToLine: function(sourceID, lineNumber) + continueToLocation: function(sourceID, lineNumber, columnNumber) { - InspectorBackend.continueToLocation(sourceID, lineNumber, 0); + DebuggerAgent.continueToLocation(sourceID, lineNumber, columnNumber); }, setBreakpoint: function(url, lineNumber, columnNumber, condition, enabled) @@ -89,7 +89,7 @@ WebInspector.DebuggerModel.prototype = { this._saveBreakpoints(); this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointAdded, breakpoint); } - InspectorBackend.setJavaScriptBreakpoint(url, lineNumber, columnNumber, condition, enabled, didSetBreakpoint.bind(this, this._breakpointsPushedToBackend)); + DebuggerAgent.setJavaScriptBreakpoint(url, lineNumber, columnNumber, condition, enabled, didSetBreakpoint.bind(this, this._breakpointsPushedToBackend)); }, setBreakpointBySourceId: function(sourceID, lineNumber, columnNumber, condition, enabled) @@ -103,12 +103,12 @@ WebInspector.DebuggerModel.prototype = { this._breakpoints[breakpointId] = breakpoint; this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointAdded, breakpoint); } - InspectorBackend.setJavaScriptBreakpointBySourceId(sourceID, lineNumber, columnNumber, condition, enabled, didSetBreakpoint.bind(this)); + DebuggerAgent.setJavaScriptBreakpointBySourceId(sourceID, lineNumber, columnNumber, condition, enabled, didSetBreakpoint.bind(this)); }, removeBreakpoint: function(breakpointId) { - InspectorBackend.removeJavaScriptBreakpoint(breakpointId); + DebuggerAgent.removeJavaScriptBreakpoint(breakpointId); var breakpoint = this._breakpoints[breakpointId]; delete this._breakpoints[breakpointId]; this._saveBreakpoints(); @@ -173,17 +173,6 @@ WebInspector.DebuggerModel.prototype = { return breakpoints; }, - findBreakpoint: function(sourceID, lineNumber) - { - for (var id in this._breakpoints) { - var locations = this._breakpoints[id].locations; - for (var i = 0; i < locations.length; ++i) { - if (locations[i].sourceID == sourceID && locations[i].lineNumber + 1 === lineNumber) - return this._breakpoints[id]; - } - } - }, - reset: function() { this._paused = false; @@ -230,7 +219,7 @@ WebInspector.DebuggerModel.prototype = { } else WebInspector.log(newBodyOrErrorMessage, WebInspector.ConsoleMessage.MessageLevel.Warning); } - InspectorBackend.editScriptSource(sourceID, scriptSource, didEditScriptSource.bind(this)); + DebuggerAgent.editScriptSource(sourceID, scriptSource, didEditScriptSource.bind(this)); }, _updateScriptSource: function(sourceID, scriptSource) @@ -286,6 +275,7 @@ WebInspector.DebuggerModel.prototype = { { this._paused = true; this._callFrames = details.callFrames; + details.breakpoint = this._breakpointForCallFrame(details.callFrames[0]); this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, details); }, @@ -296,6 +286,23 @@ WebInspector.DebuggerModel.prototype = { this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed); }, + _breakpointForCallFrame: function(callFrame) + { + function match(location) + { + if (location.sourceID != callFrame.sourceID) + return false; + return location.lineNumber === callFrame.line && location.columnNumber === callFrame.column; + } + for (var id in this._breakpoints) { + var breakpoint = this._breakpoints[id]; + for (var i = 0; i < breakpoint.locations.length; ++i) { + if (match(breakpoint.locations[i])) + return breakpoint; + } + } + }, + _parsedScriptSource: function(sourceID, sourceURL, lineOffset, columnOffset, length, scriptWorldType) { var script = new WebInspector.Script(sourceID, sourceURL, "", lineOffset, columnOffset, length, undefined, undefined, scriptWorldType); diff --git a/Source/WebCore/inspector/front-end/DebuggerPresentationModel.js b/Source/WebCore/inspector/front-end/DebuggerPresentationModel.js new file mode 100644 index 0000000..a97db34 --- /dev/null +++ b/Source/WebCore/inspector/front-end/DebuggerPresentationModel.js @@ -0,0 +1,131 @@ +/* + * 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.DebuggerPresentationModel = function() +{ + this._breakpoints = {}; + this._sourceLocationToBreakpointId = {}; + + 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.BreakpointResolved, this._breakpointResolved, this); +} + +WebInspector.DebuggerPresentationModel.Events = { + BreakpointAdded: "breakpoint-added", + BreakpointRemoved: "breakpoint-removed" +} + +WebInspector.DebuggerPresentationModel.prototype = { + breakpointsForSourceFileId: function(sourceFileId) + { + var breakpoints = []; + for (var id in this._breakpoints) { + var breakpoint = this._breakpoints[id]; + if (breakpoint.sourceFileId === sourceFileId) + breakpoints.push(breakpoint); + } + return breakpoints; + }, + + _breakpointAdded: function(event) + { + var breakpoint = event.data; + var location = breakpoint.locations.length ? breakpoint.locations[0] : breakpoint; + var sourceLocation = this._actualLocationToSourceLocation(breakpoint.url || breakpoint.sourceID, location.lineNumber, location.columnNumber); + + var encodedSourceLocation = this._encodeSourceLocation(sourceLocation.sourceFileId, sourceLocation.lineNumber); + if (encodedSourceLocation in this._sourceLocationToBreakpointId) { + // We can't show more than one breakpoint on a single source frame line. Remove newly added breakpoint. + WebInspector.debuggerModel.removeBreakpoint(breakpoint.id); + return; + } + + var presentationBreakpoint = { + sourceFileId: sourceLocation.sourceFileId, + lineNumber: sourceLocation.lineNumber, + url: breakpoint.url, + resolved: !!breakpoint.locations.length, + condition: breakpoint.condition, + enabled: breakpoint.enabled + }; + + this._sourceLocationToBreakpointId[encodedSourceLocation] = breakpoint.id; + this._breakpoints[breakpoint.id] = presentationBreakpoint; + + this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, presentationBreakpoint); + }, + + _breakpointRemoved: function(event) + { + var breakpointId = event.data; + var breakpoint = this._breakpoints[breakpointId]; + var encodedSourceLocation = this._encodeSourceLocation(breakpoint.sourceFileId, breakpoint.lineNumber); + delete this._breakpoints[breakpointId]; + delete this._sourceLocationToBreakpointId[encodedSourceLocation]; + this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint); + }, + + _breakpointResolved: function(event) + { + var breakpoint = event.data; + this._breakpointRemoved({ data: breakpoint.id }); + this._breakpointAdded({ data: breakpoint }); + }, + + _encodeSourceLocation: function(sourceFileId, lineNumber) + { + return sourceFileId + ":" + lineNumber; + }, + + set selectedCallFrame(callFrame) + { + this._selectedCallFrame = callFrame; + if (!callFrame) + return; + + var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.sourceID); + callFrame.sourceLocation = this._actualLocationToSourceLocation(script.sourceURL || script.sourceID, callFrame.line, callFrame.column); + this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, callFrame); + }, + + get selectedCallFrame() + { + return this._selectedCallFrame; + }, + + _actualLocationToSourceLocation: function(sourceID, lineNumber, columnNumber) + { + // TODO: use source mapping to obtain source location. + return { sourceFileId: sourceID, lineNumber: lineNumber, columnNumber: columnNumber }; + } +} + +WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype; diff --git a/Source/WebCore/inspector/front-end/DetailedHeapshotGridNodes.js b/Source/WebCore/inspector/front-end/DetailedHeapshotGridNodes.js new file mode 100644 index 0000000..14ba142 --- /dev/null +++ b/Source/WebCore/inspector/front-end/DetailedHeapshotGridNodes.js @@ -0,0 +1,707 @@ +/* + * 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.HeapSnapshotGridNode = function(tree, hasChildren, populateCount) +{ + WebInspector.DataGridNode.call(this, null, hasChildren); + this._defaultPopulateCount = populateCount; + this._provider = null; + this.addEventListener("populate", this._populate, this); +} + +WebInspector.HeapSnapshotGridNode.prototype = { + createCell: function(columnIdentifier) + { + var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); + if (this._searchMatched) + cell.addStyleClass("highlight"); + return cell; + }, + + _populate: function(event) + { + WebInspector.PleaseWaitMessage.prototype.startAction(this.dataGrid.element, doPopulate.bind(this)); + + function doPopulate() + { + this._provider.sort(this.comparator()); + this._provider.first(); + this.populateChildren(); + this.removeEventListener("populate", this._populate, this); + } + }, + + populateChildren: function(provider, howMany, atIndex) + { + if (!howMany && provider) { + howMany = provider.instancesCount; + provider.resetInstancesCount(); + } + provider = provider || this._provider; + howMany = howMany || this._defaultPopulateCount; + atIndex = atIndex || this.children.length; + var haveSavedChildren = !!this._savedChildren; + if (haveSavedChildren) { + haveSavedChildren = false; + for (var c in this._savedChildren) { + haveSavedChildren = true; + break; + } + } + for ( ; howMany > 0 && provider.hasNext(); provider.next(), provider.incInstancesCount(), --howMany) { + var item = provider.item; + if (haveSavedChildren) { + var hash = this._childHashForEntity(item); + if (hash in this._savedChildren) { + this.insertChild(this._savedChildren[hash], atIndex++); + continue; + } + } + this.insertChild(this._createChildNode(provider), atIndex++); + } + if (provider.hasNext()) + this.insertChild(new WebInspector.ShowMoreDataGridNode(this.populateChildren.bind(this, provider), this._defaultPopulateCount, provider.length), atIndex++); + }, + + _saveChildren: function() + { + this._savedChildren = {}; + for (var i = 0, childrenCount = this.children.length; i < childrenCount; ++i) { + var child = this.children[i]; + if (child.expanded) + this._savedChildren[this._childHashForNode(child)] = child; + } + }, + + sort: function() + { + var comparator = this.comparator(); + WebInspector.PleaseWaitMessage.prototype.startAction(this.dataGrid.element, doSort.bind(this)); + + function doSort() + { + if (!this._provider.sort(comparator)) + return; + this._saveChildren(); + this.removeChildren(); + this._provider.first(); + this.populateChildren(this._provider); + for (var i = 0, l = this.children.length; i < l; ++i) { + var child = this.children[i]; + if (child.expanded) + child.sort(); + } + } + } +}; + +WebInspector.HeapSnapshotGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype; + +WebInspector.HeapSnapshotGenericObjectNode = function(tree, node, hasChildren, populateCount) +{ + WebInspector.HeapSnapshotGridNode.call(this, tree, hasChildren, populateCount); + this._name = node.name; + this._type = node.type; + this._shallowSize = node.selfSize; + this._retainedSize = node.retainedSize; + this._retainedSizeExact = this._shallowSize === this._retainedSize; + this.snapshotNodeId = node.id; + this.snapshotNodeIndex = node.nodeIndex; +}; + +WebInspector.HeapSnapshotGenericObjectNode.prototype = { + createCell: function(columnIdentifier) + { + var cell = columnIdentifier !== "object" ? WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier) : this._createObjectCell(); + if (this._searchMatched) + cell.addStyleClass("highlight"); + return cell; + }, + + _createObjectCell: function() + { + var cell = document.createElement("td"); + cell.className = "object-column"; + var div = document.createElement("div"); + div.className = "source-code event-properties"; + div.style.overflow = "hidden"; + var data = this.data["object"]; + if (this._prefixObjectCell) + this._prefixObjectCell(div, data); + var valueSpan = document.createElement("span"); + valueSpan.className = "value console-formatted-" + data.valueStyle; + valueSpan.textContent = data.value; + div.appendChild(valueSpan); + cell.appendChild(div); + cell.addStyleClass("disclosure"); + if (this.depth) + cell.style.setProperty("padding-left", (this.depth * this.dataGrid.indentWidth) + "px"); + return cell; + }, + + get _countPercent() + { + return this._count / this.tree.snapshot.nodesCount * 100.0; + }, + + get data() + { + var data = this._emptyData(); + + var value = this._name; + var valueStyle = "object"; + switch (this._type) { + case "string": + value = "\"" + value + "\""; + valueStyle = "string"; + break; + case "regexp": + value = "/" + value + "/"; + valueStyle = "string"; + break; + case "closure": + value = "function " + value + "()"; + valueStyle = "function"; + break; + case "number": + valueStyle = "number"; + break; + case "hidden": + valueStyle = "null"; + break; + case "array": + value += "[]"; + break; + }; + data["object"] = { valueStyle: valueStyle, value: value + " @" + this.snapshotNodeId }; + + var view = this.dataGrid.snapshotView; + data["shallowSize"] = view.showShallowSizeAsPercent ? WebInspector.UIString("%.2f%%", this._shallowSizePercent) : Number.bytesToString(this._shallowSize); + data["retainedSize"] = (this._retainedSizeExact ? "" : "\u2248") + (view.showRetainedSizeAsPercent ? WebInspector.UIString("%.2f%%", this._retainedSizePercent) : Number.bytesToString(this._retainedSize)); + + return this._enhanceData ? this._enhanceData(data) : data; + }, + + set exactRetainedSize(size) + { + this._retainedSize = size; + this._retainedSizeExact = true; + this.refresh(); + }, + + get _retainedSizePercent() + { + return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0; + }, + + get _shallowSizePercent() + { + return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0; + } +} + +WebInspector.HeapSnapshotGenericObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype; + +WebInspector.HeapSnapshotObjectNode = function(tree, edge) +{ + var node = edge.node; + var provider = this._createProvider(tree.snapshot, node.rawEdges); + WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node, !provider.isEmpty, 100); + this._referenceName = edge.name; + this._referenceType = edge.type; + this._provider = provider; +} + +WebInspector.HeapSnapshotObjectNode.prototype = { + _createChildNode: function(provider) + { + return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, provider.item); + }, + + _createProvider: function(snapshot, rawEdges) + { + var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData; + return new WebInspector.HeapSnapshotEdgesProvider( + snapshot, + rawEdges, + function(edge) { + return !edge.isInvisible + && (showHiddenData || (!edge.isHidden && !edge.node.isHidden)); + }); + }, + + _childHashForEntity: function(edge) + { + return edge.type + "#" + edge.name; + }, + + _childHashForNode: function(childNode) + { + return childNode._referenceType + "#" + childNode._referenceName; + }, + + comparator: function() + { + var sortAscending = this.dataGrid.sortOrder === "ascending"; + var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier; + var sortFields = { + object: ["!edgeName", sortAscending, "retainedSize", false], + count: ["!edgeName", true, "retainedSize", false], + shallowSize: ["selfSize", sortAscending, "!edgeName", true], + retainedSize: ["retainedSize", sortAscending, "!edgeName", true] + }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false]; + return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields); + }, + + _emptyData: function() + { + return {count:"", addedCount: "", removedCount: "", countDelta:"", addedSize: "", removedSize: "", sizeDelta: ""}; + }, + + _enhanceData: function(data) + { + var name = this._referenceName; + if (name === "") name = "(empty)"; + var nameClass = "name"; + switch (this._referenceType) { + case "context": + nameClass = "console-formatted-number"; + break; + case "internal": + case "hidden": + nameClass = "console-formatted-null"; + break; + } + data["object"].nameClass = nameClass; + data["object"].name = name; + return data; + }, + + _prefixObjectCell: function(div, data) + { + var nameSpan = document.createElement("span"); + nameSpan.className = data.nameClass; + nameSpan.textContent = data.name; + var separatorSpan = document.createElement("span"); + separatorSpan.className = "separator"; + separatorSpan.textContent = ": "; + div.appendChild(nameSpan); + div.appendChild(separatorSpan); + } +} + +WebInspector.HeapSnapshotObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype; + +WebInspector.HeapSnapshotInstanceNode = function(tree, baseSnapshot, snapshot, node) +{ + var provider = this._createProvider(baseSnapshot || snapshot, node.rawEdges); + WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node, !provider.isEmpty, 100); + this._isDeletedNode = !!baseSnapshot; + this._provider = provider; +}; + +WebInspector.HeapSnapshotInstanceNode.prototype = { + _createChildNode: function(provider) + { + return new WebInspector.HeapSnapshotObjectNode(this.dataGrid, provider.item); + }, + + _createProvider: function(snapshot, rawEdges) + { + var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData; + return new WebInspector.HeapSnapshotEdgesProvider( + snapshot, + rawEdges, + function(edge) { + return !edge.isInvisible + && (showHiddenData || (!edge.isHidden && !edge.node.isHidden)); + }); + }, + + _childHashForEntity: function(edge) + { + return edge.type + "#" + edge.name; + }, + + _childHashForNode: function(childNode) + { + return childNode._referenceType + "#" + childNode._referenceName; + }, + + comparator: function() + { + var sortAscending = this.dataGrid.sortOrder === "ascending"; + var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier; + var sortFields = { + object: ["!edgeName", sortAscending, "retainedSize", false], + count: ["!edgeName", true, "retainedSize", false], + addedSize: ["selfSize", sortAscending, "!edgeName", true], + removedSize: ["selfSize", sortAscending, "!edgeName", true], + shallowSize: ["selfSize", sortAscending, "!edgeName", true], + retainedSize: ["retainedSize", sortAscending, "!edgeName", true] + }[sortColumnIdentifier] || ["!edgeName", true, "retainedSize", false]; + return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields); + }, + + _emptyData: function() + { + return {count:"", countDelta:"", sizeDelta: ""}; + }, + + _enhanceData: function(data) + { + if (this._isDeletedNode) { + data["addedCount"] = ""; + data["addedSize"] = ""; + data["removedCount"] = "\u2022"; + data["removedSize"] = Number.bytesToString(this._shallowSize); + } else { + data["addedCount"] = "\u2022"; + data["addedSize"] = Number.bytesToString(this._shallowSize); + data["removedCount"] = ""; + data["removedSize"] = ""; + } + return data; + } +} + +WebInspector.HeapSnapshotInstanceNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype; + +WebInspector.HeapSnapshotConstructorNode = function(tree, constructor, aggregate) +{ + WebInspector.HeapSnapshotGridNode.call(this, tree, aggregate.count > 0, 100); + this._name = constructor; + this._count = aggregate.count; + this._shallowSize = aggregate.self; + this._retainedSize = aggregate.maxRet; + this._provider = this._createNodesProvider(tree.snapshot, aggregate.type, aggregate.name); +} + +WebInspector.HeapSnapshotConstructorNode.prototype = { + _createChildNode: function(provider) + { + return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, this.dataGrid.snapshot, provider.item); + }, + + _createNodesProvider: function(snapshot, nodeType, nodeName) + { + return new WebInspector.HeapSnapshotNodesProvider( + snapshot, + snapshot.allNodes, + function (node) { + return node.type === nodeType + && (nodeName === null || node.name === nodeName); + }); + }, + + comparator: function() + { + var sortAscending = this.dataGrid.sortOrder === "ascending"; + var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier; + var sortFields = { + object: ["id", sortAscending, "retainedSize", false], + count: ["id", true, "retainedSize", false], + shallowSize: ["selfSize", sortAscending, "id", true], + retainedSize: ["retainedSize", sortAscending, "id", true] + }[sortColumnIdentifier]; + return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields); + }, + + _childHashForEntity: function(node) + { + return node.id; + }, + + _childHashForNode: function(childNode) + { + return childNode.snapshotNodeId; + }, + + get data() + { + var data = {object: this._name, count: this._count}; + var view = this.dataGrid.snapshotView; + data["count"] = view.showCountAsPercent ? WebInspector.UIString("%.2f%%", this._countPercent) : this._count; + data["shallowSize"] = view.showShallowSizeAsPercent ? WebInspector.UIString("%.2f%%", this._shallowSizePercent) : Number.bytesToString(this._shallowSize); + data["retainedSize"] = "> " + (view.showRetainedSizeAsPercent ? WebInspector.UIString("%.2f%%", this._retainedSizePercent) : Number.bytesToString(this._retainedSize)); + return data; + }, + + get _countPercent() + { + return this._count / this.dataGrid.snapshot.nodesCount * 100.0; + }, + + get _retainedSizePercent() + { + return this._retainedSize / this.dataGrid.snapshot.totalSize * 100.0; + }, + + get _shallowSizePercent() + { + return this._shallowSize / this.dataGrid.snapshot.totalSize * 100.0; + } +}; + +WebInspector.HeapSnapshotConstructorNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype; + +WebInspector.HeapSnapshotIteratorsTuple = function(it1, it2) +{ + this._it1 = it1; + this._it2 = it2; +} + +WebInspector.HeapSnapshotIteratorsTuple.prototype = { + first: function() + { + this._it1.first(); + this._it2.first(); + }, + + resetInstancesCount: function() + { + this._it1.resetInstancesCount(); + this._it2.resetInstancesCount(); + }, + + sort: function(comparator) + { + this._it1.sort(comparator); + this._it2.sort(comparator); + } +}; + +WebInspector.HeapSnapshotDiffNode = function(tree, constructor, baseAggregate, aggregate) +{ + if (!baseAggregate) + baseAggregate = { count: 0, self: 0, maxRet: 0, type:aggregate.type, name:aggregate.name, idxs: [] }; + if (!aggregate) + aggregate = { count: 0, self: 0, maxRet: 0, type:baseAggregate.type, name:baseAggregate.name, idxs: [] }; + WebInspector.HeapSnapshotGridNode.call(this, tree, true, 50); + this._name = constructor; + this._calculateDiff(tree.baseSnapshot, tree.snapshot, baseAggregate.idxs, aggregate.idxs); + this._provider = this._createNodesProvider(tree.baseSnapshot, tree.snapshot, aggregate.type, aggregate.name); +} + +WebInspector.HeapSnapshotDiffNode.prototype = { + _calculateDiff: function(baseSnapshot, snapshot, baseIndexes, currentIndexes) + { + var i = 0, l = baseIndexes.length; + var j = 0, m = currentIndexes.length; + this._addedCount = 0; + this._removedCount = 0; + this._addedSize = 0; + this._removedSize = 0; + var nodeA = new WebInspector.HeapSnapshotNode(baseSnapshot); + var nodeB = new WebInspector.HeapSnapshotNode(snapshot); + nodeA.nodeIndex = baseIndexes[i]; + nodeB.nodeIndex = currentIndexes[j]; + while (i < l && j < m) { + if (nodeA.id < nodeB.id) { + this._removedCount++; + this._removedSize += nodeA.selfSize; + nodeA.nodeIndex = baseIndexes[++i]; + } else if (nodeA.id > nodeB.id) { + this._addedCount++; + this._addedSize += nodeB.selfSize; + nodeB.nodeIndex = currentIndexes[++j]; + } else { + nodeA.nodeIndex = baseIndexes[++i]; + nodeB.nodeIndex = currentIndexes[++j]; + } + } + while (i < l) { + this._removedCount++; + this._removedSize += nodeA.selfSize; + nodeA.nodeIndex = baseIndexes[++i]; + } + while (j < m) { + this._addedCount++; + this._addedSize += nodeB.selfSize; + nodeB.nodeIndex = currentIndexes[++j]; + } + this._countDelta = this._addedCount - this._removedCount; + this._sizeDelta = this._addedSize - this._removedSize; + }, + + _createChildNode: function(provider) + { + if (provider === this._provider._it1) + return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, provider.snapshot, provider.item); + else + return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, provider.snapshot, null, provider.item); + }, + + _createNodesProvider: function(baseSnapshot, snapshot, nodeType, nodeName) + { + return new WebInspector.HeapSnapshotIteratorsTuple( + createProvider(snapshot, baseSnapshot), createProvider(baseSnapshot, snapshot)); + + function createProvider(snapshot, otherSnapshot) + { + return new WebInspector.HeapSnapshotNodesProvider( + snapshot, + snapshot.allNodes, + function (node) { + return node.type === nodeType + && (nodeName === null || node.name === nodeName) + && !(node.id in otherSnapshot.idsMap); + }); + } + }, + + comparator: function() + { + var sortAscending = this.dataGrid.sortOrder === "ascending"; + var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier; + var sortFields = { + object: ["id", sortAscending, "selfSize", false], + addedCount: ["selfSize", sortAscending, "id", true], + removedCount: ["selfSize", sortAscending, "id", true], + countDelta: ["selfSize", sortAscending, "id", true], + addedSize: ["selfSize", sortAscending, "id", true], + removedSize: ["selfSize", sortAscending, "id", true], + sizeDelta: ["selfSize", sortAscending, "id", true] + }[sortColumnIdentifier]; + return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields); + }, + + populateChildren: function(provider, howMany, atIndex) + { + if (!provider && !howMany) { + WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1, this._defaultPopulateCount); + WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2, this._defaultPopulateCount); + } else if (!howMany) { + WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it1); + WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, this._provider._it2); + } else + WebInspector.HeapSnapshotGridNode.prototype.populateChildren.call(this, provider, howMany, atIndex); + }, + + _signForDelta: function(delta) + { + if (delta === 0) + return ""; + if (delta > 0) + return "+"; + else + return "\u2212"; // Math minus sign, same width as plus. + }, + + get data() + { + var data = {object: this._name}; + + data["addedCount"] = this._addedCount; + data["removedCount"] = this._removedCount; + data["countDelta"] = WebInspector.UIString("%s%d", this._signForDelta(this._countDelta), Math.abs(this._countDelta)); + data["addedSize"] = Number.bytesToString(this._addedSize); + data["removedSize"] = Number.bytesToString(this._removedSize); + data["sizeDelta"] = WebInspector.UIString("%s%s", this._signForDelta(this._sizeDelta), Number.bytesToString(Math.abs(this._sizeDelta))); + + return data; + }, + + get zeroDiff() + { + return this._addedCount === 0 && this._removedCount === 0; + } +}; + +WebInspector.HeapSnapshotDiffNode.prototype.__proto__ = WebInspector.HeapSnapshotGridNode.prototype; + +WebInspector.HeapSnapshotDominatorObjectNode = function(tree, node) +{ + var provider = this._createProvider(tree.snapshot, node.nodeIndex); + WebInspector.HeapSnapshotGenericObjectNode.call(this, tree, node, !provider.isEmpty, 25); + this._provider = provider; +}; + +WebInspector.HeapSnapshotDominatorObjectNode.prototype = { + _createChildNode: function(provider) + { + return new WebInspector.HeapSnapshotDominatorObjectNode(this.dataGrid, provider.item); + }, + + _createProvider: function(snapshot, nodeIndex) + { + var showHiddenData = WebInspector.DetailedHeapshotView.prototype.showHiddenData; + return new WebInspector.HeapSnapshotNodesProvider( + snapshot, + snapshot.allNodes, + function (node) { + var dominatorIndex = node.dominatorIndex(); + return dominatorIndex === nodeIndex + && dominatorIndex !== node.nodeIndex + && (showHiddenData || !node.isHidden); + }); + }, + + _childHashForEntity: function(node) + { + return node.id; + }, + + _childHashForNode: function(childNode) + { + return childNode.snapshotNodeId; + }, + + comparator: function() + { + var sortAscending = this.dataGrid.sortOrder === "ascending"; + var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier; + var sortFields = { + object: ["id", sortAscending, "retainedSize", false], + shallowSize: ["selfSize", sortAscending, "id", true], + retainedSize: ["retainedSize", sortAscending, "id", true] + }[sortColumnIdentifier]; + return WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator(sortFields); + }, + + _emptyData: function() + { + return {}; + } +}; + +WebInspector.HeapSnapshotDominatorObjectNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype; + +function MixInSnapshotNodeFunctions(sourcePrototype, targetPrototype) +{ + targetPrototype._childHashForEntity = sourcePrototype._childHashForEntity; + targetPrototype._childHashForNode = sourcePrototype._childHashForNode; + targetPrototype.comparator = sourcePrototype.comparator; + targetPrototype._createChildNode = sourcePrototype._createChildNode; + targetPrototype._createProvider = sourcePrototype._createProvider; + targetPrototype.populateChildren = sourcePrototype.populateChildren; + targetPrototype._saveChildren = sourcePrototype._saveChildren; + targetPrototype.sort = sourcePrototype.sort; +} diff --git a/Source/WebCore/inspector/front-end/DetailedHeapshotView.js b/Source/WebCore/inspector/front-end/DetailedHeapshotView.js index 5291bf2..ffce1dd 100644 --- a/Source/WebCore/inspector/front-end/DetailedHeapshotView.js +++ b/Source/WebCore/inspector/front-end/DetailedHeapshotView.js @@ -28,30 +28,879 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +WebInspector.HeapSnapshotContainmentDataGrid = function() +{ + var columns = { + object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true, sort: "ascending" }, + shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true }, + retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sortable: true } + }; + WebInspector.DataGrid.call(this, columns); + this.addEventListener("sorting changed", this.sort, this); + this._defaultPopulateCount = 100; +} + +WebInspector.HeapSnapshotContainmentDataGrid.prototype = { + setDataSource: function(snapshotView, snapshot) + { + this.snapshotView = snapshotView; + this.snapshot = snapshot; + this.snapshotNodeIndex = this.snapshot._rootNodeIndex; + this._provider = this._createProvider(snapshot, snapshot.rootNode.rawEdges); + this.sort(); + } +}; + +MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotObjectNode.prototype, WebInspector.HeapSnapshotContainmentDataGrid.prototype); +WebInspector.HeapSnapshotContainmentDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype; + +WebInspector.HeapSnapshotSortableDataGrid = function(columns) +{ + WebInspector.DataGrid.call(this, columns); + this.addEventListener("sorting changed", this.sortingChanged, this); +} + +WebInspector.HeapSnapshotSortableDataGrid.prototype = { + sortingChanged: function() + { + var sortAscending = this.sortOrder === "ascending"; + var sortColumnIdentifier = this.sortColumnIdentifier; + var sortFields = this._sortFields(sortColumnIdentifier, sortAscending); + + function SortByTwoFields(nodeA, nodeB) + { + var field1 = nodeA[sortFields[0]]; + var field2 = nodeB[sortFields[0]]; + var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); + if (!sortFields[1]) + result = -result; + if (result !== 0) + return result; + field1 = nodeA[sortFields[2]]; + field2 = nodeB[sortFields[2]]; + result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0); + if (!sortFields[3]) + result = -result; + return result; + } + + this._performSorting(SortByTwoFields); + }, + + _performSorting: function(sortFunction) + { + var children = this.children; + this.removeChildren(); + children.sort(sortFunction); + for (var i = 0, l = children.length; i < l; ++i) { + var child = children[i]; + this.appendChild(child); + if (child.expanded) + child.sort(); + } + } +}; + +WebInspector.HeapSnapshotSortableDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype; + +WebInspector.HeapSnapshotConstructorsDataGrid = function() +{ + var columns = { + object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true }, + count: { title: WebInspector.UIString("#"), width: "45px", sortable: true }, + shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true }, + retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true } + }; + WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); +} + +WebInspector.HeapSnapshotConstructorsDataGrid.prototype = { + _sortFields: function(sortColumn, sortAscending) + { + return { + object: ["_name", sortAscending, "_count", false], + count: ["_count", sortAscending, "_name", true], + shallowSize: ["_shallowSize", sortAscending, "_name", true], + retainedSize: ["_retainedSize", sortAscending, "_name", true] + }[sortColumn]; + }, + + setDataSource: function(snapshotView, snapshot) + { + this.snapshotView = snapshotView; + this.snapshot = snapshot; + this.populateChildren(); + this.sortingChanged(); + }, + + populateChildren: function() + { + var aggregates = this.snapshot.aggregates(); + for (var constructor in aggregates) + this.appendChild(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor])); + } +}; + +WebInspector.HeapSnapshotConstructorsDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype; + +WebInspector.HeapSnapshotDiffDataGrid = function() +{ + var columns = { + object: { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true }, + // \xb1 is a "plus-minus" sign. + addedCount: { title: WebInspector.UIString("# New"), width: "72px", sortable: true, sort: "descending" }, + removedCount: { title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true }, + // \u0394 is a Greek delta letter. + countDelta: { title: "\u0394", width: "40px", sortable: true }, + addedSize: { title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true }, + removedSize: { title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true }, + sizeDelta: { title: "\u0394", width: "72px", sortable: true } + }; + WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); +} + +WebInspector.HeapSnapshotDiffDataGrid.prototype = { + _sortFields: function(sortColumn, sortAscending) + { + return { + object: ["_name", sortAscending, "_count", false], + addedCount: ["_addedCount", sortAscending, "_name", true], + removedCount: ["_removedCount", sortAscending, "_name", true], + countDelta: ["_countDelta", sortAscending, "_name", true], + addedSize: ["_addedSize", sortAscending, "_name", true], + removedSize: ["_removedSize", sortAscending, "_name", true], + sizeDelta: ["_sizeDelta", sortAscending, "_name", true] + }[sortColumn]; + }, + + setDataSource: function(snapshotView, snapshot) + { + this.snapshotView = snapshotView; + this.snapshot = snapshot; + }, + + setBaseDataSource: function(baseSnapshot) + { + this.baseSnapshot = baseSnapshot; + this.removeChildren(); + if (this.baseSnapshot !== this.snapshot) { + this.populateChildren(); + this.sortingChanged(); + } + }, + + populateChildren: function() + { + var baseClasses = this.baseSnapshot.aggregates(true); + var classes = this.snapshot.aggregates(true); + for (var clss in baseClasses) { + var node = new WebInspector.HeapSnapshotDiffNode(this, clss, baseClasses[clss], classes[clss]); + if (!node.zeroDiff) + this.appendChild(node); + } + for (clss in classes) { + if (!(clss in baseClasses)) { + var node = new WebInspector.HeapSnapshotDiffNode(this, clss, null, classes[clss]); + if (!node.zeroDiff) + this.appendChild(node); + } + } + } +}; + +WebInspector.HeapSnapshotDiffDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype; + +WebInspector.HeapSnapshotDominatorsDataGrid = function() +{ + var columns = { + object: { title: WebInspector.UIString("Object"), disclosure: true, sortable: true }, + shallowSize: { title: WebInspector.UIString("Shallow Size"), width: "90px", sortable: true }, + retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true } + }; + WebInspector.DataGrid.call(this, columns); + this.addEventListener("sorting changed", this.sort, this); + this._defaultPopulateCount = 25; +} + +WebInspector.HeapSnapshotDominatorsDataGrid.prototype = { + setDataSource: function(snapshotView, snapshot) + { + this.snapshotView = snapshotView; + this.snapshot = snapshot; + this.snapshotNodeIndex = this.snapshot._rootNodeIndex; + this._provider = this._createProvider(snapshot, this.snapshotNodeIndex); + this.sort(); + } +}; + +MixInSnapshotNodeFunctions(WebInspector.HeapSnapshotDominatorObjectNode.prototype, WebInspector.HeapSnapshotDominatorsDataGrid.prototype); +WebInspector.HeapSnapshotDominatorsDataGrid.prototype.__proto__ = WebInspector.DataGrid.prototype; + +WebInspector.HeapSnapshotRetainingPathsList = function() +{ + var columns = { + path: { title: WebInspector.UIString("Retaining path"), sortable: true }, + len: { title: WebInspector.UIString("Length"), width: "90px", sortable: true, sort: "ascending" } + }; + WebInspector.HeapSnapshotSortableDataGrid.call(this, columns); +} + +WebInspector.HeapSnapshotRetainingPathsList.prototype = { + _sortFields: function(sortColumn, sortAscending) + { + return { + path: ["path", sortAscending, "len", true], + len: ["len", sortAscending, "path", true] + }[sortColumn]; + }, + + setDataSource: function(snapshotView, snapshot, nodeIndex, prefix) + { + this.snapshotView = snapshotView; + this._prefix = prefix; + + if (this.pathFinder) + this.searchCancelled(); + + this.pathFinder = new WebInspector.HeapSnapshotPathFinder(snapshot, nodeIndex); + + this.removeChildren(); + + this._counter = 0; + this.showNext(10); + }, + + showNext: function(pathsCount) + { + WebInspector.PleaseWaitMessage.prototype.show(this.element, this.searchCancelled.bind(this, pathsCount)); + window.setTimeout(startSearching.bind(this), 500); + + function startSearching() + { + if (this._cancel !== this.pathFinder) { + if (this._counter < pathsCount) { + var result = this.pathFinder.findNext(); + if (result === null) { + WebInspector.PleaseWaitMessage.prototype.hide(); + if (!this.children.length) + this.appendChild(new WebInspector.DataGridNode({path:WebInspector.UIString("This object is either only accessible via hidden properties, or current path search depth isn't enough."), len:""}, false)); + return; + } else if (result !== false) { + if (this._prefix) + result.path = this._prefix + result.path; + this.appendChild(new WebInspector.DataGridNode(result, false)); + ++this._counter; + } + window.setTimeout(startSearching.bind(this), 0); + return; + } else + this.searchCancelled.call(this, pathsCount); + } + this._cancel = false; + } + }, + + searchCancelled: function(pathsCount) + { + WebInspector.PleaseWaitMessage.prototype.hide(); + this._counter = 0; + this._cancel = this.pathFinder; + if (pathsCount) { + this.appendChild(new WebInspector.ShowMoreDataGridNode(this.showNext.bind(this), pathsCount)); + this.sortingChanged(); + } + }, + + _performSorting: function(sortFunction) + { + function DataExtractorWrapper(nodeA, nodeB) + { + return sortFunction(nodeA.data, nodeB.data); + } + + this.sortNodes(DataExtractorWrapper); + } +}; + +WebInspector.HeapSnapshotRetainingPathsList.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype; + WebInspector.DetailedHeapshotView = function(parent, profile) { WebInspector.View.call(this); - this.element.addStyleClass("heap-snapshot-view"); + this.element.addStyleClass("detailed-heapshot-view"); this.parent = parent; - this.profile = profile; + this.parent.addEventListener("profile added", this._updateBaseOptions, this); + + this.showCountAsPercent = false; + this.showShallowSizeAsPercent = false; + this.showRetainedSizeAsPercent = false; + + this.containmentView = new WebInspector.View(); + this.containmentView.element.addStyleClass("view"); + this.containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(); + this.containmentDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true); + this.containmentDataGrid.element.addEventListener("dblclick", this._dblClickInContainmentGrid.bind(this), true); + this.containmentView.element.appendChild(this.containmentDataGrid.element); + this.element.appendChild(this.containmentView.element); + + this.constructorsView = new WebInspector.View(); + this.constructorsView.element.addStyleClass("view"); + this.constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(); + this.constructorsDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true); + this.constructorsDataGrid.element.addEventListener("dblclick", this._dblClickInContainmentGrid.bind(this), true); + this.constructorsView.element.appendChild(this.constructorsDataGrid.element); + this.element.appendChild(this.constructorsView.element); + + this.diffView = new WebInspector.View(); + this.diffView.element.addStyleClass("view"); + this.diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(); + this.diffDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true); + this.diffDataGrid.element.addEventListener("dblclick", this._dblClickInContainmentGrid.bind(this), true); + this.diffView.element.appendChild(this.diffDataGrid.element); + this.element.appendChild(this.diffView.element); + + this.dominatorView = new WebInspector.View(); + this.dominatorView.element.addStyleClass("view"); + this.dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid(); + this.dominatorDataGrid.element.addEventListener("click", this._mouseClickInContainmentGrid.bind(this), true); + this.dominatorDataGrid.element.addEventListener("dblclick", this._dblClickInContainmentGrid.bind(this), true); + this.dominatorView.element.appendChild(this.dominatorDataGrid.element); + this.element.appendChild(this.dominatorView.element); + + var retainmentView = new WebInspector.View(); + retainmentView.element.addStyleClass("view retaining-paths-view"); + var retainingPathsTitleDiv = document.createElement("div"); + retainingPathsTitleDiv.className = "title"; + var retainingPathsTitle = document.createElement("span"); + retainingPathsTitle.textContent = WebInspector.UIString("Retaining paths of the selected object"); + retainingPathsTitleDiv.appendChild(retainingPathsTitle); + retainmentView.element.appendChild(retainingPathsTitleDiv); + this.retainmentDataGrid = new WebInspector.HeapSnapshotRetainingPathsList(); + retainmentView.element.appendChild(this.retainmentDataGrid.element); + retainmentView.visible = true; + this.element.appendChild(retainmentView.element); + + this.dataGrid = this.constructorsDataGrid; + this.currentView = this.constructorsView; + + this.viewSelectElement = document.createElement("select"); + this.viewSelectElement.className = "status-bar-item"; + this.viewSelectElement.addEventListener("change", this._changeView.bind(this), false); + + var classesViewOption = document.createElement("option"); + classesViewOption.label = WebInspector.UIString("Summary"); + var diffViewOption = document.createElement("option"); + diffViewOption.label = WebInspector.UIString("Comparison"); + var containmentViewOption = document.createElement("option"); + containmentViewOption.label = WebInspector.UIString("Containment"); + var dominatorsViewOption = document.createElement("option"); + dominatorsViewOption.label = WebInspector.UIString("Dominators"); + this.viewSelectElement.appendChild(classesViewOption); + this.viewSelectElement.appendChild(diffViewOption); + this.viewSelectElement.appendChild(containmentViewOption); + this.viewSelectElement.appendChild(dominatorsViewOption); + this.views = ["Summary", "Comparison", "Containment", "Dominators"]; + this.views.current = 0; + + this._profileUid = profile.uid; + + this.baseSelectElement = document.createElement("select"); + this.baseSelectElement.className = "status-bar-item hidden"; + this.baseSelectElement.addEventListener("change", this._changeBase.bind(this), false); + this._updateBaseOptions(); + + this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item status-bar-item"); + this.percentButton.addEventListener("click", this._percentClicked.bind(this), false); + this.helpButton = new WebInspector.StatusBarButton("", "heapshot-help-status-bar-item status-bar-item"); + this.helpButton.addEventListener("click", this._helpClicked.bind(this), false); + + this._loadProfile(this._profileUid, profileCallback.bind(this)); + + function profileCallback(profile) + { + var list = this._profiles(); + var profileIndex; + for (var i = 0; i < list.length; ++i) + if (list[i].uid === profile.uid) { + profileIndex = i; + break; + } + if (profileIndex > 0) + this.baseSelectElement.selectedIndex = profileIndex - 1; + else + this.baseSelectElement.selectedIndex = profileIndex; + this.dataGrid.setDataSource(this, this.profileWrapper); + this._updatePercentButton(); + } } WebInspector.DetailedHeapshotView.prototype = { + dispose: function() + { + if (this._profileWrapper) + this._profileWrapper.dispose(); + if (this._baseProfileWrapper) + this._baseProfileWrapper.dispose(); + }, + + get statusBarItems() + { + return [this.viewSelectElement, this.baseSelectElement, this.percentButton.element, this.helpButton.element]; + }, + get profile() { - return this._profile; + return this.parent.getProfile(WebInspector.HeapSnapshotProfileType.TypeId, this._profileUid); }, - set profile(profile) + get profileWrapper() { - this._profile = profile; + if (!this._profileWrapper) + this._profileWrapper = new WebInspector.HeapSnapshot(this.profile); + return this._profileWrapper; + }, + + get baseProfile() + { + return this.parent.getProfile(WebInspector.HeapSnapshotProfileType.TypeId, this._baseProfileUid); + }, + + get baseProfileWrapper() + { + if (!this._baseProfileWrapper) { + if (this.baseProfile !== this.profile) + this._baseProfileWrapper = new WebInspector.HeapSnapshot(this.baseProfile); + else + this._baseProfileWrapper = this.profileWrapper; + } + return this._baseProfileWrapper; + }, + + show: function(parentElement) + { + WebInspector.View.prototype.show.call(this, parentElement); + if (!this.profile._loaded) + this._loadProfile(this._profileUid, profileCallback1.bind(this)); + else + profileCallback1.call(this, this.profile); + + function profileCallback1(profile) { + this.profileWrapper.restore(profile); + if (this.baseProfile && !this.baseProfile._loaded) + this._loadProfile(this._baseProfileUid, profileCallback2.bind(this)); + else + profileCallback2.call(this, this.baseProfile); + } + + function profileCallback2(profile) { + if (profile) + this.baseProfileWrapper.restore(profile); + this.currentView.show(); + this.dataGrid.updateWidths(); + } + }, + + hide: function() + { + WebInspector.View.prototype.hide.call(this); + this._currentSearchResultIndex = -1; + }, + + resize: function() + { + if (this.dataGrid) + this.dataGrid.updateWidths(); + }, + + refreshShowAsPercents: function() + { + this._updatePercentButton(); + this.refreshVisibleData(); + }, + + searchCanceled: function() + { + if (this._searchResults) { + for (var i = 0; i < this._searchResults.length; ++i) { + var node = this._searchResults[i].node; + delete node._searchMatched; + node.refresh(); + } + } + + delete this._searchFinishedCallback; + this._currentSearchResultIndex = -1; + this._searchResults = []; + }, + + performSearch: function(query, finishedCallback) + { + // Call searchCanceled since it will reset everything we need before doing a new search. + this.searchCanceled(); + + query = query.trim(); + + if (!query.length) + return; + if (this.currentView !== this.constructorsView && this.currentView !== this.diffView) + return; + + this._searchFinishedCallback = finishedCallback; + + function matchesByName(gridNode) { + return ("name" in gridNode) && gridNode.name.hasSubstring(query, true); + } + + function matchesById(gridNode) { + return ("snapshotNodeId" in gridNode) && gridNode.snapshotNodeId === query; + } + + var matchPredicate; + if (query.charAt(0) !== "@") + matchPredicate = matchesByName; + else { + query = parseInt(query.substring(1), 10); + matchPredicate = matchesById; + } + + function matchesQuery(gridNode) + { + delete gridNode._searchMatched; + if (matchPredicate(gridNode)) { + gridNode._searchMatched = true; + gridNode.refresh(); + return true; + } + return false; + } + + var current = this.dataGrid.children[0]; + var depth = 0; + var info = {}; + + // Restrict to type nodes and instances. + const maxDepth = 1; + + while (current) { + if (matchesQuery(current)) + this._searchResults.push({ node: current }); + current = current.traverseNextNode(false, null, (depth >= maxDepth), info); + depth += info.depthChange; + } + + finishedCallback(this, this._searchResults.length); + }, + + jumpToFirstSearchResult: function() + { + if (!this._searchResults || !this._searchResults.length) + return; + this._currentSearchResultIndex = 0; + this._jumpToSearchResult(this._currentSearchResultIndex); + }, + + jumpToLastSearchResult: function() + { + if (!this._searchResults || !this._searchResults.length) + return; + this._currentSearchResultIndex = (this._searchResults.length - 1); + this._jumpToSearchResult(this._currentSearchResultIndex); + }, + + jumpToNextSearchResult: function() + { + if (!this._searchResults || !this._searchResults.length) + return; + if (++this._currentSearchResultIndex >= this._searchResults.length) + this._currentSearchResultIndex = 0; + this._jumpToSearchResult(this._currentSearchResultIndex); + }, + + jumpToPreviousSearchResult: function() + { + if (!this._searchResults || !this._searchResults.length) + return; + if (--this._currentSearchResultIndex < 0) + this._currentSearchResultIndex = (this._searchResults.length - 1); + this._jumpToSearchResult(this._currentSearchResultIndex); + }, + + showingFirstSearchResult: function() + { + return (this._currentSearchResultIndex === 0); + }, + + showingLastSearchResult: function() + { + return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1)); + }, + + _jumpToSearchResult: function(index) + { + var searchResult = this._searchResults[index]; + if (!searchResult) + return; + + var node = searchResult.node; + node.reveal(); + node.select(); + }, + + refreshVisibleData: function() + { + var child = this.dataGrid.children[0]; + while (child) { + child.refresh(); + child = child.traverseNextNode(false, null, true); + } + }, + + _changeBase: function() + { + if (this._baseProfileUid === this._profiles()[this.baseSelectElement.selectedIndex].uid) + return; + + this._baseProfileUid = this._profiles()[this.baseSelectElement.selectedIndex].uid; + this._loadProfile(this._baseProfileUid, baseProfileLoaded.bind(this)); + + function baseProfileLoaded(profile) + { + delete this._baseProfileWrapper; + this.baseProfile._lastShown = Date.now(); + WebInspector.PleaseWaitMessage.prototype.startAction(this.currentView.element, showDiffData.bind(this)); + } + + function showDiffData() + { + this.diffDataGrid.setBaseDataSource(this.baseProfileWrapper); + } + + if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) + return; + + // The current search needs to be performed again. First negate out previous match + // count by calling the search finished callback with a negative number of matches. + // Then perform the search again with the same query and callback. + this._searchFinishedCallback(this, -this._searchResults.length); + this.performSearch(this.currentQuery, this._searchFinishedCallback); + }, + + _profiles: function() + { + return WebInspector.panels.profiles.getProfiles(WebInspector.HeapSnapshotProfileType.TypeId); + }, + + _loadProfile: function(profileUid, callback) + { + WebInspector.panels.profiles.loadHeapSnapshot(profileUid, callback); + }, + + isDetailedSnapshot: function(snapshot) + { + var s = new WebInspector.HeapSnapshot(snapshot); + for (var iter = s.rootNode.edges; iter.hasNext(); iter.next()) + if (iter.edge.node.name === "(GC roots)") + return true; + return false; + }, + + processLoadedSnapshot: function(profile, snapshot) + { + profile.nodes = snapshot.nodes; + profile.strings = snapshot.strings; + var s = new WebInspector.HeapSnapshot(profile); + profile.sideBarElement.subtitle = Number.bytesToString(s.totalSize); + }, + + _dblClickInContainmentGrid: function(event) + { + var cell = event.target.enclosingNodeOrSelfWithNodeName("td"); + if (!cell || (!cell.hasStyleClass("retainedSize-column"))) + return; + var nodeItem = event.target.enclosingNodeOrSelfWithNodeName("tr")._dataGridNode; + ProfilerAgent.getExactHeapSnapshotNodeRetainedSize(this._profileUid, nodeItem.snapshotNodeId, setExactRetainedSize); + + function setExactRetainedSize(exactSize) { + if (exactSize && exactSize != -1) + nodeItem.exactRetainedSize = exactSize; + } + }, + + _mouseClickInContainmentGrid: function(event) + { + var cell = event.target.enclosingNodeOrSelfWithNodeName("td"); + if (!cell || !(cell.hasStyleClass("object-column") || cell.hasStyleClass("shallowSize-column"))) + return; + var row = event.target.enclosingNodeOrSelfWithNodeName("tr"); + if (!row) + return; + var nodeItem = row._dataGridNode; + if (!nodeItem || nodeItem.isEventWithinDisclosureTriangle(event) || !nodeItem.snapshotNodeIndex) + return; + + this.retainmentDataGrid.setDataSource(this, nodeItem.isDeletedNode ? nodeItem.dataGrid.baseSnapshot : nodeItem.dataGrid.snapshot, nodeItem.snapshotNodeIndex, nodeItem.isDeletedNode ? this.baseSelectElement.childNodes[this.baseSelectElement.selectedIndex].label + " | " : ""); + }, + + _changeView: function(event) + { + if (!event || !this._profileUid) + return; + if (event.target.selectedIndex === this.views.current) + return; + + this.views.current = event.target.selectedIndex; + this.currentView.hide(); + if (this.views[this.views.current] === "Containment") { + this.currentView = this.containmentView; + this.dataGrid = this.containmentDataGrid; + } else if (this.views[this.views.current] === "Summary") { + this.currentView = this.constructorsView; + this.dataGrid = this.constructorsDataGrid; + } else if (this.views[this.views.current] === "Comparison") { + this.currentView = this.diffView; + this.dataGrid = this.diffDataGrid; + } else if (this.views[this.views.current] === "Dominators") { + this.currentView = this.dominatorView; + this.dataGrid = this.dominatorDataGrid; + } + this.currentView.show(); + this.refreshVisibleData(); + if (this.currentView === this.diffView) { + this.baseSelectElement.removeStyleClass("hidden"); + if (!this.dataGrid.snapshotView) { + this.dataGrid.setDataSource(this, this.profileWrapper); + this._changeBase(); + } + } else { + this.baseSelectElement.addStyleClass("hidden"); + if (!this.dataGrid.snapshotView) + WebInspector.PleaseWaitMessage.prototype.startAction(this.currentView.element, loadData.bind(this)); + } + + function loadData() + { + this.dataGrid.setDataSource(this, this.profileWrapper); + } + + if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) + return; + + // The current search needs to be performed again. First negate out previous match + // count by calling the search finished callback with a negative number of matches. + // Then perform the search again the with same query and callback. + this._searchFinishedCallback(this, -this._searchResults.length); + this.performSearch(this.currentQuery, this._searchFinishedCallback); + }, + + get _isShowingAsPercent() + { + return this.showCountAsPercent && this.showShallowSizeAsPercent && this.showRetainedSizeAsPercent; + }, + + _percentClicked: function(event) + { + var currentState = this._isShowingAsPercent; + this.showCountAsPercent = !currentState; + this.showShallowSizeAsPercent = !currentState; + this.showRetainedSizeAsPercent = !currentState; + this.refreshShowAsPercents(); + }, + + _helpClicked: function(event) + { + if (!this.helpPopover) { + var refTypes = ["a:", "console-formatted-name", WebInspector.UIString("property"), + "0:", "console-formatted-name", WebInspector.UIString("element"), + "a:", "console-formatted-number", WebInspector.UIString("context var"), + "a:", "console-formatted-null", WebInspector.UIString("system prop")]; + var objTypes = [" a ", "console-formatted-object", "Object", + "\"a\"", "console-formatted-string", "String", + "/a/", "console-formatted-string", "RegExp", + "a()", "console-formatted-function", "Function", + "a[]", "console-formatted-object", "Array", + "num", "console-formatted-number", "Number", + " a ", "console-formatted-null", "System"]; + + var contentElement = document.createElement("table"); + contentElement.className = "heapshot-help"; + var headerRow = document.createElement("tr"); + var propsHeader = document.createElement("th"); + propsHeader.textContent = WebInspector.UIString("Property types:"); + headerRow.appendChild(propsHeader); + var objsHeader = document.createElement("th"); + objsHeader.textContent = WebInspector.UIString("Object types:"); + headerRow.appendChild(objsHeader); + contentElement.appendChild(headerRow); + var len = Math.max(refTypes.length, objTypes.length); + for (var i = 0; i < len; i += 3) { + var row = document.createElement("tr"); + var refCell = document.createElement("td"); + if (refTypes[i]) + appendHelp(refTypes, i, refCell); + row.appendChild(refCell); + var objCell = document.createElement("td"); + if (objTypes[i]) + appendHelp(objTypes, i, objCell); + row.appendChild(objCell); + contentElement.appendChild(row); + } + this.helpPopover = new WebInspector.Popover(contentElement); + + function appendHelp(help, index, cell) + { + var div = document.createElement("div"); + div.className = "source-code event-properties"; + var name = document.createElement("span"); + name.textContent = help[index]; + name.className = help[index + 1]; + div.appendChild(name); + var desc = document.createElement("span"); + desc.textContent = " " + help[index + 2]; + div.appendChild(desc); + cell.appendChild(div); + } + } + if (this.helpPopover.visible) + this.helpPopover.hide(); + else + this.helpPopover.show(this.helpButton.element); + }, + + _updateBaseOptions: function() + { + var list = this._profiles(); + // We're assuming that snapshots can only be added. + if (this.baseSelectElement.length === list.length) + return; + + for (var i = this.baseSelectElement.length, n = list.length; i < n; ++i) { + var baseOption = document.createElement("option"); + var title = list[i].title; + if (!title.indexOf(UserInitiatedProfileName)) + title = WebInspector.UIString("Snapshot %d", title.substring(UserInitiatedProfileName.length + 1)); + baseOption.label = title; + this.baseSelectElement.appendChild(baseOption); + } + }, + + _updatePercentButton: function() + { + if (this._isShowingAsPercent) { + this.percentButton.title = WebInspector.UIString("Show absolute counts and sizes."); + this.percentButton.toggled = true; + } else { + this.percentButton.title = WebInspector.UIString("Show counts and sizes as percentages."); + this.percentButton.toggled = false; + } } }; WebInspector.DetailedHeapshotView.prototype.__proto__ = WebInspector.View.prototype; +WebInspector.DetailedHeapshotView.prototype.showHiddenData = true; + WebInspector.DetailedHeapshotProfileType = function() { WebInspector.ProfileType.call(this, WebInspector.HeapSnapshotProfileType.TypeId, WebInspector.UIString("HEAP SNAPSHOTS")); diff --git a/Source/WebCore/inspector/front-end/ElementsPanel.js b/Source/WebCore/inspector/front-end/ElementsPanel.js index e6af93c..246abe8 100644 --- a/Source/WebCore/inspector/front-end/ElementsPanel.js +++ b/Source/WebCore/inspector/front-end/ElementsPanel.js @@ -57,7 +57,7 @@ WebInspector.ElementsPanel = function() this.panel.updateEventListeners(); if (this._focusedDOMNode) { - InspectorBackend.addInspectedNode(this._focusedDOMNode.id); + DOMAgent.addInspectedNode(this._focusedDOMNode.id); WebInspector.extensionServer.notifyObjectSelected(this.panel.name); } }; @@ -141,6 +141,9 @@ WebInspector.ElementsPanel.prototype = { this.treeOutline.updateSelection(); if (this.recentlyModifiedNodes.length) this.updateModifiedNodes(); + + if (!this.rootDOMNode) + WebInspector.domAgent.requestDocument(); }, hide: function() @@ -211,7 +214,7 @@ WebInspector.ElementsPanel.prototype = { } if (this._selectedPathOnReset) - InspectorBackend.pushNodeByPathToFrontend(this._selectedPathOnReset, selectLastSelectedNode.bind(this)); + WebInspector.domAgent.pushNodeByPathToFrontend(this._selectedPathOnReset, selectLastSelectedNode.bind(this)); else selectNode.call(this); delete this._selectedPathOnReset; @@ -222,11 +225,11 @@ WebInspector.ElementsPanel.prototype = { delete this._searchQuery; this._hideSearchHighlights(); - WebInspector.updateSearchMatchesCount(0, this); + WebInspector.searchController.updateSearchMatchesCount(0, this); - this._currentSearchResultIndex = 0; + delete this._currentSearchResultIndex; this._searchResults = []; - InspectorBackend.searchCanceled(); + DOMAgent.searchCanceled(); }, performSearch: function(query) @@ -242,7 +245,7 @@ WebInspector.ElementsPanel.prototype = { this._matchesCountUpdateTimeout = null; this._searchQuery = query; - InspectorBackend.performSearch(whitespaceTrimmedQuery, false); + DOMAgent.performSearch(whitespaceTrimmedQuery, false); }, populateHrefContextMenu: function(contextMenu, event, anchorElement) @@ -264,14 +267,14 @@ WebInspector.ElementsPanel.prototype = { switchToAndFocus: function(node) { // Reset search restore. - WebInspector.cancelSearch(); + WebInspector.searchController.cancelSearch(); WebInspector.currentPanel = this; this.focusedDOMNode = node; }, _updateMatchesCount: function() { - WebInspector.updateSearchMatchesCount(this._searchResults.length, this); + WebInspector.searchController.updateSearchMatchesCount(this._searchResults.length, this); this._matchesCountUpdateTimeout = null; this._updatedMatchCountOnce = true; }, @@ -291,6 +294,7 @@ WebInspector.ElementsPanel.prototype = { if (!nodeIds.length) return; + var oldSearchResultIndex = this._currentSearchResultIndex; for (var i = 0; i < nodeIds.length; ++i) { var nodeId = nodeIds[i]; var node = WebInspector.domAgent.nodeForId(nodeId); @@ -300,7 +304,10 @@ WebInspector.ElementsPanel.prototype = { this._currentSearchResultIndex = 0; this._searchResults.push(node); } - this._highlightCurrentSearchResult(); + + // Avoid invocations of highlighting for every chunk of nodeIds. + if (oldSearchResultIndex !== this._currentSearchResultIndex) + this._highlightCurrentSearchResult(); this._updateMatchesCountSoon(); }, @@ -375,6 +382,9 @@ WebInspector.ElementsPanel.prototype = { this.recentlyModifiedNodes.push({node: event.target, updated: true}); if (this.visible) this._updateModifiedNodesSoon(); + + if (!this.sidebarPanes.styles.isModifyingStyle && event.target === this.focusedDOMNode) + this._styleSheetChanged(); }, _characterDataModified: function(event) @@ -1023,7 +1033,7 @@ WebInspector.ElementsPanel.prototype = { return; event.clipboardData.clearData(); event.preventDefault(); - InspectorBackend.copyNode(this.focusedDOMNode.id); + DOMAgent.copyNode(this.focusedDOMNode.id); }, rightSidebarResizerDragStart: function(event) @@ -1070,7 +1080,7 @@ WebInspector.ElementsPanel.prototype = { setSearchingForNode: function(enabled) { - InspectorBackend.setSearchingForNode(enabled, this._setSearchingForNode.bind(this)); + InspectorAgent.setSearchingForNode(enabled, this._setSearchingForNode.bind(this)); }, toggleSearchingForNode: function() diff --git a/Source/WebCore/inspector/front-end/ElementsTreeOutline.js b/Source/WebCore/inspector/front-end/ElementsTreeOutline.js index 56c3e75..7b5ff2f 100644 --- a/Source/WebCore/inspector/front-end/ElementsTreeOutline.js +++ b/Source/WebCore/inspector/front-end/ElementsTreeOutline.js @@ -329,8 +329,11 @@ WebInspector.ElementsTreeElement.prototype = { if (this._searchQuery === searchQuery) return; + if (searchQuery) + delete this._searchHighlightedHTML; // A new search query (not clear-the-current-highlighting). + this._searchQuery = searchQuery; - this.updateTitle(); + this.updateTitle(true); }, get hovered() @@ -400,17 +403,35 @@ WebInspector.ElementsTreeElement.prototype = { if (!node.nodeName || node.nodeName.toLowerCase() !== "img") return; - function setTooltip(properties) + function setTooltip(result) { - if (!properties) + if (!result || result.type !== "string") return; - if (properties.offsetHeight === properties.naturalHeight && properties.offsetWidth === properties.naturalWidth) - this.tooltip = WebInspector.UIString("%d × %d pixels", properties.offsetWidth, properties.offsetHeight); - else - this.tooltip = WebInspector.UIString("%d × %d pixels (Natural: %d × %d pixels)", properties.offsetWidth, properties.offsetHeight, properties.naturalWidth, properties.naturalHeight); + try { + var properties = JSON.parse(result.description); + var offsetWidth = properties[0]; + var offsetHeight = properties[1]; + var naturalWidth = properties[2]; + var naturalHeight = properties[3]; + if (offsetHeight === naturalHeight && offsetWidth === naturalWidth) + this.tooltip = WebInspector.UIString("%d \xd7 %d pixels", offsetWidth, offsetHeight); + else + this.tooltip = WebInspector.UIString("%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)", offsetWidth, offsetHeight, naturalWidth, naturalHeight); + } catch (e) { + console.error(e); + } + } + + function resolvedNode(objectPayload) + { + if (!objectPayload) + return; + + var object = WebInspector.RemoteObject.fromPayload(objectPayload); + object.evaluate("return '[' + this.offsetWidth + ',' + this.offsetHeight + ',' + this.naturalWidth + ',' + this.naturalHeight + ']'", setTooltip.bind(this)); } - InspectorBackend.getNodeProperties(node.id, ["naturalHeight", "naturalWidth", "offsetHeight", "offsetWidth"], setTooltip.bind(this)); + DOMAgent.resolveNode(node.id, "", resolvedNode.bind(this)); }, updateSelection: function() @@ -1154,7 +1175,7 @@ WebInspector.ElementsTreeElement.prototype = { moveToNextAttributeIfNeeded.call(newTreeItem); } - InspectorBackend.changeTagName(this.representedObject.id, newText, changeTagNameCallback); + DOMAgent.changeTagName(this.representedObject.id, newText, changeTagNameCallback); }, _textNodeEditingCommitted: function(element, newText) @@ -1198,14 +1219,20 @@ WebInspector.ElementsTreeElement.prototype = { return (tags.length === 1 ? null : tags[tags.length-1]); }, - updateTitle: function() + updateTitle: function(onlySearchQueryChanged) { // If we are editing, return early to prevent canceling the edit. // After editing is committed updateTitle will be called. if (this._editing) return; - this.titleHTML = "<span class=\"highlight\">" + this._nodeTitleInfo(WebInspector.linkifyURL).titleHTML + "</span>"; + if (onlySearchQueryChanged && this._normalHTML) + this.titleHTML = this._normalHTML; + else { + delete this._normalHTML; + this.titleHTML = "<span class=\"highlight\">" + this._nodeTitleInfo(WebInspector.linkifyURL).titleHTML + "</span>"; + } + delete this.selectionElement; this.updateSelection(); this._preventFollowingLinksOnDoubleClick(); @@ -1381,7 +1408,7 @@ WebInspector.ElementsTreeElement.prototype = { parentElement.adjustCollapsedRange(true); } - InspectorBackend.removeNode(this.representedObject.id, removeNodeCallback); + DOMAgent.removeNode(this.representedObject.id, removeNodeCallback); }, _editAsHTML: function() @@ -1408,32 +1435,41 @@ WebInspector.ElementsTreeElement.prototype = { function commitChange(value) { - InspectorBackend.setOuterHTML(node.id, value, selectNode); + DOMAgent.setOuterHTML(node.id, value, selectNode); } - InspectorBackend.getOuterHTML(node.id, this._startEditingAsHTML.bind(this, commitChange)); + DOMAgent.getOuterHTML(node.id, this._startEditingAsHTML.bind(this, commitChange)); }, _copyHTML: function() { - InspectorBackend.copyNode(this.representedObject.id); + DOMAgent.copyNode(this.representedObject.id); }, _highlightSearchResults: function() { if (!this._searchQuery) return; + if (this._searchHighlightedHTML) { + this.listItemElement.innerHTML = this._searchHighlightedHTML; + return; + } + + if (!this._normalHTML) + this._normalHTML = this.titleHTML; + var text = this.listItemElement.textContent; - var regexObject = createSearchRegex(this._searchQuery); + var regexObject = createSearchRegex(this._searchQuery, "g"); var offset = 0; var match = regexObject.exec(text); + var matchRanges = []; while (match) { - highlightSearchResult(this.listItemElement, offset + match.index, match[0].length); - offset += match.index + 1; - text = text.substring(match.index + 1); + matchRanges.push({ offset: match.index, length: match[0].length }); match = regexObject.exec(text); } + highlightSearchResults(this.listItemElement, matchRanges); + this._searchHighlightedHTML = this.listItemElement.innerHTML; } } diff --git a/Source/WebCore/inspector/front-end/ExtensionPanel.js b/Source/WebCore/inspector/front-end/ExtensionPanel.js index 144d55d..142b8c1 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) { - InspectorBackend.evaluate(expression, "extension-watch", false, this._onEvaluate.bind(this, title)); + RuntimeAgent.evaluate(expression, "extension-watch", false, 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 7d33b73..f9af7dc 100644 --- a/Source/WebCore/inspector/front-end/ExtensionServer.js +++ b/Source/WebCore/inspector/front-end/ExtensionServer.js @@ -34,6 +34,8 @@ WebInspector.ExtensionServer = function() this._handlers = {}; this._subscribers = {}; this._extraHeaders = {}; + this._resources = {}; + this._lastResourceId = 0; this._status = new WebInspector.ExtensionStatus(); this._registerHandler("addRequestHeaders", this._onAddRequestHeaders.bind(this)); @@ -108,10 +110,15 @@ WebInspector.ExtensionServer.prototype = { delete this._clientObjects[auditRun.id]; }, + resetResources: function() + { + this._resources = {}; + }, + _notifyResourceFinished: function(event) { var resource = event.data; - this._postNotification("resource-finished", resource.identifier, (new WebInspector.HAREntry(resource)).build()); + this._postNotification("resource-finished", this._resourceId(resource), (new WebInspector.HAREntry(resource)).build()); }, _postNotification: function(type, details) @@ -166,7 +173,7 @@ WebInspector.ExtensionServer.prototype = { allHeaders[name] = headers[name]; } } - InspectorBackend.setExtraHeaders(allHeaders); + NetworkAgent.setExtraHeaders(allHeaders); }, _onCreatePanel: function(message, port) @@ -176,13 +183,12 @@ WebInspector.ExtensionServer.prototype = { // shouldn't be hit unless someone is bypassing the API. if (id in this._clientObjects || id in WebInspector.panels) return this._status.E_EXISTS(id); + var panel = new WebInspector.ExtensionPanel(id, message.title, message.icon); this._clientObjects[id] = panel; - - var toolbarElement = document.getElementById("toolbar"); - var lastToolbarItem = WebInspector.panelOrder[WebInspector.panelOrder.length - 1].toolbarItem; - WebInspector.addPanelToolbarIcon(toolbarElement, panel, lastToolbarItem); WebInspector.panels[id] = panel; + WebInspector.addPanel(panel); + var iframe = this._createClientIframe(panel.element, message.url); iframe.style.height = "100%"; return this._status.OK(); @@ -255,9 +261,9 @@ WebInspector.ExtensionServer.prototype = { _onReload: function(message) { if (typeof message.userAgent === "string") - InspectorBackend.setUserAgentOverride(message.userAgent); + InspectorAgent.setUserAgentOverride(message.userAgent); - InspectorBackend.reloadPage(false); + InspectorAgent.reloadPage(false); return this._status.OK(); }, @@ -273,7 +279,7 @@ WebInspector.ExtensionServer.prototype = { this._dispatchCallback(message.requestId, port, result); } var evalExpression = "JSON.stringify(eval(unescape('" + escape(message.expression) + "')));"; - InspectorBackend.evaluate(evalExpression, "none", true, callback.bind(this)); + RuntimeAgent.evaluate(evalExpression, "", true, callback.bind(this)); }, _onRevealAndSelect: function(message) @@ -289,7 +295,7 @@ WebInspector.ExtensionServer.prototype = { var id = message.id; var resource = null; - resource = WebInspector.networkResourceById(id) || WebInspector.resourceForURL(id); + resource = this._resourceById(id) || WebInspector.resourceForURL(id); if (!resource) return this._status.E_NOTFOUND(typeof id + ": " + id); @@ -304,9 +310,10 @@ WebInspector.ExtensionServer.prototype = { _onGetHAR: function(request) { - var harLog = new WebInspector.HARLog(); - harLog.includeResourceIds = true; - return harLog.build(); + var harLog = (new WebInspector.HARLog()).build(); + for (var i = 0; i < harLog.entries.length; ++i) + harLog.entries[i]._resourceId = this._resourceId(WebInspector.networkResources[i]); + return harLog; }, _onGetResourceContent: function(message, port) @@ -319,12 +326,26 @@ WebInspector.ExtensionServer.prototype = { }; this._dispatchCallback(message.requestId, port, response); } - var resource = WebInspector.networkResourceById(message.id); + var resource = this._resourceById(message.id); if (!resource) return this._status.E_NOTFOUND(message.id); resource.requestContent(onContentAvailable.bind(this)); }, + _resourceId: function(resource) + { + if (!resource._extensionResourceId) { + resource._extensionResourceId = ++this._lastResourceId; + this._resources[resource._extensionResourceId] = resource; + } + return resource._extensionResourceId; + }, + + _resourceById: function(id) + { + return this._resources[id]; + }, + _onAddAuditCategory: function(request) { var category = new WebInspector.ExtensionAuditCategory(request.id, request.displayName, request.resultCount); diff --git a/Source/WebCore/inspector/front-end/FontView.js b/Source/WebCore/inspector/front-end/FontView.js index 82559ef..55110b7 100644 --- a/Source/WebCore/inspector/front-end/FontView.js +++ b/Source/WebCore/inspector/front-end/FontView.js @@ -33,6 +33,12 @@ WebInspector.FontView = function(resource) this.element.addStyleClass("font"); } +WebInspector.FontView._fontInnerHTML = "ABCDEFGHIJKLM<br>NOPQRSTUVWXYZ<br>abcdefghijklm<br>nopqrstuvwxyz<br>1234567890"; + +WebInspector.FontView._fontId = 0; + +WebInspector.FontView._measureFontSize = 50; + WebInspector.FontView.prototype = { hasContent: function() { @@ -44,33 +50,57 @@ WebInspector.FontView.prototype = { if (this.fontPreviewElement) return; - var uniqueFontName = "WebInspectorFontPreview" + this.resource.identifier; + var uniqueFontName = "WebInspectorFontPreview" + (++WebInspector.FontView._fontId); this.fontStyleElement = document.createElement("style"); this.fontStyleElement.textContent = "@font-face { font-family: \"" + uniqueFontName + "\"; src: url(" + this.resource.url + "); }"; document.head.appendChild(this.fontStyleElement); this.fontPreviewElement = document.createElement("div"); - this.element.appendChild(this.fontPreviewElement); - + this.fontPreviewElement.innerHTML = WebInspector.FontView._fontInnerHTML; this.fontPreviewElement.style.setProperty("font-family", uniqueFontName, null); - this.fontPreviewElement.innerHTML = "ABCDEFGHIJKLM<br>NOPQRSTUVWXYZ<br>abcdefghijklm<br>nopqrstuvwxyz<br>1234567890"; - this._lineCount = this.fontPreviewElement.getElementsByTagName("br").length + 1; + this.fontPreviewElement.style.setProperty("visibility", "hidden", null); - this.updateFontPreviewSize(); + this._dummyElement = document.createElement("div"); + this._dummyElement.style.visibility = "hidden"; + this._dummyElement.style.zIndex = "-1"; + this._dummyElement.style.display = "inline"; + this._dummyElement.style.position = "absolute"; + this._dummyElement.style.setProperty("font-family", uniqueFontName, null); + this._dummyElement.style.setProperty("font-size", WebInspector.FontView._measureFontSize + "px", null); + this._dummyElement.innerHTML = WebInspector.FontView._fontInnerHTML; + + this.element.appendChild(this.fontPreviewElement); }, show: function(parentElement) { WebInspector.ResourceView.prototype.show.call(this, parentElement); this._createContentIfNeeded(); + this.updateFontPreviewSize(); }, resize: function() { - this.updateFontPreviewSize(); - WebInspector.ResourceView.prototype.resize.call(this); + if (this._inResize) + return; + + this._inResize = true; + try { + this.updateFontPreviewSize(); + } finally { + delete this._inResize; + } + }, + + _measureElement: function() + { + this.element.appendChild(this._dummyElement); + var result = { width: this._dummyElement.offsetWidth, height: this._dummyElement.offsetHeight }; + this.element.removeChild(this._dummyElement); + + return result; }, updateFontPreviewSize: function() @@ -78,31 +108,26 @@ WebInspector.FontView.prototype = { if (!this.fontPreviewElement || !this.visible) return; - const measureFontSize = 50; - this.fontPreviewElement.style.setProperty("font-size", measureFontSize + "px", null); - this.fontPreviewElement.style.setProperty("position", "absolute", null); - this.fontPreviewElement.style.removeProperty("height"); + this.fontPreviewElement.style.removeProperty("visibility"); + var dimension = this._measureElement(); - const height = this.fontPreviewElement.offsetHeight; - const width = this.fontPreviewElement.offsetWidth; + const height = dimension.height; + const width = dimension.width; - // Subtract some padding. This should match the padding in the CSS plus room for the scrollbar. + // Subtract some padding. This should match the paddings in the CSS plus room for the scrollbar. const containerWidth = this.element.offsetWidth - 50; + const containerHeight = this.element.offsetHeight - 30; - if (!height || !width || !containerWidth) { + if (!height || !width || !containerWidth || !containerHeight) { this.fontPreviewElement.style.removeProperty("font-size"); - this.fontPreviewElement.style.removeProperty("position"); return; } - var realLineHeight = Math.floor(height / this._lineCount); - var fontSizeLineRatio = measureFontSize / realLineHeight; var widthRatio = containerWidth / width; - var finalFontSize = Math.floor(realLineHeight * widthRatio * fontSizeLineRatio) - 2; + var heightRatio = containerHeight / height; + var finalFontSize = Math.floor(WebInspector.FontView._measureFontSize * Math.min(widthRatio, heightRatio)) - 2; this.fontPreviewElement.style.setProperty("font-size", finalFontSize + "px", null); - this.fontPreviewElement.style.setProperty("height", this.fontPreviewElement.offsetHeight + "px", null); - this.fontPreviewElement.style.removeProperty("position"); } } diff --git a/Source/WebCore/inspector/front-end/HAREntry.js b/Source/WebCore/inspector/front-end/HAREntry.js index 4d690b3..b5223b6 100644 --- a/Source/WebCore/inspector/front-end/HAREntry.js +++ b/Source/WebCore/inspector/front-end/HAREntry.js @@ -189,7 +189,6 @@ WebInspector.HAREntry._toMilliseconds = function(time) WebInspector.HARLog = function() { - this.includeResourceIds = false; } WebInspector.HARLog.prototype = { @@ -230,10 +229,7 @@ WebInspector.HARLog.prototype = { _convertResource: function(resource) { - var entry = (new WebInspector.HAREntry(resource)).build(); - if (this.includeResourceIds) - entry._resourceId = resource.identifier; - return entry; + return (new WebInspector.HAREntry(resource)).build(); }, _pageEventTime: function(time) diff --git a/Source/WebCore/inspector/front-end/HeapSnapshot.js b/Source/WebCore/inspector/front-end/HeapSnapshot.js index ef450af..215f31c 100644 --- a/Source/WebCore/inspector/front-end/HeapSnapshot.js +++ b/Source/WebCore/inspector/front-end/HeapSnapshot.js @@ -80,6 +80,11 @@ WebInspector.HeapSnapshotEdge.prototype = { return this._type() === this._snapshot._edgeInternalType; }, + get isInvisible() + { + return this._type() === this._snapshot._edgeInvisibleType; + }, + get isShortcut() { return this._type() === this._snapshot._edgeShortcutType; @@ -123,6 +128,7 @@ WebInspector.HeapSnapshotEdge.prototype = { return "[" + this.name + "]"; case "internal": case "hidden": + case "invisible": return "{" + this.name + "}"; }; return "?" + this.name + "?"; @@ -377,6 +383,10 @@ WebInspector.HeapSnapshot.prototype = { this._edgeHiddenType = this._edgeTypes.indexOf("hidden"); this._edgeInternalType = this._edgeTypes.indexOf("internal"); this._edgeShortcutType = this._edgeTypes.indexOf("shortcut"); + this._edgeInvisibleType = this._edgeTypes.length; + this._edgeTypes.push("invisible"); + + this._markInvisibleEdges(); }, dispose: function() @@ -549,17 +559,44 @@ WebInspector.HeapSnapshot.prototype = { }); this._aggregatesWithIndexes = true; + }, + + _markInvisibleEdges: function() + { + // Mark hidden edges of global objects as invisible. + // FIXME: This is a temporary measure. Normally, we should + // really hide all hidden nodes. + for (var iter = this.rootNode.edges; iter.hasNext(); iter.next()) { + var edge = iter.edge; + if (!edge.isShortcut) + continue; + var node = edge.node; + var propNames = {}; + for (var innerIter = node.edges; innerIter.hasNext(); innerIter.next()) { + var globalObjEdge = innerIter.edge; + if (globalObjEdge.isShortcut) + propNames[globalObjEdge._nameOrIndex] = true; + } + for (innerIter.first(); innerIter.hasNext(); innerIter.next()) { + var globalObjEdge = innerIter.edge; + if (!globalObjEdge.isShortcut + && globalObjEdge.node.isHidden + && globalObjEdge._hasStringName + && (globalObjEdge._nameOrIndex in propNames)) + this._nodes[globalObjEdge._edges._start + globalObjEdge.edgeIndex + this._edgeTypeOffset] = this._edgeInvisibleType; + } + } } }; -WebInspector.HeapSnapshotFilteredOrderedIterator = function(snapshot, iterator, filter) +WebInspector.HeapSnapshotFilteredOrderedIterator = function(iterator, filter) { - this._snapshot = snapshot; this._filter = filter; this._iterator = iterator; this._iterationOrder = null; this._position = 0; this._lastComparator = null; + this._instancesCount = 0; } WebInspector.HeapSnapshotFilteredOrderedIterator.prototype = { @@ -588,6 +625,16 @@ WebInspector.HeapSnapshotFilteredOrderedIterator.prototype = { return this._position < this._iterationOrder.length; }, + incInstancesCount: function() + { + ++this._instancesCount; + }, + + get instancesCount() + { + return this._instancesCount; + }, + get isEmpty() { if (this._iterationOrder) @@ -623,6 +670,11 @@ WebInspector.HeapSnapshotFilteredOrderedIterator.prototype = { next: function() { ++this._position; + }, + + resetInstancesCount: function() + { + this._instancesCount = 0; } } @@ -633,7 +685,8 @@ WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator = fu WebInspector.HeapSnapshotEdgesProvider = function(snapshot, rawEdges, filter) { - WebInspector.HeapSnapshotFilteredOrderedIterator.call(this, snapshot, new WebInspector.HeapSnapshotEdgeIterator(new WebInspector.HeapSnapshotEdge(snapshot, rawEdges)), filter); + this.snapshot = snapshot; + WebInspector.HeapSnapshotFilteredOrderedIterator.call(this, new WebInspector.HeapSnapshotEdgeIterator(new WebInspector.HeapSnapshotEdge(snapshot, rawEdges)), filter); } WebInspector.HeapSnapshotEdgesProvider.prototype = { @@ -649,8 +702,8 @@ WebInspector.HeapSnapshotEdgesProvider.prototype = { var edgeA = this._iterator.item.clone(); var edgeB = edgeA.clone(); - var nodeA = new WebInspector.HeapSnapshotNode(this._snapshot); - var nodeB = new WebInspector.HeapSnapshotNode(this._snapshot); + var nodeA = new WebInspector.HeapSnapshotNode(this.snapshot); + var nodeB = new WebInspector.HeapSnapshotNode(this.snapshot); function sortByEdgeFieldName(ascending, indexA, indexB) { @@ -715,7 +768,8 @@ WebInspector.HeapSnapshotEdgesProvider.prototype.__proto__ = WebInspector.HeapSn WebInspector.HeapSnapshotNodesProvider = function(snapshot, nodes, filter) { - WebInspector.HeapSnapshotFilteredOrderedIterator.call(this, snapshot, nodes, filter); + this.snapshot = snapshot; + WebInspector.HeapSnapshotFilteredOrderedIterator.call(this, nodes, filter); } WebInspector.HeapSnapshotNodesProvider.prototype = { @@ -729,8 +783,8 @@ WebInspector.HeapSnapshotNodesProvider.prototype = { var ascending1 = comparator.ascending1; var ascending2 = comparator.ascending2; - var nodeA = new WebInspector.HeapSnapshotNode(this._snapshot); - var nodeB = new WebInspector.HeapSnapshotNode(this._snapshot); + var nodeA = new WebInspector.HeapSnapshotNode(this.snapshot); + var nodeB = new WebInspector.HeapSnapshotNode(this.snapshot); function sortByNodeField(fieldName, ascending, indexA, indexB) { @@ -832,7 +886,8 @@ WebInspector.HeapSnapshotPathFinder.prototype = { _skipEdge: function(edge) { - return (this._skipHidden && (edge.isHidden || edge.node.isHidden)) + return edge.isInvisible + || (this._skipHidden && (edge.isHidden || edge.node.isHidden)) || this._hasInPath(edge.nodeIndex); }, diff --git a/Source/WebCore/inspector/front-end/HeapSnapshotView.js b/Source/WebCore/inspector/front-end/HeapSnapshotView.js index 44b95c3..87e02f7 100644 --- a/Source/WebCore/inspector/front-end/HeapSnapshotView.js +++ b/Source/WebCore/inspector/front-end/HeapSnapshotView.js @@ -1010,7 +1010,7 @@ WebInspector.HeapSnapshotProfileType.prototype = { buttonClicked: function() { - InspectorBackend.takeHeapSnapshot(false); + ProfilerAgent.takeHeapSnapshot(false); }, get welcomeMessage() diff --git a/Source/WebCore/inspector/front-end/Images/helpButtonGlyph.png b/Source/WebCore/inspector/front-end/Images/helpButtonGlyph.png Binary files differnew file mode 100644 index 0000000..92fe59a --- /dev/null +++ b/Source/WebCore/inspector/front-end/Images/helpButtonGlyph.png diff --git a/Source/WebCore/inspector/front-end/MetricsSidebarPane.js b/Source/WebCore/inspector/front-end/MetricsSidebarPane.js index 3c0f315..14039ef 100644 --- a/Source/WebCore/inspector/front-end/MetricsSidebarPane.js +++ b/Source/WebCore/inspector/front-end/MetricsSidebarPane.js @@ -40,7 +40,7 @@ WebInspector.MetricsSidebarPane.prototype = { else node = this.node; - if (!node || !node.ownerDocument.defaultView || node.nodeType !== Node.ELEMENT_NODE) { + if (!node || node.nodeType !== Node.ELEMENT_NODE) { this.bodyElement.removeChildren(); return; } diff --git a/Source/WebCore/inspector/front-end/NetworkManager.js b/Source/WebCore/inspector/front-end/NetworkManager.js index da043fe..246b53c 100644 --- a/Source/WebCore/inspector/front-end/NetworkManager.js +++ b/Source/WebCore/inspector/front-end/NetworkManager.js @@ -33,7 +33,7 @@ WebInspector.NetworkManager = function(resourceTreeModel) WebInspector.Object.call(this); this._resourceTreeModel = resourceTreeModel; this._dispatcher = new WebInspector.NetworkDispatcher(resourceTreeModel, this); - InspectorBackend.cachedResources(this._processCachedResources.bind(this)); + NetworkAgent.enable(this._processCachedResources.bind(this)); } WebInspector.NetworkManager.EventTypes = { @@ -44,11 +44,11 @@ WebInspector.NetworkManager.EventTypes = { } WebInspector.NetworkManager.prototype = { - reset: function() + frontendReused: function() { WebInspector.panels.network.clear(); this._resourceTreeModel.reset(); - InspectorBackend.cachedResources(this._processCachedResources.bind(this)); + NetworkAgent.enable(this._processCachedResources.bind(this)); }, requestContent: function(resource, base64Encode, callback) @@ -57,7 +57,7 @@ WebInspector.NetworkManager.prototype = { { callback(success ? content : null); } - InspectorBackend.resourceContent(resource.loader.frameId, resource.url, base64Encode, callbackWrapper); + NetworkAgent.resourceContent(resource.loader.frameId, resource.url, base64Encode, callbackWrapper); }, _processCachedResources: function(mainFramePayload) diff --git a/Source/WebCore/inspector/front-end/NetworkPanel.js b/Source/WebCore/inspector/front-end/NetworkPanel.js index 085f468..06983f0 100644 --- a/Source/WebCore/inspector/front-end/NetworkPanel.js +++ b/Source/WebCore/inspector/front-end/NetworkPanel.js @@ -110,7 +110,7 @@ WebInspector.NetworkPanel.prototype = { { WebInspector.Panel.prototype.resize.call(this); this._dataGrid.updateWidths(); - this._positionSummaryBar(); + this._updateOffscreenRows(); }, updateSidebarWidth: function(width) @@ -133,41 +133,6 @@ WebInspector.NetworkPanel.prototype = { } }, - _positionSummaryBar: function() - { - // Position the total bar. - - var fillerRow = this._dataGrid.dataTableBody.lastChild; - if (this._summaryBarElement.parentElement !== this.element && fillerRow.offsetHeight > 0) { - // Glue status to bottom. - if (this._summaryBarRowNode) { - this._dataGrid.removeChild(this._summaryBarRowNode); - delete this._summaryBarRowNode; - } - this._summaryBarElement.addStyleClass("network-summary-bar-bottom"); - this.element.appendChild(this._summaryBarElement); - this._dataGrid.element.style.bottom = "20px"; - return; - } - - if (!this._summaryBarRowNode && !fillerRow.offsetHeight) { - // Glue status to table. - this._summaryBarRowNode = new WebInspector.NetworkTotalGridNode(this._summaryBarElement); - this._summaryBarElement.removeStyleClass("network-summary-bar-bottom"); - this._dataGrid.appendChild(this._summaryBarRowNode); - this._dataGrid.element.style.bottom = 0; - this._sortItems(); - } - this._updateOffscreenRows(); - }, - - _resetSummaryBar: function() - { - delete this._summaryBarRowNode; - this._summaryBarElement.parentElement.removeChild(this._summaryBarElement); - this._updateSummaryBar(); - }, - _createTimelineGrid: function() { this._timelineGrid = new WebInspector.TimelineGrid(); @@ -368,14 +333,17 @@ WebInspector.NetworkPanel.prototype = { _createSummaryBar: function() { - this._summaryBarElement = document.createElement("div"); - this._summaryBarElement.className = "network-summary-bar"; - this.containerElement.appendChild(this._summaryBarElement); + var tbody = this._dataGrid.dataTableBody; + var tfoot = document.createElement("tfoot"); + var tr = tfoot.createChild("tr", "revealed network-summary-bar"); + var td = tr.createChild("td"); + td.setAttribute("colspan", 7); + tbody.parentNode.insertBefore(tfoot, tbody); + this._summaryBarElement = td; }, _updateSummaryBar: function() { - this._positionSummaryBar(); // Grid is growing. var numRequests = this._resources.length; if (!numRequests) { @@ -387,7 +355,6 @@ WebInspector.NetworkPanel.prototype = { img.src = "Images/warningIcon.png"; this._summaryBarElement.removeChildren(); this._summaryBarElement.appendChild(img); - this._summaryBarElement.appendChild(document.createTextNode(" ")); this._summaryBarElement.appendChild(document.createTextNode( WebInspector.UIString("No requests captured. Reload the page to see detailed information on the network activity."))); return; @@ -438,7 +405,6 @@ WebInspector.NetworkPanel.prototype = { selectMultiple = true; this._filter(e.target, selectMultiple); - this._positionSummaryBar(); }, _filter: function(target, selectMultiple) @@ -478,6 +444,7 @@ WebInspector.NetworkPanel.prototype = { target.addStyleClass("selected"); this._showCategory(target.category); + this._updateOffscreenRows(); return; } @@ -655,7 +622,6 @@ WebInspector.NetworkPanel.prototype = { this.visibleView.show(this._viewsContainerElement); this._dataGrid.updateWidths(); - this._positionSummaryBar(); }, hide: function() @@ -727,6 +693,7 @@ WebInspector.NetworkPanel.prototype = { this._staleResources = []; this._sortItems(); this._updateSummaryBar(); + this._updateOffscreenRows(); this._dataGrid.updateWidths(); if (wasScrolledToLastRow) @@ -756,7 +723,6 @@ WebInspector.NetworkPanel.prototype = { this._resourceGridNodes = {}; this._dataGrid.removeChildren(); - delete this._summaryBarRowNode; this._updateDividersIfNeeded(true); // End reset timeline. @@ -765,7 +731,8 @@ WebInspector.NetworkPanel.prototype = { this._viewsContainerElement.removeChildren(); this._viewsContainerElement.appendChild(this._closeButtonElement); - this._resetSummaryBar(); + this._updateSummaryBar(); + WebInspector.extensionServer.resetResources(); }, get resources() @@ -904,7 +871,7 @@ WebInspector.NetworkPanel.prototype = { this._timelineGrid.element.removeStyleClass("small"); this._viewsContainerElement.removeStyleClass("small"); } - this._positionSummaryBar(); + this._updateOffscreenRows(); }, _getPopoverAnchor: function(element) @@ -1006,7 +973,7 @@ WebInspector.NetworkPanel.prototype = { _contextMenu: function(event) { // createBlobURL is enabled conditionally, do not expose resource export if it's not available. - if (typeof window.webkitURL.createObjectURL !== "function" || !Preferences.resourceExportEnabled) + if ((window.webkitURL && typeof window.webkitURL.createObjectURL !== "function") || !Preferences.resourceExportEnabled) return; var contextMenu = new WebInspector.ContextMenu(); @@ -1049,9 +1016,6 @@ WebInspector.NetworkPanel.prototype = { var unfilteredRowIndex = 0; for (var i = 0; i < recordsCount - 1; ++i) { var row = rows[i]; - // Don't touch summaty - quit instead. - if (this._summaryBarRowNode && row === this._summaryBarRowNode.element) - break; var dataGridNode = this._dataGrid.dataGridNodeFromNode(row); if (dataGridNode.isFilteredOut()) { @@ -1395,7 +1359,7 @@ WebInspector.NetworkDataGridNode.prototype = { _openInNewTab: function() { - InspectorBackend.openInInspectedWindow(this._resource.url); + InspectorAgent.openInInspectedWindow(this._resource.url); }, get selectable() @@ -1711,32 +1675,3 @@ WebInspector.NetworkDataGridNode.ResourcePropertyComparator = function(propertyN } WebInspector.NetworkDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype; - -WebInspector.NetworkTotalGridNode = function(element) -{ - this._summaryBarElement = element; - WebInspector.DataGridNode.call(this, {summaryRow: true}); -} - -WebInspector.NetworkTotalGridNode.prototype = { - isFilteredOut: function() - { - return false; - }, - - get selectable() - { - return false; - }, - - createCells: function() - { - var td = document.createElement("td"); - td.setAttribute("colspan", 7); - td.className = "network-summary"; - td.appendChild(this._summaryBarElement); - this._element.appendChild(td); - } -} - -WebInspector.NetworkTotalGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype; diff --git a/Source/WebCore/inspector/front-end/Panel.js b/Source/WebCore/inspector/front-end/Panel.js index 4c42a60..1b99dd4 100644 --- a/Source/WebCore/inspector/front-end/Panel.js +++ b/Source/WebCore/inspector/front-end/Panel.js @@ -46,29 +46,7 @@ WebInspector.Panel.prototype = { if (this._toolbarItem) return this._toolbarItem; - // Sample toolbar item as markup: - // <button class="toolbar-item resources toggleable"> - // <div class="toolbar-icon"></div> - // <div class="toolbar-label">Resources</div> - // </button> - - this._toolbarItem = document.createElement("button"); - this._toolbarItem.className = "toolbar-item toggleable"; - this._toolbarItem.panel = this; - - this._toolbarItem.addStyleClass(this._panelName); - - var iconElement = document.createElement("div"); - iconElement.className = "toolbar-icon"; - this._toolbarItem.appendChild(iconElement); - - if ("toolbarItemLabel" in this) { - var labelElement = document.createElement("div"); - labelElement.className = "toolbar-label"; - labelElement.textContent = this.toolbarItemLabel; - this._toolbarItem.appendChild(labelElement); - } - + this._toolbarItem = WebInspector.Toolbar.createPanelToolbarItem(this); return this._toolbarItem; }, @@ -110,6 +88,12 @@ WebInspector.Panel.prototype = { this._toolbarItem.removeStyleClass("toggled-on"); }, + reset: function() + { + this.searchCanceled(); + WebInspector.resetFocusElement(); + }, + get defaultFocusedElement() { return this.sidebarTreeElement || this.element; @@ -132,7 +116,7 @@ WebInspector.Panel.prototype = { } } - WebInspector.updateSearchMatchesCount(0, this); + WebInspector.searchController.updateSearchMatchesCount(0, this); if (this._currentSearchChunkIntervalIdentifier) { clearInterval(this._currentSearchChunkIntervalIdentifier); @@ -161,7 +145,7 @@ WebInspector.Panel.prototype = { function updateMatchesCount() { - WebInspector.updateSearchMatchesCount(this._totalSearchMatches, this); + WebInspector.searchController.updateSearchMatchesCount(this._totalSearchMatches, this); matchesCountUpdateTimeout = null; } diff --git a/Source/WebCore/inspector/front-end/PleaseWaitMessage.js b/Source/WebCore/inspector/front-end/PleaseWaitMessage.js index 54d805d..e1980a0 100644 --- a/Source/WebCore/inspector/front-end/PleaseWaitMessage.js +++ b/Source/WebCore/inspector/front-end/PleaseWaitMessage.js @@ -59,7 +59,7 @@ WebInspector.PleaseWaitMessage.prototype = { get instance() { - if (!"_instance" in WebInspector.PleaseWaitMessage.prototype) + if (!("_instance" in WebInspector.PleaseWaitMessage.prototype)) WebInspector.PleaseWaitMessage.prototype._instance = new WebInspector.PleaseWaitMessage(); return WebInspector.PleaseWaitMessage.prototype._instance; }, diff --git a/Source/WebCore/inspector/front-end/Popover.js b/Source/WebCore/inspector/front-end/Popover.js index 32535e9..f20b8c6 100644 --- a/Source/WebCore/inspector/front-end/Popover.js +++ b/Source/WebCore/inspector/front-end/Popover.js @@ -40,6 +40,7 @@ WebInspector.Popover = function(contentElement) this.contentElement = contentElement; this._contentDiv = document.createElement("div"); this._contentDiv.className = "content"; + this._visible = false; } WebInspector.Popover.prototype = { @@ -60,6 +61,7 @@ WebInspector.Popover.prototype = { this.element.appendChild(this._contentDiv); document.body.appendChild(this.element); this._positionElement(anchor, preferredWidth, preferredHeight); + this._visible = true; }, hide: function() @@ -68,6 +70,12 @@ WebInspector.Popover.prototype = { delete WebInspector.Popover._popoverElement; document.body.removeChild(this.element); } + this._visible = false; + }, + + get visible() + { + return this._visible; }, _positionElement: function(anchorElement, preferredWidth, preferredHeight) diff --git a/Source/WebCore/inspector/front-end/ProfileView.js b/Source/WebCore/inspector/front-end/ProfileView.js index c325bf7..335bf03 100644 --- a/Source/WebCore/inspector/front-end/ProfileView.js +++ b/Source/WebCore/inspector/front-end/ProfileView.js @@ -94,7 +94,7 @@ WebInspector.CPUProfileView = function(profile) self._updatePercentButton(); } - InspectorBackend.getProfile(this.profile.typeId, this.profile.uid, profileCallback); + ProfilerAgent.getProfile(this.profile.typeId, this.profile.uid, profileCallback); } WebInspector.CPUProfileView.prototype = { @@ -593,9 +593,9 @@ WebInspector.CPUProfileType.prototype = { this._recording = !this._recording; if (this._recording) - InspectorBackend.startProfiling(); + InspectorAgent.startProfiling(); else - InspectorBackend.stopProfiling(); + InspectorAgent.stopProfiling(); }, get welcomeMessage() diff --git a/Source/WebCore/inspector/front-end/ProfilesPanel.js b/Source/WebCore/inspector/front-end/ProfilesPanel.js index b87ea7f..e5fb49e 100644 --- a/Source/WebCore/inspector/front-end/ProfilesPanel.js +++ b/Source/WebCore/inspector/front-end/ProfilesPanel.js @@ -185,8 +185,19 @@ WebInspector.ProfilesPanel.prototype = { _reset: function() { - for (var i = 0; i < this._profiles.length; ++i) + WebInspector.Panel.prototype.reset.call(this); + + for (var i = 0; i < this._profiles.length; ++i) { + var view = this._profiles[i]._profileView; + if (view && ("dispose" in view)) + view.dispose(); delete this._profiles[i]._profileView; + var profile = this._profiles[i]; + if (profile.nodes) { + delete profile.nodes; + delete profile.strings; + } + } delete this.visibleView; delete this.currentQuery; @@ -215,7 +226,7 @@ WebInspector.ProfilesPanel.prototype = { _clearProfiles: function() { - InspectorBackend.clearProfiles(); + ProfilerAgent.clearProfiles(); this._reset(); }, @@ -351,7 +362,7 @@ WebInspector.ProfilesPanel.prototype = { sidebarParent.removeChild(profile._profilesTreeElement); if (!profile.isTemporary) - InspectorBackend.removeProfile(profile.typeId, profile.uid); + ProfilerAgent.removeProfile(profile.typeId, profile.uid); // No other item will be selected if there aren't any other profiles, so // make sure that view gets cleared when the last profile is removed. @@ -409,6 +420,11 @@ WebInspector.ProfilesPanel.prototype = { return !!this._profilesIdMap[this._makeKey(profile.uid, profile.typeId)]; }, + getProfile: function(typeId, uid) + { + return this._profilesIdMap[this._makeKey(uid, typeId)]; + }, + loadHeapSnapshot: function(uid, callback) { var profile = this._profilesIdMap[this._makeKey(uid, WebInspector.HeapSnapshotProfileType.TypeId)]; @@ -424,7 +440,7 @@ WebInspector.ProfilesPanel.prototype = { profile._callbacks = [callback]; profile._json = ""; profile.sideBarElement.subtitle = WebInspector.UIString("Loading…"); - InspectorBackend.getProfile(profile.typeId, profile.uid); + ProfilerAgent.getProfile(profile.typeId, profile.uid); } }, @@ -455,6 +471,12 @@ WebInspector.ProfilesPanel.prototype = { delete profile._is_loading; profile._loaded = true; profile.sideBarElement.subtitle = ""; + + if (!Preferences.detailedHeapProfiles && WebInspector.DetailedHeapshotView.prototype.isDetailedSnapshot(loadedSnapshot)) { + WebInspector.panels.profiles._enableDetailedHeapProfiles(false); + return; + } + if (!Preferences.detailedHeapProfiles) WebInspector.HeapSnapshotView.prototype.processLoadedSnapshot(profile, loadedSnapshot); else @@ -592,10 +614,10 @@ WebInspector.ProfilesPanel.prototype = { { if (this._profilerEnabled) { WebInspector.settings.profilerEnabled = false; - InspectorBackend.disableProfiler(true); + InspectorAgent.disableProfiler(true); } else { WebInspector.settings.profilerEnabled = !!optionalAlways; - InspectorBackend.enableProfiler(); + InspectorAgent.enableProfiler(); } }, @@ -612,7 +634,7 @@ WebInspector.ProfilesPanel.prototype = { this._addProfileHeader(profileHeaders[i]); } - InspectorBackend.getProfileHeaders(populateCallback.bind(this)); + ProfilerAgent.getProfileHeaders(populateCallback.bind(this)); this._profilesWereRequested = true; }, @@ -658,7 +680,7 @@ WebInspector.ProfilesPanel.prototype = { } this._addProfileHeader(this._temporaryTakingSnapshot); } - InspectorBackend.takeHeapSnapshot(detailed); + ProfilerAgent.takeHeapSnapshot(detailed); }, _reportHeapSnapshotProgress: function(done, total) @@ -668,6 +690,63 @@ WebInspector.ProfilesPanel.prototype = { if (done >= total) this._removeProfileHeader(this._temporaryTakingSnapshot); } + }, + + handleShortcut: function(event) + { + if (!Preferences.heapProfilerPresent || Preferences.detailedHeapProfiles) + return; + var combo = ["U+004C", "U+0045", "U+0041", "U+004B", "U+005A"]; // "LEAKZ" + if (this._recognizeKeyboardCombo(combo, event)) { + this._displayDetailedHeapProfilesEnabledHint(); + this._enableDetailedHeapProfiles(true); + } + }, + + _recognizeKeyboardCombo: function(combo, event) + { + var isRecognized = false; + if (!this._comboPosition) { + if (event.keyIdentifier === combo[0]) + this._comboPosition = 1; + } else if (event.keyIdentifier === combo[this._comboPosition]) { + if (++this._comboPosition === combo.length) + isRecognized = true; + } else + delete this._comboPosition; + if (this._comboPosition) + event.handled = true; + return isRecognized; + }, + + _displayDetailedHeapProfilesEnabledHint: function() + { + var message = new WebInspector.HelpScreen("Congratulations!"); + message.contentElement.addStyleClass("help-table"); + message.contentElement.textContent = "Detailed Heap snapshots are now enabled."; + message.show(); + + function hideHint() + { + message._hide(); + } + + setTimeout(hideHint, 2000); + }, + + _enableDetailedHeapProfiles: function(resetAgent) + { + if (resetAgent) + this._clearProfiles(); + else + this._reset(); + var oldProfileType = this._profileTypesByIdMap[WebInspector.HeapSnapshotProfileType.TypeId]; + var profileType = new WebInspector.DetailedHeapshotProfileType(); + profileType.treeElement = oldProfileType.treeElement; + this._profileTypesByIdMap[profileType.id] = profileType; + Preferences.detailedHeapProfiles = true; + this.hide(); + this.show(); } } diff --git a/Source/WebCore/inspector/front-end/PropertiesSidebarPane.js b/Source/WebCore/inspector/front-end/PropertiesSidebarPane.js index a1e37bc..0c314bc 100644 --- a/Source/WebCore/inspector/front-end/PropertiesSidebarPane.js +++ b/Source/WebCore/inspector/front-end/PropertiesSidebarPane.js @@ -42,15 +42,40 @@ WebInspector.PropertiesSidebarPane.prototype = { return; } - function callback(prototypes) + RuntimeAgent.releaseObjectGroup(0, "dom-selection"); + WebInspector.RemoteObject.resolveNode(node, nodeResolved.bind(this)); + + function nodeResolved(objectPayload) { + if (!objectPayload) + return; + var object = WebInspector.RemoteObject.fromPayload(objectPayload); + object.evaluate("var proto = this; result = {}; var counter = 1; while (proto) { result[counter++] = proto; proto = proto.__proto__ }; return result;", nodePrototypesReady.bind(this)); + } + + function nodePrototypesReady(objectPayload) + { + if (!objectPayload) + return; + var object = WebInspector.RemoteObject.fromPayload(objectPayload); + object.getOwnProperties(false, fillSection.bind(this)); + } + + function fillSection(prototypes) + { + if (!prototypes) + return; + var body = this.bodyElement; body.removeChildren(); this.sections = []; // Get array of prototype user-friendly names. for (var i = 0; i < prototypes.length; ++i) { - var prototype = WebInspector.RemoteObject.fromPayload(prototypes[i]); + if (!parseInt(prototypes[i].name)) + continue; + + var prototype = prototypes[i].value; var title = prototype.description; if (title.match(/Prototype$/)) title = title.replace(/Prototype$/, ""); @@ -59,7 +84,6 @@ WebInspector.PropertiesSidebarPane.prototype = { body.appendChild(section.element); } } - 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 10af2e3..4a20cf1 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); } - InspectorBackend.resolveNode(node.id, mycallback); + DOMAgent.resolveNode(node.id, "dom-selection", mycallback); } WebInspector.RemoteObject.fromPayload = function(payload) @@ -118,7 +118,7 @@ WebInspector.RemoteObject.prototype = { properties[i].value = WebInspector.RemoteObject.fromPayload(properties[i].value); callback(properties); } - InspectorBackend.getProperties(this._objectId, !!ignoreHasOwnProperty, abbreviate, remoteObjectBinder); + RuntimeAgent.getProperties(this._objectId, !!ignoreHasOwnProperty, abbreviate, remoteObjectBinder); }, setPropertyValue: function(name, value, callback) @@ -127,12 +127,20 @@ WebInspector.RemoteObject.prototype = { callback(false); return; } - InspectorBackend.setPropertyValue(this._objectId, name, value, callback); + RuntimeAgent.setPropertyValue(this._objectId, name, value, callback); }, pushNodeToFrontend: function(callback) { - InspectorBackend.pushNodeToFrontend(this._objectId, callback); + if (this._objectId) + WebInspector.domAgent.pushNodeToFrontend(this._objectId, callback); + else + callback(0); + }, + + evaluate: function(expression, callback) + { + RuntimeAgent.evaluateOn(this._objectId, expression, callback); } } diff --git a/Source/WebCore/inspector/front-end/ResourceHeadersView.js b/Source/WebCore/inspector/front-end/ResourceHeadersView.js index ee1010f..e79078a 100644 --- a/Source/WebCore/inspector/front-end/ResourceHeadersView.js +++ b/Source/WebCore/inspector/front-end/ResourceHeadersView.js @@ -224,7 +224,7 @@ WebInspector.ResourceHeadersView.prototype = { if (this._resource.statusCode) { var statusImageSource = ""; - if (this._resource.statusCode < 300) + if (this._resource.statusCode < 300 || this._resource.statusCode === 304) statusImageSource = "Images/successGreenDot.png"; else if (this._resource.statusCode < 400) statusImageSource = "Images/warningOrangeDot.png"; diff --git a/Source/WebCore/inspector/front-end/ResourceView.js b/Source/WebCore/inspector/front-end/ResourceView.js index e38cd0a..83cf99d5 100644 --- a/Source/WebCore/inspector/front-end/ResourceView.js +++ b/Source/WebCore/inspector/front-end/ResourceView.js @@ -50,9 +50,8 @@ WebInspector.ResourceView.createResourceView = function(resource) case WebInspector.resourceCategories.stylesheets: case WebInspector.resourceCategories.scripts: case WebInspector.resourceCategories.xhr: - var contentProvider = new WebInspector.SourceFrameContentProviderForResource(resource); - var isScript = resource.type === WebInspector.Resource.Type.Script; - var view = new WebInspector.SourceFrame(contentProvider, resource.url, isScript); + var delegate = new WebInspector.SourceFrameDelegateForResourcesPanel(resource); + var view = new WebInspector.SourceFrame(delegate, resource.url); view.resource = resource; return view; case WebInspector.resourceCategories.images: @@ -120,35 +119,31 @@ WebInspector.ResourceView.existingResourceViewForResource = function(resource) } -WebInspector.SourceFrameContentProviderForResource = function(resource) +WebInspector.SourceFrameDelegateForResourcesPanel = function(resource) { - WebInspector.SourceFrameContentProvider.call(this); + WebInspector.SourceFrameDelegate.call(this); this._resource = resource; } //This is a map from resource.type to mime types //found in WebInspector.SourceTokenizer.Registry. -WebInspector.SourceFrameContentProviderForResource.DefaultMIMETypeForResourceType = { +WebInspector.SourceFrameDelegateForResourcesPanel.DefaultMIMETypeForResourceType = { 0: "text/html", 1: "text/css", 4: "text/javascript" } -WebInspector.SourceFrameContentProviderForResource.prototype = { +WebInspector.SourceFrameDelegateForResourcesPanel.prototype = { requestContent: function(callback) { - function contentLoaded(content) + function contentLoaded(text) { - var mimeType = WebInspector.SourceFrameContentProviderForResource.DefaultMIMETypeForResourceType[this._resource.type] || this._resource.mimeType; - callback(mimeType, content); + var mimeType = WebInspector.SourceFrameDelegateForResourcesPanel.DefaultMIMETypeForResourceType[this._resource.type] || this._resource.mimeType; + var sourceMapping = new WebInspector.IdenticalSourceMapping(); + callback(mimeType, new WebInspector.SourceFrameContent(text, sourceMapping, [])); } this._resource.requestContent(contentLoaded.bind(this)); - }, - - scripts: function() - { - return WebInspector.debuggerModel.scriptsForURL(this._resource.url); } } -WebInspector.SourceFrameContentProviderForResource.prototype.__proto__ = WebInspector.SourceFrameContentProvider.prototype; +WebInspector.SourceFrameDelegateForResourcesPanel.prototype.__proto__ = WebInspector.SourceFrameDelegate.prototype; diff --git a/Source/WebCore/inspector/front-end/ResourcesPanel.js b/Source/WebCore/inspector/front-end/ResourcesPanel.js index 7c0649f..3c85892 100644 --- a/Source/WebCore/inspector/front-end/ResourcesPanel.js +++ b/Source/WebCore/inspector/front-end/ResourcesPanel.js @@ -904,7 +904,7 @@ WebInspector.FrameTreeElement.prototype = { this._storagePanel.showCategoryView(this._displayName); this.listItemElement.removeStyleClass("hovered"); - InspectorBackend.hideFrameHighlight(); + InspectorAgent.hideFrameHighlight(); }, get displayName() @@ -937,10 +937,10 @@ WebInspector.FrameTreeElement.prototype = { { if (hovered) { this.listItemElement.addStyleClass("hovered"); - InspectorBackend.highlightFrame(this._frameId); + InspectorAgent.highlightFrame(this._frameId); } else { this.listItemElement.removeStyleClass("hovered"); - InspectorBackend.hideFrameHighlight(); + InspectorAgent.hideFrameHighlight(); } } } @@ -969,7 +969,7 @@ WebInspector.FrameResourceTreeElement.prototype = { ondblclick: function(event) { - InspectorBackend.openInInspectedWindow(this._resource.url); + InspectorAgent.openInInspectedWindow(this._resource.url); }, onattach: function() diff --git a/Source/WebCore/inspector/front-end/Script.js b/Source/WebCore/inspector/front-end/Script.js index 8d3eabf..3f27485 100644 --- a/Source/WebCore/inspector/front-end/Script.js +++ b/Source/WebCore/inspector/front-end/Script.js @@ -120,6 +120,6 @@ WebInspector.Script.prototype = { this._source = source; callback(this._source); } - InspectorBackend.getScriptSource(this.sourceID, didGetScriptSource.bind(this)); + DebuggerAgent.getScriptSource(this.sourceID, didGetScriptSource.bind(this)); } } diff --git a/Source/WebCore/inspector/front-end/ScriptFormatter.js b/Source/WebCore/inspector/front-end/ScriptFormatter.js index 5c00e51..f70d6c6 100644 --- a/Source/WebCore/inspector/front-end/ScriptFormatter.js +++ b/Source/WebCore/inspector/front-end/ScriptFormatter.js @@ -36,6 +36,38 @@ WebInspector.ScriptFormatter = function() this._tasks = []; } +WebInspector.ScriptFormatter.locationToPosition = function(lineEndings, lineNumber, columnNumber) +{ + var position = lineNumber ? lineEndings[lineNumber - 1] + 1 : 0; + return position + columnNumber; +} + +WebInspector.ScriptFormatter.positionToLocation = function(lineEndings, position) +{ + var location = {}; + location.lineNumber = lineEndings.upperBound(position - 1); + if (!location.lineNumber) + location.columnNumber = position; + else + location.columnNumber = position - lineEndings[location.lineNumber - 1] - 1; + return location; +} + +WebInspector.ScriptFormatter.findScriptRanges = function(lineEndings, scripts) +{ + var scriptRanges = []; + for (var i = 0; i < scripts.length; ++i) { + var start = { lineNumber: scripts[i].lineOffset, columnNumber: scripts[i].columnOffset }; + start.position = WebInspector.ScriptFormatter.locationToPosition(lineEndings, start.lineNumber, start.columnNumber); + var endPosition = start.position + scripts[i].length; + var end = WebInspector.ScriptFormatter.positionToLocation(lineEndings, endPosition); + end.position = endPosition; + scriptRanges.push({ start: start, end: end, sourceID: scripts[i].sourceID }); + } + scriptRanges.sort(function(x, y) { return x.start.position - y.start.position; }); + return scriptRanges; +} + WebInspector.ScriptFormatter.prototype = { formatContent: function(content, callback) { @@ -44,7 +76,18 @@ WebInspector.ScriptFormatter.prototype = { function didFormatChunks() { var result = this._buildContentFromChunks(chunks); - callback(new WebInspector.FormattedSourceFrameContent(content, result.text, result.mapping)); + + var sourceMapping = new WebInspector.SourceMappingForFormattedScript(content.text.lineEndings(), result.text.lineEndings(), result.mapping); + var formattedScriptRanges = []; + for (var i = 0; i < content.scriptRanges.length; ++i) { + var scriptRange = content.scriptRanges[i]; + formattedScriptRange = {}; + formattedScriptRange.start = sourceMapping.originalPositionToFormattedLocation(scriptRange.start.position); + formattedScriptRange.end = sourceMapping.originalPositionToFormattedLocation(scriptRange.end.position); + formattedScriptRange.sourceID = scriptRange.sourceID; + formattedScriptRanges.push(formattedScriptRange); + } + callback(new WebInspector.SourceFrameContent(result.text, sourceMapping, formattedScriptRanges)); } this._formatChunks(chunks, 0, didFormatChunks.bind(this)); }, @@ -63,11 +106,12 @@ WebInspector.ScriptFormatter.prototype = { } var currentPosition = 0; for (var i = 0; i < scriptRanges.length; ++i) { - var scriptRange = scriptRanges[i]; - if (currentPosition < scriptRange.start) - addChunk(currentPosition, scriptRange.start, false); - addChunk(scriptRange.start, scriptRange.end, true); - currentPosition = scriptRange.end; + var start = scriptRanges[i].start.position; + var end = scriptRanges[i].end.position; + if (currentPosition < start) + addChunk(currentPosition, start, false); + addChunk(start, end, true); + currentPosition = end; } if (currentPosition < text.length) addChunk(currentPosition, text.length, false); @@ -142,3 +186,48 @@ WebInspector.ScriptFormatter.prototype = { task.callback(task.source, { original: [], formatted: [] }); } } + + +WebInspector.SourceMappingForFormattedScript = function(originalLineEndings, formattedLineEndings, mapping) +{ + WebInspector.SourceMapping.call(this); + this._originalLineEndings = originalLineEndings; + this._formattedLineEndings = formattedLineEndings; + this._mapping = mapping; +} + +WebInspector.SourceMappingForFormattedScript.prototype = { + actualLocationToSourceLocation: function(lineNumber, columnNumber) + { + var position = WebInspector.ScriptFormatter.locationToPosition(this._originalLineEndings, lineNumber, columnNumber); + return this.originalPositionToFormattedLocation(position); + }, + + sourceLocationToActualLocation: function(lineNumber, columnNumber) + { + var formattedPosition = WebInspector.ScriptFormatter.locationToPosition(this._formattedLineEndings, lineNumber, columnNumber); + var position = this._convertPosition(this._mapping.formatted, this._mapping.original, formattedPosition); + return WebInspector.ScriptFormatter.positionToLocation(this._originalLineEndings, position); + }, + + originalPositionToFormattedLocation: function(position) + { + var formattedPosition = this._convertPosition(this._mapping.original, this._mapping.formatted, position); + var location = WebInspector.ScriptFormatter.positionToLocation(this._formattedLineEndings, formattedPosition); + location.position = formattedPosition; + return location; + }, + + _convertPosition: function(positions1, positions2, position) + { + var index = positions1.upperBound(position); + var range1 = positions1[index] - positions1[index - 1]; + var range2 = positions2[index] - positions2[index - 1]; + var position2 = positions2[index - 1]; + if (range1) + position2 += Math.round((position - positions1[index - 1]) * range2 / range1); + return position2; + } +} + +WebInspector.SourceMappingForFormattedScript.prototype.__proto__ = WebInspector.SourceMapping.prototype; diff --git a/Source/WebCore/inspector/front-end/ScriptsPanel.js b/Source/WebCore/inspector/front-end/ScriptsPanel.js index 264291f..2647cee 100644 --- a/Source/WebCore/inspector/front-end/ScriptsPanel.js +++ b/Source/WebCore/inspector/front-end/ScriptsPanel.js @@ -27,6 +27,8 @@ WebInspector.ScriptsPanel = function() { WebInspector.Panel.call(this, "scripts"); + this._presentationModel = new WebInspector.DebuggerPresentationModel(); + this.topStatusBar = document.createElement("div"); this.topStatusBar.className = "status-bar"; this.topStatusBar.id = "scripts-status-bar"; @@ -50,11 +52,11 @@ WebInspector.ScriptsPanel = function() this.forwardButton.addEventListener("click", this._goForward.bind(this), false); this.topStatusBar.appendChild(this.forwardButton); - this.filesSelectElement = document.createElement("select"); - this.filesSelectElement.className = "status-bar-item"; - this.filesSelectElement.id = "scripts-files"; - this.filesSelectElement.addEventListener("change", this._changeVisibleFile.bind(this), false); - this.topStatusBar.appendChild(this.filesSelectElement); + this._filesSelectElement = document.createElement("select"); + this._filesSelectElement.className = "status-bar-item"; + this._filesSelectElement.id = "scripts-files"; + this._filesSelectElement.addEventListener("change", this._filesSelectChanged.bind(this), false); + this.topStatusBar.appendChild(this._filesSelectElement); this.functionsSelectElement = document.createElement("select"); this.functionsSelectElement.className = "status-bar-item"; @@ -138,7 +140,7 @@ WebInspector.ScriptsPanel = function() this.sidebarPanes = {}; this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane(); - this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane(); + this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane(this._presentationModel); this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane(); this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane(); if (Preferences.nativeInstrumentationEnabled) { @@ -153,7 +155,6 @@ WebInspector.ScriptsPanel = function() this.sidebarElement.appendChild(this.sidebarPanes[pane].element); this.sidebarPanes.callstack.expanded = true; - this.sidebarPanes.callstack.addEventListener("call frame selected", this._callFrameSelected, this); this.sidebarPanes.scopechain.expanded = true; this.sidebarPanes.jsBreakpoints.expanded = true; @@ -189,6 +190,9 @@ WebInspector.ScriptsPanel = function() 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); + this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, this._breakpointAdded, this); + this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, this._breakpointRemoved, this); + this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, this._callFrameSelected, this); } // Keep these in sync with WebCore::ScriptDebugServer @@ -211,7 +215,7 @@ WebInspector.ScriptsPanel.prototype = { get defaultFocusedElement() { - return this.filesSelectElement; + return this._filesSelectElement; }, get paused() @@ -255,23 +259,14 @@ WebInspector.ScriptsPanel.prototype = { var sourceID = event.data.sourceID; var oldSource = event.data.oldSource; - var oldView, newView; var script = WebInspector.debuggerModel.scriptForSourceID(sourceID); if (script.resource) { - oldView = this._urlToSourceFrame[script.resource.url]; - delete this._urlToSourceFrame[script.resource.url]; - newView = this._sourceFrameForResource(script.resource); var revertHandle = WebInspector.debuggerModel.editScriptSource.bind(WebInspector.debuggerModel, sourceID, oldSource); script.resource.setContent(script.source, revertHandle); - } else { - var oldView = script._sourceFrame; - delete script._sourceFrame; - newView = this._sourceFrameForScript(script); } - newView.scrollTop = oldView.scrollTop; - if (this.visibleView === oldView) - this.visibleView = newView; + var sourceFileId = this._sourceFileIdForScript(script); + this._recreateSourceFrame(sourceFileId); var callFrames = WebInspector.debuggerModel.callFrames; if (callFrames.length) @@ -280,11 +275,20 @@ WebInspector.ScriptsPanel.prototype = { _addScript: function(script) { - var resource = WebInspector.networkManager.inflightResourceForURL(script.sourceURL) || WebInspector.resourceForURL(script.sourceURL); + if (!script.sourceURL) { + // Anonymous scripts are shown only when stepping. + return; + } + + var resource = this._resourceForURL(script.sourceURL); if (resource) { if (resource.finished) { // Resource is finished, bind the script right away. script.resource = resource; + + // Add resource url to files select if not already added while debugging inlined scripts. + if (!(resource.url in this._sourceFileIdToFilesSelectOption)) + this._addOptionToFilesSelectAndShowSourceFrameIfNeeded(resource.url); } else { // Resource is not finished, bind the script later. if (!resource._scriptsPendingResourceLoad) { @@ -292,40 +296,84 @@ WebInspector.ScriptsPanel.prototype = { resource.addEventListener("finished", this._resourceLoadingFinished, this); } resource._scriptsPendingResourceLoad.push(script); + + // Source frame content is outdated since we have new script parsed. + this._recreateSourceFrame(script.sourceURL); } + } else if (!(script.sourceURL in this._sourceFileIdToFilesSelectOption)) { + // This is a dynamic script with "//@ sourceURL=" comment. + this._addOptionToFilesSelectAndShowSourceFrameIfNeeded(script.sourceURL); } - this._addScriptToFilesMenu(script); + }, + + _resourceForURL: function(url) + { + return WebInspector.networkManager.inflightResourceForURL(url) || WebInspector.resourceForURL(url); }, _resourceLoadingFinished: function(e) { var resource = e.target; - var visible = false; - var select = this.filesSelectElement; + // Bind scripts to resource. for (var i = 0; i < resource._scriptsPendingResourceLoad.length; ++i) { - // Bind script to resource. var script = resource._scriptsPendingResourceLoad[i]; script.resource = resource; + } + delete resource._scriptsPendingResourceLoad; + + // Recreate source frame to show resource content. + this._recreateSourceFrame(resource.url); + + // Add resource url to files select if not already added while debugging inlined scripts. + if (!(resource.url in this._sourceFileIdToFilesSelectOption)) + this._addOptionToFilesSelectAndShowSourceFrameIfNeeded(resource.url); + }, - if (select.options[select.selectedIndex] === script.filesSelectOption) - visible = true; + _addOptionToFilesSelectAndShowSourceFrameIfNeeded: function(url) + { + this._addOptionToFilesSelect(url); - // Remove script from the files list. - script.filesSelectOption.parentElement.removeChild(script.filesSelectOption); + var lastViewedURL = WebInspector.settings.lastViewedScriptFile; + if (this._filesSelectElement.length === 1) { + // Option we just added is the only option in files select. + // We have to show corresponding source frame immediately. + this._showSourceFrameAndAddToHistory(url); + // Restore original value of lastViewedScriptFile because + // source frame was shown as a result of initial load. + WebInspector.settings.lastViewedScriptFile = lastViewedURL; + } else if (url === lastViewedURL) + this._showSourceFrameAndAddToHistory(url); + }, + + _addOptionToFilesSelect: function(sourceFileId) + { + var script = this._scriptForSourceFileId(sourceFileId); + var select = this._filesSelectElement; + var option = document.createElement("option"); + option.text = script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)"); + if (script.worldType === WebInspector.Script.WorldType.EXTENSIONS_WORLD) + option.addStyleClass("extension-script"); + function optionCompare(a, b) + { + if (a.text === b.text) + return 0; + return a.text < b.text ? -1 : 1; } - // Adding first script will add resource. - this._addScriptToFilesMenu(resource._scriptsPendingResourceLoad[0]); - delete resource._scriptsPendingResourceLoad; + var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare); + if (insertionIndex < 0) + select.appendChild(option); + else + select.insertBefore(option, select.childNodes.item(insertionIndex)); - if (visible) - this._showScriptOrResource(resource, { initialLoad: true }); + option._sourceFileId = sourceFileId; + this._sourceFileIdToFilesSelectOption[sourceFileId] = option; }, addConsoleMessage: function(message) { this._messages.push(message); - var sourceFrame = this._urlToSourceFrame[message.url]; + var sourceFrame = this._sourceFileIdToSourceFrame[message.url]; if (sourceFrame) sourceFrame.addMessage(message); }, @@ -333,36 +381,40 @@ WebInspector.ScriptsPanel.prototype = { clearConsoleMessages: function() { this._messages = []; - for (var url in this._urlToSourceFrame) - this._urlToSourceFrame[url].clearMessages(); + for (var url in this._sourceFileIdToSourceFrame) + this._sourceFileIdToSourceFrame[url].clearMessages(); }, - selectedCallFrameId: function() + _breakpointAdded: function(event) { - var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame; - if (!selectedCallFrame) - return null; - return selectedCallFrame.id; + var breakpoint = event.data; + + var sourceFrame = this._sourceFileIdToSourceFrame[breakpoint.sourceFileId]; + if (sourceFrame && sourceFrame.loaded) + sourceFrame.addBreakpoint(breakpoint.lineNumber, breakpoint.resolved, breakpoint.condition, breakpoint.enabled); }, - evaluateInSelectedCallFrame: function(code, updateInterface, objectGroup, includeCommandLineAPI, callback) + _breakpointRemoved: function(event) { - var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame; + var breakpoint = event.data; + + var sourceFrame = this._sourceFileIdToSourceFrame[breakpoint.sourceFileId]; + if (sourceFrame && sourceFrame.loaded) + sourceFrame.removeBreakpoint(breakpoint.lineNumber); + }, + + evaluateInSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, callback) + { + var selectedCallFrame = this._presentationModel.selectedCallFrame; if (!this._paused || !selectedCallFrame) return; - if (typeof updateInterface === "undefined") - updateInterface = true; - function updatingCallbackWrapper(result) { - if (result) { + if (result) callback(WebInspector.RemoteObject.fromPayload(result)); - if (updateInterface) - this.sidebarPanes.scopechain.update(selectedCallFrame); - } } - InspectorBackend.evaluateOnCallFrame(selectedCallFrame.id, code, objectGroup, includeCommandLineAPI, updatingCallbackWrapper.bind(this)); + DebuggerAgent.evaluateOnCallFrame(selectedCallFrame.id, code, objectGroup, includeCommandLineAPI, updatingCallbackWrapper.bind(this)); }, _debuggerPaused: function(event) @@ -377,7 +429,7 @@ WebInspector.ScriptsPanel.prototype = { WebInspector.currentPanel = this; - this.sidebarPanes.callstack.update(callFrames, event.data.eventType, event.data.eventData); + this.sidebarPanes.callstack.update(event.data); this.sidebarPanes.callstack.selectedCallFrame = callFrames[0]; window.focus(); @@ -386,6 +438,8 @@ WebInspector.ScriptsPanel.prototype = { _debuggerResumed: function() { + this._presentationModel.selectedCallFrame = null; + this._paused = false; this._waitingToPause = false; this._stepping = false; @@ -425,10 +479,10 @@ WebInspector.ScriptsPanel.prototype = { this._currentBackForwardIndex = -1; this._updateBackAndForwardButtons(); - this._urlToSourceFrame = {}; + this._sourceFileIdToSourceFrame = {}; + this._sourceFileIdToFilesSelectOption = {}; this._messages = []; - this._resourceForURLInFilesSelect = {}; - this.filesSelectElement.removeChildren(); + this._filesSelectElement.removeChildren(); this.functionsSelectElement.removeChildren(); this.viewsContainerElement.removeChildren(); @@ -458,35 +512,15 @@ WebInspector.ScriptsPanel.prototype = { canShowSourceLine: function(url, line) { - if (!this._debuggerEnabled) - return false; - return !!this._scriptOrResourceForURLAndLine(url, line); + return this._debuggerEnabled && (url in this._sourceFileIdToFilesSelectOption); }, showSourceLine: function(url, line) { - var scriptOrResource = this._scriptOrResourceForURLAndLine(url, line); - this._showScriptOrResource(scriptOrResource, {line: line, shouldHighlightLine: true}); - }, - - _scriptOrResourceForURLAndLine: function(url, line) - { - var scripts = WebInspector.debuggerModel.scriptsForURL(url); - for (var i = 0; i < scripts.length; ++i) { - var script = scripts[i]; - if (script.resource) - return script.resource; - if (script.startingLine <= line && script.startingLine + script.linesCount > line) - return script; - } - return null; - }, - - showView: function(view) - { - if (!view) + if (!(url in this._sourceFileIdToFilesSelectOption)) return; - this._showScriptOrResource(view.resource || view.script); + var sourceFrame = this._showSourceFrameAndAddToHistory(url); + sourceFrame.highlightLine(line); }, handleShortcut: function(event) @@ -500,167 +534,114 @@ WebInspector.ScriptsPanel.prototype = { this.sidebarPanes.callstack.handleShortcut(event); }, - _sourceFrameForScriptOrResource: function(scriptOrResource) + _showSourceFrameAndAddToHistory: function(sourceFileId) { - if (scriptOrResource instanceof WebInspector.Resource) - return this._sourceFrameForResource(scriptOrResource); - return this._sourceFrameForScript(scriptOrResource); + var sourceFrame = this._showSourceFrame(sourceFileId); + + var oldIndex = this._currentBackForwardIndex; + if (oldIndex >= 0) + this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex); + + // Check for a previous entry of the same object in _backForwardList. + // If one is found, remove it. + var previousEntryIndex = this._backForwardList.indexOf(sourceFileId); + if (previousEntryIndex !== -1) + this._backForwardList.splice(previousEntryIndex, 1); + + this._backForwardList.push(sourceFileId); + this._currentBackForwardIndex = this._backForwardList.length - 1; + + this._updateBackAndForwardButtons(); + + return sourceFrame; }, - _sourceFrameForResource: function(resource) + _showSourceFrame: function(sourceFileId) { - var sourceFrame = this._urlToSourceFrame[resource.url]; - if (sourceFrame) - return sourceFrame; - var contentProvider = new WebInspector.SourceFrameContentProviderForResource(resource); - var isScript = resource.type === WebInspector.Resource.Type.Script; - sourceFrame = new WebInspector.SourceFrame(contentProvider, resource.url, isScript); - for (var i = 0; i < this._messages.length; ++i) { - var message = this._messages[i]; - if (this._messages[i].url === resource.url) - sourceFrame.addMessage(message); - } - this._urlToSourceFrame[resource.url] = sourceFrame; + var index = this._sourceFileIdToFilesSelectOption[sourceFileId].index; + this._filesSelectElement.selectedIndex = index; + + var sourceFrame = this._sourceFrameForSourceFileId(sourceFileId); + this.visibleView = sourceFrame; + + var script = this._scriptForSourceFileId(sourceFileId); + if (script.sourceURL) + WebInspector.settings.lastViewedScriptFile = script.sourceURL; + return sourceFrame; }, - _sourceFrameForScript: function(script) + _sourceFrameForSourceFileId: function(sourceFileId) { - if (script._sourceFrame) - return script._sourceFrame; - var contentProvider = new WebInspector.SourceFrameContentProviderForScript(script); - script._sourceFrame = new WebInspector.SourceFrame(contentProvider, script.sourceURL, true); - return script._sourceFrame; + var sourceFrame = this._sourceFileIdToSourceFrame[sourceFileId]; + return sourceFrame || this._createSourceFrame(sourceFileId); }, - _showScriptOrResource: function(scriptOrResource, options) + _createSourceFrame: function(sourceFileId) { - // options = {line:, shouldHighlightLine:, fromBackForwardAction:, initialLoad:} - options = options || {}; + var script = this._scriptForSourceFileId(sourceFileId); + var delegate = new WebInspector.SourceFrameDelegateForScriptsPanel(script); + var sourceFrame = new WebInspector.SourceFrame(delegate, script.sourceURL); + sourceFrame._sourceFileId = sourceFileId; + sourceFrame.addEventListener(WebInspector.SourceFrame.Events.Loaded, this._sourceFrameLoaded, this); + this._sourceFileIdToSourceFrame[sourceFileId] = sourceFrame; + return sourceFrame; + }, - if (!scriptOrResource) + _recreateSourceFrame: function(sourceFileId) + { + var oldSourceFrame = this._sourceFileIdToSourceFrame[sourceFileId]; + if (!oldSourceFrame) return; - - var view = this._sourceFrameForScriptOrResource(scriptOrResource); - if (!view) + oldSourceFrame.removeEventListener(WebInspector.SourceFrame.Events.Loaded, this._sourceFrameLoaded, this); + delete this._sourceFileIdToSourceFrame[sourceFileId]; + oldSourceFrame.removeEventListener(WebInspector.SourceFrame.Events.Loaded, this._sourceFrameLoaded, this); + if (this.visibleView !== oldSourceFrame) return; - var url = scriptOrResource.url || scriptOrResource.sourceURL; - if (url && !options.initialLoad) - WebInspector.settings.lastViewedScriptFile = url; - - if (!options.fromBackForwardAction) { - var oldIndex = this._currentBackForwardIndex; - if (oldIndex >= 0) - this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex); - - // Check for a previous entry of the same object in _backForwardList. - // If one is found, remove it and update _currentBackForwardIndex to match. - var previousEntryIndex = this._backForwardList.indexOf(scriptOrResource); - if (previousEntryIndex !== -1) { - this._backForwardList.splice(previousEntryIndex, 1); - --this._currentBackForwardIndex; - } + var newSourceFrame = this._createSourceFrame(sourceFileId) + newSourceFrame.scrollTop = oldSourceFrame.scrollTop; + this.visibleView = newSourceFrame; + }, - this._backForwardList.push(scriptOrResource); - ++this._currentBackForwardIndex; + _sourceFrameLoaded: function(event) + { + var sourceFrame = event.target; + var sourceFileId = sourceFrame._sourceFileId; - this._updateBackAndForwardButtons(); + for (var i = 0; i < this._messages.length; ++i) { + var message = this._messages[i]; + if (message.url === sourceFileId) + sourceFrame.addMessage(message); } - this.visibleView = view; - - if (options.line) { - if (view.revealLine) - view.revealLine(options.line); - if (view.highlightLine && options.shouldHighlightLine) - view.highlightLine(options.line); + var breakpoints = this._presentationModel.breakpointsForSourceFileId(sourceFileId); + for (var i = 0; i < breakpoints.length; ++i) { + var breakpoint = breakpoints[i]; + sourceFrame.addBreakpoint(breakpoint.lineNumber, breakpoint.resolved, breakpoint.condition, breakpoint.enabled); } - var option; - if (scriptOrResource instanceof WebInspector.Script) { - option = scriptOrResource.filesSelectOption; - - // hasn't been added yet - happens for stepping in evals, - // so use the force option to force the script into the menu. - if (!option) { - this._addScriptToFilesMenu(scriptOrResource, true); - option = scriptOrResource.filesSelectOption; + var selectedCallFrame = this._presentationModel.selectedCallFrame; + if (selectedCallFrame) { + if (selectedCallFrame.sourceLocation.sourceFileId === sourceFileId) { + sourceFrame.setExecutionLine(selectedCallFrame.sourceLocation.lineNumber); + this._executionSourceFrame = sourceFrame; } - - console.assert(option); - } else - option = scriptOrResource.filesSelectOption; - - if (option) - this.filesSelectElement.selectedIndex = option.index; + } }, - _addScriptToFilesMenu: function(script, force) + _sourceFileIdForScript: function(script) { - if (!script.sourceURL && !force) - return; - - if (script.resource) { - if (this._resourceForURLInFilesSelect[script.resource.url]) - return; - this._resourceForURLInFilesSelect[script.resource.url] = script.resource; - } - - var displayName = script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)"); - - var select = this.filesSelectElement; - var option = document.createElement("option"); - option.representedObject = script.resource || script; - option.url = displayName; - option.startingLine = script.startingLine; - option.text = script.resource || script.startingLine === 1 ? displayName : String.sprintf("%s:%d", displayName, script.startingLine); + return script.sourceURL || script.sourceID; + }, - function optionCompare(a, b) + _scriptForSourceFileId: function(sourceFileId) + { + function filter(script) { - if (a.url < b.url) - return -1; - else if (a.url > b.url) - return 1; - - if (typeof a.startingLine !== "number") - return -1; - if (typeof b.startingLine !== "number") - return -1; - return a.startingLine - b.startingLine; - } - - var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare); - if (insertionIndex < 0) - select.appendChild(option); - else - select.insertBefore(option, select.childNodes.item(insertionIndex)); - - if (script.resource) - script.resource.filesSelectOption = option; - else - script.filesSelectOption = option; - - if (select.options[select.selectedIndex] === option) { - // Call _showScriptOrResource if the option we just appended ended up being selected. - // This will happen for the first item added to the menu. - this._showScriptOrResource(option.representedObject, {initialLoad: true}); - } else { - // If not first item, check to see if this was the last viewed - var url = option.representedObject.url || option.representedObject.sourceURL; - var lastURL = WebInspector.settings.lastViewedScriptFile; - if (url && url === lastURL) { - // For resources containing multiple <script> tags, we first report them separately and - // then glue them all together. They all share url and there is no need to show them all one - // by one. - var isResource = !!option.representedObject.url; - if (isResource || !this.visibleView || !this.visibleView.script || this.visibleView.script.sourceURL !== url) - this._showScriptOrResource(option.representedObject, {initialLoad: true}); - } + return (script.sourceURL || script.sourceID) === sourceFileId; } - - if (script.worldType === WebInspector.Script.WorldType.EXTENSIONS_WORLD) - script.filesSelectOption.addStyleClass("extension-script"); + return WebInspector.debuggerModel.queryScripts(filter)[0]; }, _clearCurrentExecutionLine: function() @@ -670,31 +651,36 @@ WebInspector.ScriptsPanel.prototype = { delete this._executionSourceFrame; }, - _callFrameSelected: function() + _callFrameSelected: function(event) { + var callFrame = event.data; + this._clearCurrentExecutionLine(); - var callStackPane = this.sidebarPanes.callstack; - var currentFrame = callStackPane.selectedCallFrame; - if (!currentFrame) + if (!callFrame) return; - this.sidebarPanes.scopechain.update(currentFrame); + this.sidebarPanes.scopechain.update(callFrame); this.sidebarPanes.watchExpressions.refreshExpressions(); - var script = WebInspector.debuggerModel.scriptForSourceID(currentFrame.sourceID); - var scriptOrResource = script.resource || script; - this._showScriptOrResource(scriptOrResource, {line: currentFrame.line}); - - this._executionSourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource); - if (this._executionSourceFrame) - this._executionSourceFrame.setExecutionLine(currentFrame.line); + var sourceFileId = callFrame.sourceLocation.sourceFileId; + if (!(sourceFileId in this._sourceFileIdToFilesSelectOption)) { + // This happens in two cases: + // 1) Current call frame function is defined in anonymous script (anonymous scripts aren't added to files select by default) + // 2) We are debugging synchronously executed inlined script and there is no resource so far + this._addOptionToFilesSelect(sourceFileId); + } + var sourceFrame = this._showSourceFrameAndAddToHistory(sourceFileId); + if (sourceFrame.loaded) { + sourceFrame.setExecutionLine(callFrame.sourceLocation.lineNumber); + this._executionSourceFrame = sourceFrame; + } }, - _changeVisibleFile: function(event) + _filesSelectChanged: function() { - var select = this.filesSelectElement; - this._showScriptOrResource(select.options[select.selectedIndex].representedObject); + var sourceFileId = this._filesSelectElement[this._filesSelectElement.selectedIndex]._sourceFileId; + this._showSourceFrameAndAddToHistory(sourceFileId); }, _startSidebarResizeDrag: function(event) @@ -747,7 +733,7 @@ WebInspector.ScriptsPanel.prototype = { this._pauseOnExceptionButton.state = pauseOnExceptionsState; WebInspector.settings.pauseOnExceptionState = pauseOnExceptionsState; } - InspectorBackend.setPauseOnExceptionsState(pauseOnExceptionsState, callback.bind(this)); + DebuggerAgent.setPauseOnExceptionsState(pauseOnExceptionsState, callback.bind(this)); }, _updateDebuggerButtons: function() @@ -812,7 +798,7 @@ WebInspector.ScriptsPanel.prototype = { return; } - this._showScriptOrResource(this._backForwardList[--this._currentBackForwardIndex], {fromBackForwardAction: true}); + this._showSourceFrame(this._backForwardList[--this._currentBackForwardIndex]); this._updateBackAndForwardButtons(); }, @@ -823,7 +809,7 @@ WebInspector.ScriptsPanel.prototype = { return; } - this._showScriptOrResource(this._backForwardList[++this._currentBackForwardIndex], {fromBackForwardAction: true}); + this._showSourceFrame(this._backForwardList[++this._currentBackForwardIndex]); this._updateBackAndForwardButtons(); }, @@ -865,11 +851,11 @@ WebInspector.ScriptsPanel.prototype = { if (this._paused) { this._paused = false; this._waitingToPause = false; - InspectorBackend.resume(); + DebuggerAgent.resume(); } else { this._stepping = false; this._waitingToPause = true; - InspectorBackend.pause(); + DebuggerAgent.pause(); } this._clearInterface(); @@ -882,7 +868,7 @@ WebInspector.ScriptsPanel.prototype = { this._clearInterface(); - InspectorBackend.stepOver(); + DebuggerAgent.stepOver(); }, _stepIntoClicked: function() @@ -892,7 +878,7 @@ WebInspector.ScriptsPanel.prototype = { this._clearInterface(); - InspectorBackend.stepInto(); + DebuggerAgent.stepInto(); }, _stepOutClicked: function() @@ -902,18 +888,18 @@ WebInspector.ScriptsPanel.prototype = { this._clearInterface(); - InspectorBackend.stepOut(); + DebuggerAgent.stepOut(); }, toggleBreakpointsClicked: function() { this.toggleBreakpointsButton.toggled = !this.toggleBreakpointsButton.toggled; if (this.toggleBreakpointsButton.toggled) { - InspectorBackend.activateBreakpoints(); + DebuggerAgent.activateBreakpoints(); this.toggleBreakpointsButton.title = WebInspector.UIString("Deactivate all breakpoints."); document.getElementById("main-panels").removeStyleClass("breakpoints-deactivated"); } else { - InspectorBackend.deactivateBreakpoints(); + DebuggerAgent.deactivateBreakpoints(); this.toggleBreakpointsButton.title = WebInspector.UIString("Activate all breakpoints."); document.getElementById("main-panels").addStyleClass("breakpoints-deactivated"); } @@ -976,8 +962,6 @@ WebInspector.ScriptsPanel.prototype = { searchCanceled: function() { - WebInspector.updateSearchMatchesCount(0, this); - if (this._searchView) this._searchView.searchCanceled(); @@ -987,6 +971,8 @@ WebInspector.ScriptsPanel.prototype = { performSearch: function(query) { + WebInspector.searchController.updateSearchMatchesCount(0, this); + if (!this.visibleView) return; @@ -1001,7 +987,7 @@ WebInspector.ScriptsPanel.prototype = { if (!searchMatches) return; - WebInspector.updateSearchMatchesCount(searchMatches, this); + WebInspector.searchController.updateSearchMatchesCount(searchMatches, this); view.jumpToFirstSearchResult(); } @@ -1053,39 +1039,205 @@ WebInspector.ScriptsPanel.prototype = { WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype; -WebInspector.SourceFrameContentProviderForScript = function(script) +WebInspector.SourceFrameDelegateForScriptsPanel = function(script) { - WebInspector.SourceFrameContentProvider.call(this); + WebInspector.SourceFrameDelegate.call(this); this._script = script; + this._popoverObjectGroup = "popover"; } -WebInspector.SourceFrameContentProviderForScript.prototype = { +WebInspector.SourceFrameDelegateForScriptsPanel.prototype = { requestContent: function(callback) { - if (this._script.source) { - callback("text/javascript", this._script.source); + function didGetTextAndScriptRanges(mimeType, text, scriptRanges) + { + this._content = new WebInspector.SourceFrameContent(text, new WebInspector.IdenticalSourceMapping(), scriptRanges); + callback(mimeType, this._content); + } + + if (this._script.resource) + this._loadResourceContent(this._script.resource, didGetTextAndScriptRanges.bind(this)); + else + this._loadAndConcatenateScriptsContent(didGetTextAndScriptRanges.bind(this)); + }, + + _loadResourceContent: function(resource, callback) + { + function didRequestContent(text) + { + var mimeType = "text/javascript"; + if (resource.type !== WebInspector.Resource.Type.Script) { + mimeType = "text/html"; + // WebKit html lexer normalizes line endings and scripts are passed to VM with "\n" line endings. + // However, resource content has original line endings, so we have to normalize line endings here. + text = text.replace(/\r\n/g, "\n"); + } + var scripts = this._scripts(); + var scriptRanges = WebInspector.ScriptFormatter.findScriptRanges(text.lineEndings(), scripts); + callback(mimeType, text, scriptRanges); + } + resource.requestContent(didRequestContent.bind(this)); + }, + + _loadAndConcatenateScriptsContent: function(callback) + { + var scripts = this._scripts(); + var scriptsLeft = scripts.length; + var sources = []; + function didRequestSource(index, source) + { + sources[index] = source; + if (--scriptsLeft) + return; + var result = this._buildSource(scripts, sources); + callback(result.mimeType, result.source, result.scriptRanges); + } + for (var i = 0; i < scripts.length; ++i) + scripts[i].requestSource(didRequestSource.bind(this, i)); + }, + + _buildSource: function(scripts, sources) + { + var source = ""; + var lineNumber = 0; + var columnNumber = 0; + var scriptRanges = []; + function appendChunk(chunk, script) + { + var start = { lineNumber: lineNumber, columnNumber: columnNumber }; + source += chunk; + var lineEndings = chunk.lineEndings(); + var lineCount = lineEndings.length; + if (lineCount === 1) + columnNumber += chunk.length; + else { + lineNumber += lineCount - 1; + columnNumber = lineEndings[lineCount - 1] - lineEndings[lineCount - 2] - 1; + } + var end = { lineNumber: lineNumber, columnNumber: columnNumber }; + if (script) + scriptRanges.push({ start: start, end: end, sourceID: script.sourceID }); + } + + var mimeType; + if (scripts.length === 1 && !scripts[0].lineOffset && !scripts[0].columnOffset) { + // Single script source. + mimeType = "text/javascript"; + appendChunk(sources[0], scripts[0]); + } else { + // Scripts inlined in html document. + mimeType = "text/html"; + var scriptOpenTag = "<script>"; + var scriptCloseTag = "</script>"; + for (var i = 0; i < scripts.length; ++i) { + // Fill the gap with whitespace characters. + while (lineNumber < scripts[i].lineOffset) + appendChunk("\n"); + while (columnNumber < scripts[i].columnOffset - scriptOpenTag.length) + appendChunk(" "); + + // Add script tag. + appendChunk(scriptOpenTag); + appendChunk(sources[i], scripts[i]); + appendChunk(scriptCloseTag); + } + } + return { mimeType: mimeType, source: source, scriptRanges: scriptRanges }; + }, + + _scripts: function() + { + var scripts = [this._script]; + if (this._script.sourceURL) + scripts = WebInspector.debuggerModel.scriptsForURL(this._script.sourceURL); + scripts.sort(function(x, y) { return x.lineOffset - y.lineOffset || x.columnOffset - y.columnOffset; }); + return scripts; + }, + + debuggingSupported: function() + { + return true; + }, + + setBreakpoint: function(lineNumber, condition, enabled) + { + var location = this._content.sourceFrameLineNumberToActualLocation(lineNumber); + if (this._script.sourceURL) + WebInspector.debuggerModel.setBreakpoint(this._script.sourceURL, location.lineNumber, location.columnNumber, condition, enabled); + else if (location.sourceID) + WebInspector.debuggerModel.setBreakpointBySourceId(location.sourceID, location.lineNumber, location.columnNumber, condition, enabled); + else return; + + if (!WebInspector.panels.scripts.breakpointsActivated) + WebInspector.panels.scripts.toggleBreakpointsClicked(); + }, + + removeBreakpoint: function(breakpointId) + { + WebInspector.debuggerModel.removeBreakpoint(breakpointId); + }, + + updateBreakpoint: function(breakpointId, condition, enabled) + { + WebInspector.debuggerModel.updateBreakpoint(breakpointId, condition, enabled); + }, + + findBreakpoint: function(lineNumber) + { + var url = this._script.sourceURL; + var location = this._content.sourceFrameLineNumberToActualLocation(lineNumber); + function filter(breakpoint) + { + if (breakpoint.url) { + if (breakpoint.url !== url) + return false; + } else { + if (breakpoint.sourceID !== location.sourceID) + return false; + } + var lineNumber = breakpoint.locations.length ? breakpoint.locations[0].lineNumber : breakpoint.lineNumber; + return lineNumber === location.lineNumber; } + return WebInspector.debuggerModel.queryBreakpoints(filter)[0]; + }, + + continueToLine: function(lineNumber) + { + var location = this._content.sourceFrameLineNumberToActualLocation(lineNumber); + if (location.sourceID) + WebInspector.debuggerModel.continueToLocation(location.sourceID, location.lineNumber, location.columnNumber); + }, - function didRequestSource(content) + canEditScriptSource: function() + { + return Preferences.canEditScriptSource && !this._script.lineOffset && !this._script.columnOffset; + }, + + editScriptSource: function(text) + { + WebInspector.debuggerModel.editScriptSource(this._script.sourceID, text); + }, + + debuggerPaused: function() + { + return WebInspector.panels.scripts.paused; + }, + + evaluateInSelectedCallFrame: function(string, callback) + { + function didEvaluateInSelectedCallFrame(result) { - var source; - if (content) { - var prefix = ""; - for (var i = 0; i < this._script.startingLine - 1; ++i) - prefix += "\n"; - source = prefix + content; - } else - source = WebInspector.UIString("<source is not available>"); - callback("text/javascript", source); + if (!result.isError() && this.debuggerPaused()) + callback(result); } - this._script.requestSource(didRequestSource.bind(this)); + WebInspector.panels.scripts.evaluateInSelectedCallFrame(string, this._popoverObjectGroup, false, didEvaluateInSelectedCallFrame.bind(this)); }, - scripts: function() + releaseEvaluationResult: function() { - return [this._script]; + RuntimeAgent.releaseObjectGroup(0, this._popoverObjectGroup); } } -WebInspector.SourceFrameContentProviderForScript.prototype.__proto__ = WebInspector.SourceFrameContentProvider.prototype; +WebInspector.SourceFrameDelegateForScriptsPanel.prototype.__proto__ = WebInspector.SourceFrameDelegate.prototype; diff --git a/Source/WebCore/inspector/front-end/SearchController.js b/Source/WebCore/inspector/front-end/SearchController.js new file mode 100755 index 0000000..735c424 --- /dev/null +++ b/Source/WebCore/inspector/front-end/SearchController.js @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com). + * Copyright (C) 2009 Joseph Pecoraro + * 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.SearchController = function() +{ + this.element = document.getElementById("search"); + this._matchesElement = document.getElementById("search-results-matches"); + this._toolbarLabelElement = document.getElementById("search-toolbar-label"); + + this.element.addEventListener("search", this._onSearch.bind(this), false); // when the search is emptied + this.element.addEventListener("mousedown", this._onSearchFieldManualFocus.bind(this), false); // when the search field is manually selected + this.element.addEventListener("keydown", this._onKeyDown.bind(this), true); +} + +WebInspector.SearchController.prototype = { + updateSearchMatchesCount: function(matches, panel) + { + if (!panel) + panel = WebInspector.currentPanel; + + panel.currentSearchMatches = matches; + + if (panel === WebInspector.currentPanel) + this._updateSearchMatchesCount(WebInspector.currentPanel.currentQuery && matches); + }, + + updateSearchLabel: function() + { + var panelName = WebInspector.currentPanel && WebInspector.currentPanel.toolbarItemLabel; + if (!panelName) + return; + var newLabel = WebInspector.UIString("Search %s", panelName); + if (WebInspector.attached) + this.element.setAttribute("placeholder", newLabel); + else { + this.element.removeAttribute("placeholder"); + this._toolbarLabelElement.textContent = newLabel; + } + }, + + cancelSearch: function() + { + this.element.value = ""; + this._performSearch(""); + }, + + handleShortcut: function(event) + { + var isMac = WebInspector.isMac(); + + switch (event.keyIdentifier) { + case "U+0046": // F key + if (isMac) + var isFindKey = event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey; + else + var isFindKey = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey; + + if (isFindKey) { + this._focusSearchField(); + event.handled = true; + } + break; + + + case "F3": + if (!isMac) { + this._focusSearchField(); + event.handled = true; + } + break; + + case "U+0047": // G key + var currentPanel = WebInspector.currentPanel; + + if (isMac && event.metaKey && !event.ctrlKey && !event.altKey) { + if (event.shiftKey) { + if (currentPanel.jumpToPreviousSearchResult) + currentPanel.jumpToPreviousSearchResult(); + } else if (currentPanel.jumpToNextSearchResult) + currentPanel.jumpToNextSearchResult(); + event.handled = true; + } + break; + } + }, + + activePanelChanged: function() + { + this.updateSearchLabel(); + + if (!this._currentQuery) + return; + + panel = WebInspector.currentPanel; + if (panel.performSearch) { + function performPanelSearch() + { + this._updateSearchMatchesCount(); + + panel.currentQuery = this._currentQuery; + panel.performSearch(this._currentQuery); + } + + // Perform the search on a timeout so the panel switches fast. + setTimeout(performPanelSearch.bind(this), 0); + } else { + // Update to show Not found for panels that can't be searched. + this._updateSearchMatchesCount(); + } + }, + + _updateSearchMatchesCount: function(matches) + { + if (matches == null) { + this._matchesElement.addStyleClass("hidden"); + return; + } + + if (matches) { + if (matches === 1) + var matchesString = WebInspector.UIString("1 match"); + else + var matchesString = WebInspector.UIString("%d matches", matches); + } else + var matchesString = WebInspector.UIString("Not Found"); + + this._matchesElement.removeStyleClass("hidden"); + this._matchesElement.textContent = matchesString; + WebInspector.toolbar.resize(); + }, + + _focusSearchField: function() + { + this.element.focus(); + this.element.select(); + }, + + _onSearchFieldManualFocus: function(event) + { + WebInspector.currentFocusElement = event.target; + }, + + _onKeyDown: function(event) + { + // Escape Key will clear the field and clear the search results + if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) { + // If focus belongs here and text is empty - nothing to do, return unhandled. + // When search was selected manually and is currently blank, we'd like Esc stay unhandled + // and hit console drawer handler. + if (event.target.value === "" && this.currentFocusElement === this.previousFocusElement) + return; + event.preventDefault(); + event.stopPropagation(); + + this.cancelSearch(event); + WebInspector.currentFocusElement = WebInspector.previousFocusElement; + if (WebInspector.currentFocusElement === event.target) + WebInspector.currentFocusElement.currentFocusElement.select(); + return false; + } + + if (!isEnterKey(event)) + return false; + + // Select all of the text so the user can easily type an entirely new query. + event.target.select(); + + // Only call performSearch if the Enter key was pressed. Otherwise the search + // performance is poor because of searching on every key. The search field has + // the incremental attribute set, so we still get incremental searches. + this._onSearch(event); + + // Call preventDefault since this was the Enter key. This prevents a "search" event + // from firing for key down. This stops performSearch from being called twice in a row. + event.preventDefault(); + }, + + _onSearch: function(event) + { + var forceSearch = event.keyIdentifier === "Enter"; + this._performSearch(event.target.value, forceSearch, event.shiftKey, false); + }, + + _performSearch: function(query, forceSearch, isBackwardSearch, repeatSearch) + { + var isShortSearch = (query.length < 3); + + // Clear a leftover short search flag due to a non-conflicting forced search. + if (isShortSearch && this._shortSearchWasForcedByKeyEvent && this._currentQuery !== query) + delete this._shortSearchWasForcedByKeyEvent; + + // Indicate this was a forced search on a short query. + if (isShortSearch && forceSearch) + this._shortSearchWasForcedByKeyEvent = true; + + if (!query || !query.length || (!forceSearch && isShortSearch)) { + // Prevent clobbering a short search forced by the user. + if (this._shortSearchWasForcedByKeyEvent) { + delete this._shortSearchWasForcedByKeyEvent; + return; + } + + delete this._currentQuery; + + for (var panelName in WebInspector.panels) { + var panel = WebInspector.panels[panelName]; + var hadCurrentQuery = !!panel.currentQuery; + delete panel.currentQuery; + if (hadCurrentQuery && panel.searchCanceled) + panel.searchCanceled(); + } + + this._updateSearchMatchesCount(); + + return; + } + + var currentPanel = WebInspector.currentPanel; + if (!repeatSearch && query === currentPanel.currentQuery && currentPanel.currentQuery === this._currentQuery) { + // When this is the same query and a forced search, jump to the next + // search result for a good user experience. + if (forceSearch) { + if (!isBackwardSearch && currentPanel.jumpToNextSearchResult) + currentPanel.jumpToNextSearchResult(); + else if (isBackwardSearch && currentPanel.jumpToPreviousSearchResult) + currentPanel.jumpToPreviousSearchResult(); + } + return; + } + + this._currentQuery = query; + + this._updateSearchMatchesCount(); + + if (!currentPanel.performSearch) + return; + + currentPanel.currentQuery = query; + currentPanel.performSearch(query); + } +} diff --git a/Source/WebCore/inspector/front-end/SourceFrame.js b/Source/WebCore/inspector/front-end/SourceFrame.js index f2e0be1..3120159 100644 --- a/Source/WebCore/inspector/front-end/SourceFrame.js +++ b/Source/WebCore/inspector/front-end/SourceFrame.js @@ -28,15 +28,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.SourceFrame = function(contentProvider, url, isScript) +WebInspector.SourceFrame = function(delegate, url) { WebInspector.View.call(this); this.element.addStyleClass("script-view"); - this._contentProvider = contentProvider; + this._delegate = delegate; this._url = url; - this._isScript = isScript; this._textModel = new WebInspector.TextEditorModel(); this._textModel.replaceTabsWithSpaces = true; @@ -47,8 +46,10 @@ WebInspector.SourceFrame = function(contentProvider, url, isScript) this._messages = []; this._rowMessages = {}; this._messageBubbles = {}; +} - this._popoverObjectGroup = "popover"; +WebInspector.SourceFrame.Events = { + Loaded: "loaded" } WebInspector.SourceFrame.prototype = { @@ -59,7 +60,7 @@ WebInspector.SourceFrame.prototype = { if (!this._contentRequested) { this._contentRequested = true; - this._contentProvider.requestContent(this._createTextViewer.bind(this)); + this._delegate.requestContent(this._createTextViewer.bind(this)); } if (this._textViewer) { @@ -85,6 +86,11 @@ WebInspector.SourceFrame.prototype = { this._clearLineHighlight(); }, + get loaded() + { + return !!this._content; + }, + hasContent: function() { return true; @@ -132,12 +138,6 @@ WebInspector.SourceFrame.prototype = { this._textViewer.resize(); }, - sizeToFitContentHeight: function() - { - if (this._textViewer) - this._textViewer.revalidateDecorationsAndPaint(); - }, - get textModel() { return this._textModel; @@ -171,18 +171,34 @@ WebInspector.SourceFrame.prototype = { delete this._lineToHighlight; }, - _createTextViewer: function(mimeType, text) + _startEditing: function() { - this._content = new WebInspector.SourceFrameContent(text, this._contentProvider.scripts()); - this._textModel.setText(null, text); + WebInspector.searchController.cancelSearch(); + this.clearMessages(); + }, + + _endEditing: function(oldRange, newRange) + { + // FIXME: Implement this. + }, + + _createTextViewer: function(mimeType, content) + { + this._content = content; + this._textModel.setText(null, content.text); this._textViewer = new WebInspector.TextViewer(this._textModel, WebInspector.platform, this._url); + this._textViewer.startEditingListener = this._startEditing.bind(this); + this._textViewer.endEditingListener = this._endEditing.bind(this); + var element = this._textViewer.element; - element.addEventListener("contextmenu", this._contextMenu.bind(this), true); - 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); + if (this._delegate.debuggingSupported()) { + element.addEventListener("contextmenu", this._contextMenu.bind(this), true); + element.addEventListener("mousedown", this._mouseDown.bind(this), true); + element.addEventListener("mousemove", this._mouseMove.bind(this), true); + element.addEventListener("dblclick", this._doubleClick.bind(this), true); + element.addEventListener("scroll", this._scroll.bind(this), true); + } this.element.appendChild(element); this._textViewer.beginUpdates(); @@ -205,11 +221,9 @@ WebInspector.SourceFrame.prototype = { delete this._delayedFindSearchMatches; } - this._textViewer.endUpdates(); + this.dispatchEventToListeners(WebInspector.SourceFrame.Events.Loaded); - 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.BreakpointResolved, this._breakpointResolved, this); + this._textViewer.endUpdates(); }, _setTextViewerDecorations: function() @@ -222,32 +236,11 @@ WebInspector.SourceFrame.prototype = { this._addExistingMessagesToSource(); this._updateDiffDecorations(); - if (this._executionLine) - this.setExecutionLine(this._executionLine); - - this._breakpointIdToTextViewerLineNumber = {}; - this._textViewerLineNumberToBreakpointId = {}; - var breakpoints = WebInspector.debuggerModel.breakpoints; - for (var id in breakpoints) - this._breakpointAdded({ data: breakpoints[id] }); - this._textViewer.resize(); this._textViewer.endUpdates(); }, - _shouldDisplayBreakpoint: function(breakpoint) - { - if (this._url) - return this._url === breakpoint.url; - var scripts = this._contentProvider.scripts(); - for (var i = 0; i < scripts.length; ++i) { - if (breakpoint.sourceID === scripts[i].sourceID) - return true; - } - return false; - }, - performSearch: function(query, callback) { // Call searchCanceled since it will reset everything we need before doing a new search. @@ -363,20 +356,15 @@ WebInspector.SourceFrame.prototype = { setExecutionLine: function(lineNumber) { - this._executionLine = lineNumber; - if (!this._textViewer) - return; - var textViewerLineNumber = this._originalLocationToTextViewerLineNumber(this._executionLine - 1, 0); - this._textViewer.addDecoration(textViewerLineNumber, "webkit-execution-line"); + this._executionLineNumber = lineNumber; + this._textViewer.addDecoration(lineNumber, "webkit-execution-line"); + this._textViewer.revealLine(lineNumber); }, clearExecutionLine: function() { - if (!this._textViewer) - return; - var textViewerLineNumber = this._originalLocationToTextViewerLineNumber(this._executionLine - 1, 0); - this._textViewer.removeDecoration(textViewerLineNumber, "webkit-execution-line"); - delete this._executionLine; + this._textViewer.removeDecoration(this._executionLineNumber, "webkit-execution-line"); + delete this._executionLineNumber; }, _updateDiffDecorations: function() @@ -467,63 +455,18 @@ WebInspector.SourceFrame.prototype = { msg._resourceMessageLineElement = messageLineElement; }, - _breakpointAdded: function(event) - { - var breakpoint = event.data; - - if (!this._shouldDisplayBreakpoint(breakpoint)) - return; - - var resolved = breakpoint.locations.length; - var location = resolved ? breakpoint.locations[0] : breakpoint; - - var textViewerLineNumber = this._originalLocationToTextViewerLineNumber(location.lineNumber, location.columnNumber); - if (textViewerLineNumber >= this._textModel.linesCount) - return; - - var existingBreakpointId = this._textViewerLineNumberToBreakpointId[textViewerLineNumber]; - if (existingBreakpointId) { - WebInspector.debuggerModel.removeBreakpoint(breakpoint.id); - return; - } - - this._breakpointIdToTextViewerLineNumber[breakpoint.id] = textViewerLineNumber; - this._textViewerLineNumberToBreakpointId[textViewerLineNumber] = breakpoint.id; - this._setBreakpointDecoration(textViewerLineNumber, resolved, breakpoint.enabled, !!breakpoint.condition); - }, - - _breakpointRemoved: function(event) - { - var breakpointId = event.data; - - var textViewerLineNumber = this._breakpointIdToTextViewerLineNumber[breakpointId]; - if (textViewerLineNumber === undefined) - return; - - delete this._breakpointIdToTextViewerLineNumber[breakpointId]; - delete this._textViewerLineNumberToBreakpointId[textViewerLineNumber]; - this._removeBreakpointDecoration(textViewerLineNumber); - }, - - _breakpointResolved: function(event) - { - var breakpoint = event.data; - this._breakpointRemoved({ data: breakpoint.id }); - this._breakpointAdded({ data: breakpoint }); - }, - - _setBreakpointDecoration: function(lineNumber, resolved, enabled, hasCondition) + addBreakpoint: function(lineNumber, resolved, conditional, enabled) { this._textViewer.beginUpdates(); this._textViewer.addDecoration(lineNumber, "webkit-breakpoint"); if (!enabled) this._textViewer.addDecoration(lineNumber, "webkit-breakpoint-disabled"); - if (hasCondition) + if (conditional) this._textViewer.addDecoration(lineNumber, "webkit-breakpoint-conditional"); this._textViewer.endUpdates(); }, - _removeBreakpointDecoration: function(lineNumber) + removeBreakpoint: function(lineNumber) { this._textViewer.beginUpdates(); this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint"); @@ -534,55 +477,48 @@ WebInspector.SourceFrame.prototype = { _contextMenu: function(event) { - if (!WebInspector.panels.scripts) - return; - var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number"); if (!target) return; - var textViewerLineNumber = target.lineNumber; + var lineNumber = target.lineNumber; var contextMenu = new WebInspector.ContextMenu(); - contextMenu.appendItem(WebInspector.UIString("Continue to Here"), this._continueToLine.bind(this, textViewerLineNumber)); + contextMenu.appendItem(WebInspector.UIString("Continue to Here"), this._delegate.continueToLine.bind(this._delegate, lineNumber)); - var breakpoint = this._findBreakpoint(textViewerLineNumber); + var breakpoint = this._delegate.findBreakpoint(lineNumber); 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, textViewerLineNumber, "", true)); + contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), this._delegate.setBreakpoint.bind(this._delegate, lineNumber, "", true)); function addConditionalBreakpoint() { - this._setBreakpointDecoration(textViewerLineNumber, true, true, true); + this.addBreakpoint(lineNumber, true, true, true); function didEditBreakpointCondition(committed, condition) { - this._removeBreakpointDecoration(textViewerLineNumber); + this.removeBreakpoint(lineNumber); if (committed) - this._setBreakpoint(textViewerLineNumber, condition, true); + this._delegate.setBreakpoint(lineNumber, condition, true); } - this._editBreakpointCondition(textViewerLineNumber, "", didEditBreakpointCondition.bind(this)); + this._editBreakpointCondition(lineNumber, "", didEditBreakpointCondition.bind(this)); } contextMenu.appendItem(WebInspector.UIString("Add Conditional Breakpoint…"), addConditionalBreakpoint.bind(this)); } else { // This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable. - function removeBreakpoint() - { - WebInspector.debuggerModel.removeBreakpoint(breakpoint.id); - } - contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeBreakpoint); + contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), this._delegate.removeBreakpoint.bind(this._delegate, breakpoint.id)); function editBreakpointCondition() { function didEditBreakpointCondition(committed, condition) { if (committed) - WebInspector.debuggerModel.updateBreakpoint(breakpoint.id, condition, breakpoint.enabled); + this._delegate.updateBreakpoint(breakpoint.id, condition, breakpoint.enabled); } - this._editBreakpointCondition(textViewerLineNumber, breakpoint.condition, didEditBreakpointCondition.bind(this)); + this._editBreakpointCondition(lineNumber, breakpoint.condition, didEditBreakpointCondition.bind(this)); } contextMenu.appendItem(WebInspector.UIString("Edit Breakpoint…"), editBreakpointCondition.bind(this)); function setBreakpointEnabled(enabled) { - WebInspector.debuggerModel.updateBreakpoint(breakpoint.id, breakpoint.condition, enabled); + this._delegate.updateBreakpoint(breakpoint.id, breakpoint.condition, enabled); } if (breakpoint.enabled) contextMenu.appendItem(WebInspector.UIString("Disable Breakpoint"), setBreakpointEnabled.bind(this, false)); @@ -606,16 +542,16 @@ WebInspector.SourceFrame.prototype = { var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number"); if (!target) return; - var textViewerLineNumber = target.lineNumber; + var lineNumber = target.lineNumber; - var breakpoint = this._findBreakpoint(textViewerLineNumber); + var breakpoint = this._delegate.findBreakpoint(lineNumber); if (breakpoint) { if (event.shiftKey) - WebInspector.debuggerModel.updateBreakpoint(breakpoint.id, breakpoint.condition, !breakpoint.enabled); + this._delegate.updateBreakpoint(breakpoint.id, breakpoint.condition, !breakpoint.enabled); else - WebInspector.debuggerModel.removeBreakpoint(breakpoint.id); + this._delegate.removeBreakpoint(breakpoint.id); } else - this._setBreakpoint(textViewerLineNumber, "", true); + this._delegate.setBreakpoint(lineNumber, "", true); event.preventDefault(); }, @@ -641,7 +577,7 @@ WebInspector.SourceFrame.prototype = { this._hoverElement = event.target; // Now that cleanup routines are set up above, leave this in case we are not on a break. - if (!WebInspector.panels.scripts || !WebInspector.panels.scripts.paused) + if (!this._delegate.debuggerPaused()) return; // We are interested in identifiers and "this" keyword. @@ -680,16 +616,13 @@ WebInspector.SourceFrame.prototype = { this._popup.hide(); delete this._popup; - InspectorBackend.releaseWrapperObjectGroup(0, this._popoverObjectGroup); + this._delegate.releaseEvaluationResult(); }, _mouseHover: function(element) { delete this._hoverTimer; - if (!WebInspector.panels.scripts || !WebInspector.panels.scripts.paused) - return; - var lineRow = element.enclosingNodeOrSelfWithClass("webkit-line-content"); if (!lineRow) return; @@ -729,9 +662,6 @@ WebInspector.SourceFrame.prototype = { function showObjectPopup(result) { - if (!WebInspector.panels.scripts.paused) - return; - var popupContentElement = null; if (result.type !== "object" && result.type !== "node" && result.type !== "array") { popupContentElement = document.createElement("span"); @@ -766,15 +696,7 @@ WebInspector.SourceFrame.prototype = { popupContentElement.addEventListener("mousemove", killHidePopupTimer.bind(this), true); } - function evaluateCallback(result) - { - if (result.isError()) - return; - if (!WebInspector.panels.scripts.paused) - return; - showObjectPopup.call(this, result); - } - WebInspector.panels.scripts.evaluateInSelectedCallFrame(element.textContent, false, this._popoverObjectGroup, false, evaluateCallback.bind(this)); + this._delegate.evaluateInSelectedCallFrame(element.textContent, showObjectPopup.bind(this)); }, _editBreakpointCondition: function(lineNumber, condition, callback) @@ -820,24 +742,6 @@ WebInspector.SourceFrame.prototype = { return conditionElement; }, - _evalSelectionInCallFrame: function(event) - { - if (!WebInspector.panels.scripts || !WebInspector.panels.scripts.paused) - return; - - var selection = this.element.contentWindow.getSelection(); - if (!selection.rangeCount) - return; - - var expression = selection.getRangeAt(0).toString().trim(); - WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, "console", function(result) { - WebInspector.showConsole(); - var commandMessage = new WebInspector.ConsoleCommand(expression); - WebInspector.console.addMessage(commandMessage); - WebInspector.console.addMessage(new WebInspector.ConsoleCommandResult(result, commandMessage)); - }); - }, - resize: function() { if (this._textViewer) @@ -851,7 +755,7 @@ WebInspector.SourceFrame.prototype = { function didFormat(formattedContent) { - this._formattedContent = formattedContent; + this._content = formattedContent; this._textModel.setText(null, formattedContent.text); this._setTextViewerDecorations(); } @@ -859,91 +763,96 @@ WebInspector.SourceFrame.prototype = { formatter.formatContent(this._content, didFormat.bind(this)) }, - _continueToLine: function(lineNumber) - { - var location = this._textViewerLineNumberToScriptLocation(lineNumber); - if (location.sourceID) - WebInspector.debuggerModel.continueToLine(location.sourceID, location.lineNumber); - }, - _doubleClick: function(event) { - if (!Preferences.canEditScriptSource || !this._isScript) + if (!this._delegate.canEditScriptSource()) return; var lineRow = event.target.enclosingNodeOrSelfWithClass("webkit-line-content"); if (!lineRow) return; // Do not trigger editing from line numbers. - var lineNumber = lineRow.lineNumber; - var location = this._textViewerLineNumberToScriptLocation(lineNumber); - if (!location.sourceID) - return; + this._textViewer.editLine(lineRow, this._didEditLine.bind(this, lineRow.lineNumber)); + }, - function didEditLine(newContent) - { - var lines = []; - var oldLines = this._content.text.split('\n'); - for (var i = 0; i < oldLines.length; ++i) { - if (i === lineNumber) - lines.push(newContent); - else - lines.push(oldLines[i]); - } - WebInspector.debuggerModel.editScriptSource(location.sourceID, lines.join("\n")); + _didEditLine: function(lineNumber, newContent) + { + var lines = []; + var oldLines = this._content.text.split('\n'); + for (var i = 0; i < oldLines.length; ++i) { + if (i === lineNumber) + lines.push(newContent); + else + lines.push(oldLines[i]); } - this._textViewer.editLine(lineRow, didEditLine.bind(this)); + this._delegate.editScriptSource(lines.join("\n")); + } +} + +WebInspector.SourceFrame.prototype.__proto__ = WebInspector.View.prototype; + + +WebInspector.SourceFrameDelegate = function() +{ +} + +WebInspector.SourceFrameDelegate.prototype = { + requestContent: function(callback) + { + // Should be implemented by subclasses. }, - _setBreakpoint: function(lineNumber, condition, enabled) + debuggingSupported: function() { - var location = this._textViewerLineNumberToScriptLocation(lineNumber); - if (this._url) - WebInspector.debuggerModel.setBreakpoint(this._url, location.lineNumber, location.columnNumber, condition, enabled); - else if (location.sourceID) - WebInspector.debuggerModel.setBreakpointBySourceId(location.sourceID, location.lineNumber, location.columnNumber, condition, enabled); - else - return; + return false; + }, - if (!WebInspector.panels.scripts.breakpointsActivated) - WebInspector.panels.scripts.toggleBreakpointsClicked(); + setBreakpoint: function(lineNumber, condition, enabled) + { + // Should be implemented by subclasses. }, - _findBreakpoint: function(textViewerLineNumber) + removeBreakpoint: function(breakpointId) { - var breakpointId = this._textViewerLineNumberToBreakpointId[textViewerLineNumber]; - return WebInspector.debuggerModel.breakpointForId(breakpointId); + // Should be implemented by subclasses. }, - _originalLocationToTextViewerLineNumber: function(lineNumber, columnNumber) + updateBreakpoint: function(breakpointId, condition, enabled) { - if (!this._formattedContent) - return lineNumber; - return this._formattedContent.originalLocationToFormattedLocation(lineNumber, columnNumber).lineNumber; + // Should be implemented by subclasses. }, - _textViewerLineNumberToScriptLocation: function(lineNumber) + findBreakpoint: function(lineNumber) { - if (!this._formattedContent) - return this._content.scriptLocationForLineNumber(lineNumber); - return this._formattedContent.scriptLocationForFormattedLineNumber(lineNumber); - } -} + // Should be implemented by subclasses. + }, -WebInspector.SourceFrame.prototype.__proto__ = WebInspector.View.prototype; + continueToLine: function(lineNumber) + { + // Should be implemented by subclasses. + }, + canEditScriptSource: function() + { + return false; + }, -WebInspector.SourceFrameContentProvider = function() -{ -} + editScriptSource: function(text) + { + // Should be implemented by subclasses. + }, -WebInspector.SourceFrameContentProvider.prototype = { - requestContent: function(callback) + debuggerPaused: function() + { + // Should be implemented by subclasses. + }, + + evaluateInSelectedCallFrame: function(string) { // Should be implemented by subclasses. }, - scripts: function() + releaseEvaluationResult: function() { // Should be implemented by subclasses. } diff --git a/Source/WebCore/inspector/front-end/SourceFrameContent.js b/Source/WebCore/inspector/front-end/SourceFrameContent.js index e4a74ec..91b397b 100644 --- a/Source/WebCore/inspector/front-end/SourceFrameContent.js +++ b/Source/WebCore/inspector/front-end/SourceFrameContent.js @@ -28,24 +28,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.SourceFrameContent = function(text, scripts) +WebInspector.SourceFrameContent = function(text, mapping, scriptRanges) { - if (scripts.length && scripts[0].length < text.length) { - // WebKit html lexer normalizes line endings and scripts are passed to VM with "\n" line endings. - // However, resource content has original line endings, so we have to normalize line endings here. - text = text.replace(/\r\n/g, "\n"); - } this._text = text; - this._lineEndings = text.findAll("\n"); - this._lineEndings.push(text.length); - - this._scriptRanges = []; - for (var i = 0; i < scripts.length; ++i) { - var script = scripts[i]; - var offset = this.locationToPosition(script.lineOffset, script.columnOffset); - this._scriptRanges.push({ start: offset, end: offset + script.length, script: script }); - } - this._scriptRanges.sort(function(x, y) { return x.start - y.start; }); + this._mapping = mapping; + this._scriptRanges = scriptRanges; } WebInspector.SourceFrameContent.prototype = { @@ -59,95 +46,77 @@ WebInspector.SourceFrameContent.prototype = { return this._scriptRanges; }, - locationToPosition: function(lineNumber, columnNumber) - { - var position = lineNumber ? this._lineEndings[lineNumber - 1] + 1 : 0; - return position + columnNumber; - }, - - positionToLocation: function(position) + sourceFrameLineNumberToActualLocation: function(lineNumber) { - var location = {}; - location.lineNumber = this._lineEndings.upperBound(position - 1); - if (!location.lineNumber) - location.columnNumber = position; - else - location.columnNumber = position - this._lineEndings[location.lineNumber - 1] - 1; + // Script content may start right after <script> tag without new line (e.g. "<script>function f()..."). + // In that case, column number should be equal to script column offset. + var columnNumber = 0; + for (var i = 0; i < this._scriptRanges.length; ++i) { + var scriptRange = this._scriptRanges[i]; + if (scriptRange.start.lineNumber < lineNumber) + continue; + if (scriptRange.start.lineNumber === lineNumber) + columnNumber = scriptRange.start.columnNumber; + break; + } + var location = this._mapping.sourceLocationToActualLocation(lineNumber, columnNumber); + location.sourceID = this._sourceIDForSourceFrameLineNumber(lineNumber); return location; }, - scriptLocationForLineNumber: function(lineNumber) - { - var range = this.lineNumberToRange(lineNumber); - return this.scriptLocationForRange(range.start, range.end); - }, - - scriptLocationForRange: function(start, end) + actualLocationToSourceFrameLineNumber: function(lineNumber, columnNumber) { - var position = start; - var scriptRange = this._intersectingScriptRange(start, end); - if (scriptRange) - position = Math.max(position, scriptRange.start); - var scriptLocation = this.positionToLocation(position); - if (scriptRange) - scriptLocation.sourceID = scriptRange.script.sourceID; - return scriptLocation; + return this._mapping.actualLocationToSourceLocation(lineNumber, columnNumber).lineNumber; }, - lineNumberToRange: function(lineNumber) - { - var previousLineEnd = this._lineEndings[lineNumber - 1] || 0; - var lineEnd = this._lineEndings[lineNumber]; - return { start: previousLineEnd + 1, end: lineEnd }; - }, - - _intersectingScriptRange: function(start, end) + _sourceIDForSourceFrameLineNumber: function(lineNumber) { for (var i = 0; i < this._scriptRanges.length; ++i) { var scriptRange = this._scriptRanges[i]; - if (start < scriptRange.end && end > scriptRange.start) - return scriptRange; + if (lineNumber < scriptRange.start.lineNumber) + return; + if (lineNumber > scriptRange.end.lineNumber) + continue; + if (lineNumber === scriptRange.end.lineNumber && !scriptRange.end.columnNumber) + continue; + return scriptRange.sourceID; } } } -WebInspector.FormattedSourceFrameContent = function(originalContent, text, mapping) +WebInspector.SourceMapping = function() { - this._originalContent = originalContent; - this._formattedContent = new WebInspector.SourceFrameContent(text, []); - this._mapping = mapping; } -WebInspector.FormattedSourceFrameContent.prototype = { - get text() +WebInspector.SourceMapping.prototype = { + actualLocationToSourceLocation: function(lineNumber, columnNumber) { - return this._formattedContent.text; + // Should be implemented by subclasses. }, - originalLocationToFormattedLocation: function(lineNumber, columnNumber) + sourceLocationToActualLocation: function(lineNumber, columnNumber) { - var originalPosition = this._originalContent.locationToPosition(lineNumber, columnNumber); - var formattedPosition = this._convertPosition(this._mapping.original, this._mapping.formatted, originalPosition); - return this._formattedContent.positionToLocation(formattedPosition); - }, + // Should be implemented by subclasses. + } +} + + +WebInspector.IdenticalSourceMapping = function() +{ + WebInspector.SourceMapping.call(this); +} - scriptLocationForFormattedLineNumber: function(lineNumber) +WebInspector.IdenticalSourceMapping.prototype = { + actualLocationToSourceLocation: function(lineNumber, columnNumber) { - var range = this._formattedContent.lineNumberToRange(lineNumber); - var start = this._convertPosition(this._mapping.formatted, this._mapping.original, range.start); - var end = this._convertPosition(this._mapping.formatted, this._mapping.original, range.end); - return this._originalContent.scriptLocationForRange(start, end); + return { lineNumber: lineNumber, columnNumber: columnNumber}; }, - _convertPosition: function(positions1, positions2, position) + sourceLocationToActualLocation: function(lineNumber, columnNumber) { - var index = positions1.upperBound(position); - var range1 = positions1[index] - positions1[index - 1]; - var range2 = positions2[index] - positions2[index - 1]; - var position2 = positions2[index - 1]; - if (range1) - position2 += Math.round((position - positions1[index - 1]) * range2 / range1); - return position2; + return { lineNumber: lineNumber, columnNumber: columnNumber}; } } + +WebInspector.IdenticalSourceMapping.prototype.__proto__ = WebInspector.SourceMapping.prototype; diff --git a/Source/WebCore/inspector/front-end/SourceTokenizer.js b/Source/WebCore/inspector/front-end/SourceTokenizer.js index d30744c..bb44a86 100644 --- a/Source/WebCore/inspector/front-end/SourceTokenizer.js +++ b/Source/WebCore/inspector/front-end/SourceTokenizer.js @@ -48,11 +48,6 @@ WebInspector.SourceTokenizer.prototype = { return this._condition; }, - get subTokenizer() - { - return this._condition.subTokenizer; - }, - getLexCondition: function() { return this.condition.lexCondition; diff --git a/Source/WebCore/inspector/front-end/StylesSidebarPane.js b/Source/WebCore/inspector/front-end/StylesSidebarPane.js index 57d3b76..c7d151a 100644 --- a/Source/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/Source/WebCore/inspector/front-end/StylesSidebarPane.js @@ -438,7 +438,7 @@ WebInspector.StylesSidebarPane.prototype = { if (computedStyle) var section = new WebInspector.ComputedStylePropertiesSection(styleRule, usedProperties, disabledComputedProperties, styleRules); else - var section = new WebInspector.StylePropertiesSection(styleRule, editable, styleRule.isInherited, lastWasSeparator); + var section = new WebInspector.StylePropertiesSection(this, styleRule, editable, styleRule.isInherited, lastWasSeparator); section.pane = this; section.expanded = true; @@ -503,7 +503,7 @@ WebInspector.StylesSidebarPane.prototype = { addBlankSection: function() { - var blankSection = new WebInspector.BlankStylePropertiesSection(appropriateSelectorForNode(this.node, true)); + var blankSection = new WebInspector.BlankStylePropertiesSection(this, appropriateSelectorForNode(this.node, true)); blankSection.pane = this; var elementStyleSection = this.sections[0][1]; @@ -591,7 +591,7 @@ WebInspector.ComputedStyleSidebarPane = function() WebInspector.ComputedStyleSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; -WebInspector.StylePropertiesSection = function(styleRule, editable, isInherited, isFirstSection) +WebInspector.StylePropertiesSection = function(parentPane, styleRule, editable, isInherited, isFirstSection) { WebInspector.PropertiesSection.call(this, ""); this.element.className = "styles-section monospace" + (isFirstSection ? " first-styles-section" : ""); @@ -613,6 +613,7 @@ WebInspector.StylePropertiesSection = function(styleRule, editable, isInherited, this._selectorElement.addEventListener("dblclick", this._handleSelectorDoubleClick.bind(this), false); this.element.addEventListener("dblclick", this._handleEmptySpaceDoubleClick.bind(this), false); + this._parentPane = parentPane; this.styleRule = styleRule; this.rule = this.styleRule.rule; this.editable = editable; @@ -792,7 +793,7 @@ WebInspector.StylePropertiesSection.prototype = { var inherited = this.isPropertyInherited(property.name); var overloaded = this.isPropertyOverloaded(property.name, isShorthand); - var item = new WebInspector.StylePropertyTreeElement(this.styleRule, style, property, isShorthand, inherited, overloaded); + var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded); this.propertiesTreeOutline.appendChild(item); handledProperties[property.name] = property; } @@ -813,7 +814,7 @@ WebInspector.StylePropertiesSection.prototype = { { var style = this.styleRule.style; var property = style.newBlankProperty(); - var item = new WebInspector.StylePropertyTreeElement(this.styleRule, style, property, false, false, false); + var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, false, false, false); this.propertiesTreeOutline.appendChild(item); item.listItemElement.textContent = ""; item._newProperty = true; @@ -1025,7 +1026,7 @@ WebInspector.ComputedStylePropertiesSection.prototype = { for (var i = 0; i < uniqueProperties.length; ++i) { var property = uniqueProperties[i]; var inherited = this._isPropertyInherited(property.name); - var item = new WebInspector.StylePropertyTreeElement(this.styleRule, style, property, false, inherited, false); + var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, false, inherited, false); this.propertiesTreeOutline.appendChild(item); this._propertyTreeElements[property.name] = item; } @@ -1070,9 +1071,9 @@ WebInspector.ComputedStylePropertiesSection.prototype = { WebInspector.ComputedStylePropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype; -WebInspector.BlankStylePropertiesSection = function(defaultSelectorText) +WebInspector.BlankStylePropertiesSection = function(parentPane, defaultSelectorText) { - WebInspector.StylePropertiesSection.call(this, {selectorText: defaultSelectorText, rule: {isViaInspector: true}}, true, false, false); + WebInspector.StylePropertiesSection.call(this, parentPane, {selectorText: defaultSelectorText, rule: {isViaInspector: true}}, true, false, false); this.element.addStyleClass("blank-section"); } @@ -1121,8 +1122,9 @@ WebInspector.BlankStylePropertiesSection.prototype = { WebInspector.BlankStylePropertiesSection.prototype.__proto__ = WebInspector.StylePropertiesSection.prototype; -WebInspector.StylePropertyTreeElement = function(styleRule, style, property, shorthand, inherited, overloaded) +WebInspector.StylePropertyTreeElement = function(parentPane, styleRule, style, property, shorthand, inherited, overloaded) { + this._parentPane = parentPane; this._styleRule = styleRule; this.style = style; this.property = property; @@ -1483,7 +1485,7 @@ WebInspector.StylePropertyTreeElement.prototype = { } var liveProperty = this.style.getLiveProperty(name); - var item = new WebInspector.StylePropertyTreeElement(this._styleRule, this.style, liveProperty, false, inherited, overloaded); + var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this._styleRule, this.style, liveProperty, false, inherited, overloaded); this.appendChild(item); } }, @@ -1609,6 +1611,7 @@ WebInspector.StylePropertyTreeElement.prototype = { return "move-forward"; } + this._parentPane.isModifyingStyle = true; WebInspector.startEditing(selectElement, { context: context, commitHandler: this.editingCommitted.bind(this), @@ -1719,6 +1722,7 @@ WebInspector.StylePropertyTreeElement.prototype = { editedElement.parentElement.removeStyleClass("child-editing"); delete this.originalPropertyText; + delete this._parentPane.isModifyingStyle; }, editingCancelled: function(element, context) @@ -1767,6 +1771,7 @@ WebInspector.StylePropertyTreeElement.prototype = { var isDirtyViaPaste = isDataPasted && (this.nameElement.textContent !== context.originalName || this.valueElement.textContent !== context.originalValue); var shouldCommitNewProperty = this._newProperty && (moveToOther || (!moveDirection && !isEditingName) || (isEditingName && blankInput)); if (((userInput !== previousContent || isDirtyViaPaste) && !this._newProperty) || shouldCommitNewProperty) { + this._parentPane.isModifyingStyle = true; this.treeOutline.section._afterUpdate = moveToNextCallback.bind(this, this._newProperty, !blankInput, this.treeOutline.section); var propertyText; if (blankInput || (this._newProperty && /^\s*$/.test(this.valueElement.textContent))) @@ -1781,7 +1786,7 @@ WebInspector.StylePropertyTreeElement.prototype = { } else { if (!isDataPasted && !this._newProperty) this.updateTitle(); - moveToNextCallback(this._newProperty, false, this.treeOutline.section); + moveToNextCallback.call(this, this._newProperty, false, this.treeOutline.section); } var moveToIndex = moveTo && this.treeOutline ? this.treeOutline.children.indexOf(moveTo) : -1; @@ -1789,6 +1794,8 @@ WebInspector.StylePropertyTreeElement.prototype = { // The Callback to start editing the next/previous property/selector. function moveToNextCallback(alreadyNew, valueChanged, section) { + delete this._parentPane.isModifyingStyle; + if (!moveDirection) return; diff --git a/Source/WebCore/inspector/front-end/TestController.js b/Source/WebCore/inspector/front-end/TestController.js index 3bfe28c..e6783f9 100644 --- a/Source/WebCore/inspector/front-end/TestController.js +++ b/Source/WebCore/inspector/front-end/TestController.js @@ -28,40 +28,20 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.TestController = function(callId) +WebInspector.TestController = function() { - this._callId = callId; - this._waitUntilDone = false; - this.results = []; } WebInspector.TestController.prototype = { - waitUntilDone: function() + notifyDone: function(callId, result) { - this._waitUntilDone = true; - }, - - notifyDone: function(result) - { - if (typeof result === "undefined" && this.results.length) - result = this.results; var message = typeof result === "undefined" ? "\"<undefined>\"" : JSON.stringify(result); - InspectorBackend.didEvaluateForTestInFrontend(this._callId, message); - }, - - runAfterPendingDispatches: function(callback) - { - if (WebInspector.pendingDispatches === 0) { - callback(); - return; - } - setTimeout(this.runAfterPendingDispatches.bind(this), 0, callback); + InspectorAgent.didEvaluateForTestInFrontend(callId, message); } } WebInspector.evaluateForTestInFrontend = function(callId, script) { - var controller = new WebInspector.TestController(callId); function invokeMethod() { try { @@ -71,11 +51,10 @@ WebInspector.evaluateForTestInFrontend = function(callId, script) else result = window.eval(script); - if (!controller._waitUntilDone) - controller.notifyDone(result); + WebInspector.TestController.prototype.notifyDone(callId, result); } catch (e) { - controller.notifyDone(e.toString()); + WebInspector.testController.prototype.notifyDone(callId, e.toString()); } } - controller.runAfterPendingDispatches(invokeMethod); + InspectorBackend.runAfterPendingDispatches(invokeMethod); } diff --git a/Source/WebCore/inspector/front-end/TextEditorHighlighter.js b/Source/WebCore/inspector/front-end/TextEditorHighlighter.js index 4ac831e..9bdcc82 100644 --- a/Source/WebCore/inspector/front-end/TextEditorHighlighter.js +++ b/Source/WebCore/inspector/front-end/TextEditorHighlighter.js @@ -34,6 +34,7 @@ WebInspector.TextEditorHighlighter = function(textModel, damageCallback) this._textModel = textModel; this._tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/html"); this._damageCallback = damageCallback; + this._highlightChunkLimit = 1000; this.reset(); } @@ -43,18 +44,23 @@ WebInspector.TextEditorHighlighter.prototype = { var tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer(mimeType); if (tokenizer) { this._tokenizer = tokenizer; - this._tokenizerCondition = this._tokenizer.initialCondition; + this._tokenizerConditionStringified = JSON.stringify(this._tokenizer.initialCondition); } }, + set highlightChunkLimit(highlightChunkLimit) + { + this._highlightChunkLimit = highlightChunkLimit; + }, + reset: function() { this._lastHighlightedLine = 0; this._lastHighlightedColumn = 0; - this._tokenizerCondition = this._tokenizer.initialCondition; + this._tokenizerConditionStringified = JSON.stringify(this._tokenizer.initialCondition); }, - highlight: function(endLine) + highlight: function(endLine, opt_forceRun) { // First check if we have work to do. if (endLine <= this._lastHighlightedLine) @@ -62,7 +68,7 @@ WebInspector.TextEditorHighlighter.prototype = { this._requestedEndLine = endLine; - if (this._highlightTimer) { + if (this._highlightTimer && !opt_forceRun) { // There is a timer scheduled, it will catch the new job based on the new endLine set. return; } @@ -75,6 +81,58 @@ WebInspector.TextEditorHighlighter.prototype = { this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, endLine), 100); }, + updateHighlight: function(startLine, endLine) + { + if (this._lastHighlightedLine < startLine) { + // Highlighter did not reach this point yet, nothing to update. It will reach it on subsequent timer tick and do the job. + return false; + } + + var savedLastHighlightedLine = this._lastHighlightedLine; + var savedLastHighlightedColumn = this._lastHighlightedColumn; + var savedTokenizerCondition = this._tokenizerConditionStringified; + + this._lastHighlightedLine = startLine; + this._lastHighlightedColumn = 0; + + // Restore highlighter context taken from the previous line. + var attributes = this._textModel.getAttribute(startLine - 1, "highlight") || {}; + this._tokenizerConditionStringified = attributes.postConditionStringified || JSON.stringify(this._tokenizer.initialCondition); + + // Try to update highlight synchronously. + this._highlightLines(endLine); + + if (this._lastHighlightedLine >= this._textModel.linesCount) { + // All is done up to the end. + return true; + } + + var attributes1 = this._textModel.getAttribute(this._lastHighlightedLine - 1, "highlight") || {}; + var attributes2 = this._textModel.getAttribute(this._lastHighlightedLine, "highlight") || {}; + if (this._lastHighlightedColumn === 0 && attributes2.preConditionStringified && attributes1.postConditionStringified === attributes2.preConditionStringified) { + // Highlighting ended ahead of time. Restore previously saved state, unless we have done more than it was before. + if (savedLastHighlightedLine >= this._lastHighlightedLine) { + this._lastHighlightedLine = savedLastHighlightedLine; + this._lastHighlightedColumn = savedLastHighlightedColumn; + this._tokenizerConditionStringified = savedTokenizerCondition; + } + return true; + } else { + // If failed to update highlight synchronously, invalidate highlight data for the subsequent lines. + if (this._lastHighlightedColumn === 0) + this._textModel.removeAttribute(this._lastHighlightedLine, "highlight"); + for (var i = this._lastHighlightedLine + 1; i < this._textModel.linesCount; ++i) + this._textModel.removeAttribute(i, "highlight"); + + // Continue highlighting on subsequent timer ticks. + this._requestedEndLine = endLine; + if (!this._highlightTimer) + this._highlightTimer = setTimeout(this._highlightInChunks.bind(this, endLine), 100); + + return false; + } + }, + _highlightInChunks: function(endLine) { delete this._highlightTimer; @@ -89,6 +147,10 @@ WebInspector.TextEditorHighlighter.prototype = { return; } + // The textModel may have been already updated. + if (this._requestedEndLine > this._textModel.linesCount) + this._requestedEndLine = this._textModel.linesCount; + this._highlightLines(this._requestedEndLine); // Schedule tail highlight if necessary. @@ -98,35 +160,49 @@ WebInspector.TextEditorHighlighter.prototype = { _highlightLines: function(endLine) { - // Tokenizer is stateless and reused accross viewers, restore its condition before highlight and save it after. - this._tokenizer.condition = this._tokenizerCondition; + // Tokenizer is stateless and reused across viewers, restore its condition before highlight and save it after. + this._tokenizer.condition = JSON.parse(this._tokenizerConditionStringified); var tokensCount = 0; for (var lineNumber = this._lastHighlightedLine; lineNumber < endLine; ++lineNumber) { var line = this._textModel.line(lineNumber); this._tokenizer.line = line; - var attributes = this._textModel.getAttribute(lineNumber, "highlight") || {}; + + if (this._lastHighlightedColumn === 0) { + var attributes = {}; + attributes.preConditionStringified = JSON.stringify(this._tokenizer.condition); + this._textModel.setAttribute(lineNumber, "highlight", attributes); + } else + var attributes = this._textModel.getAttribute(lineNumber, "highlight"); // Highlight line. do { var newColumn = this._tokenizer.nextToken(this._lastHighlightedColumn); var tokenType = this._tokenizer.tokenType; if (tokenType) - attributes[this._lastHighlightedColumn] = { length: newColumn - this._lastHighlightedColumn, tokenType: tokenType, subTokenizer: this._tokenizer.subTokenizer }; + attributes[this._lastHighlightedColumn] = { length: newColumn - this._lastHighlightedColumn, tokenType: tokenType }; this._lastHighlightedColumn = newColumn; - if (++tokensCount > 1000) + if (++tokensCount > this._highlightChunkLimit) break; - } while (this._lastHighlightedColumn < line.length) + } while (this._lastHighlightedColumn < line.length); - this._textModel.setAttribute(lineNumber, "highlight", attributes); if (this._lastHighlightedColumn < line.length) { // Too much work for single chunk - exit. break; - } else + } else { this._lastHighlightedColumn = 0; + attributes.postConditionStringified = JSON.stringify(this._tokenizer.condition); + + var nextAttributes = this._textModel.getAttribute(lineNumber + 1, "highlight") || {}; + if (nextAttributes.preConditionStringified === attributes.postConditionStringified) { + // Following lines are up to date, no need to re-highlight. + ++lineNumber; + break; + } + } } this._damageCallback(this._lastHighlightedLine, lineNumber); - this._tokenizerCondition = this._tokenizer.condition; + this._tokenizerConditionStringified = JSON.stringify(this._tokenizer.condition); this._lastHighlightedLine = lineNumber; } } diff --git a/Source/WebCore/inspector/front-end/TextViewer.js b/Source/WebCore/inspector/front-end/TextViewer.js index ce6502d..65b4724 100644 --- a/Source/WebCore/inspector/front-end/TextViewer.js +++ b/Source/WebCore/inspector/front-end/TextViewer.js @@ -37,9 +37,11 @@ WebInspector.TextViewer = function(textModel, platform, url) this.element = document.createElement("div"); this.element.className = "text-editor monospace"; + var enterTextChangeMode = this._enterInternalTextChangeMode.bind(this); + var exitTextChangeMode = this._exitInternalTextChangeMode.bind(this); var syncScrollListener = this._syncScroll.bind(this); var syncDecorationsForLineListener = this._syncDecorationsForLine.bind(this); - this._mainPanel = new WebInspector.TextEditorMainPanel(this._textModel, url, syncScrollListener, syncDecorationsForLineListener); + this._mainPanel = new WebInspector.TextEditorMainPanel(this._textModel, url, syncScrollListener, syncDecorationsForLineListener, enterTextChangeMode, exitTextChangeMode); this._gutterPanel = new WebInspector.TextEditorGutterPanel(this._textModel, syncDecorationsForLineListener); this.element.appendChild(this._mainPanel.element); this.element.appendChild(this._gutterPanel.element); @@ -51,6 +53,21 @@ WebInspector.TextViewer.prototype = { this._mainPanel.mimeType = mimeType; }, + set readOnly(readOnly) + { + this._mainPanel.readOnly = readOnly; + }, + + set startEditingListener(startEditingListener) + { + this._startEditingListener = startEditingListener; + }, + + set endEditingListener(endEditingListener) + { + this._endEditingListener = endEditingListener; + }, + get textModel() { return this._textModel; @@ -129,6 +146,7 @@ WebInspector.TextViewer.prototype = { { this._mainPanel.endUpdates(); this._gutterPanel.endUpdates(); + this._updatePanelOffsets(); }, resize: function() @@ -141,9 +159,31 @@ WebInspector.TextViewer.prototype = { // WebInspector.TextModel listener _textChanged: function(oldRange, newRange, oldText, newText) { - this._mainPanel.textChanged(); - this._gutterPanel.textChanged(); + if (!this._internalTextChangeMode) { + this._mainPanel.textChanged(oldRange, newRange); + this._gutterPanel.textChanged(oldRange, newRange); + this._updatePanelOffsets(); + } + }, + + _enterInternalTextChangeMode: function() + { + this._internalTextChangeMode = true; + + if (this._startEditingListener) + this._startEditingListener(); + }, + + _exitInternalTextChangeMode: function(oldRange, newRange) + { + this._internalTextChangeMode = false; + + // Update the gutter panel. + this._gutterPanel.textChanged(oldRange, newRange); this._updatePanelOffsets(); + + if (this._endEditingListener) + this._endEditingListener(oldRange, newRange); }, _updatePanelOffsets: function() @@ -161,13 +201,8 @@ WebInspector.TextViewer.prototype = { setTimeout(function() { var mainElement = this._mainPanel.element; var gutterElement = this._gutterPanel.element; - // Handle horizontal scroll bar at the bottom of the main panel. - if (gutterElement.offsetHeight > mainElement.clientHeight) - gutterElement.style.setProperty("padding-bottom", (gutterElement.offsetHeight - mainElement.clientHeight) + "px"); - else - gutterElement.style.removeProperty("padding-bottom"); - + this._gutterPanel.syncClientHeight(mainElement.clientHeight); gutterElement.scrollTop = mainElement.scrollTop; }.bind(this), 0); }, @@ -177,13 +212,19 @@ WebInspector.TextViewer.prototype = { if (lineNumber >= this._textModel.linesCount) return; - var mainChunk = this._mainPanel.makeLineAChunk(lineNumber); - var gutterChunk = this._gutterPanel.makeLineAChunk(lineNumber); - var height = mainChunk.height; - if (height) - gutterChunk.element.style.setProperty("height", height + "px"); - else - gutterChunk.element.style.removeProperty("height"); + var mainChunk = this._mainPanel.chunkForLine(lineNumber); + if (mainChunk.linesCount === 1) { + var gutterChunk = this._gutterPanel.makeLineAChunk(lineNumber); + var height = mainChunk.height; + if (height) + gutterChunk.element.style.setProperty("height", height + "px"); + else + gutterChunk.element.style.removeProperty("height"); + } else { + var gutterChunk = this._gutterPanel.chunkForLine(lineNumber); + if (gutterChunk.linesCount === 1) + gutterChunk.element.style.removeProperty("height"); + } } } @@ -193,6 +234,7 @@ WebInspector.TextEditorChunkedPanel = function(textModel) this._defaultChunkSize = 50; this._paintCoalescingLevel = 0; + this._domUpdateCoalescingLevel = 0; } WebInspector.TextEditorChunkedPanel.prototype = { @@ -222,23 +264,27 @@ WebInspector.TextEditorChunkedPanel.prototype = { chunk.removeDecoration(decoration); }, - textChanged: function(oldRange, newRange, oldText, newText) + textChanged: function(oldRange, newRange) { this._buildChunks(); }, _buildChunks: function() { - this.element.removeChildren(); + this.beginDomUpdates(); + + this._container.removeChildren(); this._textChunks = []; for (var i = 0; i < this._textModel.linesCount; i += this._defaultChunkSize) { var chunk = this._createNewChunk(i, i + this._defaultChunkSize); this._textChunks.push(chunk); - this.element.appendChild(chunk.element); + this._container.appendChild(chunk.element); } this._repaintAll(); + + this.endDomUpdates(); }, makeLineAChunk: function(lineNumber) @@ -251,6 +297,8 @@ WebInspector.TextEditorChunkedPanel.prototype = { if (oldChunk.linesCount === 1) return oldChunk; + this.beginDomUpdates(); + var wasExpanded = oldChunk.expanded; oldChunk.expanded = false; @@ -260,24 +308,24 @@ WebInspector.TextEditorChunkedPanel.prototype = { if (lineNumber > oldChunk.startLine) { var prefixChunk = this._createNewChunk(oldChunk.startLine, lineNumber); this._textChunks.splice(insertIndex++, 0, prefixChunk); - this.element.insertBefore(prefixChunk.element, oldChunk.element); + this._container.insertBefore(prefixChunk.element, oldChunk.element); } // Line chunk. var lineChunk = this._createNewChunk(lineNumber, lineNumber + 1); this._textChunks.splice(insertIndex++, 0, lineChunk); - this.element.insertBefore(lineChunk.element, oldChunk.element); + this._container.insertBefore(lineChunk.element, oldChunk.element); // Suffix chunk. if (oldChunk.startLine + oldChunk.linesCount > lineNumber + 1) { var suffixChunk = this._createNewChunk(lineNumber + 1, oldChunk.startLine + oldChunk.linesCount); this._textChunks.splice(insertIndex, 0, suffixChunk); - this.element.insertBefore(suffixChunk.element, oldChunk.element); + this._container.insertBefore(suffixChunk.element, oldChunk.element); } // Remove enclosing chunk. this._textChunks.splice(chunkNumber, 1); - this.element.removeChild(oldChunk.element); + this._container.removeChild(oldChunk.element); if (wasExpanded) { if (prefixChunk) @@ -287,11 +335,17 @@ WebInspector.TextEditorChunkedPanel.prototype = { suffixChunk.expanded = true; } + this.endDomUpdates(); + return lineChunk; }, _scroll: function() { + // FIXME: Replace the "2" with the padding-left value from CSS. + if (this.element.scrollLeft <= 2) + this.element.scrollLeft = 0; + this._scheduleRepaintAll(); if (this._syncScrollListener) this._syncScrollListener(); @@ -316,26 +370,52 @@ WebInspector.TextEditorChunkedPanel.prototype = { this._repaintAll(); }, + beginDomUpdates: function() + { + this._domUpdateCoalescingLevel++; + }, + + endDomUpdates: function() + { + this._domUpdateCoalescingLevel--; + }, + _chunkNumberForLine: function(lineNumber) { - for (var i = 0; i < this._textChunks.length; ++i) { - var line = this._textChunks[i].startLine; - if (lineNumber >= line && lineNumber < line + this._textChunks[i].linesCount) - return i; + function compareLineNumbers(value, chunk) + { + return value < chunk.startLine ? -1 : 1; } - return this._textChunks.length - 1; + var insertBefore = insertionIndexForObjectInListSortedByFunction(lineNumber, this._textChunks, compareLineNumbers); + return insertBefore - 1; }, - _chunkForLine: function(lineNumber) + chunkForLine: function(lineNumber) { return this._textChunks[this._chunkNumberForLine(lineNumber)]; }, + _findVisibleChunks: function(visibleFrom, visibleTo) + { + function compareOffsetTops(value, chunk) + { + return value < chunk.offsetTop ? -1 : 1; + } + var insertBefore = insertionIndexForObjectInListSortedByFunction(visibleFrom, this._textChunks, compareOffsetTops); + + var from = insertBefore - 1; + for (var to = from + 1; to < this._textChunks.length; ++to) { + if (this._textChunks[to].offsetTop >= visibleTo) + break; + } + return { start: from, end: to }; + }, + _repaintAll: function() { delete this._repaintAllTimer; - if (this._paintCoalescingLevel) + if (this._paintCoalescingLevel || this._dirtyLines) return; if (!this._textChunks) @@ -344,25 +424,10 @@ WebInspector.TextEditorChunkedPanel.prototype = { var visibleFrom = this.element.scrollTop; var visibleTo = this.element.scrollTop + this.element.clientHeight; - var offset = 0; - var fromIndex = -1; - var toIndex = 0; - for (var i = 0; i < this._textChunks.length; ++i) { - var chunk = this._textChunks[i]; - var chunkHeight = chunk.height; - if (offset + chunkHeight > visibleFrom && offset < visibleTo) { - if (fromIndex === -1) - fromIndex = i; - toIndex = i + 1; - } else { - if (offset >= visibleTo) - break; - } - offset += chunkHeight; + if (visibleTo) { + var result = this._findVisibleChunks(visibleFrom, visibleTo); + this._expandChunks(result.start, result.end); } - - if (toIndex) - this._expandChunks(fromIndex, toIndex); }, _totalHeight: function(firstElement, lastElement) @@ -370,11 +435,19 @@ WebInspector.TextEditorChunkedPanel.prototype = { lastElement = (lastElement || firstElement).nextElementSibling; if (lastElement) return lastElement.offsetTop - firstElement.offsetTop; - else if (firstElement.offsetParent) - return firstElement.offsetParent.scrollHeight - firstElement.offsetTop; - return firstElement.offsetHeight; + + var offsetParent = firstElement.offsetParent; + if (offsetParent && offsetParent.scrollHeight > offsetParent.clientHeight) + return offsetParent.scrollHeight - firstElement.offsetTop; + + var total = 0; + while (firstElement && firstElement !== lastElement) { + total += firstElement.offsetHeight; + firstElement = firstElement.nextElementSibling; + } + return total; }, - + resize: function() { this._repaintAll(); @@ -390,6 +463,10 @@ WebInspector.TextEditorGutterPanel = function(textModel, syncDecorationsForLineL this.element = document.createElement("div"); this.element.className = "text-editor-lines"; + this._container = document.createElement("div"); + this._container.className = "inner-container"; + this.element.appendChild(this._container); + this.element.addEventListener("scroll", this._scroll.bind(this), false); this.freeCachedElements(); @@ -409,9 +486,59 @@ WebInspector.TextEditorGutterPanel.prototype = { _expandChunks: function(fromIndex, toIndex) { - for (var i = 0; i < this._textChunks.length; ++i) { + for (var i = 0; i < this._textChunks.length; ++i) this._textChunks[i].expanded = (fromIndex <= i && i < toIndex); + }, + + textChanged: function(oldRange, newRange) + { + if (!this._textChunks) { + this._buildChunks(); + return; } + + var linesDiff = newRange.linesCount - oldRange.linesCount; + if (linesDiff) { + // Remove old chunks (if needed). + for (var chunkNumber = this._textChunks.length - 1; chunkNumber >= 0 ; --chunkNumber) { + var chunk = this._textChunks[chunkNumber]; + if (chunk.startLine + chunk.linesCount <= this._textModel.linesCount) + break; + chunk.expanded = false; + this._container.removeChild(chunk.element); + } + this._textChunks.length = chunkNumber + 1; + + // Add new chunks (if needed). + var totalLines = 0; + if (this._textChunks.length) { + var lastChunk = this._textChunks[this._textChunks.length - 1]; + totalLines = lastChunk.startLine + lastChunk.linesCount; + } + for (var i = totalLines; i < this._textModel.linesCount; i += this._defaultChunkSize) { + var chunk = this._createNewChunk(i, i + this._defaultChunkSize); + this._textChunks.push(chunk); + this._container.appendChild(chunk.element); + } + this._repaintAll(); + } else { + // Decorations may have been removed, so we may have to sync those lines. + var chunkNumber = this._chunkNumberForLine(newRange.startLine); + var chunk = this._textChunks[chunkNumber]; + while (chunk && chunk.startLine <= newRange.endLine) { + if (chunk.linesCount === 1) + this._syncDecorationsForLineListener(chunk.startLine); + chunk = this._textChunks[++chunkNumber]; + } + } + }, + + syncClientHeight: function(clientHeight) + { + if (this.element.offsetHeight > clientHeight) + this._container.style.setProperty("padding-bottom", (this.element.offsetHeight - clientHeight) + "px"); + else + this._container.style.removeProperty("padding-bottom"); } } @@ -444,9 +571,8 @@ WebInspector.TextEditorGutterChunk = function(textViewer, startLine, endLine) this.element.appendChild(outerSpan); } else { var lineNumbers = []; - for (var i = startLine; i < endLine; ++i) { + for (var i = startLine; i < endLine; ++i) lineNumbers.push(i + 1); - } this.element.textContent = lineNumbers.join("\n"); } } @@ -454,16 +580,18 @@ WebInspector.TextEditorGutterChunk = function(textViewer, startLine, endLine) WebInspector.TextEditorGutterChunk.prototype = { addDecoration: function(decoration) { - if (typeof decoration === "string") { + this._textViewer.beginDomUpdates(); + if (typeof decoration === "string") this.element.addStyleClass(decoration); - } + this._textViewer.endDomUpdates(); }, removeDecoration: function(decoration) { - if (typeof decoration === "string") { + this._textViewer.beginDomUpdates(); + if (typeof decoration === "string") this.element.removeStyleClass(decoration); - } + this._textViewer.endDomUpdates(); }, get expanded() @@ -484,6 +612,8 @@ WebInspector.TextEditorGutterChunk.prototype = { if (this.linesCount === 1) return; + this._textViewer.beginDomUpdates(); + if (expanded) { this._expandedLineRows = []; var parentElement = this.element.parentElement; @@ -503,12 +633,14 @@ WebInspector.TextEditorGutterChunk.prototype = { elementInserted = true; parentElement.insertBefore(this.element, lineRow); } - this._textViewer._cachedRows.push(lineRow); parentElement.removeChild(lineRow); } + this._textViewer._cachedRows.push(lineRow); } delete this._expandedLineRows; } + + this._textViewer.endDomUpdates(); }, get height() @@ -518,6 +650,11 @@ WebInspector.TextEditorGutterChunk.prototype = { return this._textViewer._totalHeight(this._expandedLineRows[0], this._expandedLineRows[this._expandedLineRows.length - 1]); }, + get offsetTop() + { + return (this._expandedLineRows && this._expandedLineRows.length) ? this._expandedLineRows[0].offsetTop : this.element.offsetTop; + }, + _createRow: function(lineNumber) { var lineRow = this._textViewer._cachedRows.pop() || document.createElement("div"); @@ -528,27 +665,42 @@ WebInspector.TextEditorGutterChunk.prototype = { } } -WebInspector.TextEditorMainPanel = function(textModel, url, syncScrollListener, syncDecorationsForLineListener) +WebInspector.TextEditorMainPanel = function(textModel, url, syncScrollListener, syncDecorationsForLineListener, enterTextChangeMode, exitTextChangeMode) { WebInspector.TextEditorChunkedPanel.call(this, textModel); this._syncScrollListener = syncScrollListener; this._syncDecorationsForLineListener = syncDecorationsForLineListener; + this._enterTextChangeMode = enterTextChangeMode; + this._exitTextChangeMode = exitTextChangeMode; this._url = url; this._highlighter = new WebInspector.TextEditorHighlighter(textModel, this._highlightDataReady.bind(this)); + this._readOnly = true; this.element = document.createElement("div"); this.element.className = "text-editor-contents"; this.element.tabIndex = 0; + this._container = document.createElement("div"); + this._container.className = "inner-container"; + this._container.tabIndex = 0; + this.element.appendChild(this._container); + this.element.addEventListener("scroll", this._scroll.bind(this), false); - this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false); - var handleDOMUpdates = this._handleDOMUpdates.bind(this); - this.element.addEventListener("DOMCharacterDataModified", handleDOMUpdates, false); - this.element.addEventListener("DOMNodeInserted", handleDOMUpdates, false); - this.element.addEventListener("DOMNodeRemoved", handleDOMUpdates, false); + // FIXME: Remove old live editing functionality and Preferences.sourceEditorEnabled flag. + if (!Preferences.sourceEditorEnabled) + this._container.addEventListener("keydown", this._handleKeyDown.bind(this), false); + + // In WebKit the DOMNodeRemoved event is fired AFTER the node is removed, thus it should be + // attached to all DOM nodes that we want to track. Instead, we attach the DOMNodeRemoved + // listeners only on the line rows, and use DOMSubtreeModified to track node removals inside + // the line rows. For more info see: https://bugs.webkit.org/show_bug.cgi?id=55666 + this._handleDOMUpdatesCallback = this._handleDOMUpdates.bind(this); + this._container.addEventListener("DOMCharacterDataModified", this._handleDOMUpdatesCallback, false); + this._container.addEventListener("DOMNodeInserted", this._handleDOMUpdatesCallback, false); + this._container.addEventListener("DOMSubtreeModified", this._handleDOMUpdatesCallback, false); this.freeCachedElements(); this._buildChunks(); @@ -560,6 +712,21 @@ WebInspector.TextEditorMainPanel.prototype = { this._highlighter.mimeType = mimeType; }, + set readOnly(readOnly) + { + // FIXME: Remove the Preferences.sourceEditorEnabled flag. + if (!Preferences.sourceEditorEnabled) + return; + + this.beginDomUpdates(); + this._readOnly = readOnly; + if (this._readOnly) + this._container.removeStyleClass("text-editor-editable"); + else + this._container.addStyleClass("text-editor-editable"); + this.endDomUpdates(); + }, + markAndRevealRange: function(range) { if (this._rangeToMark) { @@ -653,6 +820,9 @@ WebInspector.TextEditorMainPanel.prototype = { _buildChunks: function() { this._highlighter.reset(); + for (var i = 0; i < this._textModel.linesCount; ++i) + this._textModel.removeAttribute(i, "highlight"); + WebInspector.TextEditorChunkedPanel.prototype._buildChunks.call(this); }, @@ -672,9 +842,8 @@ WebInspector.TextEditorMainPanel.prototype = { this._highlighter.highlight(lastVisibleLine); delete this._muteHighlightListener; - for (var i = 0; i < this._textChunks.length; ++i) { + for (var i = 0; i < this._textChunks.length; ++i) this._textChunks[i].expanded = (fromIndex <= i && i < toIndex); - } this._restoreSelection(selection); }, @@ -686,13 +855,46 @@ WebInspector.TextEditorMainPanel.prototype = { this._paintLines(fromLine, toLine, true /*restoreSelection*/); }, + _markSkippedPaintLines: function(startLine, endLine) + { + if (!this._skippedPaintLines) + this._skippedPaintLines = [ { startLine: startLine, endLine: endLine } ]; + else { + for (var i = 0; i < this._skippedPaintLines.length; ++i) { + var chunk = this._skippedPaintLines[i]; + if (chunk.startLine <= endLine && chunk.endLine >= startLine) { + chunk.startLine = Math.min(chunk.startLine, startLine); + chunk.endLine = Math.max(chunk.endLine, endLine); + return; + } + } + this._skippedPaintLines.push({ startLine: startLine, endLine: endLine }); + } + }, + + _paintSkippedLines: function() + { + if (!this._skippedPaintLines || this._dirtyLines) + return; + for (var i = 0; i < this._skippedPaintLines.length; ++i) { + var chunk = this._skippedPaintLines[i]; + this._paintLines(chunk.startLine, chunk.endLine); + } + delete this._skippedPaintLines; + }, + _paintLines: function(fromLine, toLine, restoreSelection) { + if (this._dirtyLines) { + this._markSkippedPaintLines(fromLine, toLine); + return; + } + var selection; - var chunk = this._chunkForLine(fromLine); + var chunk = this.chunkForLine(fromLine); for (var i = fromLine; i < toLine; ++i) { if (i >= chunk.startLine + chunk.linesCount) - chunk = this._chunkForLine(i); + chunk = this.chunkForLine(i); var lineRow = chunk.getExpandedLineRow(i); if (!lineRow) continue; @@ -706,46 +908,56 @@ WebInspector.TextEditorMainPanel.prototype = { _paintLine: function(lineRow, lineNumber) { - var highlight = this._textModel.getAttribute(lineNumber, "highlight"); - if (!highlight) { - if (this._rangeToMark && this._rangeToMark.startLine === lineNumber) - this._markedRangeElement = highlightSearchResult(lineRow, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn); + if (this._dirtyLines) { + this._markSkippedPaintLines(lineNumber, lineNumber + 1); return; } - lineRow.removeChildren(); - var line = this._textModel.line(lineNumber); - if (!line) - lineRow.appendChild(document.createElement("br")); - - var plainTextStart = -1; - for (var j = 0; j < line.length;) { - if (j > 1000) { - // This line is too long - do not waste cycles on minified js highlighting. - if (plainTextStart === -1) - plainTextStart = j; - break; + this.beginDomUpdates(); + try { + var highlight = this._textModel.getAttribute(lineNumber, "highlight"); + if (!highlight) { + if (this._rangeToMark && this._rangeToMark.startLine === lineNumber) + this._markedRangeElement = highlightSearchResult(lineRow, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn); + return; } - var attribute = highlight[j]; - if (!attribute || !attribute.tokenType) { - if (plainTextStart === -1) - plainTextStart = j; - j++; - } else { - if (plainTextStart !== -1) { - this._appendTextNode(lineRow, line.substring(plainTextStart, j)); - plainTextStart = -1; + + lineRow.removeChildren(); + var line = this._textModel.line(lineNumber); + if (!line) + lineRow.appendChild(document.createElement("br")); + + var plainTextStart = -1; + for (var j = 0; j < line.length;) { + if (j > 1000) { + // This line is too long - do not waste cycles on minified js highlighting. + if (plainTextStart === -1) + plainTextStart = j; + break; + } + var attribute = highlight[j]; + if (!attribute || !attribute.tokenType) { + if (plainTextStart === -1) + plainTextStart = j; + j++; + } else { + if (plainTextStart !== -1) { + this._appendTextNode(lineRow, line.substring(plainTextStart, j)); + plainTextStart = -1; + } + this._appendSpan(lineRow, line.substring(j, j + attribute.length), attribute.tokenType); + j += attribute.length; } - this._appendSpan(lineRow, line.substring(j, j + attribute.length), attribute.tokenType); - j += attribute.length; } + if (plainTextStart !== -1) + this._appendTextNode(lineRow, line.substring(plainTextStart, line.length)); + if (this._rangeToMark && this._rangeToMark.startLine === lineNumber) + this._markedRangeElement = highlightSearchResult(lineRow, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn); + if (lineRow.decorationsElement) + lineRow.appendChild(lineRow.decorationsElement); + } finally { + this.endDomUpdates(); } - if (plainTextStart !== -1) - this._appendTextNode(lineRow, line.substring(plainTextStart, line.length)); - if (this._rangeToMark && this._rangeToMark.startLine === lineNumber) - this._markedRangeElement = highlightSearchResult(lineRow, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn); - if (lineRow.decorationsElement) - lineRow.appendChild(lineRow.decorationsElement); }, _releaseLinesHighlight: function(lineRow) @@ -774,7 +986,7 @@ WebInspector.TextEditorMainPanel.prototype = { return null; var selectionRange = selection.getRangeAt(0); // Selection may be outside of the viewer. - if (!this.element.isAncestor(selectionRange.startContainer) || !this.element.isAncestor(selectionRange.endContainer)) + if (!this._container.isAncestor(selectionRange.startContainer) || !this._container.isAncestor(selectionRange.endContainer)) return null; var start = this._selectionToPosition(selectionRange.startContainer, selectionRange.startOffset); var end = selectionRange.collapsed ? start : this._selectionToPosition(selectionRange.endContainer, selectionRange.endOffset); @@ -795,9 +1007,9 @@ WebInspector.TextEditorMainPanel.prototype = { _selectionToPosition: function(container, offset) { - if (container === this.element && offset === 0) + if (container === this._container && offset === 0) return { line: 0, column: 0 }; - if (container === this.element && offset === 1) + if (container === this._container && offset === 1) return { line: this._textModel.linesCount - 1, column: this._textModel.lineLength(this._textModel.linesCount - 1) }; var lineRow = container.enclosingNodeOrSelfWithNodeName("DIV"); @@ -835,7 +1047,7 @@ WebInspector.TextEditorMainPanel.prototype = { _positionToSelection: function(line, column) { - var chunk = this._chunkForLine(line); + var chunk = this.chunkForLine(line); var lineRow = chunk.getExpandedLineRow(line); if (lineRow) var rangeBoundary = lineRow.rangeBoundaryForOffset(column); @@ -909,17 +1121,268 @@ WebInspector.TextEditorMainPanel.prototype = { _handleDOMUpdates: function(e) { + if (this._domUpdateCoalescingLevel) + return; + var target = e.target; + if (target === this._container) + return; + var lineRow = target.enclosingNodeOrSelfWithClass("webkit-line-content"); - if (lineRow === target || !lineRow || !lineRow.decorationsElement || !lineRow.decorationsElement.isAncestor(target)) + if (!lineRow) return; - if (this._syncDecorationsForLineListener) { - // Wait until this event is processed and only then sync the sizes. This is necessary in - // case of the DOMNodeRemoved event, because it is dispatched before the removal takes place. - setTimeout(function() { + + if (lineRow.decorationsElement && (lineRow.decorationsElement === target || lineRow.decorationsElement.isAncestor(target))) { + if (this._syncDecorationsForLineListener) this._syncDecorationsForLineListener(lineRow.lineNumber); - }.bind(this), 0); + return; + } + + if (this._readOnly) + return; + + var lineNumber = lineRow.lineNumber; + if (this._dirtyLines) { + this._dirtyLines.start = Math.min(this._dirtyLines.start, lineNumber); + this._dirtyLines.end = Math.max(this._dirtyLines.end, lineNumber + 1); + } else { + this._dirtyLines = { start: lineNumber, end: lineNumber + 1 }; + setTimeout(this._applyDomUpdates.bind(this), 0); + // Remove marked ranges, if any. + this.markAndRevealRange(null); + } + }, + + _applyDomUpdates: function() + { + if (!this._dirtyLines) + return; + + // Check if the editor had been set readOnly by the moment when this async callback got executed. + if (this._readOnly) { + delete this._dirtyLines; + return; + } + + // This is a "foreign" call outside of this class. Should be before we delete the dirty lines flag. + this._enterTextChangeMode(); + + var dirtyLines = this._dirtyLines; + delete this._dirtyLines; + + var firstChunkNumber = this._chunkNumberForLine(dirtyLines.start); + var startLine = this._textChunks[firstChunkNumber].startLine; + var endLine = this._textModel.linesCount; + + // Collect lines. + var firstLineRow; + if (firstChunkNumber) { + var chunk = this._textChunks[firstChunkNumber - 1]; + firstLineRow = chunk.expanded ? chunk.getExpandedLineRow(chunk.startLine + chunk.linesCount - 1) : chunk.element; + firstLineRow = firstLineRow.nextSibling; + } else + firstLineRow = this._container.firstChild; + + var lines = []; + for (var lineRow = firstLineRow; lineRow; lineRow = lineRow.nextSibling) { + if (typeof lineRow.lineNumber === "number" && lineRow.lineNumber >= dirtyLines.end) { + endLine = lineRow.lineNumber; + break; + } + // Update with the newest lineNumber, so that the call to the _getSelection method below should work. + lineRow.lineNumber = startLine + lines.length; + this._collectLinesFromDiv(lines, lineRow); + } + + // Try to decrease the range being replaced if possible. + var startOffset = 0; + while (startLine < dirtyLines.start && startOffset < lines.length) { + if (this._textModel.line(startLine) !== lines[startOffset]) + break; + ++startOffset; + ++startLine; + } + + var endOffset = lines.length; + while (endLine > dirtyLines.end && endOffset > startOffset) { + if (this._textModel.line(endLine - 1) !== lines[endOffset - 1]) + break; + --endOffset; + --endLine; + } + + lines = lines.slice(startOffset, endOffset); + + var selection = this._getSelection(); + + if (lines.length === 0 && endLine < this._textModel.linesCount) { + var oldRange = new WebInspector.TextRange(startLine, 0, endLine, 0); + var newRange = this._textModel.setText(oldRange, ""); + } else { + var oldRange = new WebInspector.TextRange(startLine, 0, endLine - 1, this._textModel.lineLength(endLine - 1)); + var newRange = this._textModel.setText(oldRange, lines.join("\n")); + } + + this.beginDomUpdates(); + this._removeDecorationsInRange(oldRange); + this._updateChunksForRanges(oldRange, newRange); + this._updateHighlightsForRange(newRange); + this._paintSkippedLines(); + this.endDomUpdates(); + + this._restoreSelection(selection); + + this._exitTextChangeMode(oldRange, newRange); + }, + + _removeDecorationsInRange: function(range) + { + for (var i = this._chunkNumberForLine(range.startLine); i < this._textChunks.length; ++i) { + var chunk = this._textChunks[i]; + if (chunk.startLine > range.endLine) + break; + chunk.removeAllDecorations(); + } + }, + + _updateChunksForRanges: function(oldRange, newRange) + { + // Update the chunks in range: firstChunkNumber <= index <= lastChunkNumber + var firstChunkNumber = this._chunkNumberForLine(oldRange.startLine); + var lastChunkNumber = firstChunkNumber; + while (lastChunkNumber + 1 < this._textChunks.length) { + if (this._textChunks[lastChunkNumber + 1].startLine > oldRange.endLine) + break; + ++lastChunkNumber; + } + + var startLine = this._textChunks[firstChunkNumber].startLine; + var linesCount = this._textChunks[lastChunkNumber].startLine + this._textChunks[lastChunkNumber].linesCount - startLine; + var linesDiff = newRange.linesCount - oldRange.linesCount; + linesCount += linesDiff; + + if (linesDiff) { + // Lines shifted, update the line numbers of the chunks below. + for (var chunkNumber = lastChunkNumber + 1; chunkNumber < this._textChunks.length; ++chunkNumber) + this._textChunks[chunkNumber].startLine += linesDiff; + } + + var firstLineRow; + if (firstChunkNumber) { + var chunk = this._textChunks[firstChunkNumber - 1]; + firstLineRow = chunk.expanded ? chunk.getExpandedLineRow(chunk.startLine + chunk.linesCount - 1) : chunk.element; + firstLineRow = firstLineRow.nextSibling; + } else + firstLineRow = this._container.firstChild; + + // Most frequent case: a chunk remained the same. + for (var chunkNumber = firstChunkNumber; chunkNumber <= lastChunkNumber; ++chunkNumber) { + var chunk = this._textChunks[chunkNumber]; + var lineNumber = chunk.startLine; + for (var lineRow = firstLineRow; lineRow && lineNumber < chunk.startLine + chunk.linesCount; lineRow = lineRow.nextSibling) { + if (lineRow.lineNumber !== lineNumber || lineRow !== chunk.getExpandedLineRow(lineNumber) || lineRow.textContent !== this._textModel.line(lineNumber) || !lineRow.firstChild) + break; + ++lineNumber; + } + if (lineNumber < chunk.startLine + chunk.linesCount) + break; + chunk.updateCollapsedLineRow(); + ++firstChunkNumber; + firstLineRow = lineRow; + startLine += chunk.linesCount; + linesCount -= chunk.linesCount; + } + + if (firstChunkNumber > lastChunkNumber && linesCount === 0) + return; + + // Maybe merge with the next chunk, so that we should not create 1-sized chunks when appending new lines one by one. + var chunk = this._textChunks[lastChunkNumber + 1]; + var linesInLastChunk = linesCount % this._defaultChunkSize; + if (chunk && !chunk.decorated && linesInLastChunk > 0 && linesInLastChunk + chunk.linesCount <= this._defaultChunkSize) { + ++lastChunkNumber; + linesCount += chunk.linesCount; + } + + var scrollTop = this.element.scrollTop; + var scrollLeft = this.element.scrollLeft; + + // Delete all DOM elements that were either controlled by the old chunks, or have just been inserted. + var firstUnmodifiedLineRow = null; + var chunk = this._textChunks[lastChunkNumber + 1]; + if (chunk) { + firstUnmodifiedLineRow = chunk.expanded ? chunk.getExpandedLineRow(chunk.startLine) : chunk.element; } + while (firstLineRow && firstLineRow !== firstUnmodifiedLineRow) { + var lineRow = firstLineRow; + firstLineRow = firstLineRow.nextSibling; + this._container.removeChild(lineRow); + } + + // Replace old chunks with the new ones. + for (var chunkNumber = firstChunkNumber; linesCount > 0; ++chunkNumber) { + var chunkLinesCount = Math.min(this._defaultChunkSize, linesCount); + var newChunk = this._createNewChunk(startLine, startLine + chunkLinesCount); + this._container.insertBefore(newChunk.element, firstUnmodifiedLineRow); + + if (chunkNumber <= lastChunkNumber) + this._textChunks[chunkNumber] = newChunk; + else + this._textChunks.splice(chunkNumber, 0, newChunk); + startLine += chunkLinesCount; + linesCount -= chunkLinesCount; + } + if (chunkNumber <= lastChunkNumber) + this._textChunks.splice(chunkNumber, lastChunkNumber - chunkNumber + 1); + + this.element.scrollTop = scrollTop; + this.element.scrollLeft = scrollLeft; + }, + + _updateHighlightsForRange: function(range) + { + var visibleFrom = this.element.scrollTop; + var visibleTo = this.element.scrollTop + this.element.clientHeight; + + var result = this._findVisibleChunks(visibleFrom, visibleTo); + var chunk = this._textChunks[result.end - 1]; + var lastVisibleLine = chunk.startLine + chunk.linesCount; + + lastVisibleLine = Math.max(lastVisibleLine, range.endLine); + + var updated = this._highlighter.updateHighlight(range.startLine, lastVisibleLine); + if (!updated) { + // Highlights for the chunks below are invalid, so just collapse them. + for (var i = this._chunkNumberForLine(range.startLine); i < this._textChunks.length; ++i) + this._textChunks[i].expanded = false; + } + + this._repaintAll(); + }, + + _collectLinesFromDiv: function(lines, element) + { + var textContents = []; + var node = element.traverseNextNode(element); + while (node) { + if (element.decorationsElement === node) { + node = node.nextSibling; + continue; + } + if (node.nodeName.toLowerCase() === "br") + textContents.push("\n"); + else if (node.nodeType === Node.TEXT_NODE) + textContents.push(node.textContent); + node = node.traverseNextNode(element); + } + + var textContent = textContents.join(""); + // The last \n (if any) does not "count" in a DIV. + textContent = textContent.replace(/\n$/, ""); + + textContents = textContent.split("\n"); + for (var i = 0; i < textContents.length; ++i) + lines.push(textContents[i]); } } @@ -933,49 +1396,73 @@ WebInspector.TextEditorMainChunk = function(textViewer, startLine, endLine) this.element = document.createElement("div"); this.element.lineNumber = startLine; this.element.className = "webkit-line-content"; + this.element.addEventListener("DOMNodeRemoved", this._textViewer._handleDOMUpdatesCallback, false); - this.startLine = startLine; + this._startLine = startLine; endLine = Math.min(this._textModel.linesCount, endLine); this.linesCount = endLine - startLine; this._expanded = false; - var lines = []; - for (var i = startLine; i < endLine; ++i) { - lines.push(this._textModel.line(i)); - } - - this.element.textContent = lines.join("\n"); - - // The last empty line will get swallowed otherwise. - if (!lines[lines.length - 1]) - this.element.appendChild(document.createElement("br")); + this.updateCollapsedLineRow(); } WebInspector.TextEditorMainChunk.prototype = { addDecoration: function(decoration) { - if (typeof decoration === "string") { + this._textViewer.beginDomUpdates(); + if (typeof decoration === "string") this.element.addStyleClass(decoration); - return; - } - if (!this.element.decorationsElement) { - this.element.decorationsElement = document.createElement("div"); - this.element.decorationsElement.className = "webkit-line-decorations"; - this.element.appendChild(this.element.decorationsElement); + else { + if (!this.element.decorationsElement) { + this.element.decorationsElement = document.createElement("div"); + this.element.decorationsElement.className = "webkit-line-decorations"; + this.element.appendChild(this.element.decorationsElement); + } + this.element.decorationsElement.appendChild(decoration); } - this.element.decorationsElement.appendChild(decoration); + this._textViewer.endDomUpdates(); }, removeDecoration: function(decoration) { - if (typeof decoration === "string") { + this._textViewer.beginDomUpdates(); + if (typeof decoration === "string") this.element.removeStyleClass(decoration); - return; + else if (this.element.decorationsElement) + this.element.decorationsElement.removeChild(decoration); + this._textViewer.endDomUpdates(); + }, + + removeAllDecorations: function() + { + this._textViewer.beginDomUpdates(); + this.element.className = "webkit-line-content"; + if (this.element.decorationsElement) { + this.element.removeChild(this.element.decorationsElement); + delete this.element.decorationsElement; + } + this._textViewer.endDomUpdates(); + }, + + get decorated() + { + return this.element.className !== "webkit-line-content" || !!(this.element.decorationsElement && this.element.decorationsElement.firstChild); + }, + + get startLine() + { + return this._startLine; + }, + + set startLine(startLine) + { + this._startLine = startLine; + this.element.lineNumber = startLine; + if (this._expandedLineRows) { + for (var i = 0; i < this._expandedLineRows.length; ++i) + this._expandedLineRows[i].lineNumber = startLine + i; } - if (!this.element.decorationsElement) - return; - this.element.decorationsElement.removeChild(decoration); }, get expanded() @@ -996,6 +1483,8 @@ WebInspector.TextEditorMainChunk.prototype = { return; } + this._textViewer.beginDomUpdates(); + if (expanded) { this._expandedLineRows = []; var parentElement = this.element.parentElement; @@ -1016,12 +1505,14 @@ WebInspector.TextEditorMainChunk.prototype = { elementInserted = true; parentElement.insertBefore(this.element, lineRow); } - this._textViewer._releaseLinesHighlight(lineRow); parentElement.removeChild(lineRow); } + this._textViewer._releaseLinesHighlight(lineRow); } delete this._expandedLineRows; } + + this._textViewer.endDomUpdates(); }, get height() @@ -1031,11 +1522,17 @@ WebInspector.TextEditorMainChunk.prototype = { return this._textViewer._totalHeight(this._expandedLineRows[0], this._expandedLineRows[this._expandedLineRows.length - 1]); }, + get offsetTop() + { + return (this._expandedLineRows && this._expandedLineRows.length) ? this._expandedLineRows[0].offsetTop : this.element.offsetTop; + }, + _createRow: function(lineNumber) { var lineRow = this._textViewer._cachedRows.pop() || document.createElement("div"); lineRow.lineNumber = lineNumber; lineRow.className = "webkit-line-content"; + lineRow.addEventListener("DOMNodeRemoved", this._textViewer._handleDOMUpdatesCallback, false); lineRow.textContent = this._textModel.line(lineNumber); if (!lineRow.textContent) lineRow.appendChild(document.createElement("br")); @@ -1049,5 +1546,22 @@ WebInspector.TextEditorMainChunk.prototype = { if (!this._expandedLineRows) return this.element; return this._expandedLineRows[lineNumber - this.startLine]; + }, + + updateCollapsedLineRow: function() + { + if (this.linesCount === 1 && this._expanded) + return; + + var lines = []; + for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) + lines.push(this._textModel.line(i)); + + this.element.removeChildren(); + this.element.textContent = lines.join("\n"); + + // The last empty line will get swallowed otherwise. + if (!lines[lines.length - 1]) + this.element.appendChild(document.createElement("br")); } } diff --git a/Source/WebCore/inspector/front-end/TimelinePanel.js b/Source/WebCore/inspector/front-end/TimelinePanel.js index 1d8b9c9..62c5de0 100644 --- a/Source/WebCore/inspector/front-end/TimelinePanel.js +++ b/Source/WebCore/inspector/front-end/TimelinePanel.js @@ -270,10 +270,10 @@ WebInspector.TimelinePanel.prototype = { _toggleTimelineButtonClicked: function() { if (this.toggleTimelineButton.toggled) - InspectorBackend.stopTimelineProfiler(); + TimelineAgent.stop(); else { this._clearPanel(); - InspectorBackend.startTimelineProfiler(); + TimelineAgent.start(); } }, @@ -865,7 +865,6 @@ WebInspector.TimelinePanel.FormattedRecord = function(record, parentRecord, pane this.endTime = (typeof record.endTime !== "undefined") ? record.endTime / 1000 : this.startTime; this._selfTime = this.endTime - this.startTime; this._lastChildEndTime = this.endTime; - this.originalRecordForTests = record; if (record.stackTrace && record.stackTrace.length) this.stackTrace = record.stackTrace; this.totalHeapSize = record.totalHeapSize; diff --git a/Source/WebCore/inspector/front-end/Toolbar.js b/Source/WebCore/inspector/front-end/Toolbar.js new file mode 100755 index 0000000..d1cc9a6 --- /dev/null +++ b/Source/WebCore/inspector/front-end/Toolbar.js @@ -0,0 +1,220 @@ + /* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com). + * Copyright (C) 2009 Joseph Pecoraro + * 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.Toolbar = function() +{ + this.element = document.getElementById("toolbar"); + this.element.addEventListener("mousedown", this._toolbarDragStart.bind(this), true); + + this._dropdownButton = document.getElementById("toolbar-dropdown-arrow"); + this._dropdownButton.addEventListener("click", this._toggleDropdown.bind(this), false); + + document.getElementById("close-button-left").addEventListener("click", this._onClose, true); + document.getElementById("close-button-right").addEventListener("click", this._onClose, true); +} + +WebInspector.Toolbar.prototype = { + resize: function() + { + this._updateDropdownButtonAndHideDropdown(); + }, + + addPanel: function(panel) + { + this.element.appendChild(panel.toolbarItem); + this.resize(); + }, + + _toolbarDragStart: function(event) + { + if ((!WebInspector.attached && WebInspector.platformFlavor !== WebInspector.PlatformFlavor.MacLeopard && WebInspector.platformFlavor !== WebInspector.PlatformFlavor.MacSnowLeopard) || WebInspector.port == "qt") + return; + + var target = event.target; + if (target.hasStyleClass("toolbar-item") && target.hasStyleClass("toggleable")) + return; + + if (target !== this.element && !target.hasStyleClass("toolbar-item")) + return; + + this.element.lastScreenX = event.screenX; + this.element.lastScreenY = event.screenY; + + WebInspector.elementDragStart(this.element, this._toolbarDrag.bind(this), this._toolbarDragEnd.bind(this), event, (WebInspector.attached ? "row-resize" : "default")); + }, + + _toolbarDragEnd: function(event) + { + WebInspector.elementDragEnd(event); + + delete this.element.lastScreenX; + delete this.element.lastScreenY; + }, + + _toolbarDrag: function(event) + { + if (WebInspector.attached) { + var height = window.innerHeight - (event.screenY - this.element.lastScreenY); + + InspectorFrontendHost.setAttachedWindowHeight(height); + } else { + var x = event.screenX - this.element.lastScreenX; + var y = event.screenY - this.element.lastScreenY; + + // We cannot call window.moveBy here because it restricts the movement + // of the window at the edges. + InspectorFrontendHost.moveWindowBy(x, y); + } + + this.element.lastScreenX = event.screenX; + this.element.lastScreenY = event.screenY; + + event.preventDefault(); + }, + + _onClose: function() + { + WebInspector.close(); + }, + + _setDropdownVisible: function(visible) + { + if (!this._dropdown) { + if (!visible) + return; + this._dropdown = new WebInspector.ToolbarDropdown(); + } + if (visible) + this._dropdown.show(); + else + this._dropdown.hide(); + }, + + _toggleDropdown: function() + { + this._setDropdownVisible(!this._dropdown || !this._dropdown.visible); + }, + + _updateDropdownButtonAndHideDropdown: function() + { + this._setDropdownVisible(false); + + var toolbar = document.getElementById("toolbar"); + if (this.element.scrollHeight > this.element.clientHeight) + this._dropdownButton.removeStyleClass("hidden"); + else + this._dropdownButton.addStyleClass("hidden"); + } +}; + +WebInspector.Toolbar.createPanelToolbarItem = function(panel) +{ + var toolbarItem = document.createElement("button"); + toolbarItem.className = "toolbar-item toggleable"; + toolbarItem.panel = panel; + toolbarItem.addStyleClass(panel._panelName); + function onToolbarItemClicked() + { + WebInspector.toolbar._updateDropdownButtonAndHideDropdown(); + WebInspector.currentPanel = panel; + } + toolbarItem.addEventListener("click", onToolbarItemClicked); + + var iconElement = toolbarItem.createChild("div", "toolbar-icon"); + + if ("toolbarItemLabel" in panel) + toolbarItem.createChild("div", "toolbar-label").textContent = panel.toolbarItemLabel; + + if (panel === WebInspector.currentPanel) + toolbarItem.addStyleClass("toggled-on"); + + return toolbarItem; +} + +WebInspector.ToolbarDropdown = function() +{ + this._toolbar = document.getElementById("toolbar"); + this._arrow = document.getElementById("toolbar-dropdown-arrow"); + this.element = document.createElement("div"); + this.element.id = "toolbar-dropdown"; + this._contentElement = this.element.createChild("div", "scrollable-content"); + this._contentElement.tabIndex = 0; + this._contentElement.addEventListener("keydown", this._onKeyDown.bind(this), true); +} + +WebInspector.ToolbarDropdown.prototype = { + show: function() + { + if (this.visible) + return; + var style = this.element.style; + this._populate(); + var top = this._arrow.totalOffsetTop + this._arrow.clientHeight; + this._arrow.addStyleClass("dropdown-visible"); + this.element.style.top = top + "px"; + this.element.style.left = this._arrow.totalOffsetLeft + "px"; + this._contentElement.style.maxHeight = window.innerHeight - top - 20 + "px"; + this._toolbar.appendChild(this.element); + WebInspector.currentFocusElement = this.contentElement; + }, + + hide: function() + { + if (!this.visible) + return; + this._arrow.removeStyleClass("dropdown-visible"); + this.element.parentNode.removeChild(this.element); + this._contentElement.removeChildren(); + }, + + get visible() + { + return !!this.element.parentNode; + }, + + _populate: function() + { + var toolbarItems = this._toolbar.querySelectorAll(".toolbar-item.toggleable"); + + for (var i = 0; i < toolbarItems.length; ++i) { + if (toolbarItems[i].offsetTop > 0) + this._contentElement.appendChild(WebInspector.Toolbar.createPanelToolbarItem(toolbarItems[i].panel)); + } + }, + + _onKeyDown: function(event) + { + if (event.keyCode !== WebInspector.KeyboardShortcut.Keys.Esc.code) + return; + event.stopPropagation(); + this.hide(); + } +}; diff --git a/Source/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js b/Source/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js index a6f59ca..bb3460d 100644 --- a/Source/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js +++ b/Source/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js @@ -120,7 +120,7 @@ WebInspector.WatchExpressionsSection.prototype = { } // TODO: pass exact injected script id. - InspectorBackend.releaseWrapperObjectGroup(0, this._watchObjectGroupId) + RuntimeAgent.releaseObjectGroup(0, this._watchObjectGroupId) var properties = []; // Count the properties, so we known when to call this.updateProperties() diff --git a/Source/WebCore/inspector/front-end/WebKit.qrc b/Source/WebCore/inspector/front-end/WebKit.qrc index dd325ba..91d72d8 100644 --- a/Source/WebCore/inspector/front-end/WebKit.qrc +++ b/Source/WebCore/inspector/front-end/WebKit.qrc @@ -27,9 +27,11 @@ <file>Database.js</file> <file>DatabaseQueryView.js</file> <file>DatabaseTableView.js</file> + <file>DetailedHeapshotGridNodes.js</file> <file>DetailedHeapshotView.js</file> <file>DataGrid.js</file> <file>DebuggerModel.js</file> + <file>DebuggerPresentationModel.js</file> <file>DOMAgent.js</file> <file>DOMStorage.js</file> <file>DOMStorageItemsView.js</file> @@ -85,6 +87,7 @@ <file>ScriptFormatter.js</file> <file>ScriptFormatterWorker.js</file> <file>ScriptsPanel.js</file> + <file>SearchController.js</file> <file>Section.js</file> <file>Settings.js</file> <file>ShortcutsHelp.js</file> @@ -110,6 +113,7 @@ <file>TimelineGrid.js</file> <file>TimelineOverviewPane.js</file> <file>TimelinePanel.js</file> + <file>Toolbar.js</file> <file>TopDownProfileDataGridTree.js</file> <file>treeoutline.js</file> <file>utilities.js</file> diff --git a/Source/WebCore/inspector/front-end/WorkersSidebarPane.js b/Source/WebCore/inspector/front-end/WorkersSidebarPane.js index b254f3c..efdb936 100644 --- a/Source/WebCore/inspector/front-end/WorkersSidebarPane.js +++ b/Source/WebCore/inspector/front-end/WorkersSidebarPane.js @@ -74,9 +74,9 @@ WebInspector.WorkersSidebarPane.prototype = { setInstrumentation: function(enabled) { - InspectorBackend.removeAllScriptsToEvaluateOnLoad(); + InspectorAgent.removeAllScriptsToEvaluateOnLoad(); if (enabled) - InspectorBackend.addScriptToEvaluateOnLoad("(" + InjectedFakeWorker + ")"); + InspectorAgent.addScriptToEvaluateOnLoad("(" + InjectedFakeWorker + ")"); }, reset: function() diff --git a/Source/WebCore/inspector/front-end/heapProfiler.css b/Source/WebCore/inspector/front-end/heapProfiler.css index 03a6dd0..add02a1 100644 --- a/Source/WebCore/inspector/front-end/heapProfiler.css +++ b/Source/WebCore/inspector/front-end/heapProfiler.css @@ -134,3 +134,108 @@ width: 50%; left: 25%; } + +.detailed-heapshot-view { + display: none; + overflow: hidden; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + -webkit-box-orient: vertical; +} + +.detailed-heapshot-view.visible { + display: -webkit-box; +} + +.detailed-heapshot-view .view { + display: none; + -webkit-box-flex: 3; + -webkit-box-orient: vertical; +} + +.detailed-heapshot-view .view.visible { + display: -webkit-box; +} + +.detailed-heapshot-view .data-grid { + border: none; + position: relative; + -webkit-box-flex: 1; +} + +.detailed-heapshot-view .data-grid td.count-column { + text-align: right; +} + +.detailed-heapshot-view .data-grid td.addedCount-column { + text-align: right; +} + +.detailed-heapshot-view .data-grid td.removedCount-column { + text-align: right; +} + +.detailed-heapshot-view .data-grid td.countDelta-column { + text-align: right; +} + +.detailed-heapshot-view .data-grid td.addedSize-column { + text-align: right; +} + +.detailed-heapshot-view .data-grid td.removedSize-column { + text-align: right; +} + +.detailed-heapshot-view .data-grid td.sizeDelta-column { + text-align: right; +} + +.detailed-heapshot-view .data-grid td.shallowSize-column { + text-align: right; +} + +.detailed-heapshot-view .data-grid td.retainedSize-column { + text-align: right; +} + +.detailed-heapshot-view .console-formatted-object, .console-formatted-node { + display: inline; + position: static; +} + +.detailed-heapshot-view .delimiter { + height: 24px; + background-color: #d6dde5; +} + +.detailed-heapshot-view .retaining-paths-view { + -webkit-box-flex: 1; +} + +.detailed-heapshot-view .retaining-paths-view .title { + background-color: rgb(235, 235, 235); + background-image: url(Images/statusbarBackground.png); + background-repeat: repeat-x; + height: 23px; + font: -webkit-small-control; + font-weight: bold; + color: rgb(48, 48, 48); + text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0; +} + +.detailed-heapshot-view .retaining-paths-view .title > span { + vertical-align: middle; + margin-left: 4px; +} + +.heapshot-help-status-bar-item .glyph { + -webkit-mask-image: url(Images/helpButtonGlyph.png); +} + +table.heapshot-help { + border-spacing: 12px 2px; +} diff --git a/Source/WebCore/inspector/front-end/inspector.css b/Source/WebCore/inspector/front-end/inspector.css index c992806..a7bd3b3 100644 --- a/Source/WebCore/inspector/front-end/inspector.css +++ b/Source/WebCore/inspector/front-end/inspector.css @@ -76,7 +76,6 @@ img { left: 0; right: 0; height: 56px; - display: -webkit-box; padding: 0 5px; background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(191, 191, 191)), to(rgb(151, 151, 151))); border-bottom: 1px solid rgb(80, 80, 80); @@ -111,32 +110,23 @@ body.attached.inactive #toolbar { } .toolbar-item { - display: -webkit-box; - padding: 4px 6px; margin: 0; + padding: 0 6px; background-color: transparent; border-style: none; border-color: transparent; - -webkit-box-orient: vertical; - -webkit-box-align: center; - -webkit-box-pack: end; +} + +.toolbar-item.toggleable { + padding-top: 4px; } .toolbar-item.toggleable.toggled-on { border-width: 0 2px 0 2px; - padding: 4px 4px; + padding: 4px 4px 0 4px; -webkit-border-image: url(Images/toolbarItemSelected.png) 0 2 0 2; } -.toolbar-item.flexable-space { - -webkit-box-flex: 1; - visibility: hidden; -} - -.toolbar-item input { - margin-bottom: 8px; -} - .toolbar-icon { display: inline-block; width: 32px; @@ -144,7 +134,8 @@ body.attached.inactive #toolbar { -webkit-background-size: 100% auto; } -body.attached .toolbar-icon { +body.attached .toolbar-icon, +#toolbar-dropdown .toolbar-icon { width: 24px; height: 24px; vertical-align: middle; @@ -168,9 +159,9 @@ body.attached .toolbar-item:active .toolbar-icon { text-shadow: none; } -body.attached .toolbar-label { +body.attached .toolbar-label, +#toolbar-dropdown .toolbar-label { display: inline-block; - vertical-align: middle; margin-left: 3px; } @@ -178,25 +169,145 @@ body.attached #search-toolbar-label { display: none; } +#toolbar-controls { + float: right; + display: -webkit-box; + -webkit-box-align: center; + height: 100%; +} + +#toolbar-dropdown-arrow { + font-size: 16px; + font-weight: bold; + border: 0; + background-color: transparent; + -webkit-border-radius: 5px; + text-shadow: none; +} + +body.attached #toolbar-dropdown-arrow { + font-size: 14px; + padding-bottom: 4px; +} + +#toolbar-dropdown-arrow.dropdown-visible { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(251, 251, 251, 0.9)), to(rgba(231, 231, 231, 0.9))); +} + +#toolbar-dropdown-arrow:hover { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(191, 191, 191, 0.7)), to(rgba(171, 171, 171, 0.5))); +} + +#toolbar-dropdown-arrow:active { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(111, 111, 111, 0.8)), to(rgba(91, 91, 91, 0.8))); +} + +#toolbar-dropdown { + position: absolute; + z-index: 1000; + -webkit-box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.4); + border: 1px solid rgb(128, 128, 128); + padding: 4px; + background-color: inherit; + background-image: inherit; +} + +body.detached.platform-mac-leopard #toolbar-dropdown, +body.detached.platform-mac-snowleopard #toolbar-dropdown { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(191, 191, 191)), to(rgb(151, 151, 151))); +} + +#toolbar-dropdown .scrollable-content { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-box-align: start; +} + +#toolbar-dropdown .toolbar-item { + display: -webkit-box; + -webkit-box-orient: horizontal; + margin: 0px 2px; + padding: 4px; + width: 100%; + border: 1px solid rgba(0, 0, 0, 0); +} + +#toolbar-dropdown .toolbar-item.toggleable.toggled-on { + border: 1px solid rgba(100, 100, 120, 0.4); + -webkit-border-image: none; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(128, 128, 128, 0.6)), to(rgba(128, 128, 128, 0.6)), color-stop(20%, rgba(158, 158, 158, 0.2)), color-stop(80%, rgba(158, 158, 158, 0.2))); +} + +#toolbar-dropdown .toolbar-item:hover { + -webkit-border-image: none; + border: 1px solid rgba(100, 100, 120, 0.8); +} + +#toolbar-dropdown .toolbar-item.toggleable.toggled-on:hover { + border: 1px solid rgba(100, 100, 120, 1); +} + +#toolbar-dropdown .toolbar-icon { + margin-right: 0.5em; +} + +#toolbar-dropdown .toolbar-item:active .toolbar-icon { + background-position: 0 24px; +} + +.scrollable-content { + position: static; + height: 100%; + overflow-y: auto; + width: 100%; + margin-right: 12px; + padding-right: 3px; +} + +.scrollable-content::-webkit-scrollbar { + width: 11px; +} + +.scrollable-content::-webkit-scrollbar-corner, +.scrollable-content::-webkit-resizer { + display: none; +} + +.scrollable-content::-webkit-scrollbar-thumb:vertical { + background: -webkit-gradient(linear, left top, right top, from(rgb(192, 192, 192)), to(rgb(192, 192, 192)), color-stop(40%, rgb(214, 214, 214))); + border-radius: 5px; + min-height: 20px; +} + +.scrollable-content::-webkit-scrollbar-thumb:vertical:hover, +.scrollable-content::-webkit-scrollbar-thumb:vertical:active { + background: -webkit-gradient(linear, left top, right top, from(rgb(230, 230, 230)), to(rgb(230, 230, 230)), color-stop(40%, rgb(252, 252, 252))); +} + +.scrollable-content::-webkit-scrollbar-track:vertical { + background: -webkit-gradient(linear, left top, right top, from(rgb(128, 128, 128)), to(rgb(164, 164, 164)), color-stop(25%, rgb(164, 164, 164))); + border-radius: 5px; +} + +.toolbar-search-item { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-box-align: center; + -webkit-box-pack: end; +} + #search { width: 205px; font-size: 16px; - margin-bottom: 5px; } body.attached #search { font-size: 11px; - margin-bottom: 8px; } #search-results-matches { font-size: 11px; text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0; - margin-bottom: 22px; -} - -body.attached #search-results-matches { - margin-bottom: 6px; } .toolbar-item.elements .toolbar-icon { @@ -238,7 +349,7 @@ body.attached #search-results-matches { background-position: 0 0; background-color: transparent; border: 0 none transparent; - margin: 5px 0; + margin-top: 9px; } #close-button-left:hover, #close-button-right:hover { @@ -249,6 +360,10 @@ body.attached #search-results-matches { background-position: 28px 0; } +.close-left { + float: left; +} + body.detached .toolbar-item.close-left, body.detached .toolbar-item.close-right { display: none; } diff --git a/Source/WebCore/inspector/front-end/inspector.html b/Source/WebCore/inspector/front-end/inspector.html index 0435dc3..d6c810e 100644 --- a/Source/WebCore/inspector/front-end/inspector.html +++ b/Source/WebCore/inspector/front-end/inspector.html @@ -142,8 +142,10 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="ProfileView.js"></script> <script type="text/javascript" src="HeapSnapshot.js"></script> <script type="text/javascript" src="HeapSnapshotView.js"></script> + <script type="text/javascript" src="DetailedHeapshotGridNodes.js"></script> <script type="text/javascript" src="DetailedHeapshotView.js"></script> <script type="text/javascript" src="DebuggerModel.js"></script> + <script type="text/javascript" src="DebuggerPresentationModel.js"></script> <script type="text/javascript" src="DOMAgent.js"></script> <script type="text/javascript" src="TimelineAgent.js"></script> <script type="text/javascript" src="TimelinePanel.js"></script> @@ -154,14 +156,18 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="ShortcutsHelp.js"></script> <script type="text/javascript" src="HAREntry.js"></script> <script type="text/javascript" src="CookieParser.js"></script> + <script type="text/javascript" src="Toolbar.js"></script> + <script type="text/javascript" src="SearchController.js"></script> </head> <body class="detached"> <div id="toolbar"> <div class="toolbar-item close-left"><button id="close-button-left"></button></div> - <div class="toolbar-item flexable-space"></div> - <div class="toolbar-item hidden" id="search-results-matches"></div> - <div class="toolbar-item"><input id="search" type="search" incremental results="0"><div id="search-toolbar-label" class="toolbar-label"></div></div> - <div class="toolbar-item close-right"><button id="close-button-right"></button></div> + <div id="toolbar-controls"> + <div class="toolbar-item"><button id="toolbar-dropdown-arrow" class="toolbar-label">»</button></div> + <div class="toolbar-item hidden" id="search-results-matches"></div> + <div class="toolbar-item toolbar-search-item"><input id="search" type="search" incremental results="0"><div id="search-toolbar-label" class="toolbar-label"></div></div> + <div class="toolbar-item close-right"><button id="close-button-right"></button></div> + </div> </div> <div id="main"> <div id="main-panels" spellcheck="false"></div> diff --git a/Source/WebCore/inspector/front-end/inspector.js b/Source/WebCore/inspector/front-end/inspector.js index 0959289..b4c3fda 100644 --- a/Source/WebCore/inspector/front-end/inspector.js +++ b/Source/WebCore/inspector/front-end/inspector.js @@ -138,6 +138,12 @@ var WebInspector = { this._previousFocusElement.blur(); }, + resetFocusElement: function() + { + this.currentFocusElement = null; + this._previousFocusElement = null; + }, + get currentPanel() { return this._currentPanel; @@ -153,30 +159,10 @@ var WebInspector = { this._currentPanel = x; - this.updateSearchLabel(); - if (x) { x.show(); - - if (this.currentQuery) { - if (x.performSearch) { - function performPanelSearch() - { - this.updateSearchMatchesCount(); - - x.currentQuery = this.currentQuery; - x.performSearch(this.currentQuery); - } - - // Perform the search on a timeout so the panel switches fast. - setTimeout(performPanelSearch.bind(this), 0); - } else { - // Update to show Not found for panels that can't be searched. - this.updateSearchMatchesCount(); - } - } + WebInspector.searchController.activePanelChanged(); } - for (var panelName in WebInspector.panels) { if (WebInspector.panels[panelName] === x) { WebInspector.settings.lastActivePanel = panelName; @@ -248,8 +234,6 @@ var WebInspector = { this._attached = x; - this.updateSearchLabel(); - var dockToggleButton = document.getElementById("dock-status-bar-item"); var body = document.body; @@ -262,8 +246,11 @@ var WebInspector = { body.addStyleClass("detached"); dockToggleButton.title = WebInspector.UIString("Dock to main window."); } - if (this.drawer) - this.drawer.resize(); + + // This may be called before onLoadedDone, hence the bulk of inspector objects may + // not be created yet. + if (WebInspector.searchController) + WebInspector.searchController.updateSearchLabel(); }, get errors() @@ -360,9 +347,9 @@ var WebInspector = { this._highlightedDOMNodeId = nodeId; if (nodeId) - InspectorBackend.highlightDOMNode(nodeId); + InspectorAgent.highlightDOMNode(nodeId); else - InspectorBackend.hideDOMNodeHighlight(); + InspectorAgent.hideDOMNodeHighlight(); }, highlightDOMNodeForTwoSeconds: function(nodeId) @@ -492,17 +479,16 @@ WebInspector.doLoadedDone = function() this.debuggerModel = new WebInspector.DebuggerModel(); this.breakpointManager = new WebInspector.BreakpointManager(); + this.searchController = new WebInspector.SearchController(); this.panels = {}; this._createPanels(); this._panelHistory = new WebInspector.PanelHistory(); - - var toolbarElement = document.getElementById("toolbar"); - var previousToolbarItem = toolbarElement.children[0]; + this.toolbar = new WebInspector.Toolbar(); this.panelOrder = []; for (var panelName in this.panels) - previousToolbarItem = WebInspector.addPanelToolbarIcon(toolbarElement, this.panels[panelName], previousToolbarItem); + this.addPanel(this.panels[panelName]); this.Tips = { ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")} @@ -534,15 +520,6 @@ WebInspector.doLoadedDone = function() errorWarningCount.addEventListener("click", this.showConsole.bind(this), false); this._updateErrorAndWarningCounts(); - var searchField = document.getElementById("search"); - searchField.addEventListener("search", this.performSearch.bind(this), false); // when the search is emptied - searchField.addEventListener("mousedown", this._searchFieldManualFocus.bind(this), false); // when the search field is manually selected - searchField.addEventListener("keydown", this._searchKeyDown.bind(this), true); - - toolbarElement.addEventListener("mousedown", this.toolbarDragStart, true); - document.getElementById("close-button-left").addEventListener("click", this.close, true); - document.getElementById("close-button-right").addEventListener("click", this.close, true); - this.extensionServer.initExtensions(); function onPopulateScriptObjects() @@ -550,35 +527,29 @@ WebInspector.doLoadedDone = function() if (!WebInspector.currentPanel) WebInspector.showPanel(WebInspector.settings.lastActivePanel); } - InspectorBackend.populateScriptObjects(onPopulateScriptObjects); + InspectorAgent.populateScriptObjects(onPopulateScriptObjects); if (Preferences.debuggerAlwaysEnabled || WebInspector.settings.debuggerEnabled) this.debuggerModel.enableDebugger(); if (Preferences.profilerAlwaysEnabled || WebInspector.settings.profilerEnabled) - InspectorBackend.enableProfiler(); + InspectorAgent.enableProfiler(); if (WebInspector.settings.monitoringXHREnabled) - InspectorBackend.setMonitoringXHREnabled(true); + ConsoleAgent.setMonitoringXHREnabled(true); - InspectorBackend.setConsoleMessagesEnabled(true); + ConsoleAgent.setConsoleMessagesEnabled(true); function propertyNamesCallback(names) { WebInspector.cssNameCompletions = new WebInspector.CSSCompletions(names); } // As a DOMAgent method, this needs to happen after the frontend has loaded and the agent is available. - InspectorBackend.getSupportedCSSProperties(propertyNamesCallback); + CSSAgent.getSupportedCSSProperties(propertyNamesCallback); } -WebInspector.addPanelToolbarIcon = function(toolbarElement, panel, previousToolbarItem) +WebInspector.addPanel = function(panel) { - var panelToolbarItem = panel.toolbarItem; this.panelOrder.push(panel); - panelToolbarItem.addEventListener("click", this._toolbarItemClicked.bind(this)); - if (previousToolbarItem) - toolbarElement.insertBefore(panelToolbarItem, previousToolbarItem.nextSibling); - else - toolbarElement.insertBefore(panelToolbarItem, toolbarElement.firstChild); - return panelToolbarItem; + this.toolbar.addPanel(panel); } var windowLoaded = function() @@ -599,16 +570,19 @@ var windowLoaded = function() window.addEventListener("DOMContentLoaded", windowLoaded, false); +// We'd like to enforce asynchronous interaction between the inspector controller and the frontend. +// It is needed to prevent re-entering the backend code. +// Also, native dispatches do not guarantee setTimeouts to be serialized, so we +// enforce serialization using 'messagesToDispatch' queue. It is also important that JSC debugger +// tests require that each command was dispatch within individual timeout callback, so we don't batch them. + +var messagesToDispatch = []; + WebInspector.dispatch = function(message) { - // We'd like to enforce asynchronous interaction between the inspector controller and the frontend. - // This is important to LayoutTests. - function delayDispatch() - { - InspectorBackend.dispatch(message); - WebInspector.pendingDispatches--; - } - WebInspector.pendingDispatches++; - setTimeout(delayDispatch, 0); + messagesToDispatch.push(message); + setTimeout(function() { + InspectorBackend.dispatch(messagesToDispatch.shift()); + }, 0); } WebInspector.dispatchMessageFromBackend = function(messageObject) @@ -621,6 +595,7 @@ WebInspector.windowResize = function(event) if (this.currentPanel) this.currentPanel.resize(); this.drawer.resize(); + this.toolbar.resize(); } WebInspector.windowFocused = function(event) @@ -727,7 +702,7 @@ WebInspector.openResource = function(resourceURL, inResourcesPanel) WebInspector.panels.resources.showResource(resource); WebInspector.showPanel("resources"); } else - InspectorBackend.openInInspectedWindow(resource ? resource.url : resourceURL); + InspectorAgent.openInInspectedWindow(resource ? resource.url : resourceURL); } WebInspector._registerShortcuts = function() @@ -783,6 +758,12 @@ WebInspector.documentKeyDown = function(event) } } + WebInspector.searchController.handleShortcut(event); + if (event.handled) { + event.preventDefault(); + return; + } + var isMac = WebInspector.isMac(); switch (event.keyIdentifier) { case "Left": @@ -809,36 +790,6 @@ WebInspector.documentKeyDown = function(event) this.drawer.visible = !this.drawer.visible; break; - case "U+0046": // F key - if (isMac) - var isFindKey = event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey; - else - var isFindKey = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey; - - if (isFindKey) { - WebInspector.focusSearchField(); - event.preventDefault(); - } - break; - - case "F3": - if (!isMac) { - WebInspector.focusSearchField(); - event.preventDefault(); - } - break; - - case "U+0047": // G key - if (isMac && event.metaKey && !event.ctrlKey && !event.altKey) { - if (event.shiftKey) { - if (this.currentPanel.jumpToPreviousSearchResult) - this.currentPanel.jumpToPreviousSearchResult(); - } else if (this.currentPanel.jumpToNextSearchResult) - this.currentPanel.jumpToNextSearchResult(); - event.preventDefault(); - } - break; - // Windows and Mac have two different definitions of [, so accept both. case "U+005B": case "U+00DB": // [ key @@ -875,13 +826,13 @@ WebInspector.documentKeyDown = function(event) case "U+0052": // R key if ((event.metaKey && isMac) || (event.ctrlKey && !isMac)) { - InspectorBackend.reloadPage(event.shiftKey); + InspectorAgent.reloadPage(event.shiftKey); event.preventDefault(); } break; case "F5": if (!isMac) - InspectorBackend.reloadPage(event.ctrlKey || event.shiftKey); + InspectorAgent.reloadPage(event.ctrlKey || event.shiftKey); break; } } @@ -1005,27 +956,6 @@ WebInspector.animateStyle = function(animations, duration, callback) }; } -WebInspector.updateSearchLabel = function() -{ - if (!this.currentPanel) - return; - - var newLabel = WebInspector.UIString("Search %s", this.currentPanel.toolbarItemLabel); - if (this.attached) - document.getElementById("search").setAttribute("placeholder", newLabel); - else { - document.getElementById("search").removeAttribute("placeholder"); - document.getElementById("search-toolbar-label").textContent = newLabel; - } -} - -WebInspector.focusSearchField = function() -{ - var searchField = document.getElementById("search"); - searchField.focus(); - searchField.select(); -} - WebInspector.toggleAttach = function() { if (!this.attached) @@ -1034,58 +964,6 @@ WebInspector.toggleAttach = function() InspectorFrontendHost.requestDetachWindow(); } -WebInspector.toolbarDragStart = function(event) -{ - if ((!WebInspector.attached && WebInspector.platformFlavor !== WebInspector.PlatformFlavor.MacLeopard && WebInspector.platformFlavor !== WebInspector.PlatformFlavor.MacSnowLeopard) || WebInspector.port == "qt") - return; - - var target = event.target; - if (target.hasStyleClass("toolbar-item") && target.hasStyleClass("toggleable")) - return; - - var toolbar = document.getElementById("toolbar"); - if (target !== toolbar && !target.hasStyleClass("toolbar-item")) - return; - - toolbar.lastScreenX = event.screenX; - toolbar.lastScreenY = event.screenY; - - WebInspector.elementDragStart(toolbar, WebInspector.toolbarDrag, WebInspector.toolbarDragEnd, event, (WebInspector.attached ? "row-resize" : "default")); -} - -WebInspector.toolbarDragEnd = function(event) -{ - var toolbar = document.getElementById("toolbar"); - - WebInspector.elementDragEnd(event); - - delete toolbar.lastScreenX; - delete toolbar.lastScreenY; -} - -WebInspector.toolbarDrag = function(event) -{ - var toolbar = document.getElementById("toolbar"); - - if (WebInspector.attached) { - var height = window.innerHeight - (event.screenY - toolbar.lastScreenY); - - InspectorFrontendHost.setAttachedWindowHeight(height); - } else { - var x = event.screenX - toolbar.lastScreenX; - var y = event.screenY - toolbar.lastScreenY; - - // We cannot call window.moveBy here because it restricts the movement - // of the window at the edges. - InspectorFrontendHost.moveWindowBy(x, y); - } - - toolbar.lastScreenX = event.screenX; - toolbar.lastScreenY = event.screenY; - - event.preventDefault(); -} - WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor) { if (this._elementDraggingEventListener || this._elementEndDraggingEventListener) @@ -1307,6 +1185,23 @@ WebInspector.drawLoadingPieChart = function(canvas, percent) { g.fill(); } +WebInspector.inspect = function(objectId, hints) +{ + var object = WebInspector.RemoteObject.fromPayload(objectId); + if (object.type === "node") { + // Request node from backend and focus it. + object.pushNodeToFrontend(WebInspector.updateFocusedNode.bind(WebInspector)); + } else if (hints.databaseId) { + WebInspector.currentPanel = WebInspector.panels.resources; + WebInspector.panels.resources.selectDatabase(hints.databaseId); + } else if (hints.domStorageId) { + WebInspector.currentPanel = WebInspector.panels.resources; + WebInspector.panels.resources.selectDOMStorage(hints.domStorageId); + } + + RuntimeAgent.releaseObject(objectId); +} + WebInspector.updateFocusedNode = function(nodeId) { this._updateFocusedNode(nodeId); @@ -1518,156 +1413,12 @@ WebInspector.addMainEventListeners = function(doc) doc.addEventListener("click", this.documentClick.bind(this), true); } -WebInspector._searchFieldManualFocus = function(event) -{ - this.currentFocusElement = event.target; - this._previousFocusElement = event.target; -} - -WebInspector._searchKeyDown = function(event) -{ - // Escape Key will clear the field and clear the search results - if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) { - // If focus belongs here and text is empty - nothing to do, return unhandled. - if (event.target.value === "" && this.currentFocusElement === this.previousFocusElement) - return; - event.preventDefault(); - event.stopPropagation(); - // When search was selected manually and is currently blank, we'd like Esc stay unhandled - // and hit console drawer handler. - event.target.value = ""; - - this.performSearch(event); - this.currentFocusElement = this.previousFocusElement; - if (this.currentFocusElement === event.target) - this.currentFocusElement.select(); - return false; - } - - if (!isEnterKey(event)) - return false; - - // Select all of the text so the user can easily type an entirely new query. - event.target.select(); - - // Only call performSearch if the Enter key was pressed. Otherwise the search - // performance is poor because of searching on every key. The search field has - // the incremental attribute set, so we still get incremental searches. - this.performSearch(event); - - // Call preventDefault since this was the Enter key. This prevents a "search" event - // from firing for key down. This stops performSearch from being called twice in a row. - event.preventDefault(); -} - -WebInspector.performSearch = function(event) -{ - var forceSearch = event.keyIdentifier === "Enter"; - this.doPerformSearch(event.target.value, forceSearch, event.shiftKey, false); -} - -WebInspector.cancelSearch = function() -{ - document.getElementById("search").value = ""; - this.doPerformSearch(""); -} - -WebInspector.doPerformSearch = function(query, forceSearch, isBackwardSearch, repeatSearch) -{ - var isShortSearch = (query.length < 3); - - // Clear a leftover short search flag due to a non-conflicting forced search. - if (isShortSearch && this.shortSearchWasForcedByKeyEvent && this.currentQuery !== query) - delete this.shortSearchWasForcedByKeyEvent; - - // Indicate this was a forced search on a short query. - if (isShortSearch && forceSearch) - this.shortSearchWasForcedByKeyEvent = true; - - if (!query || !query.length || (!forceSearch && isShortSearch)) { - // Prevent clobbering a short search forced by the user. - if (this.shortSearchWasForcedByKeyEvent) { - delete this.shortSearchWasForcedByKeyEvent; - return; - } - - delete this.currentQuery; - - for (var panelName in this.panels) { - var panel = this.panels[panelName]; - var hadCurrentQuery = !!panel.currentQuery; - delete panel.currentQuery; - if (hadCurrentQuery && panel.searchCanceled) - panel.searchCanceled(); - } - - this.updateSearchMatchesCount(); - - return; - } - - if (!repeatSearch && query === this.currentPanel.currentQuery && this.currentPanel.currentQuery === this.currentQuery) { - // When this is the same query and a forced search, jump to the next - // search result for a good user experience. - if (forceSearch) { - if (!isBackwardSearch && this.currentPanel.jumpToNextSearchResult) - this.currentPanel.jumpToNextSearchResult(); - else if (isBackwardSearch && this.currentPanel.jumpToPreviousSearchResult) - this.currentPanel.jumpToPreviousSearchResult(); - } - return; - } - - this.currentQuery = query; - - this.updateSearchMatchesCount(); - - if (!this.currentPanel.performSearch) - return; - - this.currentPanel.currentQuery = query; - this.currentPanel.performSearch(query); -} - WebInspector.frontendReused = function() { - this.networkManager.reset(); + this.networkManager.frontendReused(); this.reset(); } -WebInspector.addNodesToSearchResult = function(nodeIds) -{ - WebInspector.panels.elements.addNodesToSearchResult(nodeIds); -} - -WebInspector.updateSearchMatchesCount = function(matches, panel) -{ - if (!panel) - panel = this.currentPanel; - - panel.currentSearchMatches = matches; - - if (panel !== this.currentPanel) - return; - - if (!this.currentPanel.currentQuery) { - document.getElementById("search-results-matches").addStyleClass("hidden"); - return; - } - - if (matches) { - if (matches === 1) - var matchesString = WebInspector.UIString("1 match"); - else - var matchesString = WebInspector.UIString("%d matches", matches); - } else - var matchesString = WebInspector.UIString("Not Found"); - - var matchesToolbarElement = document.getElementById("search-results-matches"); - matchesToolbarElement.removeStyleClass("hidden"); - matchesToolbarElement.textContent = matchesString; -} - WebInspector.UIString = function(string) { if (window.localizedStrings && string in window.localizedStrings) @@ -1675,7 +1426,7 @@ WebInspector.UIString = function(string) else { if (!(string in WebInspector.missingLocalizedStrings)) { if (!WebInspector.InspectorBackendStub) - console.error("Localized string \"" + string + "\" not found."); + console.warn("Localized string \"" + string + "\" not found."); WebInspector.missingLocalizedStrings[string] = true; } diff --git a/Source/WebCore/inspector/front-end/networkPanel.css b/Source/WebCore/inspector/front-end/networkPanel.css index c750323..2711347 100644 --- a/Source/WebCore/inspector/front-end/networkPanel.css +++ b/Source/WebCore/inspector/front-end/networkPanel.css @@ -22,7 +22,7 @@ -webkit-background-size: 1px 42px; } -.network-sidebar .data-grid td:not(.network-summary) { +.network-sidebar .data-grid td { line-height: 17px; height: 37px; border-right: 1px solid rgb(210, 210, 210); @@ -496,34 +496,34 @@ /* Summary */ +.network-sidebar tr.filler td { + padding-bottom: 20px !important; +} + .network-summary-bar { + position: absolute; + left: 0; + right: 0; + bottom: 0; + margin-right: -14px; background-color: rgb(101, 111, 130); +} + +.network-sidebar .data-grid .network-summary-bar td { color: white; height: 20px; + border: none; font-size: 11px; font-weight: bold; - padding-top: 3px; - padding-left: 10px; - z-index: 2000; + padding: 0 0 0 8px; white-space: pre; overflow : hidden; text-overflow : ellipsis; } -.network-summary-bar-bottom { - position: absolute; - bottom: 0; - left: 0; - right: 0; - padding-top: 3px; -} - -.data-grid td .network-summary-bar { - white-space: pre; -} - -.network-sidebar .data-grid td.network-summary { - padding: 0; +.network-summary-bar img { + vertical-align: middle; + padding-right: 8px; } /* Viewer */ diff --git a/Source/WebCore/inspector/front-end/textViewer.css b/Source/WebCore/inspector/front-end/textViewer.css index f6aa65e..59f2a43 100644 --- a/Source/WebCore/inspector/front-end/textViewer.css +++ b/Source/WebCore/inspector/front-end/textViewer.css @@ -15,6 +15,9 @@ bottom: 0; overflow: hidden; -webkit-user-select: none; + background-color: rgb(240, 240, 240); + border-right: 1px solid rgb(187, 187, 187); + min-width: 19px; } .text-editor-contents { @@ -27,6 +30,15 @@ -webkit-user-select: text; } +.text-editor-contents .inner-container { + position: absolute; + top: 0; + left: 0; + right: auto; + bottom: auto; + min-width: 100%; +} + .text-editor-editable { -webkit-user-modify: read-write-plaintext-only; } @@ -80,8 +92,6 @@ .webkit-line-number { color: rgb(128, 128, 128); - background-color: rgb(240, 240, 240); - border-right: 1px solid rgb(187, 187, 187); text-align: right; vertical-align: top; word-break: normal; diff --git a/Source/WebCore/inspector/front-end/utilities.js b/Source/WebCore/inspector/front-end/utilities.js index 5ed9a8c..31a5f0c 100644 --- a/Source/WebCore/inspector/front-end/utilities.js +++ b/Source/WebCore/inspector/front-end/utilities.js @@ -218,7 +218,8 @@ Element.prototype.pruneEmptyTextNodes = function() Element.prototype.isScrolledToBottom = function() { - return this.scrollTop === this.scrollHeight - this.offsetHeight; + // This code works only for 0-width border + return this.scrollTop + this.clientHeight === this.scrollHeight; } Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = function(nameArray) @@ -399,6 +400,15 @@ String.prototype.findAll = function(string) return matches; } +String.prototype.lineEndings = function() +{ + if (!this._lineEndings) { + this._lineEndings = this.findAll("\n"); + this._lineEndings.push(this.length); + } + return this._lineEndings; +} + String.prototype.asParsedURL = function() { // RegExp groups: @@ -1010,52 +1020,91 @@ function isEnterKey(event) { return event.keyCode !== 229 && event.keyIdentifier === "Enter"; } - function highlightSearchResult(element, offset, length) { + var result = highlightSearchResults(element, [{offset: offset, length: length }]); + return result.length ? result[0] : null; +} + +function highlightSearchResults(element, resultRanges) +{ + var highlightNodes = []; var lineText = element.textContent; - var endOffset = offset + length; - var highlightNode = document.createElement("span"); - highlightNode.className = "webkit-search-result"; - highlightNode.textContent = lineText.substring(offset, endOffset); - - var boundary = element.rangeBoundaryForOffset(offset); - var textNode = boundary.container; - var text = textNode.textContent; - - if (boundary.offset + length < text.length) { - // Selection belong to a single split mode. - textNode.textContent = text.substring(boundary.offset + length); - textNode.parentElement.insertBefore(highlightNode, textNode); - var prefixNode = document.createTextNode(text.substring(0, boundary.offset)); - textNode.parentElement.insertBefore(prefixNode, highlightNode); - return highlightNode; - } + var textNodeSnapshot = document.evaluate(".//text()", element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + + var snapshotLength = textNodeSnapshot.snapshotLength; + var snapshotNodeOffset = 0; + var currentSnapshotItem = 0; + + for (var i = 0; i < resultRanges.length; ++i) { + var resultLength = resultRanges[i].length; + var startOffset = resultRanges[i].offset; + var endOffset = startOffset + resultLength; + var length = resultLength; + var textNode; + var textNodeOffset; + var found; + + while (currentSnapshotItem < snapshotLength) { + textNode = textNodeSnapshot.snapshotItem(currentSnapshotItem++); + var textNodeLength = textNode.nodeValue.length; + if (snapshotNodeOffset + textNodeLength >= startOffset) { + textNodeOffset = startOffset - snapshotNodeOffset; + snapshotNodeOffset += textNodeLength; + found = true; + break; + } + snapshotNodeOffset += textNodeLength; + } - var parentElement = textNode.parentElement; - var anchorElement = textNode.nextSibling; + if (!found) { + textNode = element; + textNodeOffset = 0; + } - length -= text.length - boundary.offset; - textNode.textContent = text.substring(0, boundary.offset); - textNode = textNode.traverseNextTextNode(element); + var highlightNode = document.createElement("span"); + highlightNode.className = "webkit-search-result"; + highlightNode.textContent = lineText.substring(startOffset, endOffset); - while (textNode) { var text = textNode.textContent; - if (length < text.length) { - textNode.textContent = text.substring(length); - break; + if (textNodeOffset + resultLength < text.length) { + // Selection belongs to a single split mode. + textNode.textContent = text.substring(textNodeOffset + resultLength); + textNode.parentElement.insertBefore(highlightNode, textNode); + var prefixNode = document.createTextNode(text.substring(0, textNodeOffset)); + textNode.parentElement.insertBefore(prefixNode, highlightNode); + + highlightNodes.push(highlightNode); + continue; + } + + var parentElement = textNode.parentElement; + var anchorElement = textNode.nextSibling; + + length -= text.length - textNodeOffset; + textNode.textContent = text.substring(0, textNodeOffset); + + while (currentSnapshotItem < snapshotLength) { + textNode = textNodeSnapshot.snapshotItem(currentSnapshotItem++); + snapshotNodeOffset += textNode.nodeValue.length; + var text = textNode.textContent; + if (length < text.length) { + textNode.textContent = text.substring(length); + break; + } + + length -= text.length; + textNode.textContent = ""; } - length -= text.length; - textNode.textContent = ""; - textNode = textNode.traverseNextTextNode(element); + parentElement.insertBefore(highlightNode, anchorElement); + highlightNodes.push(highlightNode); } - parentElement.insertBefore(highlightNode, anchorElement); - return highlightNode; + return highlightNodes; } -function createSearchRegex(query) +function createSearchRegex(query, extraFlags) { var regex = ""; for (var i = 0; i < query.length; ++i) { @@ -1064,7 +1113,7 @@ function createSearchRegex(query) char = "\\]"; regex += "[" + char + "]"; } - return new RegExp(regex, "i"); + return new RegExp(regex, "i" + (extraFlags || "")); } function offerFileForDownload(contents) |
