From 97116fc35702273e506f785d8bd5dcc69db0b08f Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Wed, 25 Jan 2017 18:49:16 +0100 Subject: [PATCH] squash 'resources/unpacked/devtools' changes from 4edefc674..f4c45a030 f4c45a030 [devtools] Support different encodings for Page.CaptureScreenshot. 2cde168c3 Revert of DevTools: Correct Source Panel Show/Hide Toolbar buttons (patchset #1 id:1 of https://codereview.chromium.org/2600493002/ ) 53bf5fa91 Timeline: remove the remains of TimelineModel.Record 4a470d6bb DevTools: convert datagrid and network waterfall sort icons to UI.Icon c6ec1dae2 [DevTools] Support BreakReason.OOM 830adfe15 [DevTools] Fix overflow in devices view. ca89e2027 DevTools: Console: Provide autocompletions for Maps 6a0cd7627 DevTools: fix network waterfall sorting icon 1ac932644 Timeline: rewrite TimelineUIUtils.buildRangeStats() to work without TimelineRecord bbdd9d23c DevTools: render (verbose|info) (intervensions|deprecations|violations) with warning background. db4c1489d Revert of DevTools: Console: Provide autocompletions for Maps (patchset #6 id:100001 of https://codereview.chromium.org/2639703002/ ) 4da9dcd2c [DevTools] Hide empty toolbars f1eb3319a DevTools: introduce object previews experiment 4e4a20fb8 DevTools: Console: Provide autocompletions for Maps 50f80e6c7 DevTools: align tabbed pane close tab buttons. 3be25d011 Revert of [devtools] Support different encodings for Page.CaptureScreenshot. (patchset #8 id:480001 of https://codereview.chromium.org/2592983002/ ) git-subtree-dir: resources/unpacked/devtools git-subtree-split: f4c45a03029cde19d58bdaf4c1530df833f0c44a --- front_end/components/CustomPreviewSection.js | 2 +- front_end/components/EventListenersUtils.js | 2 +- front_end/components/EventListenersView.js | 3 +- .../components/JavaScriptAutocomplete.js | 142 ++++++++-- front_end/components/ObjectPopoverHelper.js | 1 + .../components/ObjectPropertiesSection.js | 32 ++- .../RemoteObjectPreviewFormatter.js | 36 ++- front_end/components/objectValue.css | 4 + front_end/console/ConsolePrompt.js | 2 +- front_end/console/ConsoleViewMessage.js | 22 +- front_end/data_grid/DataGrid.js | 8 +- front_end/data_grid/dataGrid.css | 18 +- front_end/devices/devicesView.css | 4 +- front_end/elements/PropertiesWidget.js | 2 +- front_end/elements/StylesSidebarPane.js | 2 +- front_end/main/Main.js | 1 + front_end/network/NetworkLogViewColumns.js | 12 +- front_end/network/networkLogView.css | 18 +- front_end/resources/ResourcesPanel.js | 5 +- front_end/sdk/DebuggerModel.js | 4 +- front_end/sdk/RemoteObject.js | 76 ++++-- front_end/sources/DebuggerPausedMessage.js | 4 +- front_end/sources/JavaScriptSourceFrame.js | 12 +- front_end/sources/SourceMapNamesResolver.js | 10 +- .../sources/WatchExpressionsSidebarPane.js | 2 +- front_end/timeline/TimelinePanel.js | 16 +- front_end/timeline/TimelineUIUtils.js | 214 ++++++++------- .../timeline_model/TimelineFrameModel.js | 18 -- front_end/timeline_model/TimelineModel.js | 249 +++++------------- front_end/ui/Icon.js | 9 +- front_end/ui/tabbedPane.css | 7 +- 31 files changed, 505 insertions(+), 432 deletions(-) diff --git a/front_end/components/CustomPreviewSection.js b/front_end/components/CustomPreviewSection.js index 5a7761f643..bf026b312d 100644 --- a/front_end/components/CustomPreviewSection.js +++ b/front_end/components/CustomPreviewSection.js @@ -125,7 +125,7 @@ Components.CustomPreviewSection = class { this._header.classList.toggle('expanded', this._expanded); this._cachedContent.classList.toggle('hidden', !this._expanded); if (this._expanded) - this._expandIcon.setIconType('smallicon-triangle-bottom'); + this._expandIcon.setIconType('smallicon-triangle-down'); else this._expandIcon.setIconType('smallicon-triangle-right'); } diff --git a/front_end/components/EventListenersUtils.js b/front_end/components/EventListenersUtils.js index 5d13d66d54..20d5b22a42 100644 --- a/front_end/components/EventListenersUtils.js +++ b/front_end/components/EventListenersUtils.js @@ -31,7 +31,7 @@ SDK.EventListener.frameworkEventListeners = function(object) { * @return {!Promise, internalProperties: ?Array.}>} */ function getOwnProperties(object) { - return object.getOwnPropertiesPromise(); + return object.getOwnPropertiesPromise(false /* generatePreview */); } /** diff --git a/front_end/components/EventListenersView.js b/front_end/components/EventListenersView.js index 34bec39aac..76e8ca18cf 100644 --- a/front_end/components/EventListenersView.js +++ b/front_end/components/EventListenersView.js @@ -285,7 +285,8 @@ Components.ObjectEventListenerBar = class extends UI.TreeElement { var subtitle = this.listItemElement.createChild('span', 'event-listener-tree-subtitle'); subtitle.appendChild(linkifier.linkifyRawLocation(this._eventListener.location(), this._eventListener.sourceURL())); - title.appendChild(Components.ObjectPropertiesSection.createValueElement(object, false)); + title.appendChild( + Components.ObjectPropertiesSection.createValueElement(object, false /* wasThrown */, false /* showPreview */)); if (this._eventListener.removeFunction()) { var deleteButton = title.createChild('span', 'event-listener-button'); diff --git a/front_end/components/JavaScriptAutocomplete.js b/front_end/components/JavaScriptAutocomplete.js index b9aad4f037..79463a2872 100644 --- a/front_end/components/JavaScriptAutocomplete.js +++ b/front_end/components/JavaScriptAutocomplete.js @@ -14,13 +14,24 @@ Components.JavaScriptAutocomplete.CompletionGroup; * @return {!Promise} */ Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function(text, query, force) { + var clippedExpression = Components.JavaScriptAutocomplete._clipExpression(text, true); + var mapCompletionsPromise = Components.JavaScriptAutocomplete._mapCompletions(text, query); + return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpression, query, force) + .then(completions => mapCompletionsPromise.then(mapCompletions => mapCompletions.concat(completions))); +}; + +/** + * @param {string} text + * @param {boolean=} allowEndingBracket + * @return {string} + */ +Components.JavaScriptAutocomplete._clipExpression = function(text, allowEndingBracket) { var index; var stopChars = new Set('=:({;,!+-*/&|^<>`'.split('')); var whiteSpaceChars = new Set(' \r\n\t'.split('')); var continueChars = new Set('[. \r\n\t'.split('')); for (index = text.length - 1; index >= 0; index--) { - // Pass less stop characters to rangeOfWord so the range will be a more complete expression. if (stopChars.has(text.charAt(index))) break; if (whiteSpaceChars.has(text.charAt(index)) && !continueChars.has(text.charAt(index - 1))) @@ -35,16 +46,112 @@ Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function( if (character === ']') bracketCount++; // Allow an open bracket at the end for property completion. - if (character === '[' && index < clippedExpression.length - 1) { + if (character === '[' && (index < clippedExpression.length - 1 || !allowEndingBracket)) { bracketCount--; if (bracketCount < 0) break; } index--; } - clippedExpression = clippedExpression.substring(index + 1).trim(); + return clippedExpression.substring(index + 1).trim(); +}; + +/** + * @param {string} text + * @param {string} query + * @return {!Promise} + */ +Components.JavaScriptAutocomplete._mapCompletions = function(text, query) { + var mapMatch = text.match(/\.\s*(get|set|delete)\s*\(\s*$/); + var executionContext = UI.context.flavor(SDK.ExecutionContext); + if (!executionContext || !mapMatch) + return Promise.resolve([]); + + var clippedExpression = Components.JavaScriptAutocomplete._clipExpression(text.substring(0, mapMatch.index)); + var fulfill; + var promise = new Promise(x => fulfill = x); + executionContext.evaluate(clippedExpression, 'completion', true, true, false, false, false, evaluated); + return promise; + + /** + * @param {?SDK.RemoteObject} result + * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails + */ + function evaluated(result, exceptionDetails) { + if (!result || !!exceptionDetails || result.subtype !== 'map') { + fulfill([]); + return; + } + result.getOwnPropertiesPromise(false).then(extractEntriesProperty); + } + + /** + * @param {!{properties: ?Array, internalProperties: ?Array}} properties + */ + function extractEntriesProperty(properties) { + var internalProperties = properties.internalProperties || []; + var entriesProperty = internalProperties.find(property => property.name === '[[Entries]]'); + if (!entriesProperty) { + fulfill([]); + return; + } + entriesProperty.value.callFunctionJSONPromise(getEntries).then(keysObj => gotKeys(Object.keys(keysObj))); + } + + /** + * @suppressReceiverCheck + * @this {!Array<{key:?, value:?}>} + * @return {!Object} + */ + function getEntries() { + var result = {__proto__: null}; + for (var i = 0; i < this.length; i++) { + if (typeof this[i].key === 'string') + result[this[i].key] = true; + } + return result; + } - return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpression, query, force); + /** + * @param {!Array} rawKeys + */ + function gotKeys(rawKeys) { + var caseSensitivePrefix = []; + var caseInsensitivePrefix = []; + var caseSensitiveAnywhere = []; + var caseInsensitiveAnywhere = []; + var quoteChar = '"'; + if (query.startsWith('\'')) + quoteChar = '\''; + var endChar = ')'; + if (mapMatch[0].indexOf('set') !== -1) + endChar = ', '; + + var sorter = rawKeys.length < 1000 ? String.naturalOrderComparator : undefined; + var keys = rawKeys.sort(sorter).map(key => quoteChar + key + quoteChar + endChar); + + for (var key of keys) { + if (key.length < query.length) + continue; + if (query.length && key.toLowerCase().indexOf(query.toLowerCase()) === -1) + continue; + // Substitute actual newlines with newline characters. @see crbug.com/498421 + var title = key.split('\n').join('\\n'); + + if (key.startsWith(query)) + caseSensitivePrefix.push({title: title, priority: 4}); + else if (key.toLowerCase().startsWith(query.toLowerCase())) + caseInsensitivePrefix.push({title: title, priority: 3}); + else if (key.indexOf(query) !== -1) + caseSensitiveAnywhere.push({title: title, priority: 2}); + else + caseInsensitiveAnywhere.push({title: title, priority: 1}); + } + var suggestions = caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere); + if (suggestions.length) + suggestions[0].subtitle = Common.UIString('Keys'); + fulfill(suggestions); + } }; /** @@ -76,8 +183,8 @@ Components.JavaScriptAutocomplete.completionsForExpression = function(expression if (!query && !expressionString && !force) return Promise.resolve([]); - var fufill; - var promise = new Promise(x => fufill = x); + var fulfill; + var promise = new Promise(x => fulfill = x); var selectedFrame = executionContext.debuggerModel.selectedCallFrame(); if (!expressionString && selectedFrame) variableNamesInScopes(selectedFrame, receivedPropertyNames); @@ -91,7 +198,7 @@ Components.JavaScriptAutocomplete.completionsForExpression = function(expression */ function evaluated(result, exceptionDetails) { if (!result || !!exceptionDetails) { - fufill([]); + fulfill([]); return; } @@ -104,7 +211,9 @@ Components.JavaScriptAutocomplete.completionsForExpression = function(expression return Promise.resolve(/** @type {?SDK.RemoteObject} */ (null)); if (object.type !== 'object' || object.subtype !== 'proxy') return Promise.resolve(/** @type {?SDK.RemoteObject} */ (object)); - return object.getOwnPropertiesPromise().then(extractTargetFromProperties).then(extractTarget); + return object.getOwnPropertiesPromise(false /* generatePreview */) + .then(extractTargetFromProperties) + .then(extractTarget); } /** @@ -206,7 +315,9 @@ Components.JavaScriptAutocomplete.completionsForExpression = function(expression for (var i = 0; i < scopeChain.length; ++i) { var scope = scopeChain[i]; var object = scope.object(); - object.getAllProperties(false, propertiesCollected.bind(null, scope.typeName())); + object.getAllProperties( + false /* accessorPropertiesOnly */, false /* generatePreview */, + propertiesCollected.bind(null, scope.typeName())); } } @@ -219,7 +330,7 @@ Components.JavaScriptAutocomplete.completionsForExpression = function(expression if (result && !exceptionDetails) receivedPropertyNames(/** @type {!Object} */ (result.value)); else - fufill([]); + fulfill([]); } /** @@ -228,7 +339,7 @@ Components.JavaScriptAutocomplete.completionsForExpression = function(expression function receivedPropertyNames(object) { executionContext.target().runtimeAgent().releaseObjectGroup('completion'); if (!object) { - fufill([]); + fulfill([]); return; } var propertyGroups = /** @type {!Array} */ (object); @@ -258,7 +369,7 @@ Components.JavaScriptAutocomplete.completionsForExpression = function(expression ]; propertyGroups.push({items: commandLineAPI}); } - fufill(Components.JavaScriptAutocomplete._completionsForQuery( + fulfill(Components.JavaScriptAutocomplete._completionsForQuery( dotNotation, bracketNotation, expressionString, query, propertyGroups)); } }; @@ -292,7 +403,7 @@ Components.JavaScriptAutocomplete._completionsForQuery = function( var result = []; var lastGroupTitle; for (var group of propertyGroups) { - group.items.sort(itemComparator); + group.items.sort(itemComparator.bind(null, group.items.length > 1000)); var caseSensitivePrefix = []; var caseInsensitivePrefix = []; var caseSensitiveAnywhere = []; @@ -336,17 +447,18 @@ Components.JavaScriptAutocomplete._completionsForQuery = function( return result; /** + * @param {boolean} naturalOrder * @param {string} a * @param {string} b * @return {number} */ - function itemComparator(a, b) { + function itemComparator(naturalOrder, a, b) { var aStartsWithUnderscore = a.startsWith('_'); var bStartsWithUnderscore = b.startsWith('_'); if (aStartsWithUnderscore && !bStartsWithUnderscore) return 1; if (bStartsWithUnderscore && !aStartsWithUnderscore) return -1; - return String.naturalOrderComparator(a, b); + return naturalOrder ? String.naturalOrderComparator(a, b) : a.localeCompare(b); } }; diff --git a/front_end/components/ObjectPopoverHelper.js b/front_end/components/ObjectPopoverHelper.js index f892558204..be78fcf66d 100644 --- a/front_end/components/ObjectPopoverHelper.js +++ b/front_end/components/ObjectPopoverHelper.js @@ -138,6 +138,7 @@ Components.ObjectPopoverHelper = class extends UI.PopoverHelper { if (result.type === 'function') { result.getOwnProperties( + false /* generatePreview */, didGetFunctionProperties.bind(this, result, popoverContentElement, valueElement, anchorElement)); return; } diff --git a/front_end/components/ObjectPropertiesSection.js b/front_end/components/ObjectPropertiesSection.js index 21f0f9756b..3b705eab13 100644 --- a/front_end/components/ObjectPropertiesSection.js +++ b/front_end/components/ObjectPropertiesSection.js @@ -77,7 +77,8 @@ Components.ObjectPropertiesSection = class extends UI.TreeOutlineInShadow { static defaultObjectPresentation(object, linkifier, skipProto) { var componentRoot = createElementWithClass('span', 'source-code'); var shadowRoot = UI.createShadowRootWithCoreStyles(componentRoot, 'components/objectValue.css'); - shadowRoot.appendChild(Components.ObjectPropertiesSection.createValueElement(object, false)); + shadowRoot.appendChild( + Components.ObjectPropertiesSection.createValueElement(object, false /* wasThrown */, true /* showPreview */)); if (!object.hasChildren) return componentRoot; @@ -218,27 +219,30 @@ Components.ObjectPropertiesSection = class extends UI.TreeOutlineInShadow { /** * @param {!SDK.RemoteObject} value * @param {boolean} wasThrown + * @param {boolean} showPreview * @param {!Element=} parentElement * @param {!Components.Linkifier=} linkifier * @return {!Element} */ - static createValueElementWithCustomSupport(value, wasThrown, parentElement, linkifier) { + static createValueElementWithCustomSupport(value, wasThrown, showPreview, parentElement, linkifier) { if (value.customPreview()) { var result = (new Components.CustomPreviewComponent(value)).element; result.classList.add('object-properties-section-custom-section'); return result; } - return Components.ObjectPropertiesSection.createValueElement(value, wasThrown, parentElement, linkifier); + return Components.ObjectPropertiesSection.createValueElement( + value, wasThrown, showPreview, parentElement, linkifier); } /** * @param {!SDK.RemoteObject} value * @param {boolean} wasThrown + * @param {boolean} showPreview * @param {!Element=} parentElement * @param {!Components.Linkifier=} linkifier * @return {!Element} */ - static createValueElement(value, wasThrown, parentElement, linkifier) { + static createValueElement(value, wasThrown, showPreview, parentElement, linkifier) { var valueElement; var type = value.type; var subtype = value.subtype; @@ -261,8 +265,13 @@ Components.ObjectPropertiesSection = class extends UI.TreeOutlineInShadow { parentElement.classList.add('hbox'); } else { valueElement = createElementWithClass('span', 'object-value-' + (subtype || type)); - valueElement.setTextContentTruncatedIfNeeded(description); valueElement.title = description || ''; + if (Runtime.experiments.isEnabled('objectPreviews') && value.preview && showPreview) { + var previewFormatter = new Components.RemoteObjectPreviewFormatter(); + previewFormatter.appendObjectPreview(valueElement, value.preview, false /* isEntry */); + } else { + valueElement.setTextContentTruncatedIfNeeded(description); + } } if (wasThrown) { @@ -546,10 +555,11 @@ Components.ObjectPropertyTreeElement = class extends UI.TreeElement { treeElement, properties, internalProperties, skipProto, targetValue || value, linkifier, emptyPlaceholder); } + var generatePreview = Runtime.experiments.isEnabled('objectPreviews'); if (flattenProtoChain) - value.getAllProperties(false, callback); + value.getAllProperties(false, generatePreview, callback); else - SDK.RemoteObject.loadFromObjectPerProto(value, callback); + SDK.RemoteObject.loadFromObjectPerProto(value, generatePreview, callback); } /** @@ -781,8 +791,9 @@ Components.ObjectPropertyTreeElement = class extends UI.TreeElement { separatorElement.textContent = ': '; if (this.property.value) { + var showPreview = this.property.name !== '__proto__'; this.valueElement = Components.ObjectPropertiesSection.createValueElementWithCustomSupport( - this.property.value, this.property.wasThrown, this.listItemElement, this._linkifier); + this.property.value, this.property.wasThrown, showPreview, this.listItemElement, this._linkifier); this.valueElement.addEventListener('contextmenu', this._contextMenuFired.bind(this, this.property), false); } else if (this.property.getter) { this.valueElement = Components.ObjectPropertyTreeElement.createRemoteObjectAccessorPropertySpan( @@ -1166,7 +1177,8 @@ Components.ArrayGroupingTreeElement = class extends UI.TreeElement { function processArrayFragment(arrayFragment, wasThrown) { if (!arrayFragment || wasThrown) return; - arrayFragment.getAllProperties(false, processProperties.bind(this)); + arrayFragment.getAllProperties( + false, Runtime.experiments.isEnabled('objectPreviews'), processProperties.bind(this)); } /** @this {Components.ArrayGroupingTreeElement} */ @@ -1224,7 +1236,7 @@ Components.ArrayGroupingTreeElement = class extends UI.TreeElement { function processObjectFragment(arrayFragment, wasThrown) { if (!arrayFragment || wasThrown) return; - arrayFragment.getOwnProperties(processProperties.bind(this)); + arrayFragment.getOwnProperties(Runtime.experiments.isEnabled('objectPreviews'), processProperties.bind(this)); } /** diff --git a/front_end/components/RemoteObjectPreviewFormatter.js b/front_end/components/RemoteObjectPreviewFormatter.js index 30cb1a700e..8266ac35ff 100644 --- a/front_end/components/RemoteObjectPreviewFormatter.js +++ b/front_end/components/RemoteObjectPreviewFormatter.js @@ -21,27 +21,43 @@ Components.RemoteObjectPreviewFormatter = class { /** * @param {!Element} parentElement * @param {!Protocol.Runtime.ObjectPreview} preview + * @param {boolean} isEntry */ - appendObjectPreview(parentElement, preview) { + appendObjectPreview(parentElement, preview, isEntry) { + const previewExperimentEnabled = Runtime.experiments.isEnabled('objectPreviews'); var description = preview.description; - if (preview.type !== 'object' || preview.subtype === 'null') { + if (preview.type !== 'object' || preview.subtype === 'null' || (previewExperimentEnabled && isEntry)) { parentElement.appendChild(this.renderPropertyPreview(preview.type, preview.subtype, description)); return; } - if (description && preview.subtype !== 'array') - parentElement.createTextChildren(description, ' '); + const isArrayOrTypedArray = preview.subtype === 'array' || preview.subtype === 'typedarray'; + if (description) { + if (previewExperimentEnabled) { + // Hide the description for plain objects and plain arrays. + const plainObjectDescription = 'Object'; + const size = SDK.RemoteObject.arrayLength(preview) || SDK.RemoteObject.mapOrSetEntriesCount(preview); + var text = preview.subtype === 'typedarray' ? SDK.RemoteObject.arrayNameFromDescription(description) : ''; + if (isArrayOrTypedArray) + text += size > 1 ? ('(' + size + ')') : ''; + else + text = description === plainObjectDescription ? '' : description; + if (text.length > 0) + parentElement.createChild('span', 'object-description').textContent = text + ' '; + } else if (preview.subtype !== 'array') { + parentElement.createTextChildren(description, ' '); + } + } - var isArray = preview.subtype === 'array' || preview.subtype === 'typedarray'; - parentElement.createTextChild(isArray ? '[' : '{'); + parentElement.createTextChild(isArrayOrTypedArray ? '[' : '{'); if (preview.entries) this._appendEntriesPreview(parentElement, preview); - else if (isArray) + else if (isArrayOrTypedArray) this._appendArrayPropertiesPreview(parentElement, preview); else this._appendObjectPropertiesPreview(parentElement, preview); if (preview.overflow) parentElement.createChild('span').textContent = '\u2026'; - parentElement.createTextChild(isArray ? ']' : '}'); + parentElement.createTextChild(isArrayOrTypedArray ? ']' : '}'); } /** @@ -164,10 +180,10 @@ Components.RemoteObjectPreviewFormatter = class { var entry = preview.entries[i]; if (entry.key) { - this.appendObjectPreview(parentElement, entry.key); + this.appendObjectPreview(parentElement, entry.key, true /* isEntry */); parentElement.createTextChild(' => '); } - this.appendObjectPreview(parentElement, entry.value); + this.appendObjectPreview(parentElement, entry.value, true /* isEntry */); } } diff --git a/front_end/components/objectValue.css b/front_end/components/objectValue.css index 828ec16dc7..cfd403e469 100644 --- a/front_end/components/objectValue.css +++ b/front_end/components/objectValue.css @@ -98,3 +98,7 @@ :host-context(.-theme-with-dark-background) .object-value-boolean { color: hsl(252, 100%, 75%); } + +.object-description { + color: gray; +} diff --git a/front_end/console/ConsolePrompt.js b/front_end/console/ConsolePrompt.js index 5351fa7ec4..11d9e3305c 100644 --- a/front_end/console/ConsolePrompt.js +++ b/front_end/console/ConsolePrompt.js @@ -264,7 +264,7 @@ Console.ConsolePrompt = class extends UI.Widget { var excludedTokens = new Set(['js-comment', 'js-string-2', 'js-def']); var trimmedBefore = before.trim(); - if (!trimmedBefore.endsWith('[')) + if (!trimmedBefore.endsWith('[') && !trimmedBefore.match(/\.\s*(get|set|delete)\s*\(\s*$/)) excludedTokens.add('js-string'); if (!trimmedBefore.endsWith('.')) excludedTokens.add('js-property'); diff --git a/front_end/console/ConsoleViewMessage.js b/front_end/console/ConsoleViewMessage.js index 8c62058d64..d62855f8b2 100644 --- a/front_end/console/ConsoleViewMessage.js +++ b/front_end/console/ConsoleViewMessage.js @@ -249,6 +249,10 @@ Console.ConsoleViewMessage = class { } else { if (consoleMessage.source === SDK.ConsoleMessage.MessageSource.Violation) messageText = Common.UIString('[Violation] %s', messageText); + else if (consoleMessage.source === SDK.ConsoleMessage.MessageSource.Intervention) + messageText = Common.UIString('[Intervention] %s', messageText); + if (consoleMessage.source === SDK.ConsoleMessage.MessageSource.Deprecation) + messageText = Common.UIString('[Deprecation] %s', messageText); var args = consoleMessage.parameters || [messageText]; messageElement = this._format(args); } @@ -330,7 +334,7 @@ Console.ConsoleViewMessage = class { * @param {boolean} expand */ function expandStackTrace(expand) { - icon.setIconType(expand ? 'smallicon-triangle-bottom' : 'smallicon-triangle-right'); + icon.setIconType(expand ? 'smallicon-triangle-down' : 'smallicon-triangle-right'); stackTraceElement.classList.toggle('hidden', !expand); } @@ -526,7 +530,7 @@ Console.ConsoleViewMessage = class { var titleElement = createElement('span'); if (includePreview && obj.preview) { titleElement.classList.add('console-object-preview'); - this._previewFormatter.appendObjectPreview(titleElement, obj.preview); + this._previewFormatter.appendObjectPreview(titleElement, obj.preview, false /* isEntry */); } else if (obj.type === 'function') { Components.ObjectPropertiesSection.formatObjectAsFunction(obj, titleElement, false); titleElement.classList.add('object-value-function'); @@ -939,6 +943,18 @@ Console.ConsoleViewMessage = class { break; } + // Render verbose and info deprecations, interventions and violations with warning background. + if (this._message.level === SDK.ConsoleMessage.MessageLevel.Verbose || + this._message.level === SDK.ConsoleMessage.MessageLevel.Info) { + switch (this._message.source) { + case SDK.ConsoleMessage.MessageSource.Violation: + case SDK.ConsoleMessage.MessageSource.Deprecation: + case SDK.ConsoleMessage.MessageSource.Intervention: + this._element.classList.add('console-warning-level'); + break; + } + } + this._element.appendChild(this.contentElement()); if (this._repeatCount > 1) this._showRepeatCountElement(); @@ -1179,7 +1195,7 @@ Console.ConsoleGroupViewMessage = class extends Console.ConsoleViewMessage { setCollapsed(collapsed) { this._collapsed = collapsed; if (this._expandGroupIcon) - this._expandGroupIcon.setIconType(this._collapsed ? 'smallicon-triangle-right' : 'smallicon-triangle-bottom'); + this._expandGroupIcon.setIconType(this._collapsed ? 'smallicon-triangle-right' : 'smallicon-triangle-down'); } /** diff --git a/front_end/data_grid/DataGrid.js b/front_end/data_grid/DataGrid.js index 0c97113512..51ac5eef46 100644 --- a/front_end/data_grid/DataGrid.js +++ b/front_end/data_grid/DataGrid.js @@ -168,7 +168,9 @@ DataGrid.DataGrid = class extends Common.Object { if (column.sortable) { cell.addEventListener('click', this._clickInHeaderCell.bind(this), false); cell.classList.add('sortable'); - cell.createChild('div', 'sort-order-icon-container').createChild('div', 'sort-order-icon'); + var icon = UI.Icon.create('', 'sort-order-icon'); + cell.createChild('div', 'sort-order-icon-container').appendChild(icon); + cell[DataGrid.DataGrid._sortIconSymbol] = icon; } } @@ -903,6 +905,9 @@ DataGrid.DataGrid = class extends Common.Object { this._sortColumnCell = cell; cell.classList.add(sortOrder); + var icon = cell[DataGrid.DataGrid._sortIconSymbol]; + icon.setIconType( + sortOrder === DataGrid.DataGrid.Order.Ascending ? 'smallicon-triangle-up' : 'smallicon-triangle-down'); this.dispatchEventToListeners(DataGrid.DataGrid.Events.SortingChanged); } @@ -1186,6 +1191,7 @@ DataGrid.DataGrid.Align = { DataGrid.DataGrid._preferredWidthSymbol = Symbol('preferredWidth'); DataGrid.DataGrid._columnIdSymbol = Symbol('columnId'); +DataGrid.DataGrid._sortIconSymbol = Symbol('sortIcon'); DataGrid.DataGrid.ColumnResizePadding = 24; DataGrid.DataGrid.CenterResizerOverBorderAdjustment = 3; diff --git a/front_end/data_grid/dataGrid.css b/front_end/data_grid/dataGrid.css index 573b9698d6..1036d2379a 100644 --- a/front_end/data_grid/dataGrid.css +++ b/front_end/data_grid/dataGrid.css @@ -159,28 +159,12 @@ .data-grid th .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; } -@media (-webkit-min-device-pixel-ratio: 1.1) { -.data-grid th .sort-order-icon { - background-image: url(Images/toolbarButtonGlyphs_2x.png); -} -} /* media */ - -.data-grid th.sort-ascending .sort-order-icon { - display: block; - background-position: -4px -111px; -} - +.data-grid th.sort-ascending .sort-order-icon, .data-grid th.sort-descending .sort-order-icon { display: block; - background-position: -20px -99px; } .data-grid th:hover { diff --git a/front_end/devices/devicesView.css b/front_end/devices/devicesView.css index f06194cfd0..4a36894c95 100644 --- a/front_end/devices/devicesView.css +++ b/front_end/devices/devicesView.css @@ -76,6 +76,7 @@ background-color: #f3f3f3; flex: none; padding: 3px 10px; + overflow: hidden; } .devices-footer > span { @@ -83,7 +84,8 @@ } .discovery-view { - overflow: auto; + overflow-x: hidden; + overflow-y: auto; padding: 15px 15px 0px 0px; } diff --git a/front_end/elements/PropertiesWidget.js b/front_end/elements/PropertiesWidget.js index 1bf84cd682..a262639cd5 100644 --- a/front_end/elements/PropertiesWidget.js +++ b/front_end/elements/PropertiesWidget.js @@ -108,7 +108,7 @@ Elements.PropertiesWidget = class extends UI.ThrottledWidget { if (!result.object || result.wasThrown) return; - var promise = result.object.getOwnPropertiesPromise().then(fillSection.bind(this)); + var promise = result.object.getOwnPropertiesPromise(false /* generatePreview */).then(fillSection.bind(this)); result.object.release(); return promise; } diff --git a/front_end/elements/StylesSidebarPane.js b/front_end/elements/StylesSidebarPane.js index 16b841aefd..daaccd60e7 100644 --- a/front_end/elements/StylesSidebarPane.js +++ b/front_end/elements/StylesSidebarPane.js @@ -2127,7 +2127,7 @@ Elements.StylePropertyTreeElement = class extends UI.TreeElement { _updateExpandElement() { if (this.expanded) - this._expandElement.setIconType('smallicon-triangle-bottom'); + this._expandElement.setIconType('smallicon-triangle-down'); else this._expandElement.setIconType('smallicon-triangle-right'); } diff --git a/front_end/main/Main.js b/front_end/main/Main.js index bf62f568e8..f8f49530cb 100644 --- a/front_end/main/Main.js +++ b/front_end/main/Main.js @@ -104,6 +104,7 @@ Main.Main = class { Runtime.experiments.register('inputEventsOnTimelineOverview', 'Input events on Timeline overview', true); Runtime.experiments.register('liveSASS', 'Live SASS'); Runtime.experiments.register('networkGroupingRequests', 'Network request groups support', true); + Runtime.experiments.register('objectPreviews', 'Object previews', true); Runtime.experiments.register('persistence2', 'Persistence 2.0'); Runtime.experiments.register('persistenceValidation', 'Validate persistence bindings'); Runtime.experiments.register('requestBlocking', 'Request blocking', true); diff --git a/front_end/network/NetworkLogViewColumns.js b/front_end/network/NetworkLogViewColumns.js index 7dc4d44a27..7d2e6ad138 100644 --- a/front_end/network/NetworkLogViewColumns.js +++ b/front_end/network/NetworkLogViewColumns.js @@ -203,8 +203,9 @@ Network.NetworkLogViewColumns = class { 'contextmenu', event => this._innerHeaderContextMenu(new UI.ContextMenu(event))); var innerElement = this._waterfallHeaderElement.createChild('div'); innerElement.textContent = Common.UIString('Waterfall'); - this._waterfallColumnSortIcon = this._waterfallHeaderElement.createChild('div', 'sort-order-icon-container') - .createChild('div', 'sort-order-icon'); + this._waterfallColumnSortIcon = UI.Icon.create('', 'sort-order-icon'); + this._waterfallHeaderElement.createChild('div', 'sort-order-icon-container') + .appendChild(this._waterfallColumnSortIcon); /** * @this {Network.NetworkLogViewColumns} @@ -264,18 +265,17 @@ Network.NetworkLogViewColumns = class { this._networkLogView.removeAllNodeHighlights(); this._waterfallRequestsAreStale = true; if (columnId === 'waterfall') { - this._waterfallColumnSortIcon.classList.remove('sort-ascending', 'sort-descending'); - if (this._dataGrid.sortOrder() === DataGrid.DataGrid.Order.Ascending) - this._waterfallColumnSortIcon.classList.add('sort-ascending'); + this._waterfallColumnSortIcon.setIconType('smallicon-triangle-up'); else - this._waterfallColumnSortIcon.classList.add('sort-descending'); + this._waterfallColumnSortIcon.setIconType('smallicon-triangle-down'); var sortFunction = Network.NetworkRequestNode.RequestPropertyComparator.bind(null, this._activeWaterfallSortId); this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscending()); this._networkLogView.dataGridSorted(); return; } + this._waterfallColumnSortIcon.setIconType(''); var columnConfig = this._columns.find(columnConfig => columnConfig.id === columnId); if (!columnConfig || !columnConfig.sortingFunction) diff --git a/front_end/network/networkLogView.css b/front_end/network/networkLogView.css index 73a9653197..133f21c376 100644 --- a/front_end/network/networkLogView.css +++ b/front_end/network/networkLogView.css @@ -376,25 +376,9 @@ bottom: 1px; display: flex; align-items: center; - padding-left: 0px; } .network-waterfall-header .sort-order-icon { + align-items: center; margin-right: 4px; - background-image: url(Images/toolbarButtonGlyphs.png); - background-size: 352px 168px; - opacity: 0.5; - width: 8px; - height: 7px; - display: none; -} - -.network-waterfall-header .sort-ascending.sort-order-icon { - display: block; - background-position: -4px -111px; -} - -.network-waterfall-header .sort-descending.sort-order-icon { - display: block; - background-position: -20px -99px; } diff --git a/front_end/resources/ResourcesPanel.js b/front_end/resources/ResourcesPanel.js index 58a3e642cd..f2efc635a2 100644 --- a/front_end/resources/ResourcesPanel.js +++ b/front_end/resources/ResourcesPanel.js @@ -639,9 +639,10 @@ Resources.ResourcesPanel = class extends UI.PanelWithSidebar { this.visibleView = view; this._storageViewToolbar.removeToolbarItems(); - var toolbarItems = view instanceof UI.SimpleView ? view.syncToolbarItems() : null; - for (var i = 0; toolbarItems && i < toolbarItems.length; ++i) + var toolbarItems = (view instanceof UI.SimpleView && view.syncToolbarItems()) || []; + for (var i = 0; i < toolbarItems.length; ++i) this._storageViewToolbar.appendToolbarItem(toolbarItems[i]); + this._storageViewToolbar.element.classList.toggle('hidden', !toolbarItems.length); } closeVisibleView() { diff --git a/front_end/sdk/DebuggerModel.js b/front_end/sdk/DebuggerModel.js index 4de10f2156..512b6f83b5 100644 --- a/front_end/sdk/DebuggerModel.js +++ b/front_end/sdk/DebuggerModel.js @@ -684,7 +684,8 @@ SDK.DebuggerModel = class extends SDK.SDKModel { * @return {!Promise} */ functionDetailsPromise(remoteObject) { - return remoteObject.getAllPropertiesPromise(/* accessorPropertiesOnly */ false).then(buildDetails.bind(this)); + return remoteObject.getAllPropertiesPromise(false /* accessorPropertiesOnly */, false /* generatePreview */) + .then(buildDetails.bind(this)); /** * @param {!{properties: ?Array., internalProperties: ?Array.}} response @@ -874,6 +875,7 @@ SDK.DebuggerModel.BreakReason = { PromiseRejection: 'promiseRejection', Assert: 'assert', DebugCommand: 'debugCommand', + OOM: 'OOM', Other: 'other' }; diff --git a/front_end/sdk/RemoteObject.js b/front_end/sdk/RemoteObject.js index efc30fd9f8..591eb5f585 100644 --- a/front_end/sdk/RemoteObject.js +++ b/front_end/sdk/RemoteObject.js @@ -63,6 +63,15 @@ SDK.RemoteObject = class { return remoteObject.type; } + /** + * @param {string} description + * @return {string} + */ + static arrayNameFromDescription(description) { + return description.replace(SDK.RemoteObject._descriptionLengthParenRegex, '') + .replace(SDK.RemoteObject._descriptionLengthSquareRegex, ''); + } + /** * @param {!SDK.RemoteObject|!Protocol.Runtime.RemoteObject|!Protocol.Runtime.ObjectPreview} object * @return {number} @@ -72,7 +81,19 @@ SDK.RemoteObject = class { return 0; // Array lengths in V8-generated descriptions switched from square brackets to parentheses. // Both formats are checked in case the front end is dealing with an old version of V8. - var matches = object.description.match(/\[([0-9]+)\]/) || object.description.match(/\(([0-9]+)\)/); + var parenMatches = object.description.match(SDK.RemoteObject._descriptionLengthParenRegex); + var squareMatches = object.description.match(SDK.RemoteObject._descriptionLengthSquareRegex); + return parenMatches ? parseInt(parenMatches[1], 10) : (squareMatches ? parseInt(squareMatches[1], 10) : 0); + } + + /** + * @param {!Protocol.Runtime.ObjectPreview} preview + * @return {number} + */ + static mapOrSetEntriesCount(preview) { + if (preview.subtype !== 'map' && preview.subtype !== 'set') + return 0; + var matches = preview.description.match(SDK.RemoteObject._descriptionLengthParenRegex); if (!matches) return 0; return parseInt(matches[1], 10); @@ -119,9 +140,10 @@ SDK.RemoteObject = class { /** * @param {!SDK.RemoteObject} object + * @param {boolean} generatePreview * @param {function(?Array., ?Array.)} callback */ - static loadFromObjectPerProto(object, callback) { + static loadFromObjectPerProto(object, generatePreview, callback) { // Combines 2 asynch calls. Doesn't rely on call-back orders (some calls may be loop-back). var savedOwnProperties; var savedAccessorProperties; @@ -177,8 +199,8 @@ SDK.RemoteObject = class { processCallback(); } - object.getAllProperties(true, allAccessorPropertiesCallback); - object.getOwnProperties(ownPropertiesCallback); + object.getAllProperties(true /* accessorPropertiesOnly */, generatePreview, allAccessorPropertiesCallback); + object.getOwnProperties(generatePreview, ownPropertiesCallback); } /** @@ -216,16 +238,18 @@ SDK.RemoteObject = class { } /** + * @param {boolean} generatePreview * @param {function(?Array., ?Array.)} callback */ - getOwnProperties(callback) { + getOwnProperties(generatePreview, callback) { throw 'Not implemented'; } /** + * @param {boolean} generatePreview * @return {!Promise, internalProperties: ?Array.}>} */ - getOwnPropertiesPromise() { + getOwnPropertiesPromise(generatePreview) { return new Promise(promiseConstructor.bind(this)); /** @@ -233,7 +257,7 @@ SDK.RemoteObject = class { * @this {SDK.RemoteObject} */ function promiseConstructor(success) { - this.getOwnProperties(getOwnPropertiesCallback.bind(null, success)); + this.getOwnProperties(!!generatePreview, getOwnPropertiesCallback.bind(null, success)); } /** @@ -248,17 +272,19 @@ SDK.RemoteObject = class { /** * @param {boolean} accessorPropertiesOnly + * @param {boolean} generatePreview * @param {function(?Array, ?Array)} callback */ - getAllProperties(accessorPropertiesOnly, callback) { + getAllProperties(accessorPropertiesOnly, generatePreview, callback) { throw 'Not implemented'; } /** * @param {boolean} accessorPropertiesOnly + * @param {boolean} generatePreview * @return {!Promise, internalProperties: ?Array}>} */ - getAllPropertiesPromise(accessorPropertiesOnly) { + getAllPropertiesPromise(accessorPropertiesOnly, generatePreview) { return new Promise(promiseConstructor.bind(this)); /** @@ -266,7 +292,7 @@ SDK.RemoteObject = class { * @this {SDK.RemoteObject} */ function promiseConstructor(success) { - this.getAllProperties(accessorPropertiesOnly, getAllPropertiesCallback.bind(null, success)); + this.getAllProperties(accessorPropertiesOnly, generatePreview, getAllPropertiesCallback.bind(null, success)); } /** @@ -494,19 +520,21 @@ SDK.RemoteObjectImpl = class extends SDK.RemoteObject { /** * @override + * @param {boolean} generatePreview * @param {function(?Array., ?Array.)} callback */ - getOwnProperties(callback) { - this.doGetProperties(true, false, false, callback); + getOwnProperties(generatePreview, callback) { + this.doGetProperties(true, false, generatePreview, callback); } /** * @override * @param {boolean} accessorPropertiesOnly + * @param {boolean} generatePreview * @param {function(?Array., ?Array.)} callback */ - getAllProperties(accessorPropertiesOnly, callback) { - this.doGetProperties(false, accessorPropertiesOnly, false, callback); + getAllProperties(accessorPropertiesOnly, generatePreview, callback) { + this.doGetProperties(false, accessorPropertiesOnly, generatePreview, callback); } /** @@ -1125,18 +1153,20 @@ SDK.LocalJSONObject = class extends SDK.RemoteObject { /** * @override + * @param {boolean} generatePreview * @param {function(?Array., ?Array.)} callback */ - getOwnProperties(callback) { + getOwnProperties(generatePreview, callback) { callback(this._children(), null); } /** * @override * @param {boolean} accessorPropertiesOnly + * @param {boolean} generatePreview * @param {function(?Array., ?Array.)} callback */ - getAllProperties(accessorPropertiesOnly, callback) { + getAllProperties(accessorPropertiesOnly, generatePreview, callback) { if (accessorPropertiesOnly) callback([], null); else @@ -1368,7 +1398,7 @@ SDK.RemoteFunction = class { * @return {!Promise} */ targetFunction() { - return this._object.getOwnPropertiesPromise().then(targetFunction.bind(this)); + return this._object.getOwnPropertiesPromise(false /* generatePreview */).then(targetFunction.bind(this)); /** * @param {!{properties: ?Array, internalProperties: ?Array}} ownProperties @@ -1423,3 +1453,15 @@ SDK.RemoteFunction = class { return this._object; } }; + +/** + * @const + * @type {!RegExp} + */ +SDK.RemoteObject._descriptionLengthParenRegex = /\(([0-9]+)\)/; + +/** + * @const + * @type {!RegExp} + */ +SDK.RemoteObject._descriptionLengthSquareRegex = /\[([0-9]+)\]/; diff --git a/front_end/sources/DebuggerPausedMessage.js b/front_end/sources/DebuggerPausedMessage.js index b2c3ed141f..0fa179c364 100644 --- a/front_end/sources/DebuggerPausedMessage.js +++ b/front_end/sources/DebuggerPausedMessage.js @@ -51,6 +51,8 @@ Sources.DebuggerPausedMessage = class { messageWrapper = buildWrapper(Common.UIString('Paused on assertion')); } else if (details.reason === SDK.DebuggerModel.BreakReason.DebugCommand) { messageWrapper = buildWrapper(Common.UIString('Paused on debugged function')); + } else if (details.reason === SDK.DebuggerModel.BreakReason.OOM) { + messageWrapper = buildWrapper(Common.UIString('Paused before potential out-of-memory crash')); } else if (details.callFrames.length) { var uiLocation = debuggerWorkspaceBinding.rawLocationToUILocation(details.callFrames[0].location()); var breakpoint = uiLocation ? @@ -65,7 +67,7 @@ Sources.DebuggerPausedMessage = class { var errorLike = details.reason === SDK.DebuggerModel.BreakReason.Exception || details.reason === SDK.DebuggerModel.BreakReason.PromiseRejection || - details.reason === SDK.DebuggerModel.BreakReason.Assert; + details.reason === SDK.DebuggerModel.BreakReason.Assert || details.reason === SDK.DebuggerModel.BreakReason.OOM; status.classList.toggle('error-reason', errorLike); if (messageWrapper) status.appendChild(messageWrapper); diff --git a/front_end/sources/JavaScriptSourceFrame.js b/front_end/sources/JavaScriptSourceFrame.js index 22513acb29..ba9737d2db 100644 --- a/front_end/sources/JavaScriptSourceFrame.js +++ b/front_end/sources/JavaScriptSourceFrame.js @@ -571,7 +571,7 @@ Sources.JavaScriptSourceFrame = class extends SourceFrame.UISourceCodeFrame { var functionLocation = callFrame.functionLocation(); if (localScope && functionLocation) { Sources.SourceMapNamesResolver.resolveScopeInObject(localScope) - .getAllProperties(false, this._prepareScopeVariables.bind(this, callFrame)); + .getAllProperties(false, false, this._prepareScopeVariables.bind(this, callFrame)); } if (this._clearValueWidgetsTimer) { @@ -689,10 +689,12 @@ Sources.JavaScriptSourceFrame = class extends SourceFrame.UISourceCodeFrame { var value = valuesMap.get(name); var propertyCount = value.preview ? value.preview.properties.length : 0; var entryCount = value.preview && value.preview.entries ? value.preview.entries.length : 0; - if (value.preview && propertyCount + entryCount < 10) - formatter.appendObjectPreview(nameValuePair, value.preview); - else - nameValuePair.appendChild(Components.ObjectPropertiesSection.createValueElement(value, false)); + if (value.preview && propertyCount + entryCount < 10) { + formatter.appendObjectPreview(nameValuePair, value.preview, false /* isEntry */); + } else { + nameValuePair.appendChild(Components.ObjectPropertiesSection.createValueElement( + value, false /* wasThrown */, false /* showPreview */)); + } ++renderedNameCount; } diff --git a/front_end/sources/SourceMapNamesResolver.js b/front_end/sources/SourceMapNamesResolver.js index 2649b9bf67..11fc49fa3c 100644 --- a/front_end/sources/SourceMapNamesResolver.js +++ b/front_end/sources/SourceMapNamesResolver.js @@ -403,18 +403,20 @@ Sources.SourceMapNamesResolver.RemoteObject = class extends SDK.RemoteObject { /** * @override + * @param {boolean} generatePreview * @param {function(?Array., ?Array.)} callback */ - getOwnProperties(callback) { - this._object.getOwnProperties(callback); + getOwnProperties(generatePreview, callback) { + this._object.getOwnProperties(generatePreview, callback); } /** * @override * @param {boolean} accessorPropertiesOnly + * @param {boolean} generatePreview * @param {function(?Array, ?Array)} callback */ - getAllProperties(accessorPropertiesOnly, callback) { + getAllProperties(accessorPropertiesOnly, generatePreview, callback) { /** * @param {?Array.} properties * @param {?Array.} internalProperties @@ -445,7 +447,7 @@ Sources.SourceMapNamesResolver.RemoteObject = class extends SDK.RemoteObject { callback(newProperties, internalProperties); } - this._object.getAllProperties(accessorPropertiesOnly, wrappedCallback.bind(this)); + this._object.getAllProperties(accessorPropertiesOnly, generatePreview, wrappedCallback.bind(this)); } /** diff --git a/front_end/sources/WatchExpressionsSidebarPane.js b/front_end/sources/WatchExpressionsSidebarPane.js index fe488fbcd4..4982a85afa 100644 --- a/front_end/sources/WatchExpressionsSidebarPane.js +++ b/front_end/sources/WatchExpressionsSidebarPane.js @@ -335,7 +335,7 @@ Sources.WatchExpression = class extends Common.Object { this._valueElement.textContent = Common.UIString(''); } else { this._valueElement = Components.ObjectPropertiesSection.createValueElementWithCustomSupport( - result, !!exceptionDetails, titleElement, this._linkifier); + result, !!exceptionDetails, false /* showPreview */, titleElement, this._linkifier); } var separatorElement = createElementWithClass('span', 'watch-expressions-separator'); separatorElement.textContent = ': '; diff --git a/front_end/timeline/TimelinePanel.js b/front_end/timeline/TimelinePanel.js index 17940aec4e..036bdaeef9 100644 --- a/front_end/timeline/TimelinePanel.js +++ b/front_end/timeline/TimelinePanel.js @@ -805,10 +805,10 @@ Timeline.TimelinePanel = class extends UI.Panel { var markers = new Map(); var recordTypes = TimelineModel.TimelineModel.RecordType; var zeroTime = this._model.minimumRecordTime(); - for (var record of this._model.eventDividerRecords()) { - if (record.type() === recordTypes.TimeStamp || record.type() === recordTypes.ConsoleTime) + for (var event of this._model.eventDividers()) { + if (event.name === recordTypes.TimeStamp || event.name === recordTypes.ConsoleTime) continue; - markers.set(record.startTime(), Timeline.TimelineUIUtils.createDividerForRecord(record, zeroTime, 0)); + markers.set(event.startTime, Timeline.TimelineUIUtils.createEventDivider(event, zeroTime)); } this._overviewPane.setMarkers(markers); } @@ -1103,26 +1103,26 @@ Timeline.TimelinePanel = class extends UI.Panel { function findLowUtilizationRegion(startIndex, stopIndex) { var /** @const */ threshold = 0.1; var cutIndex = startIndex; - var cutTime = (tasks[cutIndex].startTime() + tasks[cutIndex].endTime()) / 2; + var cutTime = (tasks[cutIndex].startTime + tasks[cutIndex].endTime) / 2; var usedTime = 0; var step = Math.sign(stopIndex - startIndex); for (var i = startIndex; i !== stopIndex; i += step) { var task = tasks[i]; - var taskTime = (task.startTime() + task.endTime()) / 2; + var taskTime = (task.startTime + task.endTime) / 2; var interval = Math.abs(cutTime - taskTime); if (usedTime < threshold * interval) { cutIndex = i; cutTime = taskTime; usedTime = 0; } - usedTime += task.endTime() - task.startTime(); + usedTime += task.duration; } return cutIndex; } var rightIndex = findLowUtilizationRegion(tasks.length - 1, 0); var leftIndex = findLowUtilizationRegion(0, rightIndex); - var leftTime = tasks[leftIndex].startTime(); - var rightTime = tasks[rightIndex].endTime(); + var leftTime = tasks[leftIndex].startTime; + var rightTime = tasks[rightIndex].endTime; var span = rightTime - leftTime; var totalSpan = this._tracingModel.maximumRecordTime() - this._tracingModel.minimumRecordTime(); if (span < totalSpan * 0.1) { diff --git a/front_end/timeline/TimelineUIUtils.js b/front_end/timeline/TimelineUIUtils.js index d50a11ce77..fa83cb8470 100644 --- a/front_end/timeline/TimelineUIUtils.js +++ b/front_end/timeline/TimelineUIUtils.js @@ -256,14 +256,6 @@ Timeline.TimelineUIUtils = class { return regExp.test(tokens.join('|')); } - /** - * @param {!TimelineModel.TimelineModel.Record} record - * @return {!Timeline.TimelineCategory} - */ - static categoryForRecord(record) { - return Timeline.TimelineUIUtils.eventStyle(record.traceEvent()).category; - } - /** * @param {!SDK.TracingModel.Event} event * @return {!{title: string, category: !Timeline.TimelineCategory}} @@ -939,42 +931,7 @@ Timeline.TimelineUIUtils = class { * @return {!DocumentFragment} */ static buildRangeStats(model, startTime, endTime) { - var aggregatedStats = {}; - - /** - * @param {number} value - * @param {!TimelineModel.TimelineModel.Record} task - * @return {number} - */ - function compareEndTime(value, task) { - return value < task.endTime() ? -1 : 1; - } - var mainThreadTasks = model.mainThreadTasks(); - var taskIndex = mainThreadTasks.lowerBound(startTime, compareEndTime); - for (; taskIndex < mainThreadTasks.length; ++taskIndex) { - var task = mainThreadTasks[taskIndex]; - if (task.startTime() > endTime) - break; - if (task.startTime() > startTime && task.endTime() < endTime) { - // cache stats for top-level entries that fit the range entirely. - var taskStats = task[Timeline.TimelineUIUtils._aggregatedStatsKey]; - if (!taskStats) { - taskStats = {}; - Timeline.TimelineUIUtils._collectAggregatedStatsForRecord(task, startTime, endTime, taskStats); - task[Timeline.TimelineUIUtils._aggregatedStatsKey] = taskStats; - } - for (var key in taskStats) - aggregatedStats[key] = (aggregatedStats[key] || 0) + taskStats[key]; - continue; - } - Timeline.TimelineUIUtils._collectAggregatedStatsForRecord(task, startTime, endTime, aggregatedStats); - } - - var aggregatedTotal = 0; - for (var categoryName in aggregatedStats) - aggregatedTotal += aggregatedStats[categoryName]; - aggregatedStats['idle'] = Math.max(0, endTime - startTime - aggregatedTotal); - + var aggregatedStats = Timeline.TimelineUIUtils.statsForTimeRange(model, startTime, endTime); var startOffset = startTime - model.minimumRecordTime(); var endOffset = endTime - model.minimumRecordTime(); @@ -987,27 +944,116 @@ Timeline.TimelineUIUtils = class { } /** - * @param {!TimelineModel.TimelineModel.Record} record + * @param {!TimelineModel.TimelineModel} model + * @param {number} startTime + * @param {number} endTime + * @return {!Object} + */ + static statsForTimeRange(model, startTime, endTime) { + Timeline.TimelineUIUtils._buildRangeStatsCacheIfNeeded(model); + var tasks = model.mainThreadTasks(); + if (!tasks.length) + return {}; + var statsBeforeIndex = Math.min(tasks.lowerBound(startTime, (time, task) => time - task.endTime), tasks.length - 1); + var statsAfterIndex = Math.min(tasks.lowerBound(endTime, (time, task) => time - task.endTime), tasks.length - 1); + var events = model.mainThreadEvents(); + + var statsAfter = subtractStats( + tasks[statsAfterIndex][Timeline.TimelineUIUtils._categoryBreakdownCacheSymbol], + Timeline.TimelineUIUtils._slowStatsForTimeRange(events, endTime, tasks[statsAfterIndex].endTime)); + var statsBefore = subtractStats( + tasks[statsBeforeIndex][Timeline.TimelineUIUtils._categoryBreakdownCacheSymbol], + Timeline.TimelineUIUtils._slowStatsForTimeRange(events, startTime, tasks[statsBeforeIndex].endTime)); + var aggregatedStats = subtractStats(statsAfter, statsBefore); + + /** + * @param {!Object} a + * @param {!Object} b + * @return {!Object} + */ + function subtractStats(a, b) { + var result = Object.assign({}, a); + for (var key in b) + result[key] -= b[key]; + return result; + } + + var aggregatedTotal = Object.values(aggregatedStats).reduce((a, b) => a + b, 0); + aggregatedStats['idle'] = Math.max(0, endTime - startTime - aggregatedTotal); + return aggregatedStats; + } + + /** + * @param {!Array} events * @param {number} startTime * @param {number} endTime - * @param {!Object} aggregatedStats */ - static _collectAggregatedStatsForRecord(record, startTime, endTime, aggregatedStats) { - if (!record.endTime() || record.endTime() < startTime || record.startTime() > endTime) + static _slowStatsForTimeRange(events, startTime, endTime) { + /** @type {!Object} */ + var stats = {}; + var ownTimes = []; + + TimelineModel.TimelineModel.forEachEvent( + events, onStartEvent, onEndEvent, undefined, startTime, endTime, Timeline.TimelineUIUtils._filterForStats()); + + /** + * @param {!SDK.TracingModel.Event} e + */ + function onStartEvent(e) { + var duration = Math.min(e.endTime, endTime) - Math.max(e.startTime, startTime); + if (ownTimes.length) + ownTimes[ownTimes.length - 1] -= duration; + ownTimes.push(duration); + } + + /** + * @param {!SDK.TracingModel.Event} e + */ + function onEndEvent(e) { + var category = Timeline.TimelineUIUtils.eventStyle(e).category.name; + stats[category] = (stats[category] || 0) + ownTimes.pop(); + } + return stats; + } + + /** + * @return {function(!SDK.TracingModel.Event):boolean} + */ + static _filterForStats() { + var visibleEventsFilter = Timeline.TimelineUIUtils.visibleEventsFilter(); + return event => visibleEventsFilter.accept(event) || SDK.TracingModel.isTopLevelEvent(event); + } + /** + * @param {!TimelineModel.TimelineModel} model + */ + static _buildRangeStatsCacheIfNeeded(model) { + var tasks = model.mainThreadTasks(); + if (tasks.length && tasks[0][Timeline.TimelineUIUtils._categoryBreakdownCacheSymbol]) return; + var aggregatedStats = {}; + var ownTimes = []; + TimelineModel.TimelineModel.forEachEvent( + model.mainThreadEvents(), onStartEvent, onEndEvent, undefined, undefined, undefined, + Timeline.TimelineUIUtils._filterForStats()); - var childrenTime = 0; - var children = record.children() || []; - for (var i = 0; i < children.length; ++i) { - var child = children[i]; - if (!child.endTime() || child.endTime() < startTime || child.startTime() > endTime) - continue; - childrenTime += Math.min(endTime, child.endTime()) - Math.max(startTime, child.startTime()); - Timeline.TimelineUIUtils._collectAggregatedStatsForRecord(child, startTime, endTime, aggregatedStats); + /** + * @param {!SDK.TracingModel.Event} e + */ + function onStartEvent(e) { + if (ownTimes.length) + ownTimes[ownTimes.length - 1] -= e.duration; + ownTimes.push(e.duration); + } + + /** + * @param {!SDK.TracingModel.Event} e + */ + function onEndEvent(e) { + var category = Timeline.TimelineUIUtils.eventStyle(e).category.name; + aggregatedStats[category] = (aggregatedStats[category] || 0) + ownTimes.pop(); + if (!ownTimes.length) + e[Timeline.TimelineUIUtils._categoryBreakdownCacheSymbol] = Object.assign({}, aggregatedStats); } - var categoryName = Timeline.TimelineUIUtils.categoryForRecord(record).name; - var ownTime = Math.min(endTime, record.endTime()) - Math.max(startTime, record.startTime()) - childrenTime; - aggregatedStats[categoryName] = (aggregatedStats[categoryName] || 0) + ownTime; } /** @@ -1347,47 +1393,27 @@ Timeline.TimelineUIUtils = class { } /** - * @param {!TimelineModel.TimelineModel.RecordType} recordType - * @param {?string} title - * @param {number} position + * @param {!SDK.TracingModel.Event} event + * @param {number} zeroTime * @return {!Element} */ - static createEventDivider(recordType, title, position) { - var eventDivider = createElement('div'); - eventDivider.className = 'resources-event-divider'; + static createEventDivider(event, zeroTime) { + var eventDivider = createElementWithClass('div', 'resources-event-divider'); + var startTime = Number.millisToString(event.startTime - zeroTime); + eventDivider.title = Common.UIString('%s at %s', Timeline.TimelineUIUtils.eventTitle(event), startTime); + var recordTypes = TimelineModel.TimelineModel.RecordType; + var name = event.name; + if (name === recordTypes.MarkDOMContent) + eventDivider.classList.add('resources-blue-divider'); + else if (name === recordTypes.MarkLoad) + eventDivider.classList.add('resources-red-divider'); + else if (name === recordTypes.MarkFirstPaint) + eventDivider.classList.add('resources-green-divider'); - if (recordType === recordTypes.MarkDOMContent) - eventDivider.className += ' resources-blue-divider'; - else if (recordType === recordTypes.MarkLoad) - eventDivider.className += ' resources-red-divider'; - else if (recordType === recordTypes.MarkFirstPaint) - eventDivider.className += ' resources-green-divider'; - else if ( - recordType === recordTypes.TimeStamp || recordType === recordTypes.ConsoleTime || - recordType === recordTypes.UserTiming) - eventDivider.className += ' resources-orange-divider'; - else if (recordType === recordTypes.BeginFrame) - eventDivider.className += ' timeline-frame-divider'; - - if (title) - eventDivider.title = title; - eventDivider.style.left = position + 'px'; return eventDivider; } - /** - * @param {!TimelineModel.TimelineModel.Record} record - * @param {number} zeroTime - * @param {number} position - * @return {!Element} - */ - static createDividerForRecord(record, zeroTime, position) { - var startTime = Number.millisToString(record.startTime() - zeroTime); - var title = Common.UIString('%s at %s', Timeline.TimelineUIUtils.eventTitle(record.traceEvent()), startTime); - return Timeline.TimelineUIUtils.createEventDivider(record.type(), title, position); - } - /** * @return {!Array.} */ @@ -2252,3 +2278,5 @@ Timeline.TimelineDetailsContentHelper = class { this.appendElementRow(Common.UIString('Warning'), warning, true); } }; + +Timeline.TimelineUIUtils._categoryBreakdownCacheSymbol = Symbol('categoryBreakdownCache'); diff --git a/front_end/timeline_model/TimelineFrameModel.js b/front_end/timeline_model/TimelineFrameModel.js index 2c3bc724fb..2679215438 100644 --- a/front_end/timeline_model/TimelineFrameModel.js +++ b/front_end/timeline_model/TimelineFrameModel.js @@ -236,24 +236,6 @@ TimelineModel.TimelineFrameModel = class { this._framePendingActivation = null; } - /** - * @param {!Array.} types - * @param {!TimelineModel.TimelineModel.Record} record - * @return {?TimelineModel.TimelineModel.Record} record - */ - _findRecordRecursively(types, record) { - if (types.indexOf(record.type()) >= 0) - return record; - if (!record.children()) - return null; - for (var i = 0; i < record.children().length; ++i) { - var result = this._findRecordRecursively(types, record.children()[i]); - if (result) - return result; - } - return null; - } - /** * @param {?SDK.Target} target * @param {!Array.} events diff --git a/front_end/timeline_model/TimelineModel.js b/front_end/timeline_model/TimelineModel.js index 573e8e4586..4e4a7e7cfe 100644 --- a/front_end/timeline_model/TimelineModel.js +++ b/front_end/timeline_model/TimelineModel.js @@ -41,18 +41,20 @@ TimelineModel.TimelineModel = class { } /** - * @param {!Array.} events + * @param {!Array} events * @param {function(!SDK.TracingModel.Event)} onStartEvent * @param {function(!SDK.TracingModel.Event)} onEndEvent * @param {function(!SDK.TracingModel.Event,?SDK.TracingModel.Event)|undefined=} onInstantEvent * @param {number=} startTime * @param {number=} endTime + * @param {function(!SDK.TracingModel.Event):boolean=} filter */ - static forEachEvent(events, onStartEvent, onEndEvent, onInstantEvent, startTime, endTime) { + static forEachEvent(events, onStartEvent, onEndEvent, onInstantEvent, startTime, endTime, filter) { startTime = startTime || 0; endTime = endTime || Infinity; var stack = []; - for (var i = 0; i < events.length; ++i) { + var startEvent = TimelineModel.TimelineModel._topLevelEventEndingAfter(events, startTime); + for (var i = startEvent; i < events.length; ++i) { var e = events[i]; if ((e.endTime || e.startTime) < startTime) continue; @@ -62,6 +64,8 @@ TimelineModel.TimelineModel = class { continue; while (stack.length && stack.peekLast().endTime <= e.startTime) onEndEvent(stack.pop()); + if (filter && !filter(e)) + continue; if (e.duration) { onStartEvent(e); stack.push(e); @@ -73,6 +77,17 @@ TimelineModel.TimelineModel = class { onEndEvent(stack.pop()); } + /** + * @param {!Array} events + * @param {number} time + */ + static _topLevelEventEndingAfter(events, time) { + var index = events.upperBound(time, (time, event) => time - event.startTime) - 1; + while (index > 0 && !SDK.TracingModel.isTopLevelEvent(events[index])) + index--; + return Math.max(index, 0); + } + /** * @return {!TimelineModel.TimelineModel.RecordType} */ @@ -202,7 +217,6 @@ TimelineModel.TimelineModel = class { this._inspectedTargetEvents.sort(SDK.TracingModel.Event.compareStartTime); this._processBrowserEvents(tracingModel); - this._buildTimelineRecords(); this._buildGPUEvents(tracingModel); this._insertFirstPaintEvent(); this._resetProcessingState(); @@ -311,10 +325,8 @@ TimelineModel.TimelineModel = class { this._mainThreadEvents.splice( this._mainThreadEvents.lowerBound(firstPaintEvent, SDK.TracingModel.Event.compareStartTime), 0, firstPaintEvent); - var firstPaintRecord = new TimelineModel.TimelineModel.Record(firstPaintEvent); - this._eventDividerRecords.splice( - this._eventDividerRecords.lowerBound(firstPaintRecord, TimelineModel.TimelineModel.Record._compareStartTime), 0, - firstPaintRecord); + this._eventDividers.splice( + this._eventDividers.lowerBound(firstPaintEvent, SDK.TracingModel.Event.compareStartTime), 0, firstPaintEvent); } /** @@ -333,26 +345,6 @@ TimelineModel.TimelineModel = class { this._mergeAsyncEvents(this._mainThreadAsyncEventsByGroup, asyncEventsByGroup); } - _buildTimelineRecords() { - var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThreadEvents()); - for (var i = 0; i < topLevelRecords.length; i++) { - var record = topLevelRecords[i]; - if (SDK.TracingModel.isTopLevelEvent(record.traceEvent())) - this._mainThreadTasks.push(record); - } - - /** - * @param {!TimelineModel.TimelineModel.VirtualThread} virtualThread - * @this {!TimelineModel.TimelineModel} - */ - function processVirtualThreadEvents(virtualThread) { - var threadRecords = this._buildTimelineRecordsForThread(virtualThread.events); - topLevelRecords = - topLevelRecords.mergeOrdered(threadRecords, TimelineModel.TimelineModel.Record._compareStartTime); - } - this.virtualThreads().forEach(processVirtualThreadEvents.bind(this)); - } - /** * @param {!SDK.TracingModel} tracingModel */ @@ -364,41 +356,6 @@ TimelineModel.TimelineModel = class { this._gpuEvents = thread.events().filter(event => event.name === gpuEventName); } - /** - * @param {!Array.} threadEvents - * @return {!Array.} - */ - _buildTimelineRecordsForThread(threadEvents) { - var recordStack = []; - var topLevelRecords = []; - - for (var i = 0, size = threadEvents.length; i < size; ++i) { - var event = threadEvents[i]; - for (var top = recordStack.peekLast(); top && top._event.endTime <= event.startTime; top = recordStack.peekLast()) - recordStack.pop(); - if (event.phase === SDK.TracingModel.Phase.AsyncEnd || event.phase === SDK.TracingModel.Phase.NestableAsyncEnd) - continue; - var parentRecord = recordStack.peekLast(); - // Maintain the back-end logic of old timeline, skip console.time() / console.timeEnd() that are not properly nested. - if (SDK.TracingModel.isAsyncBeginPhase(event.phase) && parentRecord && - event.endTime > parentRecord._event.endTime) - continue; - var record = new TimelineModel.TimelineModel.Record(event); - if (TimelineModel.TimelineModel.isMarkerEvent(event)) - this._eventDividerRecords.push(record); - if (!this._eventFilter.accept(event) && !SDK.TracingModel.isTopLevelEvent(event)) - continue; - if (parentRecord) - parentRecord._addChild(record); - else - topLevelRecords.push(record); - if (event.endTime) - recordStack.push(record); - } - - return topLevelRecords; - } - _resetProcessingState() { this._asyncEventTracker = new TimelineModel.TimelineAsyncEventTracker(); this._invalidationTracker = new TimelineModel.InvalidationTracker(); @@ -518,14 +475,34 @@ TimelineModel.TimelineModel = class { } this._eventStack = []; + var eventStack = this._eventStack; var i = events.lowerBound(startTime, (time, event) => time - event.startTime); var length = events.length; for (; i < length; i++) { var event = events[i]; if (endTime && event.startTime >= endTime) break; + while (eventStack.length && eventStack.peekLast().endTime <= event.startTime) + eventStack.pop(); if (!this._processEvent(event)) continue; + if (!SDK.TracingModel.isAsyncPhase(event.phase) && event.duration) { + if (eventStack.length) { + var parent = eventStack.peekLast(); + parent.selfTime -= event.duration; + if (parent.selfTime < 0) + this._fixNegativeDuration(parent, event); + } + event.selfTime = event.duration; + if (isMainThread) { + if (!eventStack.length) + this._mainThreadTasks.push(event); + } + eventStack.push(event); + } + if (isMainThread && TimelineModel.TimelineModel.isMarkerEvent(event)) + this._eventDividers.push(event); + if (groupByFrame) { var frameId = TimelineModel.TimelineData.forEvent(event).frameId; var pageFrame = frameId && this._pageFrames.get(frameId); @@ -550,6 +527,20 @@ TimelineModel.TimelineModel = class { } } + /** + * @param {!SDK.TracingModel.Event} event + * @param {!SDK.TracingModel.Event} child + */ + _fixNegativeDuration(event, child) { + var epsilon = 1e-3; + if (event.selfTime < -epsilon) { + console.error( + `Children are longer than parent at ${event.startTime} ` + + `(${(child.startTime - this.minimumRecordTime()).toFixed(3)} by ${(-event.selfTime).toFixed(3)}`); + } + event.selfTime = 0; + } + /** * @param {!Map>} asyncEventsByGroup * @param {!Array} asyncEvents @@ -581,11 +572,8 @@ TimelineModel.TimelineModel = class { * @return {boolean} */ _processEvent(event) { - var eventStack = this._eventStack; - while (eventStack.length && eventStack.peekLast().endTime <= event.startTime) - eventStack.pop(); - var recordTypes = TimelineModel.TimelineModel.RecordType; + var eventStack = this._eventStack; if (!eventStack.length) { if (this._currentTaskLayoutAndRecalcEvents && this._currentTaskLayoutAndRecalcEvents.length) { @@ -806,26 +794,6 @@ TimelineModel.TimelineModel = class { timelineData.warning = TimelineModel.TimelineModel.WarningType.IdleDeadlineExceeded; break; } - if (SDK.TracingModel.isAsyncPhase(event.phase)) - return true; - var duration = event.duration; - if (!duration) - return true; - if (eventStack.length) { - var parent = eventStack.peekLast(); - parent.selfTime -= duration; - if (parent.selfTime < 0) { - var epsilon = 1e-3; - if (parent.selfTime < -epsilon) { - console.error( - 'Children are longer than parent at ' + event.startTime + ' (' + - (event.startTime - this.minimumRecordTime()).toFixed(3) + ') by ' + parent.selfTime.toFixed(3)); - } - parent.selfTime = 0; - } - } - event.selfTime = duration; - eventStack.push(event); return true; } @@ -923,12 +891,12 @@ TimelineModel.TimelineModel = class { this._mainThreadAsyncEventsByGroup = new Map(); /** @type {!Array} */ this._inspectedTargetEvents = []; - /** @type {!Array} */ + /** @type {!Array} */ this._mainThreadTasks = []; /** @type {!Array} */ this._gpuEvents = []; - /** @type {!Array} */ - this._eventDividerRecords = []; + /** @type {!Array} */ + this._eventDividers = []; /** @type {?string} */ this._sessionId = null; /** @type {?number} */ @@ -974,13 +942,6 @@ TimelineModel.TimelineModel = class { return this._mainThreadEvents; } - /** - * @param {!Array} events - */ - _setMainThreadEvents(events) { - this._mainThreadEvents = events; - } - /** * @return {!Map>} */ @@ -1003,7 +964,7 @@ TimelineModel.TimelineModel = class { } /** - * @return {!Array.} + * @return {!Array} */ mainThreadTasks() { return this._mainThreadTasks; @@ -1017,10 +978,10 @@ TimelineModel.TimelineModel = class { } /** - * @return {!Array.} + * @return {!Array} */ - eventDividerRecords() { - return this._eventDividerRecords; + eventDividers() { + return this._eventDividers; } /** @@ -1283,92 +1244,6 @@ TimelineModel.TimelineModel.VirtualThread = class { } }; -/** - * @unrestricted - */ -TimelineModel.TimelineModel.Record = class { - /** - * @param {!SDK.TracingModel.Event} traceEvent - */ - constructor(traceEvent) { - this._event = traceEvent; - this._children = []; - } - - /** - * @param {!TimelineModel.TimelineModel.Record} a - * @param {!TimelineModel.TimelineModel.Record} b - * @return {number} - */ - static _compareStartTime(a, b) { - // Never return 0 as otherwise equal records would be merged. - return a.startTime() <= b.startTime() ? -1 : 1; - } - - /** - * @return {?SDK.Target} - */ - target() { - var threadName = this._event.thread.name(); - // FIXME: correctly specify target - return threadName === TimelineModel.TimelineModel.RendererMainThreadName ? SDK.targetManager.targets()[0] || null : - null; - } - - /** - * @return {!Array.} - */ - children() { - return this._children; - } - - /** - * @return {number} - */ - startTime() { - return this._event.startTime; - } - - /** - * @return {number} - */ - endTime() { - return this._event.endTime || this._event.startTime; - } - - /** - * @return {string} - */ - thread() { - if (this._event.thread.name() === TimelineModel.TimelineModel.RendererMainThreadName) - return TimelineModel.TimelineModel.MainThreadName; - return this._event.thread.name(); - } - - /** - * @return {!TimelineModel.TimelineModel.RecordType} - */ - type() { - return TimelineModel.TimelineModel._eventType(this._event); - } - - /** - * @return {!SDK.TracingModel.Event} - */ - traceEvent() { - return this._event; - } - - /** - * @param {!TimelineModel.TimelineModel.Record} child - */ - _addChild(child) { - this._children.push(child); - child.parent = this; - } -}; - - /** @typedef {!{page: !Array, workers: !Array}} */ TimelineModel.TimelineModel.MetadataEvents; diff --git a/front_end/ui/Icon.js b/front_end/ui/Icon.js index 112ef8010e..ed9fda1370 100644 --- a/front_end/ui/Icon.js +++ b/front_end/ui/Icon.js @@ -108,7 +108,8 @@ UI.Icon.Descriptors = { 'smallicon-bezier': {x: -80, y: -20, width: 10, height: 10, spritesheet: 'smallicons', isMask: true}, 'smallicon-dropdown-arrow': {x: -18, y: -96, width: 12, height: 12, spritesheet: 'largeicons', isMask: true}, 'smallicon-triangle-right': {x: -4, y: -98, width: 10, height: 8, spritesheet: 'largeicons', isMask: true}, - 'smallicon-triangle-bottom': {x: -20, y: -98, width: 10, height: 8, spritesheet: 'largeicons', isMask: true}, + 'smallicon-triangle-down': {x: -20, y: -98, width: 10, height: 8, spritesheet: 'largeicons', isMask: true}, + 'smallicon-triangle-up': {x: -4, y: -111, width: 10, height: 8, spritesheet: 'largeicons', isMask: true}, 'smallicon-arrow-in-circle': {x: -10, y: -127, width: 11, height: 11, spritesheet: 'largeicons', isMask: true}, 'smallicon-cross': {x: -177, y: -98, width: 10, height: 10, spritesheet: 'largeicons'}, 'smallicon-inline-breakpoint': {x: -140, y: -20, width: 10, height: 10, spritesheet: 'smallicons'}, @@ -169,8 +170,10 @@ UI.Icon.Descriptors = { 'largeicon-show-left-sidebar': {x: -160, y: -72, width: 28, height: 24, spritesheet: 'largeicons', isMask: true}, 'largeicon-hide-left-sidebar': {x: -192, y: -72, width: 28, height: 24, spritesheet: 'largeicons', isMask: true}, - 'largeicon-show-right-sidebar': {x: -192, y: -72, width: 28, height: 24, spritesheet: 'largeicons', isMask: true}, - 'largeicon-hide-right-sidebar': {x: -160, y: -72, width: 28, height: 24, spritesheet: 'largeicons', isMask: true}, + 'largeicon-show-right-sidebar': + {x: -160, y: -72, width: 28, height: 24, spritesheet: 'largeicons', isMask: true, transform: 'rotate(180deg)'}, + 'largeicon-hide-right-sidebar': + {x: -192, y: -72, width: 28, height: 24, spritesheet: 'largeicons', isMask: true, transform: 'rotate(180deg)'}, 'largeicon-show-top-sidebar': { x: -160, y: -72, diff --git a/front_end/ui/tabbedPane.css b/front_end/ui/tabbedPane.css index 0b98b26c07..1b14d72b53 100644 --- a/front_end/ui/tabbedPane.css +++ b/front_end/ui/tabbedPane.css @@ -126,12 +126,7 @@ } .tabbed-pane-header-tab .tabbed-pane-close-button { - display: inline-block; - position: relative; - top: 2px; - left: 1px; - margin-left: 2px; - margin-top: -3px; + margin: 0 -3px 0 4px; visibility: hidden; }