diff options
Diffstat (limited to 'WebCore/inspector/front-end')
40 files changed, 1129 insertions, 467 deletions
diff --git a/WebCore/inspector/front-end/DOMAgent.js b/WebCore/inspector/front-end/DOMAgent.js index 6889408..834f527 100644 --- a/WebCore/inspector/front-end/DOMAgent.js +++ b/WebCore/inspector/front-end/DOMAgent.js @@ -153,10 +153,13 @@ WebInspector.DOMNode.prototype = { _insertChild: function(prev, payload) { var node = new WebInspector.DOMNode(this.ownerDocument, payload); - if (!prev) - // First node - this.children = [ node ]; - else + if (!prev) { + if (!this.children) { + // First node + this.children = [ node ]; + } else + this.children.unshift(node); + } else this.children.splice(this.children.indexOf(prev) + 1, 0, node); this._renumber(); return node; diff --git a/WebCore/inspector/front-end/ElementsPanel.js b/WebCore/inspector/front-end/ElementsPanel.js index e839a60..897fdd1 100644 --- a/WebCore/inspector/front-end/ElementsPanel.js +++ b/WebCore/inspector/front-end/ElementsPanel.js @@ -245,13 +245,8 @@ WebInspector.ElementsPanel.prototype = { searchCanceled: function() { - if (this._searchResults) { - for (var i = 0; i < this._searchResults.length; ++i) { - var treeElement = this.treeOutline.findTreeElement(this._searchResults[i]); - if (treeElement) - treeElement.highlighted = false; - } - } + delete this._searchQuery; + this._hideSearchHighlights(); WebInspector.updateSearchMatchesCount(0, this); @@ -271,6 +266,7 @@ WebInspector.ElementsPanel.prototype = { this._updatedMatchCountOnce = false; this._matchesCountUpdateTimeout = null; + this._searchQuery = query; InjectedScriptAccess.getDefault().performSearch(whitespaceTrimmedQuery, function() {}); }, @@ -304,25 +300,10 @@ WebInspector.ElementsPanel.prototype = { if (!node) continue; - if (!this._searchResults.length) { - this._currentSearchResultIndex = 0; - - // Only change the focusedDOMNode if the search was manually performed, because - // the search may have been performed programmatically and we wouldn't want to - // change the current focusedDOMNode. - if (WebInspector.currentFocusElement === document.getElementById("search")) - this.focusedDOMNode = node; - } - + this._currentSearchResultIndex = 0; this._searchResults.push(node); - - // Highlight the tree element to show it matched the search. - // FIXME: highlight the substrings in text nodes and attributes. - var treeElement = this.treeOutline.findTreeElement(node); - if (treeElement) - treeElement.highlighted = true; } - + this._highlightCurrentSearchResult(); this._updateMatchesCountSoon(); }, @@ -330,18 +311,41 @@ WebInspector.ElementsPanel.prototype = { { if (!this._searchResults || !this._searchResults.length) return; + if (++this._currentSearchResultIndex >= this._searchResults.length) this._currentSearchResultIndex = 0; - this.focusedDOMNode = this._searchResults[this._currentSearchResultIndex]; + this._highlightCurrentSearchResult(); }, jumpToPreviousSearchResult: function() { if (!this._searchResults || !this._searchResults.length) return; + if (--this._currentSearchResultIndex < 0) this._currentSearchResultIndex = (this._searchResults.length - 1); - this.focusedDOMNode = this._searchResults[this._currentSearchResultIndex]; + this._highlightCurrentSearchResult(); + }, + + _highlightCurrentSearchResult: function() + { + this._hideSearchHighlights(); + var node = this._searchResults[this._currentSearchResultIndex]; + var treeElement = this.treeOutline.findTreeElement(node); + if (treeElement) { + treeElement.highlightSearchResults(this._searchQuery); + treeElement.reveal(); + } + }, + + _hideSearchHighlights: function(node) + { + for (var i = 0; this._searchResults && i < this._searchResults.length; ++i) { + var node = this._searchResults[i]; + var treeElement = this.treeOutline.findTreeElement(node); + if (treeElement) + treeElement.highlightSearchResults(null); + } }, renameSelector: function(oldIdentifier, newIdentifier, oldSelector, newSelector) @@ -523,7 +527,8 @@ WebInspector.ElementsPanel.prototype = { if (this.recentlyModifiedNodes[i].updated) { var nodeItem = this.treeOutline.findTreeElement(node); - nodeItem.updateTitle(); + if (nodeItem) + nodeItem.updateTitle(); continue; } diff --git a/WebCore/inspector/front-end/ElementsTreeOutline.js b/WebCore/inspector/front-end/ElementsTreeOutline.js index 4a8dae0..fe7ae53 100644 --- a/WebCore/inspector/front-end/ElementsTreeOutline.js +++ b/WebCore/inspector/front-end/ElementsTreeOutline.js @@ -59,9 +59,16 @@ WebInspector.ElementsTreeOutline.prototype = { this._rootDOMNode = x; + this._isXMLMimeType = !!(WebInspector.mainResource && WebInspector.mainResource.mimeType && WebInspector.mainResource.mimeType.match(/x(?:ht)?ml/i)); + this.update(); }, + get isXMLMimeType() + { + return this._isXMLMimeType; + }, + get focusedDOMNode() { return this._focusedDOMNode; @@ -155,12 +162,27 @@ WebInspector.ElementsTreeOutline.prototype = { return treeElement; }, + createTreeElementFor: function(node) + { + var treeElement = this.findTreeElement(node); + if (treeElement) + return treeElement; + if (!node.parentNode) + return null; + + var treeElement = this.createTreeElementFor(node.parentNode); + if (treeElement && treeElement.showChild(node.index)) + return treeElement.children[node.index]; + + return null; + }, + revealAndSelectNode: function(node) { if (!node) return; - var treeElement = this.findTreeElement(node); + var treeElement = this.createTreeElementFor(node); if (!treeElement) return; @@ -277,9 +299,9 @@ WebInspector.ElementsTreeOutline.prototype = { var tag = event.target.enclosingNodeOrSelfWithClass("webkit-html-tag"); var textNode = event.target.enclosingNodeOrSelfWithClass("webkit-html-text-node"); - if (tag) + if (tag && listItem.treeElement._populateTagContextMenu) listItem.treeElement._populateTagContextMenu(contextMenu, event); - else if (textNode) + else if (textNode && listItem.treeElement._populateTextContextMenu) listItem.treeElement._populateTextContextMenu(contextMenu, textNode); contextMenu.show(event); } @@ -296,27 +318,28 @@ WebInspector.ElementsTreeElement = function(node) if (this.representedObject.nodeType == Node.ELEMENT_NODE) this._canAddAttributes = true; + this._searchQuery = null; + this._expandedChildrenLimit = WebInspector.ElementsTreeElement.InitialChildrenLimit; } -WebInspector.ElementsTreeElement.prototype = { - get highlighted() - { - return this._highlighted; - }, +WebInspector.ElementsTreeElement.InitialChildrenLimit = 500; + +// A union of HTML4 and HTML5-Draft elements that explicitly +// or implicitly (for HTML5) forbid the closing tag. +// FIXME: Revise once HTML5 Final is published. +WebInspector.ElementsTreeElement.ForbiddenClosingTagElements = [ + "area", "base", "basefont", "br", "canvas", "col", "command", "embed", "frame", + "hr", "img", "input", "isindex", "keygen", "link", "meta", "param", "source" +].keySet(); - set highlighted(x) +WebInspector.ElementsTreeElement.prototype = { + highlightSearchResults: function(searchQuery) { - if (this._highlighted === x) + if (this._searchQuery === searchQuery) return; - this._highlighted = x; - - if (this.listItemElement) { - if (x) - this.listItemElement.addStyleClass("highlighted"); - else - this.listItemElement.removeStyleClass("highlighted"); - } + this._searchQuery = searchQuery; + this.updateTitle(); }, get hovered() @@ -341,6 +364,42 @@ WebInspector.ElementsTreeElement.prototype = { } }, + get expandedChildrenLimit() + { + return this._expandedChildrenLimit; + }, + + set expandedChildrenLimit(x) + { + if (this._expandedChildrenLimit === x) + return; + + this._expandedChildrenLimit = x; + if (this.treeOutline && !this._updateChildrenInProgress) + this._updateChildren(true); + }, + + get expandedChildCount() + { + var count = this.children.length; + if (count && this.children[count - 1].elementCloseTag) + count--; + if (count && this.children[count - 1].expandAllButton) + count--; + return count; + }, + + showChild: function(index) + { + if (index >= this.expandedChildrenLimit) { + this._expandedChildrenLimit = index + 1; + this._updateChildren(true); + } + + // Whether index-th child is visible in the children tree + return this.expandedChildCount > index; + }, + createTooltipForImageNode: function(node, callback) { function createTooltipThenCallback(properties) @@ -386,9 +445,6 @@ WebInspector.ElementsTreeElement.prototype = { { this.listItemElement.addEventListener("mousedown", this.onmousedown.bind(this), false); - if (this._highlighted) - this.listItemElement.addStyleClass("highlighted"); - if (this._hovered) { this.updateSelection(); this.listItemElement.addStyleClass("hovered"); @@ -422,9 +478,34 @@ WebInspector.ElementsTreeElement.prototype = { WebInspector.domAgent.getChildNodesAsync(this.representedObject, this._updateChildren.bind(this, fullRefresh)); }, + insertChildElement: function(child, index) + { + var newElement = new WebInspector.ElementsTreeElement(child); + newElement.selectable = this.treeOutline.selectEnabled; + this.insertChild(newElement, index); + return newElement; + }, + + moveChild: function(child, targetIndex) + { + var wasSelected = child.selected; + treeElement.removeChild(child); + treeElement.insertChild(child, targetIndex); + if (wasSelected) + existingTreeElement.select(); + }, + _updateChildren: function(fullRefresh) { + if (this._updateChildrenInProgress) + return; + + this._updateChildrenInProgress = true; + var focusedNode = this.treeOutline.focusedDOMNode; + var originalScrollTop; if (fullRefresh) { + var treeOutlineContainerElement = this.treeOutline.element.parentNode; + originalScrollTop = treeOutlineContainerElement.scrollTop; var selectedTreeElement = this.treeOutline.selectedTreeElement; if (selectedTreeElement && selectedTreeElement.hasAncestor(this)) this.select(); @@ -433,6 +514,7 @@ WebInspector.ElementsTreeElement.prototype = { var treeElement = this; var treeChildIndex = 0; + var elementToSelect; function updateChildrenOfNode(node) { @@ -443,7 +525,7 @@ WebInspector.ElementsTreeElement.prototype = { if (!currentTreeElement || currentTreeElement.representedObject !== child) { // Find any existing element that is later in the children list. var existingTreeElement = null; - for (var i = (treeChildIndex + 1); i < treeElement.children.length; ++i) { + for (var i = (treeChildIndex + 1), size = treeElement.expandedChildCount; i < size; ++i) { if (treeElement.children[i].representedObject === child) { existingTreeElement = treeElement.children[i]; break; @@ -452,16 +534,16 @@ WebInspector.ElementsTreeElement.prototype = { if (existingTreeElement && existingTreeElement.parent === treeElement) { // If an existing element was found and it has the same parent, just move it. - var wasSelected = existingTreeElement.selected; - treeElement.removeChild(existingTreeElement); - treeElement.insertChild(existingTreeElement, treeChildIndex); - if (wasSelected) - existingTreeElement.select(); + treeElement.moveChild(existingTreeElement, treeChildIndex); } else { // No existing element found, insert a new element. - var newElement = new WebInspector.ElementsTreeElement(child); - newElement.selectable = treeOutline.selectEnabled; - treeElement.insertChild(newElement, treeChildIndex); + if (treeChildIndex < treeElement.expandedChildrenLimit) { + var newElement = treeElement.insertChildElement(child, treeChildIndex); + if (child === focusedNode) + elementToSelect = newElement; + if (treeElement.expandedChildCount > treeElement.expandedChildrenLimit) + treeElement.expandedChildrenLimit++; + } } } @@ -490,6 +572,7 @@ WebInspector.ElementsTreeElement.prototype = { } updateChildrenOfNode(this.representedObject); + this.adjustCollapsedRange(false); var lastChild = this.children[this.children.length - 1]; if (this.representedObject.nodeType == Node.ELEMENT_NODE && (!lastChild || !lastChild.elementCloseTag)) { @@ -499,15 +582,66 @@ WebInspector.ElementsTreeElement.prototype = { item.elementCloseTag = true; this.appendChild(item); } + + // We want to restore the original selection and tree scroll position after a full refresh, if possible. + if (fullRefresh && elementToSelect) { + elementToSelect.select(); + if (treeOutlineContainerElement && originalScrollTop <= treeOutlineContainerElement.scrollHeight) + treeOutlineContainerElement.scrollTop = originalScrollTop; + } + + delete this._updateChildrenInProgress; + }, + + adjustCollapsedRange: function() + { + // Ensure precondition: only the tree elements for node children are found in the tree + // (not the Expand All button or the closing tag). + if (this.expandAllButtonElement && this.expandAllButtonElement.__treeElement.parent) + this.removeChild(this.expandAllButtonElement.__treeElement); + + const node = this.representedObject; + if (!node.children) + return; + const childNodeCount = node.children.length; + + // In case some nodes from the expanded range were removed, pull some nodes from the collapsed range into the expanded range at the bottom. + for (var i = this.expandedChildCount, limit = Math.min(this.expandedChildrenLimit, childNodeCount); i < limit; ++i) + this.insertChildElement(node.children[i], i); + + const expandedChildCount = this.expandedChildCount; + if (childNodeCount > this.expandedChildCount) { + var targetButtonIndex = expandedChildCount; + if (!this.expandAllButtonElement) { + var title = "<button class=\"show-all-nodes\" value=\"\" />"; + var item = new TreeElement(title, null, false); + item.selectable = false; + item.expandAllButton = true; + this.insertChild(item, targetButtonIndex); + this.expandAllButtonElement = item.listItemElement.firstChild; + this.expandAllButtonElement.__treeElement = item; + this.expandAllButtonElement.addEventListener("click", this.handleLoadAllChildren.bind(this), false); + } else if (!this.expandAllButtonElement.__treeElement.parent) + this.insertChild(this.expandAllButtonElement.__treeElement, targetButtonIndex); + this.expandAllButtonElement.textContent = WebInspector.UIString("Show All Nodes (%d More)", childNodeCount - expandedChildCount); + } else if (this.expandAllButtonElement) + delete this.expandAllButtonElement; + }, + + handleLoadAllChildren: function() + { + this.expandedChildrenLimit = Math.max(this.representedObject._childNodeCount, this.expandedChildrenLimit + WebInspector.ElementsTreeElement.InitialChildrenLimit); }, onexpand: function() { + this.updateTitle(); this.treeOutline.updateSelection(); }, oncollapse: function() { + this.updateTitle(); this.treeOutline.updateSelection(); }, @@ -879,11 +1013,12 @@ WebInspector.ElementsTreeElement.prototype = { var self = this; function callback(tooltipText) { - var title = self._nodeTitleInfo(self.representedObject, self.hasChildren, WebInspector.linkifyURL, tooltipText).title; + var title = self._nodeTitleInfo(WebInspector.linkifyURL, tooltipText).title; self.title = "<span class=\"highlight\">" + title + "</span>"; delete self.selectionElement; self.updateSelection(); self._preventFollowingLinksOnDoubleClick(); + self._highlightSearchResults(); }; // TODO: Replace with InjectedScriptAccess.getBasicProperties(obj, [names]). @@ -916,9 +1051,10 @@ WebInspector.ElementsTreeElement.prototype = { return hrefValue; }, - _nodeTitleInfo: function(node, hasChildren, linkify, tooltipText) + _nodeTitleInfo: function(linkify, tooltipText) { - var info = {title: "", hasChildren: hasChildren}; + var node = this.representedObject; + var info = {title: "", hasChildren: this.hasChildren}; switch (node.nodeType) { case Node.DOCUMENT_NODE: @@ -930,7 +1066,8 @@ WebInspector.ElementsTreeElement.prototype = { break; case Node.ELEMENT_NODE: - info.title = "<span class=\"webkit-html-tag\"><" + node.nodeName.toLowerCase().escapeHTML(); + var tagName = node.nodeName.toLowerCase().escapeHTML(); + info.title = "<span class=\"webkit-html-tag\"><" + tagName; if (node.hasAttributes()) { for (var i = 0; i < node.attributes.length; ++i) { @@ -951,15 +1088,21 @@ WebInspector.ElementsTreeElement.prototype = { } info.title += "></span>​"; + const closingTagHTML = "<span class=\"webkit-html-tag\"></" + tagName + "></span>​"; + var textChild = onlyTextChild.call(node); + var showInlineText = textChild && textChild.textContent.length < Preferences.maxInlineTextChildLength; + + if (!this.expanded && (!showInlineText && (this.treeOutline.isXMLMimeType || !WebInspector.ElementsTreeElement.ForbiddenClosingTagElements[tagName]))) { + if (this.hasChildren) + info.title += "<span class=\"webkit-html-text-node\">…</span>​"; + info.title += closingTagHTML; + } + // If this element only has a single child that is a text node, // just show that text and the closing tag inline rather than // create a subtree for them - - var textChild = onlyTextChild.call(node); - var showInlineText = textChild && textChild.textContent.length < Preferences.maxInlineTextChildLength; - if (showInlineText) { - info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>​<span class=\"webkit-html-tag\"></" + node.nodeName.toLowerCase().escapeHTML() + "></span>"; + info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>​" + closingTagHTML; info.hasChildren = false; } break; @@ -1038,6 +1181,7 @@ WebInspector.ElementsTreeElement.prototype = { return; parentElement.removeChild(self); + parentElement.adjustCollapsedRange(true); } var callId = WebInspector.Callback.wrap(removeNodeCallback); @@ -1077,6 +1221,23 @@ WebInspector.ElementsTreeElement.prototype = { _copyHTML: function() { InspectorBackend.copyNode(this.representedObject.id); + }, + + _highlightSearchResults: function() + { + if (!this._searchQuery) + return; + var text = this.listItemElement.textContent; + var regexObject = createSearchRegex(this._searchQuery); + + var offset = 0; + var match = regexObject.exec(text); + while (match) { + highlightSearchResult(this.listItemElement, offset + match.index, match[0].length); + offset += match.index + 1; + text = text.substring(match.index + 1); + match = regexObject.exec(text); + } } } diff --git a/WebCore/inspector/front-end/EventListenersSidebarPane.js b/WebCore/inspector/front-end/EventListenersSidebarPane.js index 649eea8..e454256 100644 --- a/WebCore/inspector/front-end/EventListenersSidebarPane.js +++ b/WebCore/inspector/front-end/EventListenersSidebarPane.js @@ -191,7 +191,7 @@ WebInspector.EventListenerBar.prototype = { // Just build properties in place - no need to reach out for injected script. var value = this.eventListener[propertyName]; if (value instanceof WebInspector.DOMNode) - value = new WebInspector.ObjectProxy(value.injectedScriptId, value.id, [], 0, appropriateSelectorForNode(value), true); + value = new WebInspector.ObjectProxy(value.injectedScriptId, value.id, [], appropriateSelectorForNode(value), true); else value = WebInspector.ObjectProxy.wrapPrimitiveValue(value); properties.push(new WebInspector.ObjectPropertyProxy(propertyName, value)); diff --git a/WebCore/inspector/front-end/Images/gearButtonGlyph.png b/WebCore/inspector/front-end/Images/gearButtonGlyph.png Binary files differnew file mode 100644 index 0000000..19659c9 --- /dev/null +++ b/WebCore/inspector/front-end/Images/gearButtonGlyph.png diff --git a/WebCore/inspector/front-end/Images/popoverArrows.png b/WebCore/inspector/front-end/Images/popoverArrows.png Binary files differnew file mode 100644 index 0000000..ccefa16 --- /dev/null +++ b/WebCore/inspector/front-end/Images/popoverArrows.png diff --git a/WebCore/inspector/front-end/Images/popoverBackground.png b/WebCore/inspector/front-end/Images/popoverBackground.png Binary files differnew file mode 100644 index 0000000..f20c988 --- /dev/null +++ b/WebCore/inspector/front-end/Images/popoverBackground.png diff --git a/WebCore/inspector/front-end/Images/thumbActiveHoriz.png b/WebCore/inspector/front-end/Images/thumbActiveHoriz.png Binary files differnew file mode 100644 index 0000000..a6ee561 --- /dev/null +++ b/WebCore/inspector/front-end/Images/thumbActiveHoriz.png diff --git a/WebCore/inspector/front-end/Images/thumbActiveVert.png b/WebCore/inspector/front-end/Images/thumbActiveVert.png Binary files differnew file mode 100644 index 0000000..a3eabe8 --- /dev/null +++ b/WebCore/inspector/front-end/Images/thumbActiveVert.png diff --git a/WebCore/inspector/front-end/Images/thumbHoriz.png b/WebCore/inspector/front-end/Images/thumbHoriz.png Binary files differnew file mode 100644 index 0000000..c16559a --- /dev/null +++ b/WebCore/inspector/front-end/Images/thumbHoriz.png diff --git a/WebCore/inspector/front-end/Images/thumbHoverHoriz.png b/WebCore/inspector/front-end/Images/thumbHoverHoriz.png Binary files differnew file mode 100644 index 0000000..0fe8d6a --- /dev/null +++ b/WebCore/inspector/front-end/Images/thumbHoverHoriz.png diff --git a/WebCore/inspector/front-end/Images/thumbHoverVert.png b/WebCore/inspector/front-end/Images/thumbHoverVert.png Binary files differnew file mode 100644 index 0000000..30e315a --- /dev/null +++ b/WebCore/inspector/front-end/Images/thumbHoverVert.png diff --git a/WebCore/inspector/front-end/Images/thumbVert.png b/WebCore/inspector/front-end/Images/thumbVert.png Binary files differnew file mode 100644 index 0000000..61fbc06 --- /dev/null +++ b/WebCore/inspector/front-end/Images/thumbVert.png diff --git a/WebCore/inspector/front-end/Images/trackHoriz.png b/WebCore/inspector/front-end/Images/trackHoriz.png Binary files differnew file mode 100644 index 0000000..517d306 --- /dev/null +++ b/WebCore/inspector/front-end/Images/trackHoriz.png diff --git a/WebCore/inspector/front-end/Images/trackVert.png b/WebCore/inspector/front-end/Images/trackVert.png Binary files differnew file mode 100644 index 0000000..d49620d --- /dev/null +++ b/WebCore/inspector/front-end/Images/trackVert.png diff --git a/WebCore/inspector/front-end/InjectedScript.js b/WebCore/inspector/front-end/InjectedScript.js index 95867c4..8d8fa88 100644 --- a/WebCore/inspector/front-end/InjectedScript.js +++ b/WebCore/inspector/front-end/InjectedScript.js @@ -485,25 +485,30 @@ InjectedScript.getProperties = function(objectProxy, ignoreHasOwnProperty, abbre var object = InjectedScript._resolveObject(objectProxy); if (!InjectedScript._isDefined(object)) return false; - var properties = []; + var propertyNames = ignoreHasOwnProperty ? InjectedScript._getPropertyNames(object) : Object.getOwnPropertyNames(object); + if (!ignoreHasOwnProperty && object.__proto__) + propertyNames.push("__proto__"); // Go over properties, prepare results. - for (var propertyName in object) { - if (!ignoreHasOwnProperty && "hasOwnProperty" in object && !object.hasOwnProperty(propertyName)) - continue; + for (var i = 0; i < propertyNames.length; ++i) { + var propertyName = propertyNames[i]; var property = {}; - property.name = propertyName; + property.name = propertyName + ""; property.parentObjectProxy = objectProxy; var isGetter = object["__lookupGetter__"] && object.__lookupGetter__(propertyName); if (!property.isGetter) { - var childObject = object[propertyName]; - var childObjectProxy = new InjectedScript.createProxyObject(childObject, objectProxy.objectId, abbreviate); - childObjectProxy.path = objectProxy.path ? objectProxy.path.slice() : []; - childObjectProxy.path.push(propertyName); - childObjectProxy.protoDepth = objectProxy.protoDepth || 0; - property.value = childObjectProxy; + try { + var childObject = object[propertyName]; + var childObjectProxy = new InjectedScript.createProxyObject(childObject, objectProxy.objectId, abbreviate); + childObjectProxy.path = objectProxy.path ? objectProxy.path.slice() : []; + childObjectProxy.path.push(propertyName); + property.value = childObjectProxy; + } catch(e) { + property.value = { description: e.toString() }; + property.isError = true; + } } else { // FIXME: this should show something like "getter" (bug 16734). property.value = { description: "\u2014" }; // em dash @@ -571,24 +576,25 @@ InjectedScript.setOuterHTML = function(nodeId, value, expanded) return InjectedScriptHost.pushNodePathToFrontend(newNode, expanded, false); } -InjectedScript._getPropertyNames = function(object, resultSet) +InjectedScript._populatePropertyNames = function(object, resultSet) { - if (Object.getOwnPropertyNames) { - for (var o = object; o; o = o.__proto__) { - try { - var names = Object.getOwnPropertyNames(o); - for (var i = 0; i < names.length; ++i) - resultSet[names[i]] = true; - } catch (e) { - } + for (var o = object; o; o = o.__proto__) { + try { + var names = Object.getOwnPropertyNames(o); + for (var i = 0; i < names.length; ++i) + resultSet[names[i] + ""] = true; + } catch (e) { } - } else { - // Chromium doesn't support getOwnPropertyNames yet. - for (var name in object) - resultSet[name] = true; } } +InjectedScript._getPropertyNames = function(object, resultSet) +{ + var propertyNameSet = {}; + InjectedScript._populatePropertyNames(object, propertyNameSet); + return Object.keys(propertyNameSet); +} + InjectedScript.getCompletions = function(expression, includeInspectorCommandLineAPI, callFrameId) { var props = {}; @@ -605,7 +611,7 @@ InjectedScript.getCompletions = function(expression, includeInspectorCommandLine // Evaluate into properties in scope of the selected call frame. var scopeChain = callFrame.scopeChain; for (var i = 0; i < scopeChain.length; ++i) - InjectedScript._getPropertyNames(scopeChain[i], props); + InjectedScript._populatePropertyNames(scopeChain[i], props); } } else { if (!expression) @@ -613,7 +619,7 @@ InjectedScript.getCompletions = function(expression, includeInspectorCommandLine expressionResult = InjectedScript._evaluateOn(InjectedScript._window().eval, InjectedScript._window(), expression); } if (typeof expressionResult == "object") - InjectedScript._getPropertyNames(expressionResult, props); + InjectedScript._populatePropertyNames(expressionResult, props); if (includeInspectorCommandLineAPI) for (var prop in InjectedScript._window().console._inspectorCommandLineAPI) if (prop.charAt(0) !== '_') @@ -677,6 +683,12 @@ InjectedScript.addInspectedNode = function(nodeId) InjectedScript.performSearch = function(whitespaceTrimmedQuery) { + // FIXME: Few things are missing here: + // 1) Search works with node granularity - number of matches within node is not calculated. + // 2) Search does not work outside main documents' domain - we need to use specific InjectedScript instances + // for other domains. + // 3) There is no need to push all search results to the front-end at a time, pushing next / previous result + // is sufficient. var tagNameQuery = whitespaceTrimmedQuery; var attributeNameQuery = whitespaceTrimmedQuery; var startTagFound = (tagNameQuery.indexOf("<") === 0); @@ -1073,10 +1085,6 @@ InjectedScript._resolveObject = function(objectProxy) for (var i = 0; InjectedScript._isDefined(object) && path && i < path.length; ++i) object = object[path[i]]; - // Get to the necessary proto layer. - for (var i = 0; InjectedScript._isDefined(object) && protoDepth && i < protoDepth; ++i) - object = object.__proto__; - return object; } @@ -1136,14 +1144,12 @@ InjectedScript.createProxyObject = function(object, objectId, abbreviate) result.injectedScriptId = injectedScriptId; result.objectId = objectId; result.type = InjectedScript._type(object); + if (result.type === "array") + result.propertyLength = object.length; var type = typeof object; - if ((type === "object" && object !== null) || type === "function") { - for (var subPropertyName in object) { - result.hasChildren = true; - break; - } - } + + result.hasChildren = (type === "object" && object !== null && (Object.getOwnPropertyNames(object).length || object.__proto__)) || type === "function"; try { result.description = InjectedScript._describe(object, abbreviate); } catch (e) { diff --git a/WebCore/inspector/front-end/ObjectPropertiesSection.js b/WebCore/inspector/front-end/ObjectPropertiesSection.js index 6d71090..a32e799 100644 --- a/WebCore/inspector/front-end/ObjectPropertiesSection.js +++ b/WebCore/inspector/front-end/ObjectPropertiesSection.js @@ -77,6 +77,7 @@ WebInspector.ObjectPropertiesSection.prototype = { var infoElement = new TreeElement(title, null, false); this.propertiesTreeOutline.appendChild(infoElement); } + this.propertiesForTest = properties; } } @@ -86,6 +87,10 @@ WebInspector.ObjectPropertiesSection.CompareProperties = function(propertyA, pro { var a = propertyA.name; var b = propertyB.name; + if (a === "__proto__") + return 1; + if (b === "__proto__") + return -1; // if used elsewhere make sure to // - convert a and b to strings (not needed here, properties are all strings) @@ -173,8 +178,12 @@ WebInspector.ObjectPropertyTreeElement.prototype = { this.valueElement = document.createElement("span"); this.valueElement.className = "value"; this.valueElement.textContent = this.property.value.description; + if (typeof this.property.value.propertyLength !== "undefined") + this.valueElement.textContent += " (" + this.property.value.propertyLength + ")"; if (this.property.isGetter) - this.valueElement.addStyleClass("dimmed"); + this.valueElement.addStyleClass("dimmed"); + if (this.property.isError) + this.valueElement.addStyleClass("error"); this.listItemElement.removeChildren(); diff --git a/WebCore/inspector/front-end/ObjectProxy.js b/WebCore/inspector/front-end/ObjectProxy.js index 62517b8..ef139c6 100644 --- a/WebCore/inspector/front-end/ObjectProxy.js +++ b/WebCore/inspector/front-end/ObjectProxy.js @@ -28,12 +28,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.ObjectProxy = function(injectedScriptId, objectId, path, protoDepth, description, hasChildren) +WebInspector.ObjectProxy = function(injectedScriptId, objectId, path, description, hasChildren) { this.objectId = objectId; this.injectedScriptId = injectedScriptId; this.path = path || []; - this.protoDepth = protoDepth || 0; this.description = description; this.hasChildren = hasChildren; } diff --git a/WebCore/inspector/front-end/Panel.js b/WebCore/inspector/front-end/Panel.js index 5e81e2d..5b01191 100644 --- a/WebCore/inspector/front-end/Panel.js +++ b/WebCore/inspector/front-end/Panel.js @@ -230,16 +230,17 @@ WebInspector.Panel.prototype = { var currentView = this._searchResults[this._currentSearchResultIndex]; if (currentView.showingLastSearchResult()) { - if (++this._currentSearchResultIndex >= this._searchResults.length) - this._currentSearchResultIndex = 0; - currentView = this._searchResults[this._currentSearchResultIndex]; + if (this.searchIteratesOverViews()) { + if (++this._currentSearchResultIndex >= this._searchResults.length) + this._currentSearchResultIndex = 0; + currentView = this._searchResults[this._currentSearchResultIndex]; + } showFirstResult = true; } if (currentView !== this.visibleView) { - currentView = this.visibleView; - this._currentSearchResultIndex = 0; - showFirstResult = true; + this.showView(currentView); + WebInspector.focusSearchField(); } if (showFirstResult) @@ -264,14 +265,18 @@ WebInspector.Panel.prototype = { var currentView = this._searchResults[this._currentSearchResultIndex]; if (currentView.showingFirstSearchResult()) { - if (--this._currentSearchResultIndex < 0) - this._currentSearchResultIndex = (this._searchResults.length - 1); - currentView = this._searchResults[this._currentSearchResultIndex]; + if (this.searchIteratesOverViews()) { + if (--this._currentSearchResultIndex < 0) + this._currentSearchResultIndex = (this._searchResults.length - 1); + currentView = this._searchResults[this._currentSearchResultIndex]; + } showLastResult = true; } - if (currentView !== this.visibleView) + if (currentView !== this.visibleView) { this.showView(currentView); + WebInspector.focusSearchField(); + } if (showLastResult) currentView.jumpToLastSearchResult(); @@ -376,6 +381,11 @@ WebInspector.Panel.prototype = { showSourceLineForURL: function(url, line) { return false; + }, + + searchIteratesOverViews: function() + { + return false; } } diff --git a/WebCore/inspector/front-end/Popover.js b/WebCore/inspector/front-end/Popover.js new file mode 100644 index 0000000..70e4ac9 --- /dev/null +++ b/WebCore/inspector/front-end/Popover.js @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2009 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.Popover = function(contentElement) +{ + this.element = document.createElement("div"); + this.element.className = "popover"; + + this._popupArrowElement = document.createElement("div"); + this._popupArrowElement.className = "arrow"; + this.element.appendChild(this._popupArrowElement); + + this.contentElement = contentElement; +} + +WebInspector.Popover.prototype = { + show: function(anchor, preferredWidth, preferredHeight) + { + // This should not happen, but we hide previous popup to be on the safe side. + if (WebInspector.Popover._popoverElement) + document.body.removeChild(WebInspector.Popover._popoverElement); + WebInspector.Popover._popoverElement = this.element; + + // Temporarily attach in order to measure preferred dimensions. + this.contentElement.positionAt(0, 0); + document.body.appendChild(this.contentElement); + var preferredWidth = preferredWidth || this.contentElement.offsetWidth; + var preferredHeight = preferredHeight || this.contentElement.offsetHeight; + + this.contentElement.addStyleClass("content"); + this.element.appendChild(this.contentElement); + document.body.appendChild(this.element); + this._positionElement(anchor, preferredWidth, preferredHeight); + }, + + hide: function() + { + delete WebInspector.Popover._popoverElement; + document.body.removeChild(this.element); + }, + + _positionElement: function(anchorElement, preferredWidth, preferredHeight) + { + const borderWidth = 25; + const scrollerWidth = 11; + const arrowHeight = 10; + const arrowOffset = 15; + + // Skinny tooltips are not pretty, their arrow location is not nice. + preferredWidth = Math.max(preferredWidth, 50); + const totalWidth = window.innerWidth; + const totalHeight = window.innerHeight; + + var anchorBox = {x: anchorElement.totalOffsetLeft, y: anchorElement.totalOffsetTop, width: anchorElement.offsetWidth, height: anchorElement.offsetHeight}; + while (anchorElement !== document.body) { + if (anchorElement.scrollLeft) + anchorBox.x -= anchorElement.scrollLeft; + if (anchorElement.scrollTop) + anchorBox.y -= anchorElement.scrollTop; + anchorElement = anchorElement.parentElement; + } + + var newElementPosition = { x: 0, y: 0, width: preferredWidth + borderWidth * 2, height: preferredHeight + borderWidth * 2 }; + + var verticalAlignment; + var roomAbove = anchorBox.y; + var roomBelow = totalHeight - anchorBox.y - anchorBox.height; + + if (roomAbove > roomBelow) { + // Positioning above the anchor. + if (anchorBox.y > newElementPosition.height) + newElementPosition.y = anchorBox.y - newElementPosition.height; + else { + newElementPosition.y = 0; + newElementPosition.height = anchorBox.y - newElementPosition.y; + // Reserve room for vertical scroller anyways. + newElementPosition.width += scrollerWidth; + } + verticalAlignment = "bottom"; + } else { + // Positioning below the anchor. + newElementPosition.y = anchorBox.y + anchorBox.height; + if (newElementPosition.y + newElementPosition.height >= totalHeight) { + newElementPosition.height = totalHeight - anchorBox.y - anchorBox.height; + // Reserve room for vertical scroller. + newElementPosition.width += scrollerWidth; + } + // Align arrow. + newElementPosition.y -= arrowHeight; + verticalAlignment = "top"; + } + + var horizontalAlignment; + if (anchorBox.x + newElementPosition.width < totalWidth) { + newElementPosition.x = Math.max(0, anchorBox.x) - borderWidth - arrowOffset; + horizontalAlignment = "left"; + } else if (newElementPosition.width < totalWidth) { + newElementPosition.x = totalWidth - newElementPosition.width; + horizontalAlignment = "right"; + // Position arrow accurately. + this._popupArrowElement.style.right = totalWidth - anchorBox.x - borderWidth - anchorBox.width + "px"; + } else { + newElementPosition.x = 0; + newElementPosition.width = totalWidth; + horizontalAlignment = "left"; + if (verticalAlignment === "bottom") + newElementPosition.y -= scrollerWidth; + // Position arrow accurately. + this._popupArrowElement.style.left = anchorBox.x - borderWidth + "px"; + } + + // Reserve room for horizontal scroller. + newElementPosition.height += scrollerWidth; + + this.element.className = "popover " + verticalAlignment + "-" + horizontalAlignment + "-arrow"; + this.element.positionAt(newElementPosition.x, newElementPosition.y); + this.element.style.width = newElementPosition.width + "px"; + this.element.style.height = newElementPosition.height + "px"; + } +} diff --git a/WebCore/inspector/front-end/Popup.js b/WebCore/inspector/front-end/Popup.js deleted file mode 100644 index 9c8ef24..0000000 --- a/WebCore/inspector/front-end/Popup.js +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -/** - * This class provides a popup that can be shown relative to an anchor element - * or at an arbitrary absolute position. - * Points are Objects: {x: xValue, y: yValue}. - * Rectangles are Objects: {x: xValue, y: yValue, width: widthValue, height: heightValue}. - * - * element is an optional unparented visible element (style.display != "none" AND style.visibility != "hidden"). - * If the element is absent/undefined, it must have been set with the element(x) setter before the show() method invocation. - */ -WebInspector.Popup = function(element) -{ - if (element) - this.element = element; - this._keyHandler = this._keyEventHandler.bind(this); - this._mouseDownHandler = this._mouseDownEventHandler.bind(this); - this._visible = false; - this._autoHide = true; -} - -WebInspector.Popup.prototype = { - show: function() - { - if (this.visible) - return; - var ownerDocument = this._contentElement.ownerDocument; - if (!ownerDocument) - return; - - this._glasspaneElement = ownerDocument.createElement("div"); - this._glasspaneElement.className = "popup-glasspane"; - ownerDocument.body.appendChild(this._glasspaneElement); - - this._contentElement.positionAt(0, 0); - this._contentElement.removeStyleClass("hidden"); - ownerDocument.body.appendChild(this._contentElement); - - this.positionElement(); - this._visible = true; - ownerDocument.addEventListener("keydown", this._keyHandler, false); - ownerDocument.addEventListener("mousedown", this._mouseDownHandler, false); - }, - - hide: function() - { - if (this.visible) { - this._visible = false; - this._contentElement.ownerDocument.removeEventListener("keydown", this._keyHandler, false); - this._contentElement.ownerDocument.removeEventListener("mousedown", this._mouseDownHandler, false); - this._glasspaneElement.parentElement.removeChild(this._glasspaneElement); - this._contentElement.parentElement.removeChild(this._contentElement); - } - }, - - get visible() - { - return this._visible; - }, - - set element(x) - { - this._checkNotVisible(); - this._contentElement = x; - this._contentElement.addStyleClass("hidden"); - }, - - get element() - { - return this._contentElement; - }, - - positionElement: function() - { - var element = this._contentElement; - var anchorElement = this._anchorElement; - - var targetDocument = element.ownerDocument; - var targetDocumentBody = targetDocument.body; - var targetDocumentElement = targetDocument.documentElement; - var clippingBox = {x: 0, y: 0, width: targetDocumentElement.clientWidth, height: targetDocumentElement.clientHeight}; - var parentElement = element.offsetParent || element.parentElement; - - var anchorPosition = {x: anchorElement.totalOffsetLeft, y: anchorElement.totalOffsetTop}; - - // FIXME(apavlov@chromium.org): Translate anchorPosition to the element.ownerDocument frame when https://bugs.webkit.org/show_bug.cgi?id=28913 is fixed. - var anchorBox = {x: anchorPosition.x, y: anchorPosition.y, width: anchorElement.offsetWidth, height: anchorElement.offsetHeight}; - var elementBox = {x: element.totalOffsetLeft, y: element.totalOffsetTop, width: element.offsetWidth, height: element.offsetHeight}; - var newElementPosition = {x: 0, y: 0}; - - if (anchorBox.y - elementBox.height >= clippingBox.y) - newElementPosition.y = anchorBox.y - elementBox.height; - else - newElementPosition.y = Math.min(anchorBox.y + anchorBox.height, Math.max(clippingBox.y, clippingBox.y + clippingBox.height - elementBox.height)); - - if (anchorBox.x + elementBox.height <= clippingBox.x + clippingBox.height) - newElementPosition.x = anchorBox.x; - else - newElementPosition.x = Math.max(clippingBox.x, clippingBox.x + clippingBox.height - elementBox.height); - element.positionAt(newElementPosition.x, newElementPosition.y); - }, - - set anchor(x) - { - this._checkNotVisible(); - this._anchorElement = x; - }, - - get anchor() - { - return this._anchorElement; - }, - - set autoHide(x) - { - this._autoHide = x; - }, - - _checkNotVisible: function() - { - if (this.visible) - throw new Error("The popup must not be visible."); - }, - - _keyEventHandler: function(event) - { - // Escape hides the popup. - if (event.keyIdentifier == "U+001B") { - this.hide(); - event.preventDefault(); - event.handled = true; - } - }, - - _mouseDownEventHandler: function(event) - { - if (this._autoHide && event.originalTarget === this._glasspaneElement) - this.hide(); - } -} diff --git a/WebCore/inspector/front-end/PropertiesSidebarPane.js b/WebCore/inspector/front-end/PropertiesSidebarPane.js index 857d9a7..9df6448 100644 --- a/WebCore/inspector/front-end/PropertiesSidebarPane.js +++ b/WebCore/inspector/front-end/PropertiesSidebarPane.js @@ -48,12 +48,14 @@ WebInspector.PropertiesSidebarPane.prototype = { body.removeChildren(); self.sections = []; + var path = []; // Get array of prototype user-friendly names. for (var i = 0; i < prototypes.length; ++i) { - var prototype = new WebInspector.ObjectProxy(node.injectedScriptId, node.id, [], i); + var prototype = new WebInspector.ObjectProxy(node.injectedScriptId, node.id, path.slice()); var section = new WebInspector.ObjectPropertiesSection(prototype, prototypes[i], WebInspector.UIString("Prototype")); self.sections.push(section); body.appendChild(section.element); + path.push("__proto__"); } }; InjectedScriptAccess.get(node.injectedScriptId).getPrototypes(node.id, callback); diff --git a/WebCore/inspector/front-end/ResourcesPanel.js b/WebCore/inspector/front-end/ResourcesPanel.js index 40a380c..19325bb 100644 --- a/WebCore/inspector/front-end/ResourcesPanel.js +++ b/WebCore/inspector/front-end/ResourcesPanel.js @@ -706,6 +706,11 @@ WebInspector.ResourcesPanel.prototype = { get _resources() { return this.items; + }, + + searchIteratesOverViews: function() + { + return true; } } diff --git a/WebCore/inspector/front-end/Section.js b/WebCore/inspector/front-end/Section.js index 394f86d..7710192 100644 --- a/WebCore/inspector/front-end/Section.js +++ b/WebCore/inspector/front-end/Section.js @@ -31,6 +31,7 @@ WebInspector.Section = function(title, subtitle) { this.element = document.createElement("div"); this.element.className = "section"; + this.element.sectionForTest = this; this.headerElement = document.createElement("div"); this.headerElement.className = "header"; diff --git a/WebCore/inspector/front-end/SourceFrame.js b/WebCore/inspector/front-end/SourceFrame.js index 0f90700..799628e 100644 --- a/WebCore/inspector/front-end/SourceFrame.js +++ b/WebCore/inspector/front-end/SourceFrame.js @@ -45,6 +45,7 @@ WebInspector.SourceFrame = function(parentElement, addBreakpointDelegate, remove this._addBreakpointDelegate = addBreakpointDelegate; this._removeBreakpointDelegate = removeBreakpointDelegate; + this._popoverObjectGroup = "popover"; } WebInspector.SourceFrame.prototype = { @@ -53,6 +54,9 @@ WebInspector.SourceFrame.prototype = { { this._visible = visible; this._createViewerIfNeeded(); + if (!visible && this._textViewer) + this._textViewer.freeCachedElements(); + }, get executionLine() @@ -157,6 +161,8 @@ WebInspector.SourceFrame.prototype = { element.addEventListener("keydown", this._keyDown.bind(this), true); 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); this._parentElement.appendChild(element); this._needsProgramCounterImage = true; @@ -193,14 +199,7 @@ WebInspector.SourceFrame.prototype = { var ranges = []; // First do case-insensitive search. - var regex = ""; - for (var i = 0; i < query.length; ++i) { - var char = query.charAt(i); - if (char === "]") - char = "\\]"; - regex += "[" + char + "]"; - } - var regexObject = new RegExp(regex, "i"); + var regexObject = createSearchRegex(query); this._collectRegexMatches(regexObject, ranges); // Then try regex search if user knows the / / hint. @@ -427,8 +426,15 @@ WebInspector.SourceFrame.prototype = { contextMenu.show(event); }, + _scroll: function(event) + { + this._hidePopup(); + }, + _mouseDown: function(event) { + this._resetHoverTimer(); + this._hidePopup(); if (event.button != 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return; if (event.target.className !== "webkit-line-number") @@ -445,6 +451,164 @@ WebInspector.SourceFrame.prototype = { event.preventDefault(); }, + _mouseMove: function(event) + { + // Pretend that nothing has happened. + if (this._hoverElement === event.target) + return; + + this._resetHoverTimer(); + + // User has 500ms to reach the popup. + if (this._popup) { + var self = this; + function doHide() + { + self._hidePopup(); + delete self._hidePopupTimer; + } + this._hidePopupTimer = setTimeout(doHide, 500); + } + + 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) + return; + + // We are interested in identifiers and "this" keyword. + if (this._hoverElement.hasStyleClass("webkit-javascript-keyword")) { + if (this._hoverElement.textContent !== "this") + return; + } else if (!this._hoverElement.hasStyleClass("webkit-javascript-ident")) + return; + + const toolTipDelay = 1500; + this._hoverTimer = setTimeout(this._mouseHover.bind(this, this._hoverElement), toolTipDelay); + }, + + _resetHoverTimer: function() + { + if (this._hoverTimer) { + clearTimeout(this._hoverTimer); + delete this._hoverTimer; + } + }, + + _hidePopup: function() + { + if (this._popup) { + this._popup.hide(); + delete this._popup; + InspectorBackend.releaseWrapperObjectGroup(0, this._popoverObjectGroup); + } + }, + + _mouseHover: function(element) + { + delete this._hoverTimer; + + if (!WebInspector.panels.scripts || !WebInspector.panels.scripts.paused) + return; + + var lineRow = element.enclosingNodeOrSelfWithNodeName("tr"); + if (!lineRow) + return; + + // Find text offset of the hovered node (iterate over text nodes until we hit ours). + var offset = 0; + var node = lineRow.lastChild.traverseNextTextNode(lineRow.lastChild); + while (node && node !== element.firstChild) { + offset += node.nodeValue.length; + node = node.traverseNextTextNode(lineRow.lastChild); + } + + // Imagine that the line is "foo(A.B.C.D)" and we hit C. Following code goes through following steps: + // "foo(A.B.C" -> "C.B.A(oof" -> "C.B.A" -> "A.B.C" (target eval expression). + var lineNumber = lineRow.lineNumber; + var prefix = this._textModel.line(lineNumber).substring(0, offset + element.textContent.length); + var reversedPrefix = prefix.split("").reverse().join(""); + var match = /[a-zA-Z\x80-\xFF\_$0-9.]+/.exec(reversedPrefix); + if (!match) + return; + var expression = match[0].split("").reverse().join(""); + this._showPopup(element, expression); + }, + + _showPopup: function(element, expression) + { + function killHidePopupTimer() + { + if (this._hidePopupTimer) { + clearTimeout(this._hidePopupTimer); + delete this._hidePopupTimer; + + // We know that we reached the popup, but we might have moved over other elements. + // Discard pending command. + this._resetHoverTimer(); + } + } + + function showTextPopup(text) + { + if (!WebInspector.panels.scripts.paused) + return; + + var popupContentElement = document.createElement("span"); + popupContentElement.className = "monospace"; + popupContentElement.style.whiteSpace = "pre"; + popupContentElement.textContent = text; + this._popup = new WebInspector.Popover(popupContentElement); + this._popup.show(element); + popupContentElement.addEventListener("mousemove", killHidePopupTimer.bind(this), true); + } + + 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"); + popupContentElement.className = "monospace"; + popupContentElement.style.whiteSpace = "pre"; + popupContentElement.textContent = result.description; + this._popup = new WebInspector.Popover(popupContentElement); + this._popup.show(element); + } else { + var popupContentElement = document.createElement("div"); + + var titleElement = document.createElement("div"); + titleElement.className = "source-frame-popover-title monospace"; + titleElement.textContent = result.description; + popupContentElement.appendChild(titleElement); + + var section = new WebInspector.ObjectPropertiesSection(result, "", null, false); + section.expanded = true; + section.element.addStyleClass("source-frame-popover-tree"); + section.headerElement.addStyleClass("hidden"); + popupContentElement.appendChild(section.element); + + this._popup = new WebInspector.Popover(popupContentElement); + const popupWidth = 300; + const popupHeight = 250; + this._popup.show(element, popupWidth, popupHeight); + } + popupContentElement.addEventListener("mousemove", killHidePopupTimer.bind(this), true); + } + + function evaluateCallback(result, exception) + { + if (exception) + return; + if (!WebInspector.panels.scripts.paused) + return; + showObjectPopup.call(this, result); + } + WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, this._popoverObjectGroup, evaluateCallback.bind(this)); + }, + _editBreakpointCondition: function(breakpoint) { this._showBreakpointConditionPopup(breakpoint.line); @@ -662,4 +826,5 @@ WebInspector.SourceFrame.prototype = { } } + WebInspector.SourceFrame.prototype.__proto__ = WebInspector.Object.prototype; diff --git a/WebCore/inspector/front-end/SourceHTMLTokenizer.js b/WebCore/inspector/front-end/SourceHTMLTokenizer.js index 8856ff5..3c9bd8c 100644 --- a/WebCore/inspector/front-end/SourceHTMLTokenizer.js +++ b/WebCore/inspector/front-end/SourceHTMLTokenizer.js @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Tue Feb 2 00:44:38 2010 */ +/* Generated by re2c 0.13.5 on Mon Feb 15 19:30:21 2010 */ /* * Copyright (C) 2009 Google Inc. All rights reserved. * @@ -413,6 +413,12 @@ case 66: ++cursor; this.setLexCondition(this._lexConditions.TAG); { + if (this._parseCondition & this._parseConditions.SCRIPT) { + // Do not tokenize script tag contents, keep lexer state although processing "<". + this.setLexCondition(this._lexConditions.INITIAL); + this.tokenType = null; + return cursor; + } this.tokenType = "html-tag"; this._parseCondition = this._parseConditions.SCRIPT; this._setExpectingAttribute(); diff --git a/WebCore/inspector/front-end/SourceHTMLTokenizer.re2js b/WebCore/inspector/front-end/SourceHTMLTokenizer.re2js index 89c535a..cfa8834 100644 --- a/WebCore/inspector/front-end/SourceHTMLTokenizer.re2js +++ b/WebCore/inspector/front-end/SourceHTMLTokenizer.re2js @@ -174,6 +174,12 @@ WebInspector.SourceHTMLTokenizer.prototype = { <INITIAL> ScriptStart => TAG { + if (this._parseCondition & this._parseConditions.SCRIPT) { + // Do not tokenize script tag contents, keep lexer state although processing "<". + this.setLexCondition(this._lexConditions.INITIAL); + this.tokenType = null; + return cursor; + } this.tokenType = "html-tag"; this._parseCondition = this._parseConditions.SCRIPT; this._setExpectingAttribute(); diff --git a/WebCore/inspector/front-end/SourceJavaScriptTokenizer.js b/WebCore/inspector/front-end/SourceJavaScriptTokenizer.js index 816023f..75abeee 100644 --- a/WebCore/inspector/front-end/SourceJavaScriptTokenizer.js +++ b/WebCore/inspector/front-end/SourceJavaScriptTokenizer.js @@ -49,7 +49,7 @@ WebInspector.SourceJavaScriptTokenizer = function() "null", "true", "false", "break", "case", "catch", "const", "default", "finally", "for", "instanceof", "new", "var", "continue", "function", "return", "void", "delete", "if", "this", "do", "while", "else", "in", "switch", "throw", "try", "typeof", "debugger", - "class", "enum", "export", "extends", "import", "super", "get", "set" + "class", "enum", "export", "extends", "import", "super", "get", "set", "with" ].keySet(); this._lexConditions = { diff --git a/WebCore/inspector/front-end/SourceJavaScriptTokenizer.re2js b/WebCore/inspector/front-end/SourceJavaScriptTokenizer.re2js index 8630ccb..053c82f 100644 --- a/WebCore/inspector/front-end/SourceJavaScriptTokenizer.re2js +++ b/WebCore/inspector/front-end/SourceJavaScriptTokenizer.re2js @@ -48,7 +48,7 @@ WebInspector.SourceJavaScriptTokenizer = function() "null", "true", "false", "break", "case", "catch", "const", "default", "finally", "for", "instanceof", "new", "var", "continue", "function", "return", "void", "delete", "if", "this", "do", "while", "else", "in", "switch", "throw", "try", "typeof", "debugger", - "class", "enum", "export", "extends", "import", "super", "get", "set" + "class", "enum", "export", "extends", "import", "super", "get", "set", "with" ].keySet(); this._lexConditions = { diff --git a/WebCore/inspector/front-end/SourceTokenizer.js b/WebCore/inspector/front-end/SourceTokenizer.js index 1192383..d498028 100644 --- a/WebCore/inspector/front-end/SourceTokenizer.js +++ b/WebCore/inspector/front-end/SourceTokenizer.js @@ -74,10 +74,11 @@ WebInspector.SourceTokenizer.prototype = { WebInspector.SourceTokenizer.Registry = function() { this._tokenizers = {}; this._tokenizerConstructors = { - "text/css": WebInspector.SourceCSSTokenizer, - "text/html": WebInspector.SourceHTMLTokenizer, - "text/javascript": WebInspector.SourceJavaScriptTokenizer, - "application/x-javascript": WebInspector.SourceJavaScriptTokenizer + "text/css": "SourceCSSTokenizer", + "text/html": "SourceHTMLTokenizer", + "text/javascript": "SourceJavaScriptTokenizer", + "application/javascript": "SourceJavaScriptTokenizer", + "application/x-javascript": "SourceJavaScriptTokenizer" }; } @@ -93,9 +94,10 @@ WebInspector.SourceTokenizer.Registry.prototype = { { if (!this._tokenizerConstructors[mimeType]) return null; - var tokenizer = this._tokenizers[mimeType]; + var tokenizerClass = this._tokenizerConstructors[mimeType]; + var tokenizer = this._tokenizers[tokenizerClass]; if (!tokenizer) { - tokenizer = new this._tokenizerConstructors[mimeType](); + tokenizer = new WebInspector[tokenizerClass](); this._tokenizers[mimeType] = tokenizer; } return tokenizer; diff --git a/WebCore/inspector/front-end/TextEditorHighlighter.js b/WebCore/inspector/front-end/TextEditorHighlighter.js index c73e036..cf0b590 100644 --- a/WebCore/inspector/front-end/TextEditorHighlighter.js +++ b/WebCore/inspector/front-end/TextEditorHighlighter.js @@ -32,36 +32,8 @@ WebInspector.TextEditorHighlighter = function(textModel, damageCallback) { this._textModel = textModel; - - this._styles = []; - - this._styles["css-comment"] = "rgb(0, 116, 0)"; - this._styles["css-params"] = "rgb(7, 144, 154)"; - this._styles["css-string"] = "rgb(7, 144, 154)"; - this._styles["css-keyword"] = "rgb(7, 144, 154)"; - this._styles["css-number"] = "rgb(50, 0, 255)"; - this._styles["css-property"] = "rgb(200, 0, 0)"; - this._styles["css-at-rule"] = "rgb(200, 0, 0)"; - this._styles["css-selector"] = "rgb(0, 0, 0)"; - this._styles["css-important"] = "rgb(200, 0, 180)"; - - /* Keep this in sync with inspector.css and view-source.css */ - this._styles["html-tag"] = "rgb(136, 18, 128)"; - this._styles["html-attribute-name"] = "rgb(153, 69, 0)"; - this._styles["html-attribute-value"] = "rgb(26, 26, 166)"; - this._styles["html-comment"] = "rgb(35, 110, 37)"; - this._styles["html-doctype"] = "rgb(192, 192, 192)"; - this._styles["html-external-link"] = "#00e"; - this._styles["html-resource-link"] = "#00e"; - - this._styles["javascript-comment"] = "rgb(0, 116, 0)"; - this._styles["javascript-string"] = "rgb(196, 26, 22)"; - this._styles["javascript-regexp"] = "rgb(196, 26, 22)"; - this._styles["javascript-keyword"] = "rgb(170, 13, 145)"; - this._styles["javascript-number"] = "rgb(28, 0, 207)"; - - this.mimeType = "text/html"; - this._damageCallback = damageCallback; + this._tokenizer = WebInspector.SourceTokenizer.Registry.getInstance().getTokenizer("text/html"); + this._damageCallback = damageCallback; } WebInspector.TextEditorHighlighter.prototype = { @@ -190,7 +162,7 @@ WebInspector.TextEditorHighlighter.prototype = { var newColumn = this._tokenizer.nextToken(column); var tokenType = this._tokenizer.tokenType; if (tokenType) - attributes[column] = { length: newColumn - column, tokenType: tokenType, style: this._styles[tokenType] }; + attributes[column] = { length: newColumn - column, tokenType: tokenType }; column = newColumn; } while (column < line.length) } diff --git a/WebCore/inspector/front-end/TextViewer.js b/WebCore/inspector/front-end/TextViewer.js index 096464f..9be6a00 100644 --- a/WebCore/inspector/front-end/TextViewer.js +++ b/WebCore/inspector/front-end/TextViewer.js @@ -50,6 +50,9 @@ WebInspector.TextViewer = function(textModel, platform, url) this._defaultChunkSize = 50; this._paintCoalescingLevel = 0; + + this.freeCachedElements(); + this._buildChunks(); } WebInspector.TextViewer.prototype = { @@ -96,7 +99,10 @@ WebInspector.TextViewer.prototype = { this._rangeToMark = range; this.revealLine(range.startLine); this._paintLines(range.startLine, range.startLine + 1); + if (this._markedRangeElement) + this._markedRangeElement.scrollIntoViewIfNeeded(); } + delete this._markedRangeElement; }, highlightLine: function(lineNumber) @@ -111,14 +117,20 @@ WebInspector.TextViewer.prototype = { chunk.addDecoration("webkit-highlighted-line"); }, + freeCachedElements: function() + { + this._cachedSpans = []; + this._cachedTextNodes = []; + this._cachedRows = []; + }, + _buildChunks: function() { this._linesContainerElement.removeChildren(); - var paintLinesCallback = this._paintLines.bind(this); this._textChunks = []; for (var i = 0; i < this._textModel.linesCount; i += this._defaultChunkSize) { - var chunk = new WebInspector.TextChunk(this._textModel, i, i + this._defaultChunkSize, paintLinesCallback); + var chunk = new WebInspector.TextChunk(this, i, i + this._defaultChunkSize); this._textChunks.push(chunk); this._linesContainerElement.appendChild(chunk.element); } @@ -140,23 +152,22 @@ WebInspector.TextViewer.prototype = { oldChunk.expanded = false; var insertIndex = oldChunk.chunkNumber + 1; - var paintLinesCallback = this._paintLines.bind(this); // Prefix chunk. if (lineNumber > oldChunk.startLine) { - var prefixChunk = new WebInspector.TextChunk(this._textModel, oldChunk.startLine, lineNumber, paintLinesCallback); + var prefixChunk = new WebInspector.TextChunk(this, oldChunk.startLine, lineNumber); this._textChunks.splice(insertIndex++, 0, prefixChunk); this._linesContainerElement.insertBefore(prefixChunk.element, oldChunk.element); } // Line chunk. - var lineChunk = new WebInspector.TextChunk(this._textModel, lineNumber, lineNumber + 1, paintLinesCallback); + var lineChunk = new WebInspector.TextChunk(this, lineNumber, lineNumber + 1); this._textChunks.splice(insertIndex++, 0, lineChunk); this._linesContainerElement.insertBefore(lineChunk.element, oldChunk.element); // Suffix chunk. if (oldChunk.startLine + oldChunk.linesCount > lineNumber + 1) { - var suffixChunk = new WebInspector.TextChunk(this._textModel, lineNumber + 1, oldChunk.startLine + oldChunk.linesCount, paintLinesCallback); + var suffixChunk = new WebInspector.TextChunk(this, lineNumber + 1, oldChunk.startLine + oldChunk.linesCount); this._textChunks.splice(insertIndex, 0, suffixChunk); this._linesContainerElement.insertBefore(suffixChunk.element, oldChunk.element); } @@ -185,7 +196,11 @@ WebInspector.TextViewer.prototype = { _scroll: function() { - this._repaintAll(); + var scrollTop = this.element.scrollTop; + setTimeout(function() { + if (scrollTop === this.element.scrollTop) + this._repaintAll(); + }.bind(this), 50); }, beginUpdates: function(enabled) @@ -320,7 +335,7 @@ WebInspector.TextViewer.prototype = { if (!highlighterState) { if (this._rangeToMark && this._rangeToMark.startLine === lineNumber) - this._markRange(element, line, this._rangeToMark.startColumn, this._rangeToMark.endColumn); + this._markedRangeElement = highlightSearchResult(element, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn); return; } @@ -330,30 +345,53 @@ WebInspector.TextViewer.prototype = { for (var j = 0; j < line.length;) { if (j > 1000) { // This line is too long - do not waste cycles on minified js highlighting. + plainTextStart = j; break; } var attribute = highlighterState && highlighterState.attributes[j]; - if (!attribute || !attribute.style) { + if (!attribute || !attribute.tokenType) { if (plainTextStart === -1) plainTextStart = j; j++; } else { if (plainTextStart !== -1) { - element.appendChild(document.createTextNode(line.substring(plainTextStart, j))); + this._appendTextNode(element, line.substring(plainTextStart, j)); plainTextStart = -1; } - element.appendChild(this._createSpan(line.substring(j, j + attribute.length), attribute.tokenType)); + this._appendSpan(element, line.substring(j, j + attribute.length), attribute.tokenType); j += attribute.length; } } if (plainTextStart !== -1) - element.appendChild(document.createTextNode(line.substring(plainTextStart, line.length))); + this._appendTextNode(element, line.substring(plainTextStart, line.length)); if (this._rangeToMark && this._rangeToMark.startLine === lineNumber) - this._markRange(element, line, this._rangeToMark.startColumn, this._rangeToMark.endColumn); + this._markedRangeElement = highlightSearchResult(element, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn); if (lineRow.decorationsElement) element.appendChild(lineRow.decorationsElement); }, + _releaseLinesHighlight: function(fromLine, toLine) + { + for (var i = fromLine; i < toLine; ++i) { + var lineRow = this._textModel.getAttribute(i, "line-row"); + if (!lineRow) + continue; + var element = lineRow.lastChild; + if ("spans" in element) { + var spans = element.spans; + for (var j = 0; j < spans.length; ++j) + this._cachedSpans.push(spans[j]); + delete element.spans; + } + if ("textNodes" in element) { + var textNodes = element.textNodes; + for (var j = 0; j < textNodes.length; ++j) + this._cachedTextNodes.push(textNodes[j]); + delete element.textNodes; + } + } + }, + _getSelection: function() { var selection = window.getSelection(); @@ -447,15 +485,33 @@ WebInspector.TextViewer.prototype = { return { line: lineRow.lineNumber, column: column }; }, - _createSpan: function(content, className) + _appendSpan: function(element, content, className) { - if (className === "html-resource-link" || className === "html-external-link") - return this._createLink(content, className === "html-external-link"); + if (className === "html-resource-link" || className === "html-external-link") { + element.appendChild(this._createLink(content, className === "html-external-link")); + return; + } - var span = document.createElement("span"); + var span = this._cachedSpans.pop() || document.createElement("span"); span.className = "webkit-" + className; - span.appendChild(document.createTextNode(content)); - return span; + span.textContent = content; + element.appendChild(span); + if (!("spans" in element)) + element.spans = []; + element.spans.push(span); + }, + + _appendTextNode: function(element, text) + { + var textNode = this._cachedTextNodes.pop(); + if (textNode) { + textNode.nodeValue = text; + } else + textNode = document.createTextNode(text); + element.appendChild(textNode); + if (!("textNodes" in element)) + element.textNodes = []; + element.textNodes.push(textNode); }, _createLink: function(content, isExternal) @@ -484,58 +540,19 @@ WebInspector.TextViewer.prototype = { return WebInspector.completeURL(this._url, hrefValue); }, - _markRange: function(element, lineText, startOffset, endOffset) - { - var markNode = document.createElement("span"); - markNode.className = "webkit-markup"; - markNode.textContent = lineText.substring(startOffset, endOffset); - - var markLength = endOffset - startOffset; - var boundary = element.rangeBoundaryForOffset(startOffset); - var textNode = boundary.container; - var text = textNode.textContent; - - if (boundary.offset + markLength < text.length) { - // Selection belong to a single split mode. - textNode.textContent = text.substring(boundary.offset + markLength); - textNode.parentElement.insertBefore(markNode, textNode); - var prefixNode = document.createTextNode(text.substring(0, boundary.offset)); - textNode.parentElement.insertBefore(prefixNode, markNode); - return; - } - - var parentElement = textNode.parentElement; - var anchorElement = textNode.nextSibling; - - markLength -= text.length - boundary.offset; - textNode.textContent = text.substring(0, boundary.offset); - textNode = textNode.traverseNextTextNode(element); - - while (textNode) { - var text = textNode.textContent; - if (markLength < text.length) { - textNode.textContent = text.substring(markLength); - break; - } - - markLength -= text.length; - textNode.textContent = ""; - textNode = textNode.traverseNextTextNode(element); - } - - parentElement.insertBefore(markNode, anchorElement); - }, - resize: function() { this._repaintAll(); } } -WebInspector.TextChunk = function(textModel, startLine, endLine, paintLinesCallback) +var cachedSpans = []; + +WebInspector.TextChunk = function(textViewer, startLine, endLine) { + this._textViewer = textViewer; this.element = document.createElement("tr"); - this._textModel = textModel; + this._textModel = textViewer._textModel; this.element.chunk = this; this.element.lineNumber = startLine; @@ -545,7 +562,6 @@ WebInspector.TextChunk = function(textModel, startLine, endLine, paintLinesCallb this._lineNumberElement = document.createElement("td"); this._lineNumberElement.className = "webkit-line-number"; - this._lineNumberElement.textContent = this._lineNumberText(this.startLine); this.element.appendChild(this._lineNumberElement); this._lineContentElement = document.createElement("td"); @@ -554,11 +570,14 @@ WebInspector.TextChunk = function(textModel, startLine, endLine, paintLinesCallb this._expanded = false; + var lineNumbers = []; var lines = []; - for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) + for (var i = startLine; i < endLine; ++i) { + lineNumbers.push(i + 1); lines.push(this._textModel.line(i)); + } + this._lineNumberElement.textContent = lineNumbers.join("\n"); this._lineContentElement.textContent = lines.join("\n"); - this._paintLines = paintLinesCallback; } WebInspector.TextChunk.prototype = { @@ -601,40 +620,30 @@ WebInspector.TextChunk.prototype = { if (this.linesCount === 1) { this._textModel.setAttribute(this.startLine, "line-row", this.element); if (expanded) - this._paintLines(this.startLine, this.startLine + 1); + this._textViewer._paintLines(this.startLine, this.startLine + 1); return; } if (expanded) { var parentElement = this.element.parentElement; for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) { - var lineRow = document.createElement("tr"); - lineRow.lineNumber = i; - - var lineNumberElement = document.createElement("td"); - lineNumberElement.className = "webkit-line-number"; - lineNumberElement.textContent = this._lineNumberText(i); - lineRow.appendChild(lineNumberElement); - - var lineContentElement = document.createElement("td"); - lineContentElement.className = "webkit-line-content"; - lineContentElement.textContent = this._textModel.line(i); - lineRow.appendChild(lineContentElement); - + var lineRow = this._createRow(i); this._textModel.setAttribute(i, "line-row", lineRow); parentElement.insertBefore(lineRow, this.element); } parentElement.removeChild(this.element); - this._paintLines(this.startLine, this.startLine + this.linesCount); + this._textViewer._paintLines(this.startLine, this.startLine + this.linesCount); } else { var firstLine = this._textModel.getAttribute(this.startLine, "line-row"); var parentElement = firstLine.parentElement; + this._textViewer._releaseLinesHighlight(this.startLine, this.startLine + this.linesCount); parentElement.insertBefore(this.element, firstLine); for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) { var lineRow = this._textModel.getAttribute(i, "line-row"); this._textModel.removeAttribute(i, "line-row"); + this._textViewer._cachedRows.push(lineRow); parentElement.removeChild(lineRow); } } @@ -652,15 +661,28 @@ WebInspector.TextChunk.prototype = { return result; }, - _lineNumberText: function(lineNumber) + _createRow: function(lineNumber) { - var totalDigits = Math.ceil(Math.log(this._textModel.linesCount + 1) / Math.log(10)); - var digits = Math.ceil(Math.log(lineNumber + 2) / Math.log(10)); + var cachedRows = this._textViewer._cachedRows; + if (cachedRows.length) { + var lineRow = cachedRows[cachedRows.length - 1]; + cachedRows.length--; + var lineNumberElement = lineRow.firstChild; + var lineContentElement = lineRow.lastChild; + } else { + var lineRow = document.createElement("tr"); - var text = ""; - for (var i = digits; i < totalDigits; ++i) - text += " "; - text += lineNumber + 1; - return text; + var lineNumberElement = document.createElement("td"); + lineNumberElement.className = "webkit-line-number"; + lineRow.appendChild(lineNumberElement); + + var lineContentElement = document.createElement("td"); + lineContentElement.className = "webkit-line-content"; + lineRow.appendChild(lineContentElement); + } + lineRow.lineNumber = lineNumber; + lineNumberElement.textContent = lineNumber + 1; + lineContentElement.textContent = this._textModel.line(lineNumber); + return lineRow; } } diff --git a/WebCore/inspector/front-end/WebKit.qrc b/WebCore/inspector/front-end/WebKit.qrc index efa2bfc..73309d1 100644 --- a/WebCore/inspector/front-end/WebKit.qrc +++ b/WebCore/inspector/front-end/WebKit.qrc @@ -45,7 +45,7 @@ <file>Panel.js</file> <file>PanelEnablerView.js</file> <file>Placard.js</file> - <file>Popup.js</file> + <file>Popover.js</file> <file>ProfileDataGridTree.js</file> <file>ProfilesPanel.js</file> <file>ProfileView.js</file> @@ -91,6 +91,7 @@ <file>audits.css</file> <file>inspector.css</file> <file>inspectorSyntaxHighlight.css</file> + <file>popover.css</file> <file>textViewer.css</file> <file>Images/back.png</file> <file>Images/checker.png</file> @@ -125,6 +126,7 @@ <file>Images/excludeButtonGlyph.png</file> <file>Images/focusButtonGlyph.png</file> <file>Images/forward.png</file> + <file>Images/gearButtonGlyph.png</file> <file>Images/glossyHeader.png</file> <file>Images/glossyHeaderPressed.png</file> <file>Images/glossyHeaderSelected.png</file> @@ -142,6 +144,8 @@ <file>Images/paneSettingsButtons.png</file> <file>Images/pauseOnExceptionButtonGlyph.png</file> <file>Images/percentButtonGlyph.png</file> + <file>Images/popoverArrows.png</file> + <file>Images/popoverBackground.png</file> <file>Images/profileGroupIcon.png</file> <file>Images/profileIcon.png</file> <file>Images/profilesIcon.png</file> @@ -185,6 +189,12 @@ <file>Images/statusbarResizerVertical.png</file> <file>Images/storageIcon.png</file> <file>Images/successGreenDot.png</file> + <file>Images/thumbActiveHoriz.png</file> + <file>Images/thumbActiveVert.png</file> + <file>Images/thumbHoriz.png</file> + <file>Images/thumbVert.png</file> + <file>Images/thumbHoverHoriz.png</file> + <file>Images/thumbHoverVert.png</file> <file>Images/timelineBarBlue.png</file> <file>Images/timelineBarGray.png</file> <file>Images/timelineBarGreen.png</file> @@ -210,6 +220,8 @@ <file>Images/timelinePillRed.png</file> <file>Images/timelinePillYellow.png</file> <file>Images/toolbarItemSelected.png</file> + <file>Images/trackHoriz.png</file> + <file>Images/trackVert.png</file> <file>Images/treeDownTriangleBlack.png</file> <file>Images/treeDownTriangleWhite.png</file> <file>Images/treeRightTriangleBlack.png</file> diff --git a/WebCore/inspector/front-end/inspector.css b/WebCore/inspector/front-end/inspector.css index 53f1e4b..76b31fc 100644 --- a/WebCore/inspector/front-end/inspector.css +++ b/WebCore/inspector/front-end/inspector.css @@ -29,8 +29,11 @@ body { cursor: default; - height: 100%; - width: 100%; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; overflow: hidden; font-family: Lucida Grande, sans-serif; font-size: 10px; @@ -1348,7 +1351,6 @@ body.inactive .placard.selected { margin: 0; padding: 2px 6px 3px; list-style: none; - background-color: white; min-height: 18px; } @@ -1577,6 +1579,10 @@ li.editing .swatch, li.editing .enabled-button, li.editing-sub-part .delete-but color: rgb(100, 100, 100); } +.section .properties .value.error { + color: red; +} + .section .properties .number, .event-properties .number { color: blue; } @@ -2245,7 +2251,7 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des margin: 0 0 5px 20px; } -.panel-enabler-view button:not(.status-bar-item), .pane button { +.panel-enabler-view button:not(.status-bar-item), .pane button, button.show-all-nodes { color: rgb(6, 6, 6); background-color: transparent; border: 1px solid rgb(165, 165, 165); @@ -2262,6 +2268,13 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des height: 24px; } +button.show-all-nodes { + font-size: 13px; + margin: 0; + padding: 0 20px; + height: 20px; +} + .panel-enabler-view.welcome { z-index: auto; } @@ -2293,12 +2306,12 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des padding: 2px 9px; } -.panel-enabler-view button:active:not(.status-bar-item), .pane button:active { +.panel-enabler-view button:active:not(.status-bar-item), .pane button:active, button.show-all-nodes:active { background-color: rgb(215, 215, 215); background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239))); } -body.inactive .panel-enabler-view button:not(.status-bar-item), .panel-enabler-view button:disabled:not(.status-bar-item), body.inactive .pane button, .pane button:disabled { +body.inactive .panel-enabler-view button:not(.status-bar-item), .panel-enabler-view button:disabled:not(.status-bar-item), body.inactive .pane button, .pane button:disabled, body.inactive button.show-all-nodes { color: rgb(130, 130, 130); border-color: rgb(212, 212, 212); background-color: rgb(239, 239, 239); @@ -3801,3 +3814,21 @@ ol.breakpoint-list { outline: none !important; -webkit-user-modify: read-write; } + +.source-frame-popover-title { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + font-weight: bold; + padding-left: 18px; +} + +.source-frame-popover-tree { + border-top: 1px solid rgb(190, 190, 190); + overflow: auto; + position: absolute; + top: 15px; + bottom: 0; + left: 0; + right: 0; +} diff --git a/WebCore/inspector/front-end/inspector.html b/WebCore/inspector/front-end/inspector.html index 4ddd10e..85633b5 100644 --- a/WebCore/inspector/front-end/inspector.html +++ b/WebCore/inspector/front-end/inspector.html @@ -30,9 +30,10 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="audits.css"> - <link rel="stylesheet" type="text/css" href="textViewer.css"> <link rel="stylesheet" type="text/css" href="inspector.css"> <link rel="stylesheet" type="text/css" href="inspectorSyntaxHighlight.css"> + <link rel="stylesheet" type="text/css" href="popover.css"> + <link rel="stylesheet" type="text/css" href="textViewer.css"> <script type="text/javascript" src="utilities.js"></script> <script type="text/javascript" src="treeoutline.js"></script> <script type="text/javascript" src="inspector.js"></script> @@ -43,7 +44,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="ContextMenu.js"></script> <script type="text/javascript" src="KeyboardShortcut.js"></script> <script type="text/javascript" src="TextPrompt.js"></script> - <script type="text/javascript" src="Popup.js"></script> + <script type="text/javascript" src="Popover.js"></script> <script type="text/javascript" src="Placard.js"></script> <script type="text/javascript" src="View.js"></script> <script type="text/javascript" src="Callback.js"></script> diff --git a/WebCore/inspector/front-end/inspector.js b/WebCore/inspector/front-end/inspector.js index 77d3f42..8bcdf63 100644 --- a/WebCore/inspector/front-end/inspector.js +++ b/WebCore/inspector/front-end/inspector.js @@ -686,9 +686,7 @@ WebInspector.documentKeyDown = function(event) var isFindKey = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey; if (isFindKey) { - var searchField = document.getElementById("search"); - searchField.focus(); - searchField.select(); + WebInspector.focusSearchField(); event.preventDefault(); } @@ -878,6 +876,13 @@ WebInspector.updateSearchLabel = function() } } +WebInspector.focusSearchField = function() +{ + var searchField = document.getElementById("search"); + searchField.focus(); + searchField.select(); +} + WebInspector.toggleAttach = function() { this.attached = !this.attached; @@ -1337,7 +1342,7 @@ WebInspector.log = function(message) WebInspector.log.repeatCount = repeatCount; // ConsoleMessage expects a proxy object - message = new WebInspector.ObjectProxy(null, null, [], 0, message, false); + message = new WebInspector.ObjectProxy(null, null, [], message, false); // post the message var msg = new WebInspector.ConsoleMessage( diff --git a/WebCore/inspector/front-end/inspectorSyntaxHighlight.css b/WebCore/inspector/front-end/inspectorSyntaxHighlight.css index 1292f00..0965a5c 100644 --- a/WebCore/inspector/front-end/inspectorSyntaxHighlight.css +++ b/WebCore/inspector/front-end/inspectorSyntaxHighlight.css @@ -26,29 +26,29 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - .webkit-css-comment { - color: rgb(0, 116, 0); - } +.webkit-css-comment { + color: rgb(0, 116, 0); +} - .webkit-css-url, .webkit-css-color, .webkit-css-string, .webkit-css-keyword { - color: rgb(7, 144, 154); +.webkit-css-url, .webkit-css-color, .webkit-css-string, .webkit-css-keyword { + color: rgb(7, 144, 154); } - .webkit-css-number { - color: rgb(50, 0, 255); - } +.webkit-css-number { + color: rgb(50, 0, 255); +} - .webkit-css-property, .webkit-css-at-rule { - color: rgb(200, 0, 0); - } +.webkit-css-property, .webkit-css-at-rule { + color: rgb(200, 0, 0); +} - .webkit-css-selector { - rgb(0, 0, 0); - } +.webkit-css-selector { + color: black; +} - .webkit-css-important { - color: rgb(200, 0, 180); - } +.webkit-css-important { + color: rgb(200, 0, 180); +} .webkit-javascript-comment { color: rgb(0, 116, 0); @@ -66,6 +66,10 @@ color: rgb(196, 26, 22); } +.webkit-javascript-ident { + color: black; +} + .webkit-html-comment { /* Keep this in sync with view-source.css (.webkit-html-comment) */ color: rgb(35, 110, 37); diff --git a/WebCore/inspector/front-end/popover.css b/WebCore/inspector/front-end/popover.css new file mode 100644 index 0000000..ae6f610 --- /dev/null +++ b/WebCore/inspector/front-end/popover.css @@ -0,0 +1,200 @@ +.popover { + position: absolute; + -webkit-border-image: url(Images/popoverBackground.png) 25 25 25 25; + border-width: 25px; + z-index: 100; + pointer-events: none; +} + +.popover .content { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + pointer-events: auto; + overflow: auto; +} + +.popover .arrow { + position: absolute; + background-image: url(Images/popoverArrows.png); + width: 19px; + height: 19px; + margin-left: 15px; + margin-top: -25px; + top: 0; + left: 0; +} + +.popover.top-left-arrow .arrow { + /* The default is top-left, no styles needed. */ +} + +.popover.top-right-arrow .arrow { + right: 25px; + left: auto; +} + +.popover.bottom-left-arrow .arrow { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -25px; + background-position: 0 -19px; +} + +.popover.bottom-right-arrow .arrow { + right: 15px; + left: auto; + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -25px; + background-position: 0 -19px; +} + +.popover.left-top-arrow .arrow { + top: 0; + margin-top: 15px; + margin-left: -25px; + background-position: 0 -38px; +} + +.popover.left-bottom-arrow .arrow { + top: auto; + bottom: 0; + margin-bottom: 15px; + margin-left: -25px; + background-position: 0 -38px; +} + +.popover.right-top-arrow .arrow { + right: 0; + left: auto; + top: 0; + margin-top: 15px; + margin-right: -25px; + background-position: 0 -57px; +} + +.popover.right-bottom-arrow .arrow { + right: 0; + left: auto; + top: auto; + bottom: 0; + margin-bottom: 15px; + margin-right: -25px; + background-position: 0 -57px; +} + +.popover ::-webkit-scrollbar { + width: 11px; + height: 11px; +} + +.popover ::-webkit-scrollbar-corner { + display: none; +} + +.popover ::-webkit-resizer { + display: none; +} + +/* Horizontal Scrollbar Styles */ + +.popover ::-webkit-scrollbar:horizontal:corner-present { + border-right-width: 0; +} + +.popover ::-webkit-scrollbar-thumb:horizontal { + -webkit-border-image: url(Images/thumbHoriz.png) 0 11 0 11; + border-color: transparent; + border-width: 0 11px; + min-width: 20px; +} + +.popover ::-webkit-scrollbar-thumb:horizontal:hover { + -webkit-border-image: url(Images/thumbHoverHoriz.png) 0 11 0 11; +} + +.popover ::-webkit-scrollbar-thumb:horizontal:active { + -webkit-border-image: url(Images/thumbActiveHoriz.png) 0 11 0 11; +} + +.popover ::-webkit-scrollbar-track-piece:horizontal:start { + margin-left: 5px; +} + +.popover ::-webkit-scrollbar-track-piece:horizontal:end { + margin-right: 5px; +} + +.popover ::-webkit-scrollbar-track-piece:horizontal:end:corner-present { + margin-right: 4px; +} + +.popover ::-webkit-scrollbar-track-piece:horizontal:decrement { + -webkit-border-image: url(Images/trackHoriz.png) 0 11 0 11; + border-color: transparent; + border-width: 0 0 0 11px; +} + +.popover ::-webkit-scrollbar-track-piece:horizontal:increment { + -webkit-border-image: url(Images/trackHoriz.png) 0 11 0 11; + border-color: transparent; + border-width: 0 11px 0 0; +} + +/* Vertical Scrollbar Styles */ + + +.popover ::-webkit-scrollbar:vertical:corner-present { + border-bottom-width: 0; +} + +.popover ::-webkit-scrollbar-thumb:vertical { + -webkit-border-image: url(Images/thumbVert.png) 11 0 11 0; + border-color: transparent; + border-width: 11px 0; + min-height: 20px; +} + +.popover ::-webkit-scrollbar-thumb:vertical:hover { + -webkit-border-image: url(Images/thumbHoverVert.png) 11 0 11 0; +} + +.popover ::-webkit-scrollbar-thumb:vertical:active { + -webkit-border-image: url(Images/thumbActiveVert.png) 11 0 11 0; +} + + +.popover ::-webkit-scrollbar-track-piece:vertical:start { + margin-top: 5px; +} + +.popover ::-webkit-scrollbar-track-piece:vertical:end { + margin-bottom: 5px; +} + +.popover ::-webkit-scrollbar-track-piece:vertical:end:corner-present { + margin-bottom: 4px; +} + +.popover ::-webkit-scrollbar-track-piece:vertical:decrement { + -webkit-border-image: url(Images/trackVert.png) 11 0 11 0; + border-color: transparent; + border-width: 11px 0 0 0; +} + +.popover ::-webkit-scrollbar-track-piece:vertical:increment { + -webkit-border-image: url(Images/trackVert.png) 11 0 11 0; + border-color: transparent; + border-width: 0 0 11px 0; +} + +/* Forced Scrollbar Mode Styles */ + +.popover ::-webkit-scrollbar-button { + display: none; +} diff --git a/WebCore/inspector/front-end/textViewer.css b/WebCore/inspector/front-end/textViewer.css index af079bc..1447dfd 100644 --- a/WebCore/inspector/front-end/textViewer.css +++ b/WebCore/inspector/front-end/textViewer.css @@ -4,14 +4,13 @@ left:0; right:0; bottom:0; - white-space: pre-wrap; + white-space: pre; overflow: auto; } .text-editor-lines { border: 0; width: 100%; - vertical-align: baseline; -webkit-border-horizontal-spacing: 0; -webkit-border-vertical-spacing: 0; -webkit-user-select: text; @@ -63,21 +62,20 @@ .webkit-line-number { color: rgb(128, 128, 128); text-align: right; - white-space: pre; word-break: normal; -webkit-user-select: none; background-color: rgb(240, 240, 240); border-right: 1px solid rgb(187, 187, 187) !important; padding-left: 2px; padding-right: 2px; - vertical-align: top; background-repeat: no-repeat; background-position: right 1px; + vertical-align: top; } .webkit-line-content { - white-space: pre-wrap; padding-left: 2px; + vertical-align: top; } .webkit-execution-line .webkit-line-number { @@ -130,13 +128,14 @@ outline: 1px solid rgb(64, 115, 244); } -.webkit-markup { +.webkit-search-result { -webkit-border-radius: 4px; - padding: 2px 1px 2px 3px; - margin-left: -4px; - margin-top: -2px; + padding: 2px 2px 2px 3px; + margin: -2px -2px -2px -3px; + opacity: 0.8; -webkit-box-shadow: rgba(0, 0, 0, .5) 3px 3px 4px; background-color: rgb(241, 234, 0); + color: black; } .webkit-highlighted-line .webkit-line-content { diff --git a/WebCore/inspector/front-end/utilities.js b/WebCore/inspector/front-end/utilities.js index 60d3b45..f30ab9f 100644 --- a/WebCore/inspector/front-end/utilities.js +++ b/WebCore/inspector/front-end/utilities.js @@ -271,7 +271,7 @@ Element.prototype.__defineGetter__("totalOffsetLeft", function() { var total = 0; for (var element = this; element; element = element.offsetParent) - total += element.offsetLeft; + total += element.offsetLeft + (this !== element ? element.clientLeft : 0); return total; }); @@ -279,7 +279,7 @@ Element.prototype.__defineGetter__("totalOffsetTop", function() { var total = 0; for (var element = this; element; element = element.offsetParent) - total += element.offsetTop; + total += element.offsetTop + (this !== element ? element.clientTop : 0); return total; }); @@ -850,3 +850,60 @@ function isEnterKey(event) { // Check if in IME. return event.keyCode !== 229 && event.keyIdentifier === "Enter"; } + + +function highlightSearchResult(element, offset, length) +{ + 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 parentElement = textNode.parentElement; + var anchorElement = textNode.nextSibling; + + length -= text.length - boundary.offset; + textNode.textContent = text.substring(0, boundary.offset); + textNode = textNode.traverseNextTextNode(element); + + while (textNode) { + var text = textNode.textContent; + if (length < text.length) { + textNode.textContent = text.substring(length); + break; + } + + length -= text.length; + textNode.textContent = ""; + textNode = textNode.traverseNextTextNode(element); + } + + parentElement.insertBefore(highlightNode, anchorElement); + return highlightNode; +} + +function createSearchRegex(query) +{ + var regex = ""; + for (var i = 0; i < query.length; ++i) { + var char = query.charAt(i); + if (char === "]") + char = "\\]"; + regex += "[" + char + "]"; + } + return new RegExp(regex, "i"); +} |