diff --git a/BUILD.gn b/BUILD.gn index 47616182fb..8a8459e048 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -23,6 +23,7 @@ devtools_core_base_files = [ "front_end/Runtime.js", ] devtools_common_js_files = [ + "front_end/common/CharacterIdMap.js", "front_end/common/Color.js", "front_end/common/Console.js", "front_end/common/ContentProvider.js", @@ -533,6 +534,7 @@ devtools_network_js_files = [ "front_end/network/NetworkOverview.js", "front_end/network/NetworkPanel.js", "front_end/network/NetworkTimeCalculator.js", + "front_end/network/NetworkTimelineColumn.js", "front_end/network/RequestCookiesView.js", "front_end/network/RequestHeadersView.js", "front_end/network/RequestHTMLView.js", diff --git a/front_end/common/CharacterIdMap.js b/front_end/common/CharacterIdMap.js new file mode 100644 index 0000000000..81e1b5df42 --- /dev/null +++ b/front_end/common/CharacterIdMap.js @@ -0,0 +1,44 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @constructor + * @template T + */ +WebInspector.CharacterIdMap = function() +{ + /** @type {!Map} */ + this._elementToCharacter = new Map(); + /** @type {!Map} */ + this._characterToElement = new Map(); + this._charCode = 33; +} + +WebInspector.CharacterIdMap.prototype = { + /** + * @param {T} object + * @return {string} + */ + toChar: function(object) + { + var character = this._elementToCharacter.get(object); + if (!character) { + if (this._charCode >= 0xFFFF) + throw new Error("CharacterIdMap ran out of capacity!"); + character = String.fromCharCode(this._charCode++); + this._elementToCharacter.set(object, character); + this._characterToElement.set(character, object); + } + return character; + }, + + /** + * @param {string} character + * @return {?T} + */ + fromChar: function(character) + { + return this._characterToElement.get(character) || null; + } +} diff --git a/front_end/common/module.json b/front_end/common/module.json index aa695da2a0..938d274f37 100644 --- a/front_end/common/module.json +++ b/front_end/common/module.json @@ -26,6 +26,7 @@ "Trie.js", "UIString.js", "ModuleExtensionInterfaces.js", - "FormatterWorkerPool.js" + "FormatterWorkerPool.js", + "CharacterIdMap.js" ] } diff --git a/front_end/components/DOMPresentationUtils.js b/front_end/components/DOMPresentationUtils.js index 65c96e2da4..d0420f871f 100644 --- a/front_end/components/DOMPresentationUtils.js +++ b/front_end/components/DOMPresentationUtils.js @@ -137,7 +137,7 @@ WebInspector.DOMPresentationUtils.linkifyDeferredNodeReference = function(deferr var link = shadowRoot.createChild("div", "node-link"); link.createChild("content"); link.addEventListener("click", deferredNode.resolve.bind(deferredNode, onDeferredNodeResolved), false); - link.addEventListener("mousedown", consumeEvent, false); + link.addEventListener("mousedown", (e) => e.consume(), false); /** * @param {?WebInspector.DOMNode} node diff --git a/front_end/components/DockController.js b/front_end/components/DockController.js index e04e202e9b..19be63370b 100644 --- a/front_end/components/DockController.js +++ b/front_end/components/DockController.js @@ -114,6 +114,7 @@ WebInspector.DockController.prototype = { /** * @param {string} dockSide + * @suppressGlobalPropertiesCheck */ setDockSide: function(dockSide) { @@ -126,7 +127,7 @@ WebInspector.DockController.prototype = { if (this._dockSide) this._lastDockStateSetting.set(this._dockSide); - WebInspector.DockController._previousFocusedElement = WebInspector.currentFocusElement(); + this._savedFocus = document.deepActiveElement(); var eventData = { from: this._dockSide, to: dockSide }; this.dispatchEventToListeners(WebInspector.DockController.Events.BeforeDockSideChanged, eventData); console.timeStamp("DockController.setIsDocked"); @@ -143,10 +144,10 @@ WebInspector.DockController.prototype = { _setIsDockedResponse: function(eventData) { this.dispatchEventToListeners(WebInspector.DockController.Events.AfterDockSideChanged, eventData); - - if (WebInspector.DockController._previousFocusedElement) - WebInspector.DockController._previousFocusedElement.focus(); - delete WebInspector.DockController._previousFocusedElement; + if (this._savedFocus) { + this._savedFocus.focus(); + this._savedFocus = null; + } }, /** diff --git a/front_end/components/Spectrum.js b/front_end/components/Spectrum.js index 0d33461d0c..f9fae6a195 100644 --- a/front_end/components/Spectrum.js +++ b/front_end/components/Spectrum.js @@ -48,6 +48,7 @@ WebInspector.Spectrum = function() WebInspector.VBox.call(this, true); this.registerRequiredCSS("components/spectrum.css"); this.contentElement.tabIndex = 0; + this.setDefaultFocusedElement(this.contentElement); this._colorElement = this.contentElement.createChild("div", "spectrum-color"); this._colorDragElement = this._colorElement.createChild("div", "spectrum-sat fill").createChild("div", "spectrum-val fill").createChild("div", "spectrum-dragger"); @@ -228,8 +229,8 @@ WebInspector.Spectrum.prototype = { _focus: function() { - if (this.isShowing() && WebInspector.currentFocusElement() !== this.contentElement) - WebInspector.setCurrentFocusElement(this.contentElement); + if (this.isShowing()) + this.contentElement.focus(); }, /** @@ -328,7 +329,7 @@ WebInspector.Spectrum.prototype = { this._shadesContainer.appendChild(shadeElement); } - WebInspector.setCurrentFocusElement(this._shadesContainer); + this._shadesContainer.focus(); this._shadesCloseHandler = closeLightnessShades.bind(this, colorElement); this._shadesContainer.ownerDocument.addEventListener("mousedown", this._shadesCloseHandler, true); }, diff --git a/front_end/console/ConsoleView.js b/front_end/console/ConsoleView.js index c134ccfae9..f9e16d377d 100644 --- a/front_end/console/ConsoleView.js +++ b/front_end/console/ConsoleView.js @@ -332,7 +332,6 @@ WebInspector.ConsoleView.prototype = { wasShown: function() { this._viewport.refresh(); - this.focus(); }, focus: function() diff --git a/front_end/console/ConsoleViewMessage.js b/front_end/console/ConsoleViewMessage.js index b83f1d7bda..10ab001b42 100644 --- a/front_end/console/ConsoleViewMessage.js +++ b/front_end/console/ConsoleViewMessage.js @@ -45,23 +45,6 @@ WebInspector.ConsoleViewMessage = function(consoleMessage, linkifier, nestingLev /** @type {?WebInspector.DataGrid} */ this._dataGrid = null; - - /** @type {!Object.} */ - this._customFormatters = { - "array": this._formatParameterAsArray, - "typedarray": this._formatParameterAsArray, - "error": this._formatParameterAsError, - "function": this._formatParameterAsFunction, - "generator": this._formatParameterAsObject, - "iterator": this._formatParameterAsObject, - "map": this._formatParameterAsObject, - "node": this._formatParameterAsNode, - "object": this._formatParameterAsObject, - "promise": this._formatParameterAsObject, - "proxy": this._formatParameterAsObject, - "set": this._formatParameterAsObject, - "string": this._formatParameterAsString - }; this._previewFormatter = new WebInspector.RemoteObjectPreviewFormatter(); this._searchRegex = null; } @@ -298,14 +281,12 @@ WebInspector.ConsoleViewMessage.prototype = { { var anchorElement = null; if (consoleMessage.source !== WebInspector.ConsoleMessage.MessageSource.Network || consoleMessage.request) { - if (consoleMessage.scriptId) { + if (consoleMessage.scriptId) anchorElement = this._linkifyScriptId(consoleMessage.scriptId, consoleMessage.url || "", consoleMessage.line, consoleMessage.column); - } else { - if (consoleMessage.stackTrace && consoleMessage.stackTrace.callFrames.length) - anchorElement = this._linkifyStackTraceTopFrame(consoleMessage.stackTrace); - else if (consoleMessage.url && consoleMessage.url !== "undefined") - anchorElement = this._linkifyLocation(consoleMessage.url, consoleMessage.line, consoleMessage.column); - } + else if (consoleMessage.stackTrace && consoleMessage.stackTrace.callFrames.length) + anchorElement = this._linkifyStackTraceTopFrame(consoleMessage.stackTrace); + else if (consoleMessage.url && consoleMessage.url !== "undefined") + anchorElement = this._linkifyLocation(consoleMessage.url, consoleMessage.line, consoleMessage.column); } else if (consoleMessage.url) { var url = consoleMessage.url; var isExternal = !WebInspector.resourceForURL(url) && !WebInspector.networkMapping.uiSourceCodeForURLForAnyTarget(url); @@ -473,15 +454,51 @@ WebInspector.ConsoleViewMessage.prototype = { */ _formatParameter: function(output, forceObjectFormat, includePreview) { - if (output.customPreview()) { + if (output.customPreview()) return (new WebInspector.CustomPreviewComponent(output)).element; - } var type = forceObjectFormat ? "object" : (output.subtype || output.type); - var formatter = this._customFormatters[type] || this._formatParameterAsValue; var span = createElement("span"); span.className = "object-value-" + type + " source-code"; - formatter.call(this, output, span, includePreview); + switch (type) { + case "array": + case "typedarray": + this._formatParameterAsArray(output, span); + break; + case "error": + this._formatParameterAsError(output, span); + break; + case "function": + case "generator": + this._formatParameterAsFunction(output, span, includePreview); + break; + case "iterator": + case "map": + case "object": + case "promise": + case "proxy": + case "set": + this._formatParameterAsObject(output, span, includePreview); + break; + case "node": + this._formatParameterAsNode(output, span); + break; + case "string": + this._formatParameterAsString(output, span); + break; + case "boolean": + case "date": + case "null": + case "number": + case "regexp": + case "symbol": + case "undefined": + this._formatParameterAsValue(output, span); + break; + default: + this._formatParameterAsValue(output, span); + console.error("Tried to format remote object of unknown type."); + } return span; }, @@ -502,28 +519,16 @@ WebInspector.ConsoleViewMessage.prototype = { * @param {boolean=} includePreview */ _formatParameterAsObject: function(obj, elem, includePreview) - { - this._formatParameterAsArrayOrObject(obj, elem, includePreview); - }, - - /** - * @param {!WebInspector.RemoteObject} obj - * @param {!Element} elem - * @param {boolean=} includePreview - */ - _formatParameterAsArrayOrObject: function(obj, elem, includePreview) { var titleElement = createElement("span"); if (includePreview && obj.preview) { titleElement.classList.add("console-object-preview"); this._previewFormatter.appendObjectPreview(titleElement, obj.preview); + } else if (obj.type === "function") { + WebInspector.ObjectPropertiesSection.formatObjectAsFunction(obj, titleElement, false); + titleElement.classList.add("object-value-function"); } else { - if (obj.type === "function") { - WebInspector.ObjectPropertiesSection.formatObjectAsFunction(obj, titleElement, false); - titleElement.classList.add("object-value-function"); - } else { - titleElement.createTextChild(obj.description || ""); - } + titleElement.createTextChild(obj.description || ""); } var section = new WebInspector.ObjectPropertiesSection(obj, titleElement, this._linkifier); @@ -612,25 +617,79 @@ WebInspector.ConsoleViewMessage.prototype = { { }, - /** - * @return {boolean} - */ - _usePrintedArrayFormatter: function() - { - return this._message.type !== WebInspector.ConsoleMessage.MessageType.DirXML && this._message.type !== WebInspector.ConsoleMessage.MessageType.Result; - }, - /** * @param {!WebInspector.RemoteObject} array * @param {!Element} elem */ _formatParameterAsArray: function(array, elem) { - var maxFlatArrayLength = 100; - if (this._usePrintedArrayFormatter() || array.arrayLength() > maxFlatArrayLength) - this._formatParameterAsArrayOrObject(array, elem, this._usePrintedArrayFormatter() || array.arrayLength() <= maxFlatArrayLength); + var usePrintedArrayFormat = this._message.type !== WebInspector.ConsoleMessage.MessageType.DirXML && this._message.type !== WebInspector.ConsoleMessage.MessageType.Result; + var isLongArray = array.arrayLength() > 100; + if (usePrintedArrayFormat || isLongArray) + this._formatParameterAsObject(array, elem, usePrintedArrayFormat || !isLongArray); else - array.getAllProperties(false, this._printArrayResult.bind(this, array, elem)); + array.getAllProperties(false, printArrayResult.bind(this)); + + /** + * @param {?Array.} properties + * @this {!WebInspector.ConsoleViewMessage} + */ + function printArrayResult(properties) + { + if (!properties) { + this._formatParameterAsObject(array, elem, false); + return; + } + + var titleElement = createElement("span"); + var elements = {}; + for (var i = 0; i < properties.length; ++i) { + var property = properties[i]; + var name = property.name; + if (isNaN(name)) + continue; + if (property.getter) + elements[name] = this._formatAsAccessorProperty(array, [name], true); + else if (property.value) + elements[name] = this._formatAsArrayEntry(property.value); + } + + titleElement.createTextChild("["); + var lastNonEmptyIndex = -1; + + function appendUndefined(titleElement, index) + { + if (index - lastNonEmptyIndex <= 1) + return; + var span = titleElement.createChild("span", "object-value-undefined"); + span.textContent = WebInspector.UIString("undefined × %d", index - lastNonEmptyIndex - 1); + } + + var length = array.arrayLength(); + for (var i = 0; i < length; ++i) { + var element = elements[i]; + if (!element) + continue; + + if (i - lastNonEmptyIndex > 1) { + appendUndefined(titleElement, i); + titleElement.createTextChild(", "); + } + + titleElement.appendChild(element); + lastNonEmptyIndex = i; + if (i < length - 1) + titleElement.createTextChild(", "); + } + appendUndefined(titleElement, length); + + titleElement.createTextChild("]"); + + var section = new WebInspector.ObjectPropertiesSection(array, titleElement, this._linkifier); + section.element.classList.add("console-view-object-properties-section"); + section.enableContextMenu(); + elem.appendChild(section.element); + } }, /** @@ -661,68 +720,6 @@ WebInspector.ConsoleViewMessage.prototype = { span.appendChild(errorSpan ? errorSpan : WebInspector.linkifyStringAsFragment(output.description || "")); }, - /** - * @param {!WebInspector.RemoteObject} array - * @param {!Element} elem - * @param {?Array.} properties - */ - _printArrayResult: function(array, elem, properties) - { - if (!properties) { - this._formatParameterAsObject(array, elem, false); - return; - } - - var titleElement = createElement("span"); - var elements = {}; - for (var i = 0; i < properties.length; ++i) { - var property = properties[i]; - var name = property.name; - if (isNaN(name)) - continue; - if (property.getter) - elements[name] = this._formatAsAccessorProperty(array, [name], true); - else if (property.value) - elements[name] = this._formatAsArrayEntry(property.value); - } - - titleElement.createTextChild("["); - var lastNonEmptyIndex = -1; - - function appendUndefined(titleElement, index) - { - if (index - lastNonEmptyIndex <= 1) - return; - var span = titleElement.createChild("span", "object-value-undefined"); - span.textContent = WebInspector.UIString("undefined × %d", index - lastNonEmptyIndex - 1); - } - - var length = array.arrayLength(); - for (var i = 0; i < length; ++i) { - var element = elements[i]; - if (!element) - continue; - - if (i - lastNonEmptyIndex > 1) { - appendUndefined(titleElement, i); - titleElement.createTextChild(", "); - } - - titleElement.appendChild(element); - lastNonEmptyIndex = i; - if (i < length - 1) - titleElement.createTextChild(", "); - } - appendUndefined(titleElement, length); - - titleElement.createTextChild("]"); - - var section = new WebInspector.ObjectPropertiesSection(array, titleElement, this._linkifier); - section.element.classList.add("console-view-object-properties-section"); - section.enableContextMenu(); - elem.appendChild(section.element); - }, - /** * @param {!WebInspector.RemoteObject} output * @return {!Element} diff --git a/front_end/diff/Diff.js b/front_end/diff/Diff.js index 297b3878c4..ee953f2f2b 100644 --- a/front_end/diff/Diff.js +++ b/front_end/diff/Diff.js @@ -21,20 +21,17 @@ WebInspector.Diff = { */ lineDiff: function(lines1, lines2) { - /** @type {!Map.} */ - var lineToChar = new Map(); - /** @type {!Map.} */ - var charToLine = new Map(); - var charCode = 33; - var text1 = encode(lines1); - var text2 = encode(lines2); + /** @type {!WebInspector.CharacterIdMap} */ + var idMap = new WebInspector.CharacterIdMap(); + var text1 = lines1.map(line => idMap.toChar(line)).join(""); + var text2 = lines2.map(line => idMap.toChar(line)).join(""); var diff = WebInspector.Diff.charDiff(text1, text2); var lineDiff = []; for (var i = 0; i < diff.length; i++) { var lines = []; for (var j = 0; j < diff[i][1].length; j++) - lines.push(charToLine.get(diff[i][1][j])); + lines.push(idMap.fromChar(diff[i][1][j])); lineDiff.push({ 0: diff[i][0], @@ -42,26 +39,6 @@ WebInspector.Diff = { }); } return lineDiff; - - /** - * @param {!Array.} lines - * @return {string} - */ - function encode(lines) - { - var text = ""; - for (var i = 0; i < lines.length; ++i) { - var line = lines[i]; - var character = lineToChar.get(line); - if (!character) { - character = String.fromCharCode(charCode++); - lineToChar.set(line, character); - charToLine.set(character, line); - } - text += character; - } - return text; - } }, /** diff --git a/front_end/elements/ComputedStyleWidget.js b/front_end/elements/ComputedStyleWidget.js index 5b67223fe5..cbd8277852 100644 --- a/front_end/elements/ComputedStyleWidget.js +++ b/front_end/elements/ComputedStyleWidget.js @@ -207,8 +207,8 @@ WebInspector.ComputedStyleWidget.prototype = { var trace = propertyTraces.get(propertyName); if (trace) { var activeProperty = this._renderPropertyTrace(cssModel, matchedStyles, nodeStyle.node, treeElement, trace); - treeElement.listItemElement.addEventListener("mousedown", consumeEvent, false); - treeElement.listItemElement.addEventListener("dblclick", consumeEvent, false); + treeElement.listItemElement.addEventListener("mousedown", (e) => e.consume(), false); + treeElement.listItemElement.addEventListener("dblclick", (e) => e.consume(), false); treeElement.listItemElement.addEventListener("click", handleClick.bind(null, treeElement), false); var gotoSourceElement = propertyValueElement.createChild("div", "goto-source-icon"); gotoSourceElement.addEventListener("click", this._navigateToSource.bind(this, activeProperty)); @@ -243,7 +243,7 @@ WebInspector.ComputedStyleWidget.prototype = { treeElement.expand(); else treeElement.collapse(); - consumeEvent(event); + event.consume(); } }, diff --git a/front_end/elements/ElementsTreeOutline.js b/front_end/elements/ElementsTreeOutline.js index e79c8021c8..a3d9b00a1c 100644 --- a/front_end/elements/ElementsTreeOutline.js +++ b/front_end/elements/ElementsTreeOutline.js @@ -154,14 +154,6 @@ WebInspector.ElementsTreeOutline.prototype = { this._element.focus(); }, - /** - * @return {boolean} - */ - hasFocus: function() - { - return this._element === WebInspector.currentFocusElement(); - }, - /** * @param {boolean} wrap */ diff --git a/front_end/elements/elementsTreeOutline.css b/front_end/elements/elementsTreeOutline.css index 17b34dd159..c236b1ab94 100644 --- a/front_end/elements/elementsTreeOutline.css +++ b/front_end/elements/elementsTreeOutline.css @@ -354,7 +354,18 @@ li.selected { z-index: 0; } -li.hovered:not(.always-parent) + ol.children, .elements-tree-outline ol.shadow-root, li.selected:not(.always-parent) + ol.children { + +li.hovered:not(.always-parent) + ol.children::before { + background-color: rgba(56, 121, 217, 0.1); + content: "a"; + position: absolute; + top: -1px; + bottom: 0; + left: -10000px; + right: 0; +} + +.elements-tree-outline ol.shadow-root, li.selected:not(.always-parent) + ol.children { margin-left: 5px; -webkit-padding-start: 6px; border-width: 1px; diff --git a/front_end/emulated_devices/Nexus5X-landscape.svg b/front_end/emulated_devices/Nexus5X-landscape.svg index 85634c6fe9..21f12a9d0c 100644 --- a/front_end/emulated_devices/Nexus5X-landscape.svg +++ b/front_end/emulated_devices/Nexus5X-landscape.svg @@ -1,20 +1,19 @@ - - Nexus 5X - - - - - - - - - - - - - - - - + + Nexus 5X + + + + + + + + + + + + + + + diff --git a/front_end/emulated_devices/Nexus5X-portrait.svg b/front_end/emulated_devices/Nexus5X-portrait.svg index 29c4e64120..d01d91e997 100644 --- a/front_end/emulated_devices/Nexus5X-portrait.svg +++ b/front_end/emulated_devices/Nexus5X-portrait.svg @@ -1,20 +1,19 @@ - - Nexus 5X - - - - - - - - - - - - - - - - + + Nexus 5X + + + + + + + + + + + + + + + diff --git a/front_end/emulated_devices/Nexus6P-landscape.svg b/front_end/emulated_devices/Nexus6P-landscape.svg index a46532c962..ad485f7814 100644 --- a/front_end/emulated_devices/Nexus6P-landscape.svg +++ b/front_end/emulated_devices/Nexus6P-landscape.svg @@ -1,53 +1,50 @@ - - Nexus 6P - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Nexus 6P + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front_end/emulated_devices/Nexus6P-portrait.svg b/front_end/emulated_devices/Nexus6P-portrait.svg index 92e1832cc3..ffd8cffbae 100644 --- a/front_end/emulated_devices/Nexus6P-portrait.svg +++ b/front_end/emulated_devices/Nexus6P-portrait.svg @@ -1,53 +1,54 @@ - - Nexus 6P - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Nexus 6P + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front_end/emulated_devices/module.json b/front_end/emulated_devices/module.json index e2b5892eff..4ea61a839c 100644 --- a/front_end/emulated_devices/module.json +++ b/front_end/emulated_devices/module.json @@ -322,7 +322,7 @@ "vertical": { "outline" : { "image": "@url(Nexus5X-portrait.svg)", - "insets" : { "left": 20, "top": 92, "right": 22, "bottom": 108 } + "insets" : { "left": 18, "top": 88, "right": 22, "bottom": 98 } }, "width": 412, "height": 732 @@ -330,7 +330,7 @@ "horizontal": { "outline" : { "image": "@url(Nexus5X-landscape.svg)", - "insets" : { "left": 94, "top": 23, "right": 100, "bottom": 20 } + "insets" : { "left": 88, "top": 21, "right": 98, "bottom": 19 } }, "width": 732, "height": 412 @@ -422,7 +422,7 @@ "horizontal": { "outline" : { "image": "@url(Nexus6P-landscape.svg)", - "insets" : { "left": 94, "top": 48, "right": 97, "bottom": 48 } + "insets" : { "left": 94, "top": 17, "right": 88, "bottom": 17 } }, "width": 732, "height": 412 @@ -431,7 +431,7 @@ "vertical": { "outline" : { "image": "@url(Nexus6P-portrait.svg)", - "insets" : { "left": 58, "top": 94, "right": 58, "bottom": 97 } + "insets" : { "left": 16, "top": 94, "right": 16, "bottom": 88 } }, "width": 412, "height": 732 diff --git a/front_end/emulation/DeviceModeView.js b/front_end/emulation/DeviceModeView.js index f738480ab2..91c52f1606 100644 --- a/front_end/emulation/DeviceModeView.js +++ b/front_end/emulation/DeviceModeView.js @@ -420,6 +420,7 @@ WebInspector.DeviceModeView.prototype = { var ctx = canvas.getContext("2d"); canvas.width = outlineRect.width; canvas.height = outlineRect.height; + ctx.imageSmoothingEnabled = false; var promise = Promise.resolve(); if (this._model.outlineImage()) diff --git a/front_end/main/Main.js b/front_end/main/Main.js index 5b97e35cb3..25468c7098 100644 --- a/front_end/main/Main.js +++ b/front_end/main/Main.js @@ -83,6 +83,7 @@ WebInspector.Main.prototype = { Runtime.experiments.register("applyCustomStylesheet", "Allow custom UI themes"); Runtime.experiments.register("autoAttachToCrossProcessSubframes", "Auto-attach to cross-process subframes", true); Runtime.experiments.register("blackboxJSFramesOnTimeline", "Blackbox JavaScript frames on Timeline", true); + Runtime.experiments.register("canvasNetworkTimeline", "Canvas based timeline in Network panel", true); Runtime.experiments.register("colorContrastRatio", "Contrast ratio line in color picker", true); Runtime.experiments.register("continueToFirstInvocation", "Continue to first invocation", true); Runtime.experiments.register("emptySourceMapAutoStepping", "Empty sourcemap auto-stepping"); @@ -472,7 +473,8 @@ WebInspector.Main.prototype = { if (event.handled) return; - var target = event.deepActiveElement(); + var document = event.target && event.target.ownerDocument; + var target = document ? document.deepActiveElement() : null; if (target) { var anchor = target.enclosingNodeOrSelfWithNodeName("a"); if (anchor && anchor.preventFollow) @@ -497,7 +499,10 @@ WebInspector.Main.prototype = { { var eventCopy = new CustomEvent("clipboard-" + event.type); eventCopy["original"] = event; - event.deepActiveElement().dispatchEvent(eventCopy); + var document = event.target && event.target.ownerDocument; + var target = document ? document.deepActiveElement() : null; + if (target) + target.dispatchEvent(eventCopy); if (eventCopy.handled) event.preventDefault(); }, @@ -666,10 +671,11 @@ WebInspector.Main.SearchActionDelegate.prototype = { * @param {!WebInspector.Context} context * @param {string} actionId * @return {boolean} + * @suppressGlobalPropertiesCheck */ handleAction: function(context, actionId) { - var searchableView = WebInspector.SearchableView.fromElement(WebInspector.currentFocusElement()) || WebInspector.inspectorView.currentPanel().searchableView(); + var searchableView = WebInspector.SearchableView.fromElement(document.deepActiveElement()) || WebInspector.inspectorView.currentPanel().searchableView(); if (!searchableView) return false; switch (actionId) { diff --git a/front_end/network/BlockedURLsPane.js b/front_end/network/BlockedURLsPane.js index fd240d640c..03d4b49439 100644 --- a/front_end/network/BlockedURLsPane.js +++ b/front_end/network/BlockedURLsPane.js @@ -18,7 +18,7 @@ WebInspector.BlockedURLsPane = function() this._blockedURLsSetting.addChangeListener(this._update, this); this._toolbar = new WebInspector.Toolbar("", this.contentElement); - this._toolbar.element.addEventListener("click", consumeEvent); + this._toolbar.element.addEventListener("click", (e) => e.consume()); var addButton = new WebInspector.ToolbarButton(WebInspector.UIString("Add pattern"), "add-toolbar-item"); addButton.addEventListener("click", this._addButtonClicked.bind(this)); this._toolbar.appendToolbarItem(addButton); diff --git a/front_end/network/NetworkDataGridNode.js b/front_end/network/NetworkDataGridNode.js index e9afa3c3eb..16025969a1 100644 --- a/front_end/network/NetworkDataGridNode.js +++ b/front_end/network/NetworkDataGridNode.js @@ -102,6 +102,16 @@ WebInspector.NetworkDataGridNode.prototype = { this._updateGraph(); }, + /** + * @param {!Element} element + * @param {string} text + */ + _setTextAndTitle: function(element, text) + { + element.textContent = text; + element.title = text; + }, + /** * @override * @param {string} columnIdentifier @@ -113,21 +123,21 @@ WebInspector.NetworkDataGridNode.prototype = { switch (columnIdentifier) { case "name": this._renderNameCell(cell); break; case "timeline": this._createTimelineBar(cell); break; - case "method": cell.setTextAndTitle(this._request.requestMethod); break; + case "method": this._setTextAndTitle(cell, this._request.requestMethod); break; case "status": this._renderStatusCell(cell); break; - case "protocol": cell.setTextAndTitle(this._request.protocol); break; - case "scheme": cell.setTextAndTitle(this._request.scheme); break; - case "domain": cell.setTextAndTitle(this._request.domain); break; - case "remoteaddress": cell.setTextAndTitle(this._request.remoteAddress()); break; - case "cookies": cell.setTextAndTitle(this._arrayLength(this._request.requestCookies)); break; - case "setcookies": cell.setTextAndTitle(this._arrayLength(this._request.responseCookies)); break; - case "priority": cell.setTextAndTitle(WebInspector.uiLabelForPriority(this._request.initialPriority())); break; - case "connectionid": cell.setTextAndTitle(this._request.connectionId); break; - case "type": this._renderTypeCell(cell); break; + case "protocol": this._setTextAndTitle(cell, this._request.protocol); break; + case "scheme": this._setTextAndTitle(cell, this._request.scheme); break; + case "domain": this._setTextAndTitle(cell, this._request.domain); break; + case "remoteaddress": this._setTextAndTitle(cell, this._request.remoteAddress()); break; + case "cookies": this._setTextAndTitle(cell, this._arrayLength(this._request.requestCookies)); break; + case "setcookies": this._setTextAndTitle(cell, this._arrayLength(this._request.responseCookies)); break; + case "priority": this._setTextAndTitle(cell, WebInspector.uiLabelForPriority(this._request.initialPriority())); break; + case "connectionid": this._setTextAndTitle(cell, this._request.connectionId); break; + case "type": this._setTextAndTitle(cell, this.displayType()); break; case "initiator": this._renderInitiatorCell(cell); break; case "size": this._renderSizeCell(cell); break; case "time": this._renderTimeCell(cell); break; - default: cell.setTextAndTitle(this._request.responseHeaderValue(columnIdentifier) || ""); break; + default: this._setTextAndTitle(cell, this._request.responseHeaderValue(columnIdentifier) || ""); break; } return cell; @@ -283,15 +293,15 @@ WebInspector.NetworkDataGridNode.prototype = { this._appendSubtitle(cell, this._request.localizedFailDescription); cell.title = failText + " " + this._request.localizedFailDescription; } else - cell.setTextAndTitle(failText); + this._setTextAndTitle(cell, failText); } else if (this._request.statusCode) { cell.createTextChild("" + this._request.statusCode); this._appendSubtitle(cell, this._request.statusText); cell.title = this._request.statusCode + " " + this._request.statusText; } else if (this._request.parsedURL.isDataURL()) { - cell.setTextAndTitle(WebInspector.UIString("(data)")); + this._setTextAndTitle(cell, WebInspector.UIString("(data)")); } else if (this._request.canceled) { - cell.setTextAndTitle(WebInspector.UIString("(canceled)")); + this._setTextAndTitle(cell, WebInspector.UIString("(canceled)")); } else if (this._request.wasBlocked()) { var reason = WebInspector.UIString("other"); switch (this._request.blockedReason()) { @@ -311,22 +321,14 @@ WebInspector.NetworkDataGridNode.prototype = { reason = WebInspector.UIString("other"); break; } - cell.setTextAndTitle(WebInspector.UIString("(blocked:%s)", reason)); + this._setTextAndTitle(cell, WebInspector.UIString("(blocked:%s)", reason)); } else if (this._request.finished) { - cell.setTextAndTitle(WebInspector.UIString("Finished")); + this._setTextAndTitle(cell, WebInspector.UIString("Finished")); } else { - cell.setTextAndTitle(WebInspector.UIString("(pending)")); + this._setTextAndTitle(cell, WebInspector.UIString("(pending)")); } }, - /** - * @param {!Element} cell - */ - _renderTypeCell: function(cell) - { - cell.setTextAndTitle(this.displayType()); - }, - /** * @param {!Element} cell */ @@ -378,18 +380,18 @@ WebInspector.NetworkDataGridNode.prototype = { _renderSizeCell: function(cell) { if (this._request.fetchedViaServiceWorker) { - cell.setTextAndTitle(WebInspector.UIString("(from ServiceWorker)")); + this._setTextAndTitle(cell, WebInspector.UIString("(from ServiceWorker)")); cell.classList.add("network-dim-cell"); } else if (this._request.cached()) { if (this._request.cachedInMemory()) - cell.setTextAndTitle(WebInspector.UIString("(from memory cache)")); + this._setTextAndTitle(cell, WebInspector.UIString("(from memory cache)")); else - cell.setTextAndTitle(WebInspector.UIString("(from disk cache)")); + this._setTextAndTitle(cell, WebInspector.UIString("(from disk cache)")); cell.classList.add("network-dim-cell"); } else { var resourceSize = Number.bytesToString(this._request.resourceSize); var transferSize = Number.bytesToString(this._request.transferSize); - cell.setTextAndTitle(transferSize); + this._setTextAndTitle(cell, transferSize); this._appendSubtitle(cell, resourceSize); } }, @@ -400,11 +402,11 @@ WebInspector.NetworkDataGridNode.prototype = { _renderTimeCell: function(cell) { if (this._request.duration > 0) { - cell.setTextAndTitle(Number.secondsToString(this._request.duration)); + this._setTextAndTitle(cell, Number.secondsToString(this._request.duration)); this._appendSubtitle(cell, Number.secondsToString(this._request.latency)); } else { cell.classList.add("network-dim-cell"); - cell.setTextAndTitle(WebInspector.UIString("Pending")); + this._setTextAndTitle(cell, WebInspector.UIString("Pending")); } }, diff --git a/front_end/network/NetworkLogView.js b/front_end/network/NetworkLogView.js index 8d576b0eec..eb1786b631 100644 --- a/front_end/network/NetworkLogView.js +++ b/front_end/network/NetworkLogView.js @@ -81,6 +81,9 @@ WebInspector.NetworkLogView = function(filterBar, progressBarContainer, networkL /** @type {number} */ this._rowHeight = 0; + this._headerHeight = 0; + this._timelineHeaderElement = null; + this._addFilters(); this._resetSuggestionBuilder(); this._initializeView(); @@ -143,6 +146,14 @@ WebInspector.NetworkLogView.IsFilterType = { WebInspector.NetworkLogView._searchKeys = Object.keys(WebInspector.NetworkLogView.FilterType).map(key => WebInspector.NetworkLogView.FilterType[key]); WebInspector.NetworkLogView.prototype = { + /** + * @return {number} + */ + headerHeight: function() + { + return this._headerHeight; + }, + /** * @param {boolean} recording */ @@ -269,9 +280,29 @@ WebInspector.NetworkLogView.prototype = { this._durationCalculator = new WebInspector.NetworkTransferDurationCalculator(); this._calculator = this._timeCalculator; - this._createTable(); - this._summaryBarElement = this.element.createChild("div", "network-summary-bar"); + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) { + this._splitWidget = new WebInspector.SplitWidget(true, false, "networkPanelSplitViewTimeline"); + this._splitWidget.show(this.element); + this._createTable(); + this._splitWidget.setSidebarWidget(this._dataGrid.asWidget()); + + this._summaryBarElement = this.element.createChild("div", "network-summary-bar"); + this._timelineWidget = new WebInspector.VBox(); + this._createTimelineHeader(); + this._timelineWidget.element.classList.add("network-timeline-view"); + this._splitWidget.setMainWidget(this._timelineWidget); + + var networkTimelineView = new WebInspector.NetworkTimelineColumn(this, this._dataGrid); + networkTimelineView.show(this._timelineWidget.element); + this.switchViewMode(false); + } else { + this._createTable(); + this._dataGrid.asWidget().show(this.element); + this._summaryBarElement = this.element.createChild("div", "network-summary-bar"); + } + + this._columns.sortByCurrentColumn(); this._updateRowsSize(); }, @@ -324,7 +355,6 @@ WebInspector.NetworkLogView.prototype = { this._dataGrid.element.addEventListener("mousedown", this._dataGridMouseDown.bind(this), true); this._dataGrid.element.addEventListener("mousemove", this._dataGridMouseMove.bind(this), true); this._dataGrid.element.addEventListener("mouseleave", this._highlightInitiatorChain.bind(this, null), true); - this._columns.sortByCurrentColumn(); }, /** @@ -810,12 +840,40 @@ WebInspector.NetworkLogView.prototype = { } }, + _createTimelineHeader: function() + { + this._timelineHeaderElement = this._timelineWidget.element.createChild("div", "network-timeline-header"); + this._timelineHeaderElement.addEventListener("click", timelineHeaderClicked.bind(this)); + this._timelineHeaderElement.addEventListener("contextmenu", this._contextMenu.bind(this)); + var innerElement = this._timelineHeaderElement.createChild("div"); + innerElement.textContent = WebInspector.UIString("Timeline"); + this._timelineColumnSortIcon = this._timelineHeaderElement.createChild("div", "sort-order-icon-container").createChild("div", "sort-order-icon"); + + /** + * @this {WebInspector.NetworkLogView} + */ + function timelineHeaderClicked() + { + var sortOrders = WebInspector.DataGrid.Order; + var sortOrder = this._dataGrid.sortOrder() === sortOrders.Ascending ? sortOrders.Descending : sortOrders.Ascending; + this._dataGrid.markColumnAsSortedBy("timeline", sortOrder); + this._columns.sortByCurrentColumn(); + } + }, + /** * @param {boolean} gridMode */ switchViewMode: function(gridMode) { this._columns.switchViewMode(gridMode); + if (!Runtime.experiments.isEnabled("canvasNetworkTimeline")) + return; + + if (gridMode && this._nodesByRequestId.size) + this._splitWidget.showBoth(); + else + this._splitWidget.hideMain(); }, /** @@ -829,8 +887,12 @@ WebInspector.NetworkLogView.prototype = { _updateRowsSize: function() { var largeRows = !!this._networkLogLargeRowsSetting.get(); + // TODO(allada) Make these non-magic numbers. this._rowHeight = largeRows ? 41 : 21; + this._headerHeight = largeRows ? 31 : 27; this._dataGrid.element.classList.toggle("small", !largeRows); + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) + this._timelineHeaderElement.classList.toggle("small", !largeRows); this._dataGrid.scheduleUpdate(); }, @@ -1058,6 +1120,18 @@ WebInspector.NetworkLogView.prototype = { dataGridSorted: function() { this._highlightNthMatchedRequestForSearch(this._updateMatchCountAndFindMatchIndex(this._currentMatchedRequestNode), false); + + if (!Runtime.experiments.isEnabled("canvasNetworkTimeline")) + return; + + this._timelineColumnSortIcon.classList.remove("sort-ascending", "sort-descending"); + if (this._dataGrid.sortColumnIdentifier() !== "timeline") + return; + + if (this._dataGrid.sortOrder() === WebInspector.DataGrid.Order.Ascending) + this._timelineColumnSortIcon.classList.add("sort-ascending"); + else + this._timelineColumnSortIcon.classList.add("sort-descending"); }, /** diff --git a/front_end/network/NetworkLogViewColumns.js b/front_end/network/NetworkLogViewColumns.js index 6274b10fdd..da5ab6ba8e 100644 --- a/front_end/network/NetworkLogViewColumns.js +++ b/front_end/network/NetworkLogViewColumns.js @@ -9,6 +9,15 @@ */ WebInspector.NetworkLogViewColumns = function(networkLogView, networkLogLargeRowsSetting) { + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) { + var timelineColumn = WebInspector.NetworkLogViewColumns._defaultColumns.find(columnConfig => columnConfig.id === "timeline"); + timelineColumn.visible = false; + timelineColumn.hideable = true; + timelineColumn.sortConfig = { + sortingFunction: WebInspector.NetworkDataGridNode.RequestPropertyComparator.bind(null, "startTime") + }; + } + this._networkLogView = networkLogView; /** @type {!WebInspector.Setting} */ @@ -335,7 +344,7 @@ WebInspector.NetworkLogViewColumns._defaultColumns = [ } ] } - }, + } ]; /** @@ -397,15 +406,15 @@ WebInspector.NetworkLogViewColumns.prototype = { this._dataGrid = new WebInspector.SortableDataGrid(this._columns.map(WebInspector.NetworkLogViewColumns._convertToDataGridDescriptor)); - this._dataGrid.asWidget().show(this._networkLogView.element); - this._updateColumns(); this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortHandler, this); this._dataGrid.addEventListener(WebInspector.DataGrid.Events.ColumnsResized, this.updateDividersIfNeeded, this); this._timelineGrid = new WebInspector.TimelineGrid(); this._timelineGrid.element.classList.add("network-timeline-grid"); - this._dataGrid.element.appendChild(this._timelineGrid.element); + if (!Runtime.experiments.isEnabled("canvasNetworkTimeline")) + this._dataGrid.element.appendChild(this._timelineGrid.element); + this._setupDropdownColumns(); this._dataGrid.markColumnAsSortedBy(WebInspector.NetworkLogViewColumns._initialSortColumn, WebInspector.DataGrid.Order.Ascending); @@ -691,6 +700,8 @@ WebInspector.NetworkLogViewColumns.prototype = { updateDividersIfNeeded: function() { + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) + return; if (!this._networkLogView.isShowing()) { this._networkLogView.scheduleRefresh(); return; @@ -765,6 +776,8 @@ WebInspector.NetworkLogViewColumns.prototype = { */ addEventDividers: function(times, className) { + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) + return; for (var i = 0; i < times.length; ++i) { var element = createElementWithClass("div", "network-event-divider " + className); this._timelineGrid.addEventDivider(element); @@ -778,6 +791,8 @@ WebInspector.NetworkLogViewColumns.prototype = { _updateEventDividers: function() { + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) + return; var calculator = this._calculatorsMap.get(WebInspector.NetworkLogViewColumns._calculatorTypes.Time); for (var divider of this._eventDividers) { var timePercent = calculator.computePercentageFromEventTime(divider.time); @@ -788,16 +803,22 @@ WebInspector.NetworkLogViewColumns.prototype = { hideEventDividers: function() { + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) + return; this._timelineGrid.hideEventDividers(); }, showEventDividers: function() { + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) + return; this._timelineGrid.showEventDividers(); }, _updateRowsSize: function() { + if (Runtime.experiments.isEnabled("canvasNetworkTimeline")) + return; this._timelineGrid.element.classList.toggle("small", !this._networkLogLargeRowsSetting.get()); } } \ No newline at end of file diff --git a/front_end/network/NetworkTimelineColumn.js b/front_end/network/NetworkTimelineColumn.js new file mode 100644 index 0000000000..2bd1c76259 --- /dev/null +++ b/front_end/network/NetworkTimelineColumn.js @@ -0,0 +1,212 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @constructor + * @extends {WebInspector.VBox} + * @param {!WebInspector.NetworkLogView} networkLogView + * @param {!WebInspector.SortableDataGrid} dataGrid + */ +WebInspector.NetworkTimelineColumn = function(networkLogView, dataGrid) +{ + WebInspector.VBox.call(this, true); + this._canvas = this.contentElement.createChild("canvas"); + this._canvas.tabIndex = 1; + this.setDefaultFocusedElement(this._canvas); + + /** @const */ + this._leftPadding = 5; + /** @const */ + this._rightPadding = 5; + + this._dataGrid = dataGrid; + this._networkLogView = networkLogView; + /** @type {!Array} */ + this._requestData = []; +} + +WebInspector.NetworkTimelineColumn.prototype = { + scheduleUpdate: function() + { + if (this._updateRequestID) + return; + this._updateRequestID = this.element.window().requestAnimationFrame(this._update.bind(this)); + }, + + _update: function() + { + this.element.window().cancelAnimationFrame(this._updateRequestID); + this._updateRequestID = null; + + this._startTime = this._networkLogView.calculator().minimumBoundary(); + this._endTime = this._networkLogView.calculator().maximumBoundary(); + this._resetCanvas(); + this._draw(); + }, + + _resetCanvas: function() + { + var ratio = window.devicePixelRatio; + this._canvas.width = this._offsetWidth * ratio; + this._canvas.height = this._offsetHeight * ratio; + this._canvas.style.width = this._offsetWidth + "px"; + this._canvas.style.height = this._offsetHeight + "px"; + }, + + /** + * @override + */ + onResize: function() + { + WebInspector.VBox.prototype.onResize.call(this); + this._offsetWidth = this.contentElement.offsetWidth; + this._offsetHeight = this.contentElement.offsetHeight; + this.scheduleUpdate(); + }, + + /** + * @param {!WebInspector.RequestTimeRangeNames} type + * @return {string} + */ + _colorForType: function(type) + { + var types = WebInspector.RequestTimeRangeNames; + switch (type) { + case types.Receiving: + case types.ReceivingPush: + return "#03A9F4"; + case types.Waiting: + return "#00C853"; + case types.Connecting: + return "#FF9800"; + case types.SSL: + return "#9C27B0"; + case types.DNS: + return "#009688"; + case types.Proxy: + return "#A1887F"; + case types.Blocking: + return "#AAAAAA"; + case types.Push: + return "#8CDBff"; + case types.Queueing: + return "white"; + case types.ServiceWorker: + case types.ServiceWorkerPreparation: + default: + return "orange"; + } + }, + + /** + * @return {number} + */ + _scrollTop: function() + { + return this._dataGrid.scrollContainer.scrollTop; + }, + + /** + * @param {number} time + * @return {number} + */ + _timeToPosition: function(time) + { + var availableWidth = this._offsetWidth - this._leftPadding - this._rightPadding; + var timeToPixel = availableWidth / (this._endTime - this._startTime); + return Math.floor(this._leftPadding + (time - this._startTime) * timeToPixel); + }, + + _draw: function() + { + var requests = this._requestData; + var context = this._canvas.getContext("2d"); + context.save(); + context.scale(window.devicePixelRatio, window.devicePixelRatio); + context.translate(0, this._networkLogView.headerHeight()); + context.rect(0, 0, this._offsetWidth, this._offsetHeight); + context.clip(); + var rowHeight = this._networkLogView.rowHeight(); + var scrollTop = this._scrollTop(); + var firstRequestIndex = Math.floor(scrollTop / rowHeight); + var lastRequestIndex = Math.min(requests.length, firstRequestIndex + Math.ceil(this._offsetHeight / rowHeight)); + for (var i = firstRequestIndex; i < lastRequestIndex; i++) { + var rowOffset = rowHeight * i; + var request = requests[i]; + var ranges = WebInspector.RequestTimingView.calculateRequestTimeRanges(request, 0); + for (var range of ranges) { + if (range.name === WebInspector.RequestTimeRangeNames.Total || + range.name === WebInspector.RequestTimeRangeNames.Sending || + range.end - range.start === 0) + continue; + this._drawBar(context, range, rowOffset - scrollTop); + } + } + context.restore(); + }, + + /** + * @return {number} + */ + _timelineDuration: function() + { + return this._networkLogView.calculator().maximumBoundary() - this._networkLogView.calculator().minimumBoundary(); + }, + + /** + * @param {!WebInspector.RequestTimeRangeNames} type + * @return {number} + */ + _getBarHeight: function(type) + { + var types = WebInspector.RequestTimeRangeNames; + switch (type) { + case types.Connecting: + case types.SSL: + case types.DNS: + case types.Proxy: + case types.Blocking: + case types.Push: + case types.Queueing: + return 7; + default: + return 13; + } + }, + + /** + * @param {!CanvasRenderingContext2D} context + * @param {!WebInspector.RequestTimeRange} range + * @param {number} y + */ + _drawBar: function(context, range, y) + { + context.save(); + context.beginPath(); + var lineWidth = 0; + var color = this._colorForType(range.name); + var borderColor = color; + if (range.name === WebInspector.RequestTimeRangeNames.Queueing) { + borderColor = "lightgrey"; + lineWidth = 2; + } + if (range.name === WebInspector.RequestTimeRangeNames.Receiving) + lineWidth = 2; + context.fillStyle = color; + var height = this._getBarHeight(range.name); + y += Math.floor(this._networkLogView.rowHeight() / 2 - height / 2) + lineWidth / 2; + var start = this._timeToPosition(range.start); + var end = this._timeToPosition(range.end); + context.rect(start, y, end - start, height - lineWidth); + if (lineWidth) { + context.lineWidth = lineWidth; + context.strokeStyle = borderColor; + context.stroke(); + } + context.fill(); + context.restore(); + }, + + __proto__: WebInspector.VBox.prototype +} diff --git a/front_end/network/module.json b/front_end/network/module.json index 354cc0ad83..f793d5c56a 100644 --- a/front_end/network/module.json +++ b/front_end/network/module.json @@ -100,6 +100,7 @@ "NetworkLogViewColumns.js", "NetworkManageCustomHeadersView.js", "NetworkOverview.js", + "NetworkTimelineColumn.js", "RequestCookiesView.js", "RequestHeadersView.js", "RequestHTMLView.js", diff --git a/front_end/network/networkLogView.css b/front_end/network/networkLogView.css index 48068d986c..076ccf3448 100644 --- a/front_end/network/networkLogView.css +++ b/front_end/network/networkLogView.css @@ -76,13 +76,13 @@ height: 21px; } -.network-log-grid.data-grid th { +.network-timeline-header, .network-log-grid.data-grid th { border-bottom: 1px solid rgb(205, 205, 205); border-left: 1px solid rgb(205, 205, 205); background: white; } -.network-log-grid.data-grid .header-container { +.network-timeline-header, .network-log-grid.data-grid .header-container { height: 31px; } @@ -90,7 +90,7 @@ top: 31px; } -.network-log-grid.data-grid.small .header-container { +.network-timeline-header.small, .network-log-grid.data-grid.small .header-container { height: 27px; } @@ -653,3 +653,56 @@ text-align: center; line-height: 28px; } + +.network-timeline-header { + position: absolute; + border-left: 0px; + width: 100%; + display: table; + z-index: 200; + background-color:transparent; +} + +.network-timeline-header:hover { + background-color:hsla(0, 0%, 80%, .5); +} + +.network-timeline-header div { + display: table-cell; + line-height: 14px; + margin: auto 0px; + vertical-align: middle; + text-align: left; + font-weight: normal; + padding: 0px 4px; + font-size: 11px; +} + +.network-timeline-header .sort-order-icon-container { + position: absolute; + top: 1px; + right: 0; + bottom: 1px; + display: flex; + align-items: center; +} + +.network-timeline-header .sort-order-icon { + margin-right: 4px; + background-image: url(Images/toolbarButtonGlyphs.png); + background-size: 352px 168px; + opacity: 0.5; + width: 8px; + height: 7px; + display: none; +} + +.network-timeline-header .sort-ascending.sort-order-icon { + display: block; + background-position: -4px -111px; +} + +.network-timeline-header .sort-descending.sort-order-icon { + display: block; + background-position: -20px -99px; +} diff --git a/front_end/platform/DOMExtension.js b/front_end/platform/DOMExtension.js index 8437dba1e2..9174e09b04 100644 --- a/front_end/platform/DOMExtension.js +++ b/front_end/platform/DOMExtension.js @@ -180,30 +180,6 @@ Element.prototype.isScrolledToBottom = function() return Math.abs(this.scrollTop + this.clientHeight - this.scrollHeight) <= 2; } -/** - * @param {!Node} fromNode - * @param {!Node} toNode - */ -function removeSubsequentNodes(fromNode, toNode) -{ - for (var node = fromNode; node && node !== toNode;) { - var nodeToRemove = node; - node = node.nextSibling; - nodeToRemove.remove(); - } -} - -/** - * @param {!Event} event - * @return {boolean} - */ -Element.prototype.containsEventPoint = function(event) -{ - var box = this.getBoundingClientRect(); - return box.left < event.x && event.x < box.right && - box.top < event.y && event.y < box.bottom; -} - /** * @param {!Array.} nameArray * @return {?Node} @@ -338,34 +314,12 @@ Node.prototype.window = function() return this.ownerDocument.defaultView; } -/** - * @param {string} query - * @return {?Node} - */ -Element.prototype.query = function(query) -{ - return this.ownerDocument.evaluate(query, this, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; -} - Element.prototype.removeChildren = function() { if (this.firstChild) this.textContent = ""; } -/** - * @return {boolean} - */ -Element.prototype.isInsertionCaretInside = function() -{ - var selection = this.getComponentSelection(); - // @see crbug.com/602541 - var selectionRange = selection && selection.rangeCount ? selection.getRangeAt(0) : null; - if (!selectionRange || !selection.isCollapsed) - return false; - return selectionRange.startContainer.isSelfOrDescendant(this); -} - /** * @param {string} tagName * @param {string=} customElementType @@ -377,20 +331,6 @@ function createElement(tagName, customElementType) return document.createElement(tagName, customElementType || ""); } -/** - * @param {string} type - * @param {boolean} bubbles - * @param {boolean} cancelable - * @return {!Event} - * @suppressGlobalPropertiesCheck - */ -function createEvent(type, bubbles, cancelable) -{ - var event = document.createEvent("Event"); - event.initEvent(type, bubbles, cancelable); - return event; -} - /** * @param {number|string} data * @return {!Text} @@ -524,20 +464,6 @@ Element.prototype.totalOffset = function() return { left: rect.left, top: rect.top }; } -/** - * @return {!{left: number, top: number}} - */ -Element.prototype.scrollOffset = function() -{ - var curLeft = 0; - var curTop = 0; - for (var element = this; element; element = element.scrollParent) { - curLeft += element.scrollLeft; - curTop += element.scrollTop; - } - return { left: curLeft, top: curTop }; -} - /** * @param {string} childType * @param {string=} className @@ -594,51 +520,30 @@ AnchorBox.prototype.equals = function(anchorBox) } /** - * @param {!Window} targetWindow + * @param {!Window=} targetWindow * @return {!AnchorBox} */ -Element.prototype.offsetRelativeToWindow = function(targetWindow) +Element.prototype.boxInWindow = function(targetWindow) { - var elementOffset = new AnchorBox(); + targetWindow = targetWindow || this.ownerDocument.defaultView; + + var anchorBox = new AnchorBox(); var curElement = this; var curWindow = this.ownerDocument.defaultView; while (curWindow && curElement) { - elementOffset.x += curElement.totalOffsetLeft(); - elementOffset.y += curElement.totalOffsetTop(); + anchorBox.x += curElement.totalOffsetLeft(); + anchorBox.y += curElement.totalOffsetTop(); if (curWindow === targetWindow) break; - curElement = curWindow.frameElement; curWindow = curWindow.parent; } - return elementOffset; -} - -/** - * @param {!Window=} targetWindow - * @return {!AnchorBox} - */ -Element.prototype.boxInWindow = function(targetWindow) -{ - targetWindow = targetWindow || this.ownerDocument.defaultView; - - var anchorBox = this.offsetRelativeToWindow(window); - anchorBox.width = Math.min(this.offsetWidth, window.innerWidth - anchorBox.x); - anchorBox.height = Math.min(this.offsetHeight, window.innerHeight - anchorBox.y); - + anchorBox.width = Math.min(this.offsetWidth, targetWindow.innerWidth - anchorBox.x); + anchorBox.height = Math.min(this.offsetHeight, targetWindow.innerHeight - anchorBox.y); return anchorBox; } -/** - * @param {string} text - */ -Element.prototype.setTextAndTitle = function(text) -{ - this.textContent = text; - this.title = text; -} - /** * @param {boolean=} preventDefault */ @@ -697,32 +602,6 @@ Element.prototype.selectionLeftOffset = function() return leftOffset; } -/** - * @this {!HTMLImageElement} element - * @return {!Promise} - */ -HTMLImageElement.prototype.completePromise = function() -{ - var element = this; - if (element.complete) - return Promise.resolve(element); - return new Promise(promiseBody); - - /** - * @param {function(!HTMLImageElement)} resolve - */ - function promiseBody(resolve) - { - element.addEventListener("load", oncomplete); - element.addEventListener("error", oncomplete); - - function oncomplete() - { - resolve(element); - } - } -} - /** * @param {...!Node} var_args */ @@ -902,43 +781,56 @@ Node.prototype.setTextContentTruncatedIfNeeded = function(text, placeholder) */ Event.prototype.deepElementFromPoint = function() { - // 1. climb to the component root. - var node = this.target; - while (node && node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE && node.nodeType !== Node.DOCUMENT_NODE) - node = node.parentNode; - - if (!node) - return null; + var root = this.target && this.target.getComponentRoot(); + return root ? root.deepElementFromPoint(this.pageX, this.pageY) : null; +} - // 2. Find deepest node by coordinates. - node = node.elementFromPoint(this.pageX, this.pageY); +/** + * @param {number} x + * @param {number} y + * @return {?Node} + */ +Document.prototype.deepElementFromPoint = function(x, y) +{ + var node = this.elementFromPoint(x, y); while (node && node.shadowRoot) - node = node.shadowRoot.elementFromPoint(this.pageX, this.pageY); + node = node.shadowRoot.elementFromPoint(x, y); return node; } +DocumentFragment.prototype.deepElementFromPoint = Document.prototype.deepElementFromPoint; + /** * @return {?Element} */ -Event.prototype.deepActiveElement = function() +Document.prototype.deepActiveElement = function() { - var activeElement = this.target && this.target.ownerDocument ? this.target.ownerDocument.activeElement : null; + var activeElement = this.activeElement; while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) activeElement = activeElement.shadowRoot.activeElement; return activeElement; } +DocumentFragment.prototype.deepActiveElement = Document.prototype.deepActiveElement; + /** - * @param {number} x - * @param {number} y - * @return {?Node} + * @return {boolean} */ -Document.prototype.deepElementFromPoint = function(x, y) +Element.prototype.hasFocus = function() { - var node = this.elementFromPoint(x, y); - while (node && node.shadowRoot) - node = node.shadowRoot.elementFromPoint(x, y); - return node; + var root = this.getComponentRoot(); + return !!root && this.isSelfOrAncestor(root.activeElement); +} + +/** + * @return {?Document|?DocumentFragment} + */ +Node.prototype.getComponentRoot = function() +{ + var node = this; + while (node && node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE && node.nodeType !== Node.DOCUMENT_NODE) + node = node.parentNode; + return /** @type {?Document|?DocumentFragment} */ (node); } /** @@ -960,11 +852,6 @@ function isEscKey(event) return event.keyCode === 27; } -function consumeEvent(e) -{ - e.consume(); -} - /** * @param {function()} callback * @suppressGlobalPropertiesCheck diff --git a/front_end/sdk/CSSStyleSheetHeader.js b/front_end/sdk/CSSStyleSheetHeader.js index 5d40bf6cec..e2e33dc4cd 100644 --- a/front_end/sdk/CSSStyleSheetHeader.js +++ b/front_end/sdk/CSSStyleSheetHeader.js @@ -44,7 +44,7 @@ WebInspector.CSSStyleSheetHeader.prototype = { */ setSourceMapURL: function(sourceMapURL) { - var completeSourceMapURL = this.sourceURL && sourceMapURL ? WebInspector.ParsedURL.completeURL(this.sourceURL, sourceMapURL) : null; + var completeSourceMapURL = this.sourceURL && sourceMapURL ? WebInspector.ParsedURL.completeURL(this.sourceURL, sourceMapURL) : sourceMapURL; this.sourceMapURL = completeSourceMapURL; }, diff --git a/front_end/source_frame/SourceFrame.js b/front_end/source_frame/SourceFrame.js index a90eb4f5f4..707a499d16 100644 --- a/front_end/source_frame/SourceFrame.js +++ b/front_end/source_frame/SourceFrame.js @@ -50,6 +50,9 @@ WebInspector.SourceFrame = function(url, lazyContent) this._searchResults = []; this._textEditor.setReadOnly(!this.canEditSource()); + this._textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.EditorFocused, this._resetCurrentSearchResultIndex, this); + this._textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.SelectionChanged, this._updateSourcePosition, this); + this._textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.TextChanged, event => this.onTextChanged(event.data.oldRange, event.data.newRange)); this._shortcuts = {}; this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false); @@ -62,13 +65,6 @@ WebInspector.SourceFrame = function(url, lazyContent) this._searchableView = null; } -/** @enum {symbol} */ -WebInspector.SourceFrame.Events = { - ScrollChanged: Symbol("ScrollChanged"), - SelectionChanged: Symbol("SelectionChanged"), - JumpHappened: Symbol("JumpHappened") -} - WebInspector.SourceFrame.prototype = { /** * @param {number} key @@ -226,7 +222,6 @@ WebInspector.SourceFrame.prototype = { }, /** - * @override * @param {!WebInspector.TextRange} oldRange * @param {!WebInspector.TextRange} newRange */ @@ -357,21 +352,6 @@ WebInspector.SourceFrame.prototype = { this._ensureContentLoaded(); }, - /** - * @override - */ - editorFocused: function() - { - this._resetCurrentSearchResultIndex(); - }, - - /** - * @override - */ - editorBlurred: function() - { - }, - _resetCurrentSearchResultIndex: function() { if (!this._searchResults.length) @@ -583,19 +563,6 @@ WebInspector.SourceFrame.prototype = { return Promise.resolve(); }, - /** - * @override - * @param {?WebInspector.TextRange} from - * @param {?WebInspector.TextRange} to - */ - onJumpToPosition: function(from, to) - { - this.dispatchEventToListeners(WebInspector.SourceFrame.Events.JumpHappened, { - from: from, - to: to - }); - }, - /** * @return {boolean} */ @@ -604,16 +571,6 @@ WebInspector.SourceFrame.prototype = { return false; }, - /** - * @override - * @param {!WebInspector.TextRange} textRange - */ - selectionChanged: function(textRange) - { - this._updateSourcePosition(); - this.dispatchEventToListeners(WebInspector.SourceFrame.Events.SelectionChanged, textRange); - }, - _updateSourcePosition: function() { var selections = this._textEditor.selections(); @@ -637,17 +594,6 @@ WebInspector.SourceFrame.prototype = { this._sourcePosition.setText(WebInspector.UIString("%d lines, %d characters selected", textRange.endLine - textRange.startLine + 1, selectedText.length)); }, - /** - * @override - * @param {number} lineNumber - */ - scrollChanged: function(lineNumber) - { - if (this._scrollTimer) - clearTimeout(this._scrollTimer); - this._scrollTimer = setTimeout(this.dispatchEventToListeners.bind(this, WebInspector.SourceFrame.Events.ScrollChanged, lineNumber), 100); - }, - _handleKeyDown: function(e) { var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(e); diff --git a/front_end/source_frame/SourcesTextEditor.js b/front_end/source_frame/SourcesTextEditor.js index b8568d0a38..38dc61c635 100644 --- a/front_end/source_frame/SourcesTextEditor.js +++ b/front_end/source_frame/SourcesTextEditor.js @@ -22,13 +22,13 @@ WebInspector.SourcesTextEditor = function(delegate) this._delegate = delegate; - this.codeMirror().on("changes", this._changesForDelegate.bind(this)); + this.codeMirror().on("changes", this._fireTextChanged.bind(this)); this.codeMirror().on("cursorActivity", this._cursorActivity.bind(this)); this.codeMirror().on("gutterClick", this._gutterClick.bind(this)); this.codeMirror().on("scroll", this._scroll.bind(this)); this.codeMirror().on("focus", this._focus.bind(this)); this.codeMirror().on("blur", this._blur.bind(this)); - this.codeMirror().on("beforeSelectionChange", this._beforeSelectionChangeForDelegate.bind(this)); + this.codeMirror().on("beforeSelectionChange", this._fireBeforeSelectionChanged.bind(this)); this.element.addEventListener("contextmenu", this._contextMenu.bind(this), false); this._blockIndentController = new WebInspector.SourcesTextEditor.BlockIndentController(this.codeMirror()); @@ -308,7 +308,7 @@ WebInspector.SourcesTextEditor.prototype = { editRange: function(range, text, origin) { var newRange = WebInspector.CodeMirrorTextEditor.prototype.editRange.call(this, range, text, origin); - this._delegate.onTextChanged(range, newRange); + this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.TextChanged, { oldRange: range, newRange: newRange }); if (WebInspector.moduleSetting("textEditorAutoDetectIndent").get()) this._onUpdateEditorIndentation(); @@ -383,7 +383,7 @@ WebInspector.SourcesTextEditor.prototype = { * @param {!CodeMirror} codeMirror * @param {!Array.} changes */ - _changesForDelegate: function(codeMirror, changes) + _fireTextChanged: function(codeMirror, changes) { if (!changes.length || this._muteTextChangedEvent) return; @@ -401,10 +401,8 @@ WebInspector.SourcesTextEditor.prototype = { } } - for (var i = 0; i < edits.length; ++i) { - var edit = edits[i]; - this._delegate.onTextChanged(edit.oldRange, edit.newRange); - } + for (var i = 0; i < edits.length; ++i) + this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.TextChanged, edits[i]); }, _cursorActivity: function() @@ -414,7 +412,7 @@ WebInspector.SourcesTextEditor.prototype = { var start = this.codeMirror().getCursor("anchor"); var end = this.codeMirror().getCursor("head"); - this._delegate.selectionChanged(WebInspector.CodeMirrorUtils.toRange(start, end)); + this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.SelectionChanged, WebInspector.CodeMirrorUtils.toRange(start, end)); }, /** @@ -425,30 +423,30 @@ WebInspector.SourcesTextEditor.prototype = { { if (from && to && from.equal(to)) return; - this._delegate.onJumpToPosition(from, to); + this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.JumpHappened, { from: from, to: to }); }, _scroll: function() { var topmostLineNumber = this.codeMirror().lineAtHeight(this.codeMirror().getScrollInfo().top, "local"); - this._delegate.scrollChanged(topmostLineNumber); + this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.ScrollChanged, topmostLineNumber); }, _focus: function() { - this._delegate.editorFocused(); + this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.EditorFocused); }, _blur: function() { - this._delegate.editorBlurred(); + this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.EditorBlurred); }, /** * @param {!CodeMirror} codeMirror * @param {{ranges: !Array.<{head: !CodeMirror.Pos, anchor: !CodeMirror.Pos}>}} selection */ - _beforeSelectionChangeForDelegate: function(codeMirror, selection) + _fireBeforeSelectionChanged: function(codeMirror, selection) { if (!this._isHandlingMouseDownEvent) return; @@ -614,7 +612,13 @@ WebInspector.SourcesTextEditor.GutterClickEventData; /** @enum {symbol} */ WebInspector.SourcesTextEditor.Events = { - GutterClick: Symbol("GutterClick") + GutterClick: Symbol("GutterClick"), + TextChanged: Symbol("TextChanged"), + SelectionChanged: Symbol("SelectionChanged"), + ScrollChanged: Symbol("ScrollChanged"), + EditorFocused: Symbol("EditorFocused"), + EditorBlurred: Symbol("EditorBlurred"), + JumpHappened: Symbol("JumpHappened") } /** @@ -622,27 +626,6 @@ WebInspector.SourcesTextEditor.Events = { */ WebInspector.SourcesTextEditorDelegate = function() { } WebInspector.SourcesTextEditorDelegate.prototype = { - - /** - * @param {!WebInspector.TextRange} oldRange - * @param {!WebInspector.TextRange} newRange - */ - onTextChanged: function(oldRange, newRange) { }, - - /** - * @param {!WebInspector.TextRange} textRange - */ - selectionChanged: function(textRange) { }, - - /** - * @param {number} lineNumber - */ - scrollChanged: function(lineNumber) { }, - - editorFocused: function() { }, - - editorBlurred: function() { }, - /** * @param {!WebInspector.ContextMenu} contextMenu * @param {number} lineNumber @@ -657,12 +640,6 @@ WebInspector.SourcesTextEditorDelegate.prototype = { * @return {!Promise} */ populateTextAreaContextMenu: function(contextMenu, lineNumber, columnNumber) { }, - - /** - * @param {?WebInspector.TextRange} from - * @param {?WebInspector.TextRange} to - */ - onJumpToPosition: function(from, to) { } } /** diff --git a/front_end/sources/AdvancedSearchView.js b/front_end/sources/AdvancedSearchView.js index 92b851fa77..eaa7af28dc 100644 --- a/front_end/sources/AdvancedSearchView.js +++ b/front_end/sources/AdvancedSearchView.js @@ -287,7 +287,7 @@ WebInspector.AdvancedSearchView.prototype = { focus: function() { - WebInspector.setCurrentFocusElement(this._search); + this._search.focus(); this._search.select(); }, diff --git a/front_end/sources/CSSSourceFrame.js b/front_end/sources/CSSSourceFrame.js index 4934e7b084..9652cfe9c4 100644 --- a/front_end/sources/CSSSourceFrame.js +++ b/front_end/sources/CSSSourceFrame.js @@ -43,6 +43,10 @@ WebInspector.CSSSourceFrame = function(uiSourceCode) suggestionsCallback: this._cssSuggestions.bind(this), isWordChar: this._isWordChar.bind(this) }); + this.textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.ScrollChanged, () => { + if (this._swatchPopoverHelper.isShowing()) + this._swatchPopoverHelper.hide(true); + }); } /** @type {number} */ @@ -317,17 +321,6 @@ WebInspector.CSSSourceFrame.prototype = { this._updateSwatches(newRange.startLine, newRange.endLine); }, - /** - * @override - * @param {number} lineNumber - */ - scrollChanged: function(lineNumber) - { - WebInspector.UISourceCodeFrame.prototype.scrollChanged.call(this, lineNumber); - if (this._swatchPopoverHelper.isShowing()) - this._swatchPopoverHelper.hide(true); - }, - /** * @param {string} char * @return {boolean} diff --git a/front_end/sources/EditingLocationHistoryManager.js b/front_end/sources/EditingLocationHistoryManager.js index 43ed0f5b37..d24283dc0a 100644 --- a/front_end/sources/EditingLocationHistoryManager.js +++ b/front_end/sources/EditingLocationHistoryManager.js @@ -48,7 +48,7 @@ WebInspector.EditingLocationHistoryManager.prototype = { */ trackSourceFrameCursorJumps: function(sourceFrame) { - sourceFrame.addEventListener(WebInspector.SourceFrame.Events.JumpHappened, this._onJumpHappened.bind(this)); + sourceFrame.textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.JumpHappened, this._onJumpHappened.bind(this)); }, /** diff --git a/front_end/sources/NavigatorView.js b/front_end/sources/NavigatorView.js index 3f4a07dffd..2b58b56eed 100644 --- a/front_end/sources/NavigatorView.js +++ b/front_end/sources/NavigatorView.js @@ -1003,9 +1003,7 @@ WebInspector.NavigatorSourceTreeElement.prototype = { if (!this._uiSourceCode.canRename()) return false; var isSelected = this === this.treeOutline.selectedTreeElement; - var document = this.treeOutline.element.ownerDocument; - var isFocused = this.treeOutline.element.isSelfOrAncestor(document.activeElement); - return isSelected && isFocused && !WebInspector.isBeingEdited(this.treeOutline.element); + return isSelected && this.treeOutline.element.hasFocus() && !WebInspector.isBeingEdited(this.treeOutline.element); }, selectOnMouseDown: function(event) diff --git a/front_end/sources/SourcesPanel.js b/front_end/sources/SourcesPanel.js index d83b72d604..9935719ab1 100644 --- a/front_end/sources/SourcesPanel.js +++ b/front_end/sources/SourcesPanel.js @@ -80,7 +80,7 @@ WebInspector.SourcesPanel = function() this._toggleNavigatorSidebarButton = this.editorView.createShowHideSidebarButton("navigator"); this._toggleDebuggerSidebarButton = this._splitWidget.createShowHideSidebarButton("debugger"); - this._showSourcesViewInPanel(); + this.editorView.setMainWidget(this._sourcesView); this._editorChanged(this._sourcesView.currentUISourceCode()); this._threadsSidebarPane = null; @@ -184,9 +184,9 @@ WebInspector.SourcesPanel.prototype = { var wrapper = WebInspector.SourcesPanel.WrapperView._instance; if (wrapper && wrapper.isShowing()) { WebInspector.inspectorView.setDrawerMinimized(true); - WebInspector.SourcesPanel.updateResizer(this); + WebInspector.SourcesPanel.updateResizerAndSidebarButtons(this); } - this._showSourcesViewInPanel(); + this.editorView.setMainWidget(this._sourcesView); }, willHide: function() @@ -196,19 +196,10 @@ WebInspector.SourcesPanel.prototype = { if (WebInspector.SourcesPanel.WrapperView.isShowing()) { WebInspector.SourcesPanel.WrapperView._instance._showViewInWrapper(); WebInspector.inspectorView.setDrawerMinimized(false); - WebInspector.SourcesPanel.updateResizer(this); + WebInspector.SourcesPanel.updateResizerAndSidebarButtons(this); } }, - _showSourcesViewInPanel: function() - { - this._sourcesView.leftToolbar().removeToolbarItems(); - this._sourcesView.leftToolbar().appendToolbarItem(this._toggleNavigatorSidebarButton); - this._sourcesView.rightToolbar().removeToolbarItems(); - this._sourcesView.rightToolbar().appendToolbarItem(this._toggleDebuggerSidebarButton); - this.editorView.setMainWidget(this._sourcesView); - }, - /** * @override * @param {string} locationName @@ -1052,7 +1043,7 @@ WebInspector.SourcesPanel.prototype = { this._splitWidget.setVertical(!vertically); this._splitWidget.element.classList.toggle("sources-split-view-vertical", vertically); - WebInspector.SourcesPanel.updateResizer(this); + WebInspector.SourcesPanel.updateResizerAndSidebarButtons(this); // Create vertical box with stack. var vbox = new WebInspector.VBox(); @@ -1339,12 +1330,23 @@ WebInspector.SourcesPanel.instance = function() /** * @param {!WebInspector.SourcesPanel} panel */ -WebInspector.SourcesPanel.updateResizer = function(panel) +WebInspector.SourcesPanel.updateResizerAndSidebarButtons = function(panel) { - if (panel._splitWidget.isVertical() || (WebInspector.SourcesPanel.WrapperView.isShowing() && !WebInspector.inspectorView.isDrawerMinimized())) + panel._sourcesView.leftToolbar().removeToolbarItems(); + panel._sourcesView.rightToolbar().removeToolbarItems(); + panel._sourcesView.bottomToolbar().removeToolbarItems(); + var isInWrapper = WebInspector.SourcesPanel.WrapperView.isShowing() && !WebInspector.inspectorView.isDrawerMinimized(); + if (panel._splitWidget.isVertical() || isInWrapper) panel._splitWidget.uninstallResizer(panel._sourcesView.toolbarContainerElement()); else panel._splitWidget.installResizer(panel._sourcesView.toolbarContainerElement()); + if (!isInWrapper) { + panel._sourcesView.leftToolbar().appendToolbarItem(panel._toggleNavigatorSidebarButton); + if (panel._splitWidget.isVertical()) + panel._sourcesView.rightToolbar().appendToolbarItem(panel._toggleDebuggerSidebarButton); + else + panel._sourcesView.bottomToolbar().appendToolbarItem(panel._toggleDebuggerSidebarButton); + } } /** @@ -1366,19 +1368,17 @@ WebInspector.SourcesPanel.WrapperView.prototype = { this._showViewInWrapper(); else WebInspector.inspectorView.setDrawerMinimized(true); - WebInspector.SourcesPanel.updateResizer(WebInspector.SourcesPanel.instance()); + WebInspector.SourcesPanel.updateResizerAndSidebarButtons(WebInspector.SourcesPanel.instance()); }, willHide: function() { WebInspector.inspectorView.setDrawerMinimized(false); - setImmediate(() => WebInspector.SourcesPanel.updateResizer(WebInspector.SourcesPanel.instance())); + setImmediate(() => WebInspector.SourcesPanel.updateResizerAndSidebarButtons(WebInspector.SourcesPanel.instance())); }, _showViewInWrapper: function() { - this._view.leftToolbar().removeToolbarItems(); - this._view.rightToolbar().removeToolbarItems(); this._view.show(this.element); }, diff --git a/front_end/sources/SourcesView.js b/front_end/sources/SourcesView.js index b857a25749..887d3164ea 100644 --- a/front_end/sources/SourcesView.js +++ b/front_end/sources/SourcesView.js @@ -48,6 +48,8 @@ WebInspector.SourcesView = function() this._toolbarEditorActions.appendToolbarItem(actions[i].button(this)); } this._scriptViewToolbar = new WebInspector.Toolbar("", this._toolbarContainerElement); + this._toolbarContainerElement.createChild("div", "sources-toolbar-spacer"); + this._bottomToolbar = new WebInspector.Toolbar("", this._toolbarContainerElement); WebInspector.startBatchUpdate(); workspace.uiSourceCodes().forEach(this._addUISourceCode.bind(this)); @@ -147,6 +149,14 @@ WebInspector.SourcesView.prototype = { return this._editorContainer.rightToolbar(); }, + /** + * @return {!WebInspector.Toolbar} + */ + bottomToolbar: function() + { + return this._bottomToolbar; + }, + /** * @param {!Array.} keys * @param {function(!Event=):boolean} handler diff --git a/front_end/sources/TabbedEditorContainer.js b/front_end/sources/TabbedEditorContainer.js index 2c4c876277..a773db77cd 100644 --- a/front_end/sources/TabbedEditorContainer.js +++ b/front_end/sources/TabbedEditorContainer.js @@ -212,18 +212,18 @@ WebInspector.TabbedEditorContainer.prototype = { _addViewListeners: function() { - if (!this._currentView) + if (!this._currentView || !this._currentView.textEditor) return; - this._currentView.addEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this); - this._currentView.addEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this); + this._currentView.textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.ScrollChanged, this._scrollChanged, this); + this._currentView.textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.SelectionChanged, this._selectionChanged, this); }, _removeViewListeners: function() { - if (!this._currentView) + if (!this._currentView || !this._currentView.textEditor) return; - this._currentView.removeEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this); - this._currentView.removeEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this); + this._currentView.textEditor.removeEventListener(WebInspector.SourcesTextEditor.Events.ScrollChanged, this._scrollChanged, this); + this._currentView.textEditor.removeEventListener(WebInspector.SourcesTextEditor.Events.SelectionChanged, this._selectionChanged, this); }, /** @@ -231,9 +231,13 @@ WebInspector.TabbedEditorContainer.prototype = { */ _scrollChanged: function(event) { + if (this._scrollTimer) + clearTimeout(this._scrollTimer); var lineNumber = /** @type {number} */ (event.data); - this._history.updateScrollLineNumber(this._currentFile.url(), lineNumber); - this._history.save(this._previouslyViewedFilesSetting); + this._scrollTimer = setTimeout(() => { + this._history.updateScrollLineNumber(this._currentFile.url(), lineNumber); + this._history.save(this._previouslyViewedFilesSetting); + }, 100); }, /** diff --git a/front_end/sources/UISourceCodeFrame.js b/front_end/sources/UISourceCodeFrame.js index bfae54509a..bcb6fe4602 100644 --- a/front_end/sources/UISourceCodeFrame.js +++ b/front_end/sources/UISourceCodeFrame.js @@ -55,6 +55,12 @@ WebInspector.UISourceCodeFrame = function(uiSourceCode) this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.LineDecorationRemoved, this._onLineDecorationRemoved, this); WebInspector.persistence.addEventListener(WebInspector.Persistence.Events.BindingCreated, this._onBindingChanged, this); WebInspector.persistence.addEventListener(WebInspector.Persistence.Events.BindingRemoved, this._onBindingChanged, this); + + this.textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.EditorBlurred, + () => WebInspector.context.setFlavor(WebInspector.UISourceCodeFrame, null)); + this.textEditor.addEventListener(WebInspector.SourcesTextEditor.Events.EditorFocused, + () => WebInspector.context.setFlavor(WebInspector.UISourceCodeFrame, this)); + this._updateStyle(); this._errorPopoverHelper = new WebInspector.PopoverHelper(this.element, this._getErrorAnchor.bind(this), this._showErrorPopover.bind(this)); @@ -99,24 +105,6 @@ WebInspector.UISourceCodeFrame.prototype = { this._uiSourceCode.removeWorkingCopyGetter(); }, - /** - * @override - */ - editorFocused: function() - { - WebInspector.SourceFrame.prototype.editorFocused.call(this); - WebInspector.context.setFlavor(WebInspector.UISourceCodeFrame, this); - }, - - /** - * @override - */ - editorBlurred: function() - { - WebInspector.context.setFlavor(WebInspector.UISourceCodeFrame, null); - WebInspector.SourceFrame.prototype.editorBlurred.call(this); - }, - /** * @override * @return {boolean} diff --git a/front_end/sources/WatchExpressionsSidebarPane.js b/front_end/sources/WatchExpressionsSidebarPane.js index d1b17f32b8..bf0207cf85 100644 --- a/front_end/sources/WatchExpressionsSidebarPane.js +++ b/front_end/sources/WatchExpressionsSidebarPane.js @@ -184,8 +184,11 @@ WebInspector.WatchExpressionsSidebarPane.prototype = { if (this._watchExpressions.length > 1) contextMenu.appendItem(WebInspector.UIString.capitalize("Delete ^all ^watch ^expressions"), this._deleteAllButtonClicked.bind(this)); + var target = event.deepElementFromPoint(); + if (!target) + return; for (var watchExpression of this._watchExpressions) - if (watchExpression.element().containsEventPoint(event)) + if (watchExpression.element().isSelfOrAncestor(target)) watchExpression._populateContextMenu(contextMenu, event); }, @@ -446,7 +449,8 @@ WebInspector.WatchExpression.prototype = { if (!this.isEditing() && this._result && (this._result.type === "number" || this._result.type === "string")) contextMenu.appendItem(WebInspector.UIString.capitalize("Copy ^value"), this._copyValueButtonClicked.bind(this)); - if (this._valueElement.containsEventPoint(event)) + var target = event.deepElementFromPoint(); + if (target && this._valueElement.isSelfOrAncestor(target)) contextMenu.appendApplicableItems(this._result); }, diff --git a/front_end/sources/sourcesView.css b/front_end/sources/sourcesView.css index a4a831f68c..f347560b2a 100644 --- a/front_end/sources/sourcesView.css +++ b/front_end/sources/sourcesView.css @@ -45,6 +45,11 @@ cursor: default; } +.sources-toolbar-spacer { + flex: auto; + pointer-events: none; +} + .source-frame-debugger-script { background-color: rgba(255, 255, 194, 0.5); } diff --git a/front_end/text_editor/CodeMirrorUtils.js b/front_end/text_editor/CodeMirrorUtils.js index d543b80e0d..f3f28bcde3 100644 --- a/front_end/text_editor/CodeMirrorUtils.js +++ b/front_end/text_editor/CodeMirrorUtils.js @@ -127,7 +127,7 @@ WebInspector.CodeMirrorUtils.prototype = { var config = editingContext.config; editingContext.cssLoadView = new WebInspector.CodeMirrorCSSLoadView(); editingContext.cssLoadView.show(element); - WebInspector.setCurrentFocusElement(element); + element.focus(); element.addEventListener("copy", this._consumeCopy, false); var codeMirror = new window.CodeMirror(element, { mode: config.mode, diff --git a/front_end/timeline/TimelineEventOverview.js b/front_end/timeline/TimelineEventOverview.js index cae97c9a73..c6e656acaf 100644 --- a/front_end/timeline/TimelineEventOverview.js +++ b/front_end/timeline/TimelineEventOverview.js @@ -488,10 +488,19 @@ WebInspector.TimelineFilmStripOverview.prototype = { */ function createImage(data) { + var fulfill; + var promise = new Promise(f => fulfill = f); + var image = /** @type {!HTMLImageElement} */ (createElement("img")); if (data) image.src = "data:image/jpg;base64," + data; - return image.completePromise(); + if (image.complete) { + fulfill(image); + } else { + image.addEventListener("load", () => fulfill(image)); + image.addEventListener("error", () => fulfill(image)); + } + return promise; } }, diff --git a/front_end/timeline/TimelineUIUtils.js b/front_end/timeline/TimelineUIUtils.js index d02ba92b5b..94e32e09bd 100644 --- a/front_end/timeline/TimelineUIUtils.js +++ b/front_end/timeline/TimelineUIUtils.js @@ -1306,7 +1306,7 @@ WebInspector.TimelineUIUtils.InvalidationsGroupElement.prototype = { for (var i = 0; i < invalidations.length; i++) { var invalidation = invalidations[i]; var invalidationNode = this._createInvalidationNode(invalidation, false); - invalidationNode.addEventListener("click", consumeEvent, false); + invalidationNode.addEventListener("click", (e) => e.consume(), false); if (invalidationNode && !invalidationNodeIdMap[invalidation.nodeId]) { invalidationNodes.push(invalidationNode); invalidationNodeIdMap[invalidation.nodeId] = true; diff --git a/front_end/timeline_model/TimelineModel.js b/front_end/timeline_model/TimelineModel.js index c679a903ab..f396900558 100644 --- a/front_end/timeline_model/TimelineModel.js +++ b/front_end/timeline_model/TimelineModel.js @@ -770,9 +770,9 @@ WebInspector.TimelineModel.prototype = { for (var profileEvent of profileGroup.children) { var eventData = profileEvent.args["data"]; if ("startTime" in eventData) - cpuProfile.startTime = eventData["startTime"]; + cpuProfile.startTime = eventData["startTime"]; if ("endTime" in eventData) - cpuProfile.endTime = eventData["endTime"]; + cpuProfile.endTime = eventData["endTime"]; cpuProfile.nodes.pushAll(eventData["nodes"] || []); cpuProfile.samples.pushAll(eventData["samples"] || []); cpuProfile.timeDeltas.pushAll(eventData["timeDeltas"] || []); @@ -987,6 +987,10 @@ WebInspector.TimelineModel.prototype = { --eventData["lineNumber"]; if (typeof eventData["columnNumber"] === "number") --eventData["columnNumber"]; + // Fallthrough intended. + case recordTypes.RunMicrotasks: + // Microtasks technically are not necessarily scripts, but for purpose of + // forced sync style recalc or layout detection they are. if (!this._currentScriptEvent) this._currentScriptEvent = event; break; diff --git a/front_end/ui/ColorSwatch.js b/front_end/ui/ColorSwatch.js index fc5a8e12ba..eeb3fcb047 100644 --- a/front_end/ui/ColorSwatch.js +++ b/front_end/ui/ColorSwatch.js @@ -91,8 +91,8 @@ WebInspector.ColorSwatch.prototype = { this._iconElement = root.createChild("span", "color-swatch"); this._iconElement.title = WebInspector.UIString("Shift-click to change color format"); this._swatchInner = this._iconElement.createChild("span", "color-swatch-inner"); - this._swatchInner.addEventListener("dblclick", consumeEvent, false); - this._swatchInner.addEventListener("mousedown", consumeEvent, false); + this._swatchInner.addEventListener("dblclick", (e) => e.consume(), false); + this._swatchInner.addEventListener("mousedown", (e) => e.consume(), false); this._swatchInner.addEventListener("click", this._handleClick.bind(this), true); root.createChild("content"); diff --git a/front_end/ui/Dialog.js b/front_end/ui/Dialog.js index 7b8c44354e..5499b122a9 100644 --- a/front_end/ui/Dialog.js +++ b/front_end/ui/Dialog.js @@ -74,14 +74,10 @@ WebInspector.Dialog.prototype = { this._glassPane.element.addEventListener("click", this._onGlassPaneClick.bind(this), false); this.element.ownerDocument.body.addEventListener("keydown", this._keyDownBound, false); - // When a dialog closes, focus should be restored to the previous focused element when - // possible, otherwise the default inspector view element. - WebInspector.Dialog._previousFocusedElement = WebInspector.currentFocusElement(); - WebInspector.Widget.prototype.show.call(this, this._glassPane.element); this._position(); - this.focus(); + this._focusRestorer = new WebInspector.WidgetFocusRestorer(this); }, /** @@ -89,16 +85,14 @@ WebInspector.Dialog.prototype = { */ detach: function() { + this._focusRestorer.restore(); + this.element.ownerDocument.body.removeEventListener("keydown", this._keyDownBound, false); WebInspector.Widget.prototype.detach.call(this); this._glassPane.dispose(); delete this._glassPane; - if (WebInspector.Dialog._previousFocusedElement) - WebInspector.Dialog._previousFocusedElement.focus(); - delete WebInspector.Dialog._previousFocusedElement; - this._restoreTabIndexOnElements(); delete WebInspector.Dialog._instance; diff --git a/front_end/ui/FilterBar.js b/front_end/ui/FilterBar.js index abb3f0f1b1..cc62f9c536 100644 --- a/front_end/ui/FilterBar.js +++ b/front_end/ui/FilterBar.js @@ -224,7 +224,6 @@ WebInspector.TextFilterUI = function(supportRegex) this._filterInputElement = /** @type {!HTMLInputElement} */ (this._filterElement.createChild("input", "filter-input-field")); this._filterInputElement.placeholder = WebInspector.UIString("Filter"); this._filterInputElement.id = "filter-input-field"; - this._filterInputElement.addEventListener("mousedown", this._onFilterFieldManualFocus.bind(this), false); // when the search field is manually selected this._filterInputElement.addEventListener("input", this._onInput.bind(this), false); this._filterInputElement.addEventListener("change", this._onChange.bind(this), false); this._filterInputElement.addEventListener("keydown", this._onInputKeyDown.bind(this), true); @@ -308,14 +307,6 @@ WebInspector.TextFilterUI.prototype = { return this._regex; }, - /** - * @param {!Event} event - */ - _onFilterFieldManualFocus: function(event) - { - WebInspector.setCurrentFocusElement(/** @type {?Node} */ (event.target)); - }, - /** * @param {!Event} event */ diff --git a/front_end/ui/HistoryInput.js b/front_end/ui/HistoryInput.js index 54daad1e18..f8ab8a5626 100644 --- a/front_end/ui/HistoryInput.js +++ b/front_end/ui/HistoryInput.js @@ -47,12 +47,12 @@ WebInspector.HistoryInput.prototype = { if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Up.code) { this._historyPosition = Math.max(this._historyPosition - 1, 0); this.value = this._history[this._historyPosition]; - this.dispatchEvent(createEvent("input", true, true)); + this.dispatchEvent(new Event("input", {"bubbles": true, "cancelable": true})); event.consume(true); } else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Down.code) { this._historyPosition = Math.min(this._historyPosition + 1, this._history.length - 1); this.value = this._history[this._historyPosition]; - this.dispatchEvent(createEvent("input", true, true)); + this.dispatchEvent(new Event("input", {"bubbles": true, "cancelable": true})); event.consume(true); } else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Enter.code) { this._saveToHistory(); diff --git a/front_end/ui/InplaceEditor.js b/front_end/ui/InplaceEditor.js index 4e5edea770..ed4e6cc1d1 100644 --- a/front_end/ui/InplaceEditor.js +++ b/front_end/ui/InplaceEditor.js @@ -68,7 +68,7 @@ WebInspector.InplaceEditor.prototype = { var oldTabIndex = element.getAttribute("tabIndex"); if (typeof oldTabIndex !== "number" || oldTabIndex < 0) element.tabIndex = 0; - WebInspector.setCurrentFocusElement(element); + this._focusRestorer = new WebInspector.ElementFocusRestorer(element); editingContext.oldTabIndex = oldTabIndex; }, @@ -149,7 +149,8 @@ WebInspector.InplaceEditor.prototype = { if (pasteCallback) element.removeEventListener("paste", pasteEventListener, true); - WebInspector.restoreFocusFromElement(element); + if (self._focusRestorer) + self._focusRestorer.restore(); self.closeEditor(editingContext); } diff --git a/front_end/ui/InspectorView.js b/front_end/ui/InspectorView.js index ac06a4f925..6f031d8770 100644 --- a/front_end/ui/InspectorView.js +++ b/front_end/ui/InspectorView.js @@ -47,12 +47,12 @@ WebInspector.InspectorView = function() this._drawerSplitWidget.show(this.element); // Create drawer tabbed pane. - this._drawerTabbedLocation = WebInspector.viewManager.createTabbedLocation(this.showDrawer.bind(this), "drawer-view", true); + this._drawerTabbedLocation = WebInspector.viewManager.createTabbedLocation(this._showDrawer.bind(this, false), "drawer-view", true); this._drawerTabbedLocation.enableMoreTabsButton(); this._drawerTabbedPane = this._drawerTabbedLocation.tabbedPane(); this._drawerTabbedPane.setMinimumSize(0, 27); var closeDrawerButton = new WebInspector.ToolbarButton(WebInspector.UIString("Close drawer"), "delete-toolbar-item"); - closeDrawerButton.addEventListener("click", this.closeDrawer.bind(this)); + closeDrawerButton.addEventListener("click", this._closeDrawer.bind(this)); this._drawerTabbedPane.rightToolbar().appendToolbarItem(closeDrawerButton); this._drawerSplitWidget.installResizer(this._drawerTabbedPane.headerElement()); this._drawerSplitWidget.setSidebarWidget(this._drawerTabbedPane); @@ -363,11 +363,18 @@ WebInspector.InspectorView.prototype = { return panel; }, - showDrawer: function() + /** + * @param {boolean} focus + */ + _showDrawer: function(focus) { - if (!this._drawerTabbedPane.isShowing()) - this._drawerSplitWidget.showBoth(); - this._drawerTabbedPane.focus(); + if (this._drawerTabbedPane.isShowing()) + return; + this._drawerSplitWidget.showBoth(); + if (focus) + this._focusRestorer = new WebInspector.WidgetFocusRestorer(this._drawerTabbedPane); + else + this._focusRestorer = null; }, /** @@ -378,11 +385,12 @@ WebInspector.InspectorView.prototype = { return this._drawerTabbedPane.isShowing(); }, - closeDrawer: function() + _closeDrawer: function() { if (!this._drawerTabbedPane.isShowing()) return; - WebInspector.restoreFocusFromElement(this._drawerTabbedPane.element); + if (this._focusRestorer) + this._focusRestorer.restore(); this._drawerSplitWidget.hideSidebar(true); }, @@ -592,9 +600,9 @@ WebInspector.InspectorView.DrawerToggleActionDelegate.prototype = { handleAction: function(context, actionId) { if (WebInspector.inspectorView.drawerVisible()) - WebInspector.inspectorView.closeDrawer(); + WebInspector.inspectorView._closeDrawer(); else - WebInspector.inspectorView.showDrawer(); + WebInspector.inspectorView._showDrawer(true); return true; } } diff --git a/front_end/ui/SearchableView.js b/front_end/ui/SearchableView.js index 1d07127968..d68913b3b4 100644 --- a/front_end/ui/SearchableView.js +++ b/front_end/ui/SearchableView.js @@ -94,7 +94,6 @@ WebInspector.SearchableView = function(searchable, settingName) this._searchNavigationNextElement.addEventListener("click", this._onNextButtonSearch.bind(this), false); this._searchNavigationNextElement.title = WebInspector.UIString("Search Next"); - this._searchInputElement.addEventListener("mousedown", this._onSearchFieldManualFocus.bind(this), false); // when the search field is manually selected this._searchInputElement.addEventListener("keydown", this._onSearchKeyDown.bind(this), true); this._searchInputElement.addEventListener("input", this._onInput.bind(this), false); @@ -250,7 +249,7 @@ WebInspector.SearchableView.prototype = { closeSearch: function() { this.cancelSearch(); - if (WebInspector.currentFocusElement() && WebInspector.currentFocusElement().isDescendant(this._footerElementContainer)) + if (this._footerElementContainer.hasFocus()) this.focus(); }, @@ -364,7 +363,7 @@ WebInspector.SearchableView.prototype = { this.cancelSearch(); var queryCandidate; - if (WebInspector.currentFocusElement() !== this._searchInputElement) { + if (!this._searchInputElement.hasFocus()) { var selection = this._searchInputElement.getComponentSelection(); if (selection.rangeCount) queryCandidate = selection.toString().replace(/\r?\n.*/, ""); @@ -389,14 +388,6 @@ WebInspector.SearchableView.prototype = { } }, - /** - * @param {!Event} event - */ - _onSearchFieldManualFocus: function(event) - { - WebInspector.setCurrentFocusElement(/** @type {?Node} */ (event.target)); - }, - /** * @param {!Event} event */ diff --git a/front_end/ui/SoftContextMenu.js b/front_end/ui/SoftContextMenu.js index 88b1c715b8..99c432ca89 100644 --- a/front_end/ui/SoftContextMenu.js +++ b/front_end/ui/SoftContextMenu.js @@ -61,7 +61,7 @@ WebInspector.SoftContextMenu.prototype = { this.element.style.left = (this._parentMenu ? x - subMenuOverlap : x) + "px"; this._contextMenuElement.tabIndex = 0; - this._contextMenuElement.addEventListener("mouseup", consumeEvent, false); + this._contextMenuElement.addEventListener("mouseup", (e) => e.consume(), false); this._contextMenuElement.addEventListener("keydown", this._menuKeyDown.bind(this), false); for (var i = 0; i < this._items.length; ++i) diff --git a/front_end/ui/SplitWidget.js b/front_end/ui/SplitWidget.js index 23d3c95656..894f333a07 100644 --- a/front_end/ui/SplitWidget.js +++ b/front_end/ui/SplitWidget.js @@ -887,8 +887,6 @@ WebInspector.SplitWidget.prototype = { */ createShowHideSidebarButton: function(title) { - console.assert(this.isVertical(), "Buttons for split widget with horizontal split are not supported yet."); - this._showHideSidebarButtonTitle = WebInspector.UIString(title); this._showHideSidebarButton = new WebInspector.ToolbarButton("", "sidebar-toolbar-item"); this._showHideSidebarButton.addEventListener("click", buttonClicked.bind(this)); @@ -914,7 +912,8 @@ WebInspector.SplitWidget.prototype = { if (!this._showHideSidebarButton) return; var sidebarHidden = this._showMode === WebInspector.SplitWidget.ShowMode.OnlyMain; - this._showHideSidebarButton.setState((this.isSidebarSecond() ? "right" : "left") + "-" + (sidebarHidden ? "show" : "hide")); + var side = this.isVertical() ? (this.isSidebarSecond() ? "right" : "left") : (this.isSidebarSecond() ? "bottom" : "top"); + this._showHideSidebarButton.setState(side + "-" + (sidebarHidden ? "show" : "hide")); this._showHideSidebarButton.setTitle(sidebarHidden ? WebInspector.UIString("Show %s", this._showHideSidebarButtonTitle) : WebInspector.UIString("Hide %s", this._showHideSidebarButtonTitle)); }, diff --git a/front_end/ui/SwatchPopoverHelper.js b/front_end/ui/SwatchPopoverHelper.js index ae761d33eb..fea94b2573 100644 --- a/front_end/ui/SwatchPopoverHelper.js +++ b/front_end/ui/SwatchPopoverHelper.js @@ -11,7 +11,7 @@ WebInspector.SwatchPopoverHelper = function() this._popover = new WebInspector.Popover(); this._popover.setCanShrink(false); this._popover.setNoMargins(true); - this._popover.element.addEventListener("mousedown", consumeEvent, false); + this._popover.element.addEventListener("mousedown", (e) => e.consume(), false); this._hideProxy = this.hide.bind(this, true); this._boundOnKeyDown = this._onKeyDown.bind(this); @@ -67,13 +67,12 @@ WebInspector.SwatchPopoverHelper.prototype = { reposition: function() { - if (!this._previousFocusElement) - this._previousFocusElement = WebInspector.currentFocusElement(); // Unbind "blur" listener to avoid reenterability: |popover.showView| will hide the popover and trigger it synchronously. this._view.contentElement.removeEventListener("focusout", this._boundFocusOut, false); this._popover.showView(this._view, this._anchorElement); this._view.contentElement.addEventListener("focusout", this._boundFocusOut, false); - WebInspector.setCurrentFocusElement(this._view.contentElement); + if (!this._focusRestorer) + this._focusRestorer = new WebInspector.WidgetFocusRestorer(this._view); }, /** @@ -93,8 +92,7 @@ WebInspector.SwatchPopoverHelper.prototype = { if (this._hiddenCallback) this._hiddenCallback.call(null, !!commitEdit); - WebInspector.setCurrentFocusElement(this._previousFocusElement); - delete this._previousFocusElement; + this._focusRestorer.restore(); delete this._anchorElement; if (this._view) { this._view.detach(); diff --git a/front_end/ui/TabbedPane.js b/front_end/ui/TabbedPane.js index 84603f58d1..f1a8e45871 100644 --- a/front_end/ui/TabbedPane.js +++ b/front_end/ui/TabbedPane.js @@ -153,6 +153,9 @@ WebInspector.TabbedPane.prototype = { this._closeableTabs = closeableTabs; }, + /** + * @override + */ focus: function() { if (this.visibleView) @@ -435,7 +438,7 @@ WebInspector.TabbedPane.prototype = { if (tab.view === view) return; - var shouldFocus = tab.view.element.isSelfOrAncestor(WebInspector.currentFocusElement()); + var shouldFocus = tab.view.hasFocus(); this.suspendInvalidations(); diff --git a/front_end/ui/TextPrompt.js b/front_end/ui/TextPrompt.js index 8a4162b252..eb4a185d40 100644 --- a/front_end/ui/TextPrompt.js +++ b/front_end/ui/TextPrompt.js @@ -153,7 +153,7 @@ WebInspector.TextPrompt.prototype = { this._proxyElement.remove(); delete this._proxyElement; this._element.classList.remove("text-prompt"); - WebInspector.restoreFocusFromElement(this._element); + this._focusRestorer.restore(); }, /** @@ -241,7 +241,7 @@ WebInspector.TextPrompt.prototype = { this._oldTabIndex = this._element.tabIndex; if (this._element.tabIndex < 0) this._element.tabIndex = 0; - WebInspector.setCurrentFocusElement(this._element); + this._focusRestorer = new WebInspector.ElementFocusRestorer(this._element); if (!this.text()) this._updateAutoComplete(); }, @@ -691,7 +691,12 @@ WebInspector.TextPrompt.prototype = { */ isCaretInsidePrompt: function() { - return this._element.isInsertionCaretInside(); + var selection = this._element.getComponentSelection(); + // @see crbug.com/602541 + var selectionRange = selection && selection.rangeCount ? selection.getRangeAt(0) : null; + if (!selectionRange || !selection.isCollapsed) + return false; + return selectionRange.startContainer.isSelfOrDescendant(this._element); }, /** diff --git a/front_end/ui/Tooltip.js b/front_end/ui/Tooltip.js index f28d2a3beb..426498cb63 100644 --- a/front_end/ui/Tooltip.js +++ b/front_end/ui/Tooltip.js @@ -99,28 +99,26 @@ WebInspector.Tooltip.prototype = { container = this.element.parentElement; // Posititon tooltip based on the anchor element. - var containerOffset = container.offsetRelativeToWindow(this.element.window()); - var containerOffsetWidth = container.offsetWidth; - var containerOffsetHeight = container.offsetHeight; + var containerBox = container.boxInWindow(this.element.window()); var anchorBox = this._anchorElement.boxInWindow(this.element.window()); const anchorOffset = 2; const pageMargin = 2; var cursorOffset = 10; this._tooltipElement.classList.toggle("tooltip-breakword", !this._tooltipElement.textContent.match("\\s")); - this._tooltipElement.style.maxWidth = (containerOffsetWidth - pageMargin * 2) + "px"; + this._tooltipElement.style.maxWidth = (containerBox.width - pageMargin * 2) + "px"; this._tooltipElement.style.maxHeight = ""; var tooltipWidth = this._tooltipElement.offsetWidth; var tooltipHeight = this._tooltipElement.offsetHeight; var anchorTooltipAtElement = this._anchorElement.nodeName === "BUTTON" || this._anchorElement.nodeName === "LABEL"; var tooltipX = anchorTooltipAtElement ? anchorBox.x : event.x + cursorOffset; tooltipX = Number.constrain(tooltipX, - containerOffset.x + pageMargin, - containerOffset.x + containerOffsetWidth - tooltipWidth - pageMargin); + containerBox.x + pageMargin, + containerBox.x + containerBox.width - tooltipWidth - pageMargin); var tooltipY; if (!anchorTooltipAtElement) { - tooltipY = event.y + cursorOffset + tooltipHeight < containerOffset.y + containerOffsetHeight ? event.y + cursorOffset : event.y - tooltipHeight; + tooltipY = event.y + cursorOffset + tooltipHeight < containerBox.y + containerBox.height ? event.y + cursorOffset : event.y - tooltipHeight; } else { - var onBottom = anchorBox.y + anchorOffset + anchorBox.height + tooltipHeight < containerOffset.y + containerOffsetHeight; + var onBottom = anchorBox.y + anchorOffset + anchorBox.height + tooltipHeight < containerBox.y + containerBox.height; tooltipY = onBottom ? anchorBox.y + anchorBox.height + anchorOffset : anchorBox.y - tooltipHeight - anchorOffset; } this._tooltipElement.positionAt(tooltipX, tooltipY); diff --git a/front_end/ui/UIUtils.js b/front_end/ui/UIUtils.js index 66d019c576..5e21725946 100644 --- a/front_end/ui/UIUtils.js +++ b/front_end/ui/UIUtils.js @@ -284,6 +284,7 @@ WebInspector.GlassPane = function(document, dimmed) this.element.style.cssText = "position:absolute;top:0;bottom:0;left:0;right:0;background-color:" + background + ";z-index:" + this._zIndex + ";overflow:hidden;"; document.body.appendChild(this.element); WebInspector._glassPane = this; + // TODO(dgozman): disallow focus outside of glass pane? } WebInspector.GlassPane.prototype = { @@ -322,16 +323,17 @@ WebInspector.isBeingEdited = function(node) /** * @return {boolean} + * @suppressGlobalPropertiesCheck */ WebInspector.isEditing = function() { if (WebInspector.__editingCount) return true; - var element = WebInspector.currentFocusElement(); - if (!element) + var focused = document.deepActiveElement(); + if (!focused) return false; - return element.classList.contains("text-prompt") || element.nodeName === "INPUT" || element.nodeName === "TEXTAREA"; + return focused.classList.contains("text-prompt") || focused.nodeName === "INPUT" || focused.nodeName === "TEXTAREA"; } /** @@ -791,93 +793,37 @@ WebInspector._windowBlurred = function(document, event) document.body.classList.add("inactive"); } -/** - * @return {!Element} - */ -WebInspector.previousFocusElement = function() -{ - return WebInspector._previousFocusElement; -} - -/** - * @return {!Element} - */ -WebInspector.currentFocusElement = function() -{ - return WebInspector._currentFocusElement; -} - /** * @param {!Event} event */ WebInspector._focusChanged = function(event) { - var node = event.deepActiveElement(); - WebInspector.Widget.focusWidgetForNode(node); - WebInspector.setCurrentFocusElement(node); + var document = event.target && event.target.ownerDocument; + var element = document ? document.deepActiveElement() : null; + WebInspector.Widget.focusWidgetForNode(element); } /** - * @param {!Document} document - * @param {!Event} event - */ -WebInspector._documentBlurred = function(document, event) -{ - // We want to know when currentFocusElement loses focus to nowhere. - // This is the case when event.relatedTarget is null (no element is being focused) - // and document.activeElement is reset to default (this is not a window blur). - if (!event.relatedTarget && document.activeElement === document.body) - WebInspector.setCurrentFocusElement(null); -} - -WebInspector._textInputTypes = new Set(["text", "search", "tel", "url", "email", "password"]); -WebInspector._isTextEditingElement = function(element) -{ - if (element instanceof HTMLInputElement) - return WebInspector._textInputTypes.has(element.type); - - if (element instanceof HTMLTextAreaElement) - return true; - - return false; -} - -/** - * @param {?Node} x + * @param {!Element} element + * @constructor */ -WebInspector.setCurrentFocusElement = function(x) +WebInspector.ElementFocusRestorer = function(element) { - if (WebInspector._glassPane && x && !WebInspector._glassPane.element.isAncestor(x)) - return; - if (x && !x.ownerDocument.isAncestor(x)) - return; - if (WebInspector._currentFocusElement !== x) - WebInspector._previousFocusElement = WebInspector._currentFocusElement; - WebInspector._currentFocusElement = x; - - if (x) { - x.focus(); - - // Make a caret selection inside the new element if there isn't a range selection and there isn't already a caret selection inside. - // This is needed (at least) to remove caret from console when focus is moved to some element in the panel. - // The code below should not be applied to text fields and text areas, hence _isTextEditingElement check. - var selection = x.getComponentSelection(); - if (!WebInspector._isTextEditingElement(x) && selection.isCollapsed && !x.isInsertionCaretInside()) { - var selectionRange = x.ownerDocument.createRange(); - selectionRange.setStart(x, 0); - selectionRange.setEnd(x, 0); - - selection.removeAllRanges(); - selection.addRange(selectionRange); - } - } else if (WebInspector._previousFocusElement) - WebInspector._previousFocusElement.blur(); + this._element = element; + this._previous = element.ownerDocument.deepActiveElement(); + element.focus(); } -WebInspector.restoreFocusFromElement = function(element) -{ - if (element && element.isSelfOrAncestor(WebInspector.currentFocusElement())) - WebInspector.setCurrentFocusElement(WebInspector.previousFocusElement()); +WebInspector.ElementFocusRestorer.prototype = { + restore: function() + { + if (!this._element) + return; + if (this._element.hasFocus() && this._previous) + this._previous.focus(); + this._previous = null; + this._element = null; + } } /** @@ -1271,7 +1217,6 @@ WebInspector.initializeUIUtils = function(document, themeSetting) document.defaultView.addEventListener("focus", WebInspector._windowFocused.bind(WebInspector, document), false); document.defaultView.addEventListener("blur", WebInspector._windowBlurred.bind(WebInspector, document), false); document.addEventListener("focus", WebInspector._focusChanged.bind(WebInspector), true); - document.addEventListener("blur", WebInspector._documentBlurred.bind(WebInspector, document), true); if (!WebInspector.themeSupport) WebInspector.themeSupport = new WebInspector.ThemeSupport(themeSetting); diff --git a/front_end/ui/View.js b/front_end/ui/View.js index 6e910f7bb5..3a39170910 100644 --- a/front_end/ui/View.js +++ b/front_end/ui/View.js @@ -435,6 +435,8 @@ WebInspector.ViewManager._ContainerWidget = function(view) WebInspector.VBox.call(this); this.element.classList.add("flex-auto", "view-container", "overflow-auto"); this._view = view; + this.element.tabIndex = 0; + this.setDefaultFocusedElement(this.element); } WebInspector.ViewManager._ContainerWidget.prototype = { @@ -447,7 +449,14 @@ WebInspector.ViewManager._ContainerWidget.prototype = { return this._materializePromise; var promises = []; promises.push(this._view.toolbarItems().then(WebInspector.ViewManager._populateToolbar.bind(WebInspector.ViewManager, this.element))); - promises.push(this._view.widget().then(widget => widget.show(this.element))); + promises.push(this._view.widget().then(widget => { + // Move focus from |this| to loaded |widget| if any. + var shouldFocus = this.element.hasFocus(); + this.setDefaultFocusedElement(null); + widget.show(this.element); + if (shouldFocus) + widget.focus(); + })); this._materializePromise = Promise.all(promises); return this._materializePromise; }, @@ -696,8 +705,8 @@ WebInspector.ViewManager._TabbedLocation.prototype = { showView: function(view, insertBefore) { this.appendView(view, insertBefore); - this._tabbedPane.focus(); this._tabbedPane.selectTab(view.viewId()); + this._tabbedPane.focus(); return this._materializeWidget(view); }, diff --git a/front_end/ui/Widget.js b/front_end/ui/Widget.js index e01fd5b4fb..6322a817ff 100644 --- a/front_end/ui/Widget.js +++ b/front_end/ui/Widget.js @@ -437,7 +437,7 @@ WebInspector.Widget.prototype = { }, /** - * @param {!Element} element + * @param {?Element} element */ setDefaultFocusedElement: function(element) { @@ -455,9 +455,13 @@ WebInspector.Widget.prototype = { focus: function() { + if (!this.isShowing()) + return; + var element = this._defaultFocusedElement; - if (element && !element.isAncestor(this.element.ownerDocument.activeElement)) { - WebInspector.setCurrentFocusElement(element); + if (element) { + if (!element.hasFocus()) + element.focus(); return; } @@ -479,8 +483,7 @@ WebInspector.Widget.prototype = { */ hasFocus: function() { - var activeElement = this.element.ownerDocument.activeElement; - return activeElement && activeElement.isSelfOrDescendant(this.element); + return this.element.hasFocus(); }, /** @@ -745,6 +748,29 @@ WebInspector.VBoxWithResizeCallback.prototype = { __proto__: WebInspector.VBox.prototype } +/** + * @param {!WebInspector.Widget} widget + * @constructor + */ +WebInspector.WidgetFocusRestorer = function(widget) +{ + this._widget = widget; + this._previous = widget.element.ownerDocument.deepActiveElement(); + widget.focus(); +} + +WebInspector.WidgetFocusRestorer.prototype = { + restore: function() + { + if (!this._widget) + return; + if (this._widget.hasFocus() && this._previous) + this._previous.focus(); + this._previous = null; + this._widget = null; + } +} + /** * @override * @param {?Node} child diff --git a/front_end/ui/toolbar.css b/front_end/ui/toolbar.css index 421a7fc518..a0aa2efa78 100644 --- a/front_end/ui/toolbar.css +++ b/front_end/ui/toolbar.css @@ -505,6 +505,26 @@ input.toolbar-item.hover { -webkit-mask-position: -256px -72px; } +.toolbar-state-top-show .sidebar-toolbar-item.toolbar-glyph { + -webkit-mask-position: -160px -72px; + transform: translate(4px, 0) rotate(90deg); +} + +.toolbar-state-top-hide .sidebar-toolbar-item.toolbar-glyph { + -webkit-mask-position: -192px -72px; + transform: translate(4px, -1px) rotate(90deg); +} + +.toolbar-state-bottom-show .sidebar-toolbar-item.toolbar-glyph { + -webkit-mask-position: -288px -72px; + transform: translate(4px, 0) rotate(90deg); +} + +.toolbar-state-bottom-hide .sidebar-toolbar-item.toolbar-glyph { + -webkit-mask-position: -256px -72px; + transform: translate(4px, -1px) rotate(90deg); +} + .toolbar-state-on .record-toolbar-item.toolbar-glyph, .toolbar-state-active .filter-toolbar-item.toolbar-glyph, .toolbar-state-active .block-toolbar-item.toolbar-glyph {