From 89a2418e0faba98832452e9f19695ed64f4e79d2 Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Wed, 22 Mar 2017 22:36:39 +0100 Subject: [PATCH] squash 'resources/unpacked/devtools' changes from 781a13a57..b68fd2e2f b68fd2e2f [Devtools] Fix offline checkbox state sharing across Network and Application>Service Workers panels 73a2c5b73 DevTools: Add 'caret-color' CSS property to _colorAwareProperties 9db9a2b51 Add certificate error handling to devtools. 8713e2d37 [DevTools] Rework Popover API bfea3be2f [DevTools] Fallback to MainFrameNavigated event url in Security panel 2b57967b4 DevTools: Tweak flamechart selection d04622ac1 DevTools: kill Common.Throttler.flush() method dbfaa6049 DevTools: introduce SDK.SourceMapManager a221e62c4 [DevTools] Remove dependency from TargetManager to ResourceTreeModel fdd03a013 [DevTools] Update SecurityDetails in Security panel if State changes 6628483c3 DevTools: Fix Network panel warning icon display when offline/throttled 57f521225 Revert of DevTools: launch What's New from experiments (patchset #1 id:1 of https://codereview.chromium.org/2749423003/ ) c8477b2b4 DevTools: Only show gutter diff for Network UISourceCodes ccc15d9be [DevTools] Wait for cachedResourcesLoaded in ConsoleModel git-subtree-dir: resources/unpacked/devtools git-subtree-split: b68fd2e2f4ea44dfd9e9611ccbd5697a75b356c2 --- BUILD.gn | 1 + front_end/animation/AnimationTimeline.js | 84 +++--- front_end/audits/AuditController.js | 12 +- front_end/bindings/CSSWorkspaceBinding.js | 5 +- front_end/bindings/SASSSourceMapping.js | 21 +- front_end/common/Throttler.js | 5 - front_end/components/DOMPresentationUtils.js | 28 +- front_end/console/ConsoleView.js | 101 +++----- front_end/console_model/ConsoleModel.js | 18 +- front_end/dom_extension/DOMExtension.js | 9 + front_end/elements/ElementsPanel.js | 51 ++-- front_end/elements/ElementsTreeOutline.js | 70 +++-- front_end/elements/StylesSidebarPane.js | 2 +- front_end/extensions/ExtensionServer.js | 2 +- front_end/help/Help.js | 2 + front_end/help/module.json | 3 +- front_end/layer_viewer/Layers3DView.js | 2 +- front_end/layers/LayerTreeModel.js | 6 +- front_end/main/Main.js | 12 +- front_end/network/NetworkLogView.js | 8 +- front_end/network/NetworkLogViewColumns.js | 50 ++-- front_end/network/NetworkOverview.js | 6 +- front_end/network/NetworkPanel.js | 2 + front_end/network/NetworkWaterfallColumn.js | 41 ++- .../NetworkConditionsSelector.js | 10 +- front_end/network_log/NetworkLog.js | 10 +- front_end/perf_ui/TimelineOverviewPane.js | 59 ++--- front_end/perf_ui/flameChart.css | 9 +- front_end/profiler/HeapSnapshotGridNodes.js | 14 +- front_end/profiler/HeapSnapshotView.js | 79 ++---- front_end/sass/SASSSourceMapFactory.js | 4 +- front_end/sdk/CSSMetadata.js | 1 + front_end/sdk/CSSModel.js | 186 +++----------- front_end/sdk/CSSStyleSheetHeader.js | 6 +- front_end/sdk/DebuggerModel.js | 30 +++ front_end/sdk/ResourceTreeModel.js | 14 +- front_end/sdk/SourceMapManager.js | 241 ++++++++++++++++++ front_end/sdk/Target.js | 2 +- front_end/sdk/TargetManager.js | 51 +--- front_end/sdk/module.json | 1 + front_end/security/SecurityModel.js | 10 + front_end/security/SecurityPanel.js | 14 +- front_end/source_frame/SourceCodeDiff.js | 33 ++- front_end/source_frame/UISourceCodeFrame.js | 59 +++-- front_end/sources/JavaScriptSourceFrame.js | 196 ++++++-------- front_end/timeline/TimelinePanel.js | 34 ++- front_end/timeline/TimelineUIUtils.js | 12 +- front_end/ui/Popover.js | 234 +++++++++-------- 48 files changed, 946 insertions(+), 904 deletions(-) create mode 100644 front_end/sdk/SourceMapManager.js diff --git a/BUILD.gn b/BUILD.gn index d9de7733db..849a8becb2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -475,6 +475,7 @@ all_devtools_files = [ "front_end/sdk/ServiceWorkerCacheModel.js", "front_end/sdk/ServiceWorkerManager.js", "front_end/sdk/SourceMap.js", + "front_end/sdk/SourceMapManager.js", "front_end/sdk/Target.js", "front_end/sdk/TargetManager.js", "front_end/sdk/TracingManager.js", diff --git a/front_end/animation/AnimationTimeline.js b/front_end/animation/AnimationTimeline.js index ddff22f0b4..771e9be07d 100644 --- a/front_end/animation/AnimationTimeline.js +++ b/front_end/animation/AnimationTimeline.js @@ -131,9 +131,8 @@ Animation.AnimationTimeline = class extends UI.VBox { this._updatePlaybackControls(); this._previewContainer = this.contentElement.createChild('div', 'animation-timeline-buffer'); - this._popoverHelper = new UI.PopoverHelper(this._previewContainer, true); - this._popoverHelper.initializeCallbacks( - this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this)); + this._popoverHelper = new UI.PopoverHelper(this._previewContainer, this._getPopoverRequest.bind(this)); + this._popoverHelper.setDisableOnClick(true); this._popoverHelper.setTimeout(0); var emptyBufferHint = this.contentElement.createChild('div', 'animation-timeline-buffer-hint'); emptyBufferHint.textContent = Common.UIString('Listening for animations...'); @@ -162,49 +161,44 @@ Animation.AnimationTimeline = class extends UI.VBox { } /** - * @param {!Element} element * @param {!Event} event - * @return {!Element|!AnchorBox|undefined} - */ - _getPopoverAnchor(element, event) { - if (element.isDescendant(this._previewContainer)) - return element; - } - - /** - * @param {!Element|!AnchorBox} anchor - * @param {!UI.GlassPane} popover - * @return {!Promise} - */ - _showPopover(anchor, popover) { - var animGroup; - for (var group of this._previewMap.keysArray()) { - if (this._previewMap.get(group).element === anchor.parentElement) - animGroup = group; - } - console.assert(animGroup); - var screenshots = animGroup.screenshots(); - if (!screenshots.length) - return Promise.resolve(false); - - var fulfill; - var promise = new Promise(x => fulfill = x); - if (!screenshots[0].complete) - screenshots[0].onload = onFirstScreenshotLoaded.bind(null, screenshots); - else - onFirstScreenshotLoaded(screenshots); - return promise; - - /** - * @param {!Array.} screenshots - */ - function onFirstScreenshotLoaded(screenshots) { - new Animation.AnimationScreenshotPopover(screenshots).show(popover.contentElement); - fulfill(true); - } - } - - _onHidePopover() { + * @return {?UI.PopoverRequest} + */ + _getPopoverRequest(event) { + var element = event.target; + if (!element.isDescendant(this._previewContainer)) + return null; + + return { + box: event.target.boxInWindow(), + show: popover => { + var animGroup; + for (var group of this._previewMap.keysArray()) { + if (this._previewMap.get(group).element === element.parentElement) + animGroup = group; + } + console.assert(animGroup); + var screenshots = animGroup.screenshots(); + if (!screenshots.length) + return Promise.resolve(false); + + var fulfill; + var promise = new Promise(x => fulfill = x); + if (!screenshots[0].complete) + screenshots[0].onload = onFirstScreenshotLoaded.bind(null, screenshots); + else + onFirstScreenshotLoaded(screenshots); + return promise; + + /** + * @param {!Array.} screenshots + */ + function onFirstScreenshotLoaded(screenshots) { + new Animation.AnimationScreenshotPopover(screenshots).show(popover.contentElement); + fulfill(true); + } + } + }; } _togglePauseAll() { diff --git a/front_end/audits/AuditController.js b/front_end/audits/AuditController.js index a349ab53f4..ca17a15c8a 100644 --- a/front_end/audits/AuditController.js +++ b/front_end/audits/AuditController.js @@ -38,7 +38,8 @@ Audits.AuditController = class { */ constructor(auditsPanel) { this._auditsPanel = auditsPanel; - SDK.targetManager.addEventListener(SDK.TargetManager.Events.Load, this._didMainResourceLoad, this); + SDK.targetManager.addModelListener( + SDK.ResourceTreeModel, SDK.ResourceTreeModel.Events.Load, this._didMainResourceLoad, this); SDK.targetManager.addModelListener( SDK.NetworkManager, SDK.NetworkManager.Events.RequestFinished, this._didLoadResource, this); } @@ -141,7 +142,7 @@ Audits.AuditController = class { */ _reloadResources(callback) { this._pageReloadCallback = callback; - SDK.targetManager.reloadPage(); + SDK.ResourceTreeModel.reloadAllPages(); } _didLoadResource() { @@ -149,7 +150,12 @@ Audits.AuditController = class { this._pageReloadCallback(); } - _didMainResourceLoad() { + /** + * @param {!Common.Event} event + */ + _didMainResourceLoad(event) { + if (event.data.resourceTreeModel.target() !== SDK.targetManager.mainTarget()) + return; if (this._pageReloadCallback) { var callback = this._pageReloadCallback; delete this._pageReloadCallback; diff --git a/front_end/bindings/CSSWorkspaceBinding.js b/front_end/bindings/CSSWorkspaceBinding.js index 620a238a5a..fe8044c67b 100644 --- a/front_end/bindings/CSSWorkspaceBinding.js +++ b/front_end/bindings/CSSWorkspaceBinding.js @@ -99,8 +99,9 @@ Bindings.CSSWorkspaceBinding.ModelInfo = class { ]; this._stylesSourceMapping = new Bindings.StylesSourceMapping(cssModel, workspace); - this._sassSourceMapping = - new Bindings.SASSSourceMapping(cssModel, workspace, Bindings.NetworkProject.forTarget(cssModel.target())); + var sourceMapManager = cssModel.sourceMapManager(); + this._sassSourceMapping = new Bindings.SASSSourceMapping( + sourceMapManager, workspace, Bindings.NetworkProject.forTarget(cssModel.target())); /** @type {!Multimap} */ this._locations = new Multimap(); diff --git a/front_end/bindings/SASSSourceMapping.js b/front_end/bindings/SASSSourceMapping.js index a6912b093b..f7f9507e99 100644 --- a/front_end/bindings/SASSSourceMapping.js +++ b/front_end/bindings/SASSSourceMapping.js @@ -33,18 +33,21 @@ */ Bindings.SASSSourceMapping = class { /** - * @param {!SDK.CSSModel} cssModel + * @param {!SDK.SourceMapManager} sourceMapManager * @param {!Workspace.Workspace} workspace * @param {!Bindings.NetworkProject} networkProject */ - constructor(cssModel, workspace, networkProject) { - this._cssModel = cssModel; + constructor(sourceMapManager, workspace, networkProject) { + this._sourceMapManager = sourceMapManager; this._networkProject = networkProject; this._workspace = workspace; this._eventListeners = [ - this._cssModel.addEventListener(SDK.CSSModel.Events.SourceMapAttached, this._sourceMapAttached, this), - this._cssModel.addEventListener(SDK.CSSModel.Events.SourceMapDetached, this._sourceMapDetached, this), - this._cssModel.addEventListener(SDK.CSSModel.Events.SourceMapChanged, this._sourceMapChanged, this) + this._sourceMapManager.addEventListener( + SDK.SourceMapManager.Events.SourceMapAttached, this._sourceMapAttached, this), + this._sourceMapManager.addEventListener( + SDK.SourceMapManager.Events.SourceMapDetached, this._sourceMapDetached, this), + this._sourceMapManager.addEventListener( + SDK.SourceMapManager.Events.SourceMapChanged, this._sourceMapChanged, this) ]; } @@ -59,7 +62,7 @@ Bindings.SASSSourceMapping = class { */ _sourceMapAttached(event) { var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data); - var sourceMap = this._cssModel.sourceMapForHeader(header); + var sourceMap = this._sourceMapManager.sourceMapForClient(header); for (var sassURL of sourceMap.sourceURLs()) { var contentProvider = sourceMap.sourceContentProvider(sassURL, Common.resourceTypes.SourceMapStyleSheet); var embeddedContent = sourceMap.embeddedContentByURL(sassURL); @@ -84,7 +87,7 @@ Bindings.SASSSourceMapping = class { _sourceMapChanged(event) { var sourceMap = /** @type {!SDK.SourceMap} */ (event.data.sourceMap); var newSources = /** @type {!Map} */ (event.data.newSources); - var headers = this._cssModel.headersForSourceMap(sourceMap); + var headers = this._sourceMapManager.clientsForSourceMap(sourceMap); var handledUISourceCodes = new Set(); for (var header of headers) { Bindings.cssWorkspaceBinding.updateLocations(header); @@ -111,7 +114,7 @@ Bindings.SASSSourceMapping = class { var header = rawLocation.header(); if (!header) return null; - var sourceMap = this._cssModel.sourceMapForHeader(header); + var sourceMap = this._sourceMapManager.sourceMapForClient(header); if (!sourceMap) return null; var entry = sourceMap.findEntry(rawLocation.lineNumber, rawLocation.columnNumber); diff --git a/front_end/common/Throttler.js b/front_end/common/Throttler.js index 5bc43cc9a1..0dd9c59386 100644 --- a/front_end/common/Throttler.js +++ b/front_end/common/Throttler.js @@ -57,11 +57,6 @@ Common.Throttler = class { this._innerSchedule(forceTimerUpdate); } - flush() { - if (this._process) - this._onTimeout(); - } - /** * @param {boolean} forceTimerUpdate */ diff --git a/front_end/components/DOMPresentationUtils.js b/front_end/components/DOMPresentationUtils.js index 69a347272c..cd0c0c92fc 100644 --- a/front_end/components/DOMPresentationUtils.js +++ b/front_end/components/DOMPresentationUtils.js @@ -148,36 +148,30 @@ Components.DOMPresentationUtils.linkifyDeferredNodeReference = function(deferred * @param {!SDK.Target} target * @param {string} originalImageURL * @param {boolean} showDimensions - * @param {function(!Element=)} userCallback * @param {!Object=} precomputedFeatures + * @return {!Promise} */ Components.DOMPresentationUtils.buildImagePreviewContents = function( - target, originalImageURL, showDimensions, userCallback, precomputedFeatures) { + target, originalImageURL, showDimensions, precomputedFeatures) { var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(target); - if (!resourceTreeModel) { - userCallback(); - return; - } + if (!resourceTreeModel) + return Promise.resolve(/** @type {?Element} */ (null)); var resource = resourceTreeModel.resourceForURL(originalImageURL); var imageURL = originalImageURL; if (!isImageResource(resource) && precomputedFeatures && precomputedFeatures.currentSrc) { imageURL = precomputedFeatures.currentSrc; resource = resourceTreeModel.resourceForURL(imageURL); } - if (!isImageResource(resource)) { - userCallback(); - return; - } + if (!isImageResource(resource)) + return Promise.resolve(/** @type {?Element} */ (null)); + var fulfill; + var promise = new Promise(x => fulfill = x); var imageElement = createElement('img'); imageElement.addEventListener('load', buildContent, false); - imageElement.addEventListener('error', errorCallback, false); + imageElement.addEventListener('error', () => fulfill(null), false); resource.populateImageSource(imageElement); - - function errorCallback() { - // Drop the event parameter when invoking userCallback. - userCallback(); - } + return promise; /** * @param {?SDK.Resource} resource @@ -212,7 +206,7 @@ Components.DOMPresentationUtils.buildImagePreviewContents = function( container.createChild('tr').createChild('td').createChild('span', 'description').textContent = String.sprintf('currentSrc: %s', imageURL.trimMiddle(100)); } - userCallback(container); + fulfill(container); } }; diff --git a/front_end/console/ConsoleView.js b/front_end/console/ConsoleView.js index d7e0df0e33..f32a81cb77 100644 --- a/front_end/console/ConsoleView.js +++ b/front_end/console/ConsoleView.js @@ -28,7 +28,6 @@ */ /** * @implements {UI.Searchable} - * @implements {SDK.TargetManager.Observer} * @implements {Console.ConsoleViewportProvider} * @unrestricted */ @@ -162,7 +161,6 @@ Console.ConsoleView = class extends UI.VBox { this._timestampsSetting.addChangeListener(this._consoleTimestampsSettingChanged, this); this._registerWithMessageSink(); - SDK.targetManager.observeTargets(this); UI.context.addFlavorChangeListener(SDK.ExecutionContext, this._executionContextChanged, this); @@ -170,6 +168,18 @@ Console.ConsoleView = class extends UI.VBox { this._messagesElement.addEventListener('mouseup', this._updateStickToBottomOnMouseUp.bind(this), false); this._messagesElement.addEventListener('mouseleave', this._updateStickToBottomOnMouseUp.bind(this), false); this._messagesElement.addEventListener('wheel', this._updateStickToBottomOnWheel.bind(this), false); + + ConsoleModel.consoleModel.addEventListener( + ConsoleModel.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this); + ConsoleModel.consoleModel.addEventListener( + ConsoleModel.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this); + ConsoleModel.consoleModel.addEventListener( + ConsoleModel.ConsoleModel.Events.MessageUpdated, this._onConsoleMessageUpdated, this); + ConsoleModel.consoleModel.addEventListener( + ConsoleModel.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this); + ConsoleModel.consoleModel.messages().forEach(this._addConsoleMessage, this); + if (this._consoleMessages.length) + this._viewport.invalidate(); } /** @@ -201,42 +211,6 @@ Console.ConsoleView = class extends UI.VBox { this._prompt.setAddCompletionsFromHistory(this._consoleHistoryAutocompleteSetting.get()); } - /** - * @param {!SDK.Target} target - */ - _initConsoleMessages(target) { - var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(target); - if (resourceTreeModel && !resourceTreeModel.cachedResourcesLoaded()) { - resourceTreeModel.addEventListener( - SDK.ResourceTreeModel.Events.CachedResourcesLoaded, this._onResourceTreeModelLoaded, this); - return; - } - this._fetchMultitargetMessages(); - } - - /** - * @param {!Common.Event} event - */ - _onResourceTreeModelLoaded(event) { - var resourceTreeModel = /** @type {!SDK.ResourceTreeModel} */ (event.data); - resourceTreeModel.removeEventListener( - SDK.ResourceTreeModel.Events.CachedResourcesLoaded, this._onResourceTreeModelLoaded, this); - this._fetchMultitargetMessages(); - } - - _fetchMultitargetMessages() { - ConsoleModel.consoleModel.addEventListener( - ConsoleModel.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this); - ConsoleModel.consoleModel.addEventListener( - ConsoleModel.ConsoleModel.Events.MessageAdded, this._onConsoleMessageAdded, this); - ConsoleModel.consoleModel.addEventListener( - ConsoleModel.ConsoleModel.Events.MessageUpdated, this._onConsoleMessageUpdated, this); - ConsoleModel.consoleModel.addEventListener( - ConsoleModel.ConsoleModel.Events.CommandEvaluated, this._commandEvaluated, this); - ConsoleModel.consoleModel.messages().forEach(this._addConsoleMessage, this); - this._viewport.invalidate(); - } - /** * @override * @return {number} @@ -271,23 +245,6 @@ Console.ConsoleView = class extends UI.VBox { return 16; } - /** - * @override - * @param {!SDK.Target} target - */ - targetAdded(target) { - if (target === SDK.targetManager.mainTarget()) - this._initConsoleMessages(target); - this._viewport.invalidate(); - } - - /** - * @override - * @param {!SDK.Target} target - */ - targetRemoved(target) { - } - _registerWithMessageSink() { Common.console.messages().forEach(this._addSinkMessage, this); Common.console.addEventListener(Common.Console.Events.MessageAdded, messageAdded, this); @@ -387,24 +344,24 @@ Console.ConsoleView = class extends UI.VBox { this._prompt.clearAutocomplete(); } - _scheduleViewportRefresh() { - /** - * @this {Console.ConsoleView} - * @return {!Promise.} - */ - function invalidateViewport() { - if (this._muteViewportUpdates) { - this._maybeDirtyWhileMuted = true; - return Promise.resolve(); - } - if (this._needsFullUpdate) { - this._updateMessageList(); - delete this._needsFullUpdate; - } else { - this._viewport.invalidate(); - } + /** + * @return {!Promise.} + */ + _invalidateViewport() { + if (this._muteViewportUpdates) { + this._maybeDirtyWhileMuted = true; return Promise.resolve(); } + if (this._needsFullUpdate) { + this._updateMessageList(); + delete this._needsFullUpdate; + } else { + this._viewport.invalidate(); + } + return Promise.resolve(); + } + + _scheduleViewportRefresh() { if (this._muteViewportUpdates) { this._maybeDirtyWhileMuted = true; this._scheduleViewportRefreshForTest(true); @@ -412,7 +369,7 @@ Console.ConsoleView = class extends UI.VBox { } else { this._scheduleViewportRefreshForTest(false); } - this._viewportThrottler.schedule(invalidateViewport.bind(this)); + this._viewportThrottler.schedule(this._invalidateViewport.bind(this)); } /** diff --git a/front_end/console_model/ConsoleModel.js b/front_end/console_model/ConsoleModel.js index 68fe55e05a..5c0bb391f8 100644 --- a/front_end/console_model/ConsoleModel.js +++ b/front_end/console_model/ConsoleModel.js @@ -50,6 +50,22 @@ ConsoleModel.ConsoleModel = class extends Common.Object { * @param {!SDK.Target} target */ targetAdded(target) { + var resourceTreeModel = target.model(SDK.ResourceTreeModel); + if (!resourceTreeModel || resourceTreeModel.cachedResourcesLoaded()) { + this._initTarget(target); + return; + } + + var eventListener = resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.CachedResourcesLoaded, () => { + Common.EventTarget.removeEventListeners([eventListener]); + this._initTarget(target); + }); + } + + /** + * @param {!SDK.Target} target + */ + _initTarget(target) { var eventListeners = []; var logModel = target.model(SDK.LogModel); @@ -98,7 +114,7 @@ ConsoleModel.ConsoleModel = class extends Common.Object { */ targetRemoved(target) { this._messageByExceptionId.delete(target); - Common.EventTarget.removeEventListeners(target[ConsoleModel.ConsoleModel._events]); + Common.EventTarget.removeEventListeners(target[ConsoleModel.ConsoleModel._events] || []); } /** diff --git a/front_end/dom_extension/DOMExtension.js b/front_end/dom_extension/DOMExtension.js index dd12c01302..f9d202ca5a 100644 --- a/front_end/dom_extension/DOMExtension.js +++ b/front_end/dom_extension/DOMExtension.js @@ -472,6 +472,15 @@ var AnchorBox = class { this.width = width || 0; this.height = height || 0; } + + /** + * @param {number} x + * @param {number} y + * @return {boolean} + */ + contains(x, y) { + return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height; + } }; /** diff --git a/front_end/elements/ElementsPanel.js b/front_end/elements/ElementsPanel.js index 6d0eeeade9..b095335250 100644 --- a/front_end/elements/ElementsPanel.js +++ b/front_end/elements/ElementsPanel.js @@ -568,41 +568,29 @@ Elements.ElementsPanel = class extends UI.Panel { } /** - * @param {!Element} element * @param {!Event} event - * @return {!Element|!AnchorBox|undefined} + * @return {?UI.PopoverRequest} */ - _getPopoverAnchor(element, event) { - var link = element; + _getPopoverRequest(event) { + var link = event.target; while (link && !link[Elements.ElementsTreeElement.HrefSymbol]) link = link.parentElementOrShadowHost(); - return link ? link : undefined; - } - - /** - * @param {!Element|!AnchorBox} link - * @param {!UI.GlassPane} popover - * @return {!Promise} - */ - _showPopover(link, popover) { - var node = this.selectedDOMNode(); - if (!node) - return Promise.resolve(false); - - var fulfill; - var promise = new Promise(x => fulfill = x); - Components.DOMPresentationUtils.buildImagePreviewContents( - node.target(), link[Elements.ElementsTreeElement.HrefSymbol], true, showPopover); - return promise; + if (!link) + return null; - /** - * @param {!Element=} contents - */ - function showPopover(contents) { - if (contents) - popover.contentElement.appendChild(contents); - fulfill(!!contents); - } + return { + box: link.boxInWindow(), + show: async popover => { + var node = this.selectedDOMNode(); + if (!node) + return false; + var preview = await Components.DOMPresentationUtils.buildImagePreviewContents( + node.target(), link[Elements.ElementsTreeElement.HrefSymbol], true); + if (preview) + popover.contentElement.appendChild(preview); + return !!preview; + } + }; } _jumpToSearchResult(index) { @@ -907,9 +895,8 @@ Elements.ElementsPanel = class extends UI.Panel { var tabbedPane = this.sidebarPaneView.tabbedPane(); if (this._popoverHelper) this._popoverHelper.hidePopover(); - this._popoverHelper = new UI.PopoverHelper(tabbedPane.element); + this._popoverHelper = new UI.PopoverHelper(tabbedPane.element, this._getPopoverRequest.bind(this)); this._popoverHelper.setHasPadding(true); - this._popoverHelper.initializeCallbacks(this._getPopoverAnchor.bind(this), this._showPopover.bind(this)); this._popoverHelper.setTimeout(0); if (horizontally) { diff --git a/front_end/elements/ElementsTreeOutline.js b/front_end/elements/ElementsTreeOutline.js index 38d3a035cb..3fe0a40d54 100644 --- a/front_end/elements/ElementsTreeOutline.js +++ b/front_end/elements/ElementsTreeOutline.js @@ -75,8 +75,7 @@ Elements.ElementsTreeOutline = class extends UI.TreeOutline { this._visible = false; - this._popoverHelper = new UI.PopoverHelper(this._element); - this._popoverHelper.initializeCallbacks(this._getPopoverAnchor.bind(this), this._showPopover.bind(this)); + this._popoverHelper = new UI.PopoverHelper(this._element, this._getPopoverRequest.bind(this)); this._popoverHelper.setHasPadding(true); this._popoverHelper.setTimeout(0, 100); @@ -522,36 +521,51 @@ Elements.ElementsTreeOutline = class extends UI.TreeOutline { } /** - * @param {!Element} element * @param {!Event} event - * @return {!Element|!AnchorBox|undefined} + * @return {?UI.PopoverRequest} */ - _getPopoverAnchor(element, event) { - var link = element; + _getPopoverRequest(event) { + var link = event.target; while (link && !link[Elements.ElementsTreeElement.HrefSymbol]) link = link.parentElementOrShadowHost(); - return link ? link : undefined; + if (!link) + return null; + + return { + box: link.boxInWindow(), + show: async popover => { + var listItem = link.enclosingNodeOrSelfWithNodeName('li'); + var node = /** @type {!Elements.ElementsTreeElement} */ (listItem.treeElement).node(); + var precomputedFeatures = await this._loadDimensionsForNode(node); + var preview = await Components.DOMPresentationUtils.buildImagePreviewContents( + node.target(), link[Elements.ElementsTreeElement.HrefSymbol], true, precomputedFeatures); + if (preview) + popover.contentElement.appendChild(preview); + return !!preview; + } + }; } /** * @param {!SDK.DOMNode} node - * @param {function()} callback + * @return {!Promise} */ - _loadDimensionsForNode(node, callback) { - if (!node.nodeName() || node.nodeName().toLowerCase() !== 'img') { - callback(); - return; - } + _loadDimensionsForNode(node) { + if (!node.nodeName() || node.nodeName().toLowerCase() !== 'img') + return Promise.resolve(); + var fulfill; + var promise = new Promise(x => fulfill = x); node.resolveToObject('', resolvedNode); + return promise; function resolvedNode(object) { if (!object) { - callback(); + fulfill(); return; } - object.callFunctionJSON(features, undefined, callback); + object.callFunctionJSON(features, undefined, fulfill); object.release(); /** @@ -571,32 +585,6 @@ Elements.ElementsTreeOutline = class extends UI.TreeOutline { } } - /** - * @param {!Element|!AnchorBox} link - * @param {!UI.GlassPane} popover - * @return {!Promise} - */ - _showPopover(link, popover) { - var fulfill; - var promise = new Promise(x => fulfill = x); - var listItem = link.enclosingNodeOrSelfWithNodeName('li'); - var node = /** @type {!Elements.ElementsTreeElement} */ (listItem.treeElement).node(); - this._loadDimensionsForNode( - node, Components.DOMPresentationUtils.buildImagePreviewContents.bind( - Components.DOMPresentationUtils, node.target(), link[Elements.ElementsTreeElement.HrefSymbol], true, - showPopover)); - return promise; - - /** - * @param {!Element=} contents - */ - function showPopover(contents) { - if (contents) - popover.contentElement.appendChild(contents); - fulfill(!!contents); - } - } - _onmousedown(event) { var element = this._treeElementFromEvent(event); diff --git a/front_end/elements/StylesSidebarPane.js b/front_end/elements/StylesSidebarPane.js index 0a4d978625..3323be720e 100644 --- a/front_end/elements/StylesSidebarPane.js +++ b/front_end/elements/StylesSidebarPane.js @@ -789,7 +789,7 @@ Elements.StylePropertiesSection = class { this._style.styleSheetId ? this._style.cssModel().styleSheetHeaderForId(this._style.styleSheetId) : null; if (!header) return false; - var sourceMap = header.cssModel().sourceMapForHeader(header); + var sourceMap = header.cssModel().sourceMapManager().sourceMapForClient(header); return sourceMap ? sourceMap.editable() : false; } diff --git a/front_end/extensions/ExtensionServer.js b/front_end/extensions/ExtensionServer.js index 570f06e436..12f8032a09 100644 --- a/front_end/extensions/ExtensionServer.js +++ b/front_end/extensions/ExtensionServer.js @@ -422,7 +422,7 @@ Extensions.ExtensionServer = class extends Common.Object { var injectedScript; if (options.injectedScript) injectedScript = '(function(){' + options.injectedScript + '})()'; - SDK.targetManager.reloadPage(!!options.ignoreCache, injectedScript); + SDK.ResourceTreeModel.reloadAllPages(!!options.ignoreCache, injectedScript); return this._status.OK(); } diff --git a/front_end/help/Help.js b/front_end/help/Help.js index 15b702a4e8..d4a2080234 100644 --- a/front_end/help/Help.js +++ b/front_end/help/Help.js @@ -33,6 +33,8 @@ Help.showReleaseNoteIfNeeded = function() { * @param {number} latestVersion */ Help._showReleaseNoteIfNeeded = function(lastSeenVersion, latestVersion) { + if (!Runtime.experiments.isEnabled('releaseNote')) + return; if (lastSeenVersion >= latestVersion) return; Help.releaseNoteVersionSetting().set(latestVersion); diff --git a/front_end/help/module.json b/front_end/help/module.json index 000b16e9bc..a91d233ebd 100644 --- a/front_end/help/module.json +++ b/front_end/help/module.json @@ -7,7 +7,8 @@ "title": "What's New?", "persistence": "closeable", "order": 1, - "className": "Help.ReleaseNoteView" + "className": "Help.ReleaseNoteView", + "experiment": "releaseNote" } ], "dependencies": [ diff --git a/front_end/layer_viewer/Layers3DView.js b/front_end/layer_viewer/Layers3DView.js index 949a6fb49c..df3eda95da 100644 --- a/front_end/layer_viewer/Layers3DView.js +++ b/front_end/layer_viewer/Layers3DView.js @@ -980,7 +980,7 @@ LayerViewer.LayerTextureManager = class { forceUpdate() { this._queue.forEach(layer => this._updateLayer(layer)); this._queue = []; - this._throttler.flush(); + this._update(); } /** diff --git a/front_end/layers/LayerTreeModel.js b/front_end/layers/LayerTreeModel.js index ba1f9decea..5aab3190ee 100644 --- a/front_end/layers/LayerTreeModel.js +++ b/front_end/layers/LayerTreeModel.js @@ -35,7 +35,11 @@ Layers.LayerTreeModel = class extends SDK.SDKModel { constructor(target) { super(target); target.registerLayerTreeDispatcher(new Layers.LayerTreeDispatcher(this)); - SDK.targetManager.addEventListener(SDK.TargetManager.Events.MainFrameNavigated, this._onMainFrameNavigated, this); + var resourceTreeModel = target.model(SDK.ResourceTreeModel); + if (resourceTreeModel) { + resourceTreeModel.addEventListener( + SDK.ResourceTreeModel.Events.MainFrameNavigated, this._onMainFrameNavigated, this); + } /** @type {?SDK.LayerTreeBase} */ this._layerTree = null; } diff --git a/front_end/main/Main.js b/front_end/main/Main.js index e4a12c32b8..0dc04d7b98 100644 --- a/front_end/main/Main.js +++ b/front_end/main/Main.js @@ -44,9 +44,7 @@ Main.Main = class { * @param {boolean} hard */ static _reloadPage(hard) { - var mainTarget = SDK.targetManager.mainTarget(); - if (mainTarget && mainTarget.hasBrowserCapability()) - SDK.targetManager.reloadPage(hard); + SDK.ResourceTreeModel.reloadAllPages(hard); } _loaded() { @@ -107,6 +105,7 @@ Main.Main = class { Runtime.experiments.register('objectPreviews', 'Object previews', true); Runtime.experiments.register('persistence2', 'Persistence 2.0'); Runtime.experiments.register('persistenceValidation', 'Validate persistence bindings'); + Runtime.experiments.register('releaseNote', 'Release note', true); Runtime.experiments.register('requestBlocking', 'Request blocking', true); Runtime.experiments.register('timelineShowAllEvents', 'Show all events on Timeline', true); Runtime.experiments.register('timelineShowAllProcesses', 'Show all processes on Timeline', true); @@ -131,6 +130,10 @@ Main.Main = class { Runtime.experiments.enableForTest('cssTrackerPanel'); if (testPath.indexOf('audits2/') !== -1) Runtime.experiments.enableForTest('audits2'); + if (testPath.indexOf('help/') !== -1) + Runtime.experiments.enableForTest('releaseNote'); + if (testPath.indexOf('sass/') !== -1) + Runtime.experiments.enableForTest('liveSASS'); } Runtime.experiments.setDefaultExperiments(['persistenceValidation']); @@ -741,7 +744,8 @@ Main.Main.MainMenuItem = class { var helpSubMenu = contextMenu.namedSubMenu('mainMenuHelp'); helpSubMenu.appendAction('settings.documentation'); - helpSubMenu.appendItem('Release Notes', () => InspectorFrontendHost.openInNewTab(Help.latestReleaseNote().link)); + if (Runtime.experiments.isEnabled('releaseNote')) + helpSubMenu.appendItem('Release Notes', () => InspectorFrontendHost.openInNewTab(Help.latestReleaseNote().link)); contextMenu.show(); } }; diff --git a/front_end/network/NetworkLogView.js b/front_end/network/NetworkLogView.js index af2120bbea..4cef0d366b 100644 --- a/front_end/network/NetworkLogView.js +++ b/front_end/network/NetworkLogView.js @@ -741,10 +741,10 @@ Network.NetworkLogView = class extends UI.VBox { if (!this._recording) return; - var data = /** @type {number} */ (event.data); - if (data) { - this._mainRequestLoadTime = data; - this._columns.addEventDividers([data], 'network-red-divider'); + var time = /** @type {number} */ (event.data.loadTime); + if (time) { + this._mainRequestLoadTime = time; + this._columns.addEventDividers([time], 'network-red-divider'); } } diff --git a/front_end/network/NetworkLogViewColumns.js b/front_end/network/NetworkLogViewColumns.js index 5f26a5cfac..6508efe84d 100644 --- a/front_end/network/NetworkLogViewColumns.js +++ b/front_end/network/NetworkLogViewColumns.js @@ -92,9 +92,7 @@ Network.NetworkLogViewColumns = class { this._loadColumnExtensions(); this._loadCustomColumnsAndSettings(); - this._popoverHelper = new UI.PopoverHelper(this._networkLogView.element); - this._popoverHelper.initializeCallbacks( - this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this)); + this._popoverHelper = new UI.PopoverHelper(this._networkLogView.element, this._getPopoverRequest.bind(this)); this._popoverHelper.setHasPadding(true); /** @type {!DataGrid.SortableDataGrid} */ @@ -570,37 +568,31 @@ Network.NetworkLogViewColumns = class { } /** - * @param {!Element} element * @param {!Event} event - * @return {!Element|!AnchorBox|undefined} + * @return {?UI.PopoverRequest} */ - _getPopoverAnchor(element, event) { + _getPopoverRequest(event) { if (!this._gridMode) - return; - var anchor = element.enclosingNodeOrSelfWithClass('network-script-initiated'); - if (anchor && anchor.request) { - var initiator = /** @type {!SDK.NetworkRequest} */ (anchor.request).initiator(); - if (initiator && initiator.stack) - return anchor; - } - } + return null; - /** - * @param {!Element|!AnchorBox} anchor - * @param {!UI.GlassPane} popover - * @return {!Promise} - */ - _showPopover(anchor, popover) { - var request = /** @type {!SDK.NetworkRequest} */ (anchor.request); - var initiator = /** @type {!Protocol.Network.Initiator} */ (request.initiator()); - var content = Components.DOMPresentationUtils.buildStackTracePreviewContents( - request.target(), this._popupLinkifier, initiator.stack); - popover.contentElement.appendChild(content); - return Promise.resolve(true); - } + var anchor = event.target.enclosingNodeOrSelfWithClass('network-script-initiated'); + var request = /** @type {?SDK.NetworkRequest} */ (anchor ? anchor.request : null); + var initiator = request ? request.initiator() : null; + if (!initiator || !initiator.stack) + return null; - _onHidePopover() { - this._popupLinkifier.reset(); + return { + box: anchor.boxInWindow(), + show: popover => { + var content = Components.DOMPresentationUtils.buildStackTracePreviewContents( + anchor.request.target(), this._popupLinkifier, initiator.stack); + popover.contentElement.appendChild(content); + return Promise.resolve(true); + }, + hide: () => { + this._popupLinkifier.reset(); + } + }; } /** diff --git a/front_end/network/NetworkOverview.js b/front_end/network/NetworkOverview.js index 9c2493b477..afb48cf947 100644 --- a/front_end/network/NetworkOverview.js +++ b/front_end/network/NetworkOverview.js @@ -48,9 +48,9 @@ Network.NetworkOverview = class extends PerfUI.TimelineOverviewBase { * @param {!Common.Event} event */ _loadEventFired(event) { - var data = /** @type {number} */ (event.data); - if (data) - this._loadEvents.push(data * 1000); + var time = /** @type {number} */ (event.data.loadTime); + if (time) + this._loadEvents.push(time * 1000); this.scheduleUpdate(); } diff --git a/front_end/network/NetworkPanel.js b/front_end/network/NetworkPanel.js index 216eab8395..79922acaeb 100644 --- a/front_end/network/NetworkPanel.js +++ b/front_end/network/NetworkPanel.js @@ -115,6 +115,8 @@ Network.NetworkPanel = class extends UI.Panel { blockedURLsSetting.addChangeListener(updateIconVisibility.bind(this)); var requestBlockingEnabledSetting = Common.moduleSetting('requestBlockingEnabled'); requestBlockingEnabledSetting.addChangeListener(updateIconVisibility.bind(this)); + SDK.multitargetNetworkManager.addEventListener( + SDK.MultitargetNetworkManager.Events.ConditionsChanged, updateIconVisibility.bind(this)); updateIconVisibility.call(this); diff --git a/front_end/network/NetworkWaterfallColumn.js b/front_end/network/NetworkWaterfallColumn.js index 1f36a35047..ed370ad2ff 100644 --- a/front_end/network/NetworkWaterfallColumn.js +++ b/front_end/network/NetworkWaterfallColumn.js @@ -33,8 +33,7 @@ Network.NetworkWaterfallColumn = class extends UI.VBox { this._startTime = this._calculator.minimumBoundary(); this._endTime = this._calculator.maximumBoundary(); - this._popoverHelper = new UI.PopoverHelper(this.element); - this._popoverHelper.initializeCallbacks(this._getPopoverAnchor.bind(this), this._showPopover.bind(this)); + this._popoverHelper = new UI.PopoverHelper(this.element, this._getPopoverRequest.bind(this)); this._popoverHelper.setHasPadding(true); this._popoverHelper.setTimeout(300, 300); @@ -187,16 +186,15 @@ Network.NetworkWaterfallColumn = class extends UI.VBox { } /** - * @param {!Element} element * @param {!Event} event - * @return {!AnchorBox|undefined} + * @return {?UI.PopoverRequest} */ - _getPopoverAnchor(element, event) { + _getPopoverRequest(event) { if (!this._hoveredNode) - return; + return null; var request = this._hoveredNode.request(); if (!request) - return; + return null; var useTimingBars = !Common.moduleSetting('networkColorCodeResourceTypes').get() && !this._calculator.startAtZero; if (useTimingBars) { var range = Network.RequestTimingView.calculateRequestTimeRanges(request, 0) @@ -216,37 +214,30 @@ Network.NetworkWaterfallColumn = class extends UI.VBox { } if (event.clientX < this._canvasPosition.left + start || event.clientX > this._canvasPosition.left + end) - return; + return null; var rowIndex = this._nodes.findIndex(node => node.hovered()); var barHeight = this._getBarHeight(range.name); var y = this._headerHeight + (this._rowHeight * rowIndex - this._scrollTop) + ((this._rowHeight - barHeight) / 2); if (event.clientY < this._canvasPosition.top + y || event.clientY > this._canvasPosition.top + y + barHeight) - return; + return null; var anchorBox = this.element.boxInWindow(); anchorBox.x += start; anchorBox.y += y; anchorBox.width = end - start; anchorBox.height = barHeight; - return anchorBox; - } - /** - * @param {!Element|!AnchorBox} anchor - * @param {!UI.GlassPane} popover - * @return {!Promise} - */ - _showPopover(anchor, popover) { - if (!this._hoveredNode) - return Promise.resolve(false); - var request = this._hoveredNode.request(); - if (!request) - return Promise.resolve(false); - var content = Network.RequestTimingView.createTimingTable(request, this._calculator); - popover.contentElement.appendChild(content); - return Promise.resolve(true); + return { + box: anchorBox, + show: popover => { + var content = + Network.RequestTimingView.createTimingTable(/** @type {!SDK.NetworkRequest} */ (request), this._calculator); + popover.contentElement.appendChild(content); + return Promise.resolve(true); + } + }; } /** diff --git a/front_end/network_conditions/NetworkConditionsSelector.js b/front_end/network_conditions/NetworkConditionsSelector.js index 82f6a3375f..25ab9f55d1 100644 --- a/front_end/network_conditions/NetworkConditionsSelector.js +++ b/front_end/network_conditions/NetworkConditionsSelector.js @@ -176,21 +176,19 @@ NetworkConditions.NetworkConditionsSelector = class { SDK.MultitargetNetworkManager.Events.ConditionsChanged, networkConditionsChanged); checkbox.setChecked(SDK.multitargetNetworkManager.networkConditions() === SDK.NetworkManager.OfflineConditions); - var lastNetworkConditions; - function forceOffline() { if (checkbox.checked()) { - lastNetworkConditions = SDK.multitargetNetworkManager.networkConditions(); + NetworkConditions.NetworkConditionsSelector._lastNetworkConditions = + SDK.multitargetNetworkManager.networkConditions(); SDK.multitargetNetworkManager.setNetworkConditions(SDK.NetworkManager.OfflineConditions); } else { - SDK.multitargetNetworkManager.setNetworkConditions(lastNetworkConditions); + SDK.multitargetNetworkManager.setNetworkConditions( + NetworkConditions.NetworkConditionsSelector._lastNetworkConditions); } } function networkConditionsChanged() { var conditions = SDK.multitargetNetworkManager.networkConditions(); - if (conditions !== SDK.NetworkManager.OfflineConditions) - lastNetworkConditions = conditions; checkbox.setChecked(conditions === SDK.NetworkManager.OfflineConditions); } return checkbox; diff --git a/front_end/network_log/NetworkLog.js b/front_end/network_log/NetworkLog.js index 2ca5bcfffc..5228f944d6 100644 --- a/front_end/network_log/NetworkLog.js +++ b/front_end/network_log/NetworkLog.js @@ -56,8 +56,7 @@ NetworkLog.NetworkLog = class { if (resourceTreeModel) { eventListeners.push(resourceTreeModel.addEventListener( SDK.ResourceTreeModel.Events.MainFrameNavigated, this._onMainFrameNavigated, this)); - eventListeners.push(resourceTreeModel.addEventListener( - SDK.ResourceTreeModel.Events.Load, this._onLoad.bind(this, resourceTreeModel))); + eventListeners.push(resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.Load, this._onLoad, this)); eventListeners.push(resourceTreeModel.addEventListener( SDK.ResourceTreeModel.Events.DOMContentLoaded, this._onDOMContentLoaded.bind(this, resourceTreeModel))); } @@ -292,13 +291,12 @@ NetworkLog.NetworkLog = class { } /** - * @param {!SDK.ResourceTreeModel} resourceTreeModel * @param {!Common.Event} event */ - _onLoad(resourceTreeModel, event) { - var pageLoad = this._currentPageLoad.get(resourceTreeModel.target()); + _onLoad(event) { + var pageLoad = this._currentPageLoad.get(event.data.resourceTreeModel.target()); if (pageLoad) - pageLoad.loadTime = /** @type {number} */ (event.data); + pageLoad.loadTime = /** @type {number} */ (event.data.loadTime); } /** diff --git a/front_end/perf_ui/TimelineOverviewPane.js b/front_end/perf_ui/TimelineOverviewPane.js index 4a22228868..8a52f32e8c 100644 --- a/front_end/perf_ui/TimelineOverviewPane.js +++ b/front_end/perf_ui/TimelineOverviewPane.js @@ -53,9 +53,7 @@ PerfUI.TimelineOverviewPane = class extends UI.VBox { this._overviewControls = []; this._markers = new Map(); - this._popoverHelper = new UI.PopoverHelper(this._cursorArea); - this._popoverHelper.initializeCallbacks( - this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._onHidePopover.bind(this)); + this._popoverHelper = new UI.PopoverHelper(this._cursorArea, this._getPopoverRequest.bind(this)); this._popoverHelper.setHasPadding(true); this._popoverHelper.setTimeout(0); @@ -67,42 +65,27 @@ PerfUI.TimelineOverviewPane = class extends UI.VBox { } /** - * @param {!Element} element * @param {!Event} event - * @return {!Element|!AnchorBox|undefined} - */ - _getPopoverAnchor(element, event) { - return this._cursorArea; - } - - /** - * @param {!Element|!AnchorBox} anchor - * @param {!UI.GlassPane} popover - * @return {!Promise} - */ - _showPopover(anchor, popover) { - return this._buildPopoverContents().then(maybeShowPopover.bind(this)); - - /** - * @this {PerfUI.TimelineOverviewPane} - * @param {!DocumentFragment} fragment - * @return {boolean} - */ - function maybeShowPopover(fragment) { - if (!fragment.firstChild) - return false; - var content = new PerfUI.TimelineOverviewPane.PopoverContents(); - this._popoverContents = content.contentElement.createChild('div'); - this._popoverContents.appendChild(fragment); - this._popover = popover; - content.show(popover.contentElement); - return true; - } - } - - _onHidePopover() { - this._popover = null; - this._popoverContents = null; + * @return {?UI.PopoverRequest} + */ + _getPopoverRequest(event) { + return { + box: this._cursorElement.boxInWindow(), + show: popover => this._buildPopoverContents().then(fragment => { + if (!fragment.firstChild) + return false; + var content = new PerfUI.TimelineOverviewPane.PopoverContents(); + this._popoverContents = content.contentElement.createChild('div'); + this._popoverContents.appendChild(fragment); + this._popover = popover; + content.show(popover.contentElement); + return true; + }), + hide: () => { + this._popover = null; + this._popoverContents = null; + } + }; } /** diff --git a/front_end/perf_ui/flameChart.css b/front_end/perf_ui/flameChart.css index a07e5e37ab..822c536e73 100644 --- a/front_end/perf_ui/flameChart.css +++ b/front_end/perf_ui/flameChart.css @@ -13,19 +13,16 @@ } .flame-chart-highlight-element { - background-color: black; position: absolute; - opacity: 0.2; pointer-events: none; + background-color: rgba(56, 121, 217, 0.1); } .flame-chart-selected-element { position: absolute; pointer-events: none; - border-color: rgb(56, 121, 217); - border-width: 1px; - border-style: solid; - background-color: rgba(56, 121, 217, 0.2); + outline: 2px solid rgb(56, 121, 217); + background-color: rgba(56, 121, 217, 0.1); } .flame-chart-v-scroll { diff --git a/front_end/profiler/HeapSnapshotGridNodes.js b/front_end/profiler/HeapSnapshotGridNodes.js index 6ded00decb..fe1ff3e18f 100644 --- a/front_end/profiler/HeapSnapshotGridNodes.js +++ b/front_end/profiler/HeapSnapshotGridNodes.js @@ -133,10 +133,10 @@ Profiler.HeapSnapshotGridNode = class extends DataGrid.DataGridNode { /** * @param {!SDK.Target} target - * @param {function(!SDK.RemoteObject)} callback * @param {string} objectGroupName + * @return {!Promise} */ - queryObjectContent(target, callback, objectGroupName) { + queryObjectContent(target, objectGroupName) { } /** @@ -605,15 +605,18 @@ Profiler.HeapSnapshotGenericObjectNode = class extends Profiler.HeapSnapshotGrid /** * @override * @param {!SDK.Target} target - * @param {function(!SDK.RemoteObject)} callback * @param {string} objectGroupName + * @return {!Promise} */ - queryObjectContent(target, callback, objectGroupName) { + queryObjectContent(target, objectGroupName) { + var fulfill; + var promise = new Promise(x => fulfill = x); + /** * @param {?SDK.RemoteObject} object */ function onResult(object) { - callback( + fulfill( object || target.runtimeModel.createRemoteObjectFromPrimitiveValue(Common.UIString('Preview is not available'))); } @@ -625,6 +628,7 @@ Profiler.HeapSnapshotGenericObjectNode = class extends Profiler.HeapSnapshotGrid onResult(null); else heapProfilerModel.objectForSnapshotObjectId(String(this.snapshotNodeId), objectGroupName).then(onResult); + return promise; } updateHasChildren() { diff --git a/front_end/profiler/HeapSnapshotView.js b/front_end/profiler/HeapSnapshotView.js index 95e833e5a9..32551e390a 100644 --- a/front_end/profiler/HeapSnapshotView.js +++ b/front_end/profiler/HeapSnapshotView.js @@ -153,9 +153,8 @@ Profiler.HeapSnapshotView = class extends UI.SimpleView { this._selectedSizeText = new UI.ToolbarText(); - this._popoverHelper = new UI.PopoverHelper(this.element, true); - this._popoverHelper.initializeCallbacks( - this._getHoverAnchor.bind(this), this._showObjectPopover.bind(this), this._onHidePopover.bind(this)); + this._popoverHelper = new UI.PopoverHelper(this.element, this._getPopoverRequest.bind(this)); + this._popoverHelper.setDisableOnClick(true); this._popoverHelper.setHasPadding(true); this.element.addEventListener('scroll', this._popoverHelper.hidePopover.bind(this._popoverHelper), true); @@ -607,58 +606,36 @@ Profiler.HeapSnapshotView = class extends UI.SimpleView { } } - _getHoverAnchor(target) { - var span = target.enclosingNodeOrSelfWithNodeName('span'); - if (!span) - return; - var row = target.enclosingNodeOrSelfWithNodeName('tr'); - if (!row) - return; - span.node = row._dataGridNode; - return span; - } - /** - * @param {!Element|!AnchorBox} element - * @param {!UI.GlassPane} popover - * @return {!Promise} + * @param {!Event} event + * @return {?UI.PopoverRequest} */ - _showObjectPopover(element, popover) { - if (!this._profile.target() || !element.node) - return Promise.resolve(false); - - var fulfill; - var promise = new Promise(x => fulfill = x); - element.node.queryObjectContent(this._profile.target(), onObjectResolved.bind(this), 'popover'); - return promise; - - /** - * @param {?SDK.RemoteObject} result - * @this {Profiler.HeapSnapshotView} - */ - function onObjectResolved(result) { - if (!result) { - fulfill(false); - return; - } - ObjectUI.ObjectPopoverHelper.buildObjectPopover(result, popover).then(objectPopoverHelper => { + _getPopoverRequest(event) { + var span = event.target.enclosingNodeOrSelfWithNodeName('span'); + var row = event.target.enclosingNodeOrSelfWithNodeName('tr'); + var target = this._profile.target(); + if (!row || !span || !target) + return null; + var node = row._dataGridNode; + var objectPopoverHelper; + return { + box: span.boxInWindow(), + show: async popover => { + var remoteObject = await node.queryObjectContent(target, 'popover'); + if (!remoteObject) + return false; + objectPopoverHelper = await ObjectUI.ObjectPopoverHelper.buildObjectPopover(remoteObject, popover); if (!objectPopoverHelper) { - this._onHidePopover(); // Cleanup object resolving artifacts. - fulfill(false); - return; + target.runtimeModel.releaseObjectGroup('popover'); + return false; } - this._objectPopoverHelper = objectPopoverHelper; - fulfill(true); - }); - } - } - - _onHidePopover() { - if (this._objectPopoverHelper) { - this._objectPopoverHelper.dispose(); - delete this._objectPopoverHelper; - } - this._profile.target().runtimeModel.releaseObjectGroup('popover'); + return true; + }, + hide: () => { + target.runtimeModel.releaseObjectGroup('popover'); + objectPopoverHelper.dispose(); + } + }; } _updatePerspectiveOptions() { diff --git a/front_end/sass/SASSSourceMapFactory.js b/front_end/sass/SASSSourceMapFactory.js index 5598cd9ac6..5bcbbb05d4 100644 --- a/front_end/sass/SASSSourceMapFactory.js +++ b/front_end/sass/SASSSourceMapFactory.js @@ -21,8 +21,8 @@ Sass.SASSSourceMapFactory = class { if (!cssModel) return Promise.resolve(/** @type {?SDK.SourceMap} */ (null)); - var header = - cssModel.styleSheetHeaders().find(styleSheetHeader => styleSheetHeader.sourceMapURL === sourceMap.url()); + var headers = cssModel.sourceMapManager().clientsForSourceMap(sourceMap); + var header = headers.length ? headers[0] : null; if (!header) return Promise.resolve(/** @type {?SDK.SourceMap} */ (null)); diff --git a/front_end/sdk/CSSMetadata.js b/front_end/sdk/CSSMetadata.js index 9646937389..b31fb71a39 100644 --- a/front_end/sdk/CSSMetadata.js +++ b/front_end/sdk/CSSMetadata.js @@ -252,6 +252,7 @@ SDK.CSSMetadata._colorAwareProperties = new Set([ 'border-top', 'border-top-color', 'box-shadow', + 'caret-color', 'color', 'column-rule', 'column-rule-color', diff --git a/front_end/sdk/CSSModel.js b/front_end/sdk/CSSModel.js index 088c0ebf21..48575841d3 100644 --- a/front_end/sdk/CSSModel.js +++ b/front_end/sdk/CSSModel.js @@ -38,6 +38,8 @@ SDK.CSSModel = class extends SDK.SDKModel { constructor(target) { super(target); this._domModel = /** @type {!SDK.DOMModel} */ (target.model(SDK.DOMModel)); + /** @type {!SDK.SourceMapManager} */ + this._sourceMapManager = new SDK.SourceMapManager(target); this._agent = target.cssAgent(); this._styleLoader = new SDK.CSSModel.ComputedStyleLoader(this); this._resourceTreeModel = target.model(SDK.ResourceTreeModel); @@ -55,14 +57,16 @@ SDK.CSSModel = class extends SDK.SDKModel { /** @type {!Map.>} */ this._originalStyleSheetText = new Map(); - /** @type {!Multimap} */ - this._sourceMapLoadingStyleSheetsIds = new Multimap(); + this._sourceMapManager.setEnabled(Common.moduleSetting('cssSourceMapsEnabled').get()); + Common.moduleSetting('cssSourceMapsEnabled') + .addChangeListener(event => this._sourceMapManager.setEnabled(/** @type {boolean} */ (event.data))); + } - /** @type {!Map} */ - this._sourceMapByURL = new Map(); - /** @type {!Multimap} */ - this._sourceMapURLToHeaders = new Multimap(); - Common.moduleSetting('cssSourceMapsEnabled').addChangeListener(this._toggleSourceMapSupport, this); + /** + * @return {!SDK.SourceMapManager} + */ + sourceMapManager() { + return this._sourceMapManager; } /** @@ -94,138 +98,6 @@ SDK.CSSModel = class extends SDK.SDKModel { return /** @type {!SDK.CSSModel} */ (node.target().model(SDK.CSSModel)); } - /** - * @param {!Common.Event} event - */ - _toggleSourceMapSupport(event) { - var enabled = /** @type {boolean} */ (event.data); - var headers = this.styleSheetHeaders(); - for (var header of headers) { - if (enabled) - this._attachSourceMap(header); - else - this._detachSourceMap(header); - } - } - - /** - * @param {!SDK.CSSStyleSheetHeader} header - * @return {?SDK.SourceMap} - */ - sourceMapForHeader(header) { - return this._sourceMapByURL.get(header.sourceMapURL) || null; - } - - _sourceMapLoadedForTest() { - } - - /** - * @param {!SDK.SourceMap} sourceMap - * @return {!Array} - */ - headersForSourceMap(sourceMap) { - return this._sourceMapURLToHeaders.get(sourceMap.url()).valuesArray(); - } - - /** - * @param {!SDK.CSSStyleSheetHeader} header - */ - _attachSourceMap(header) { - var sourceMapURL = header.sourceMapURL; - if (!sourceMapURL || !Common.moduleSetting('cssSourceMapsEnabled').get()) - return; - if (this._sourceMapByURL.has(sourceMapURL)) { - attach.call(this, sourceMapURL, header); - return; - } - if (!this._sourceMapLoadingStyleSheetsIds.has(sourceMapURL)) { - SDK.TextSourceMap.load(sourceMapURL, header.sourceURL) - .then(onTextSourceMapLoaded.bind(this, sourceMapURL)) - .then(onSourceMap.bind(this, sourceMapURL)); - } - this._sourceMapLoadingStyleSheetsIds.set(sourceMapURL, header.id); - - /** - * @param {string} sourceMapURL - * @param {?SDK.TextSourceMap} sourceMap - * @return {!Promise} - * @this {SDK.CSSModel} - */ - function onTextSourceMapLoaded(sourceMapURL, sourceMap) { - if (!sourceMap) - return Promise.resolve(/** @type {?SDK.SourceMap} */ (null)); - var factoryExtension = this._factoryForSourceMap(sourceMap); - if (!factoryExtension) - return Promise.resolve(/** @type {?SDK.SourceMap} */ (sourceMap)); - return factoryExtension.instance() - .then(factory => factory.editableSourceMap(this.target(), sourceMap)) - .then(map => map || sourceMap) - .catchException(/** @type {?SDK.SourceMap} */ (null)); - } - - /** - * @param {string} sourceMapURL - * @param {?SDK.SourceMap} sourceMap - * @this {SDK.CSSModel} - */ - function onSourceMap(sourceMapURL, sourceMap) { - this._sourceMapLoadedForTest(); - var styleSheetIds = this._sourceMapLoadingStyleSheetsIds.get(sourceMapURL); - this._sourceMapLoadingStyleSheetsIds.removeAll(sourceMapURL); - if (!sourceMap) - return; - var headers = new Set(); - for (var styleSheetId of styleSheetIds) { - var header = this.styleSheetHeaderForId(styleSheetId); - if (header) - headers.add(header); - } - if (!headers.size) - return; - this._sourceMapByURL.set(sourceMapURL, sourceMap); - for (var header of headers) - attach.call(this, sourceMapURL, header); - } - - /** - * @param {string} sourceMapURL - * @param {!SDK.CSSStyleSheetHeader} header - * @this {SDK.CSSModel} - */ - function attach(sourceMapURL, header) { - this._sourceMapURLToHeaders.set(sourceMapURL, header); - this.dispatchEventToListeners(SDK.CSSModel.Events.SourceMapAttached, header); - } - } - - /** - * @param {!SDK.SourceMap} sourceMap - * @return {?Runtime.Extension} - */ - _factoryForSourceMap(sourceMap) { - var sourceExtensions = new Set(); - for (var url of sourceMap.sourceURLs()) - sourceExtensions.add(Common.ParsedURL.extractExtension(url)); - for (var runtimeExtension of self.runtime.extensions(SDK.SourceMapFactory)) { - var supportedExtensions = new Set(runtimeExtension.descriptor()['extensions']); - if (supportedExtensions.containsAll(sourceExtensions)) - return runtimeExtension; - } - return null; - } - - /** - * @param {!SDK.CSSStyleSheetHeader} header - */ - _detachSourceMap(header) { - if (!header.sourceMapURL || !this._sourceMapURLToHeaders.hasValue(header.sourceMapURL, header)) - return; - this._sourceMapURLToHeaders.remove(header.sourceMapURL, header); - if (!this._sourceMapURLToHeaders.has(header.sourceMapURL)) - this._sourceMapByURL.delete(header.sourceMapURL); - this.dispatchEventToListeners(SDK.CSSModel.Events.SourceMapDetached, header); - } - /** * @return {!SDK.DOMModel} */ @@ -246,7 +118,7 @@ SDK.CSSModel = class extends SDK.SDKModel { if (!header) return original(); - var sourceMap = this.sourceMapForHeader(header); + var sourceMap = this._sourceMapManager.sourceMapForClient(header); if (!sourceMap) return original(); @@ -296,9 +168,7 @@ SDK.CSSModel = class extends SDK.SDKModel { if (!success) return originalAndDetach(); - this._sourceMapByURL.set(header.sourceMapURL, editResult.map); - this.dispatchEventToListeners( - SDK.CSSModel.Events.SourceMapChanged, {sourceMap: editResult.map, newSources: editResult.newSources}); + this._sourceMapManager.applySourceMapEdit(editResult); return Promise.resolve(true); } @@ -311,7 +181,7 @@ SDK.CSSModel = class extends SDK.SDKModel { function onError(header, error) { Common.console.error(Common.UIString('LiveSASS failed: %s', sourceMap.compiledURL())); console.error(error); - this._detachSourceMap(header); + this._sourceMapManager.detachSourceMap(header); return original(); } @@ -330,7 +200,7 @@ SDK.CSSModel = class extends SDK.SDKModel { */ function detachIfSuccess(success) { if (success) - this._detachSourceMap(header); + this._sourceMapManager.detachSourceMap(header); return success; } } @@ -830,7 +700,7 @@ SDK.CSSModel = class extends SDK.SDKModel { frameIdToStyleSheetIds[styleSheetHeader.frameId] = styleSheetIds; } styleSheetIds.push(styleSheetHeader.id); - this._attachSourceMap(styleSheetHeader); + this._sourceMapManager.attachSourceMap(styleSheetHeader, styleSheetHeader.sourceURL, styleSheetHeader.sourceMapURL); this.dispatchEventToListeners(SDK.CSSModel.Events.StyleSheetAdded, styleSheetHeader); } @@ -854,7 +724,7 @@ SDK.CSSModel = class extends SDK.SDKModel { this._styleSheetIdsForURL.remove(url); } this._originalStyleSheetText.remove(header); - this._detachSourceMap(header); + this._sourceMapManager.detachSourceMap(header); this.dispatchEventToListeners(SDK.CSSModel.Events.StyleSheetRemoved, header); } @@ -895,9 +765,9 @@ SDK.CSSModel = class extends SDK.SDKModel { * @this {SDK.CSSModel} */ function callback(error, sourceMapURL) { - this._detachSourceMap(header); + this._sourceMapManager.detachSourceMap(header); header.setSourceMapURL(sourceMapURL); - this._attachSourceMap(header); + this._sourceMapManager.attachSourceMap(header, header.sourceURL, header.sourceMapURL); if (error) return error; if (majorChange) @@ -933,12 +803,9 @@ SDK.CSSModel = class extends SDK.SDKModel { this._styleSheetIdsForURL.clear(); this._styleSheetIdToHeader.clear(); for (var i = 0; i < headers.length; ++i) { - this._detachSourceMap(headers[i]); + this._sourceMapManager.detachSourceMap(headers[i]); this.dispatchEventToListeners(SDK.CSSModel.Events.StyleSheetRemoved, headers[i]); } - this._sourceMapByURL.clear(); - this._sourceMapURLToHeaders.clear(); - this._sourceMapLoadingStyleSheetsIds.clear(); } /** @@ -984,6 +851,14 @@ SDK.CSSModel = class extends SDK.SDKModel { delete this._cachedMatchedCascadeNode; delete this._cachedMatchedCascadePromise; } + + /** + * @override + */ + dispose() { + super.dispose(); + this._sourceMapManager.dispose(); + } }; SDK.SDKModel.register(SDK.CSSModel, SDK.Target.Capability.DOM); @@ -999,10 +874,7 @@ SDK.CSSModel.Events = { PseudoStateForced: Symbol('PseudoStateForced'), StyleSheetAdded: Symbol('StyleSheetAdded'), StyleSheetChanged: Symbol('StyleSheetChanged'), - StyleSheetRemoved: Symbol('StyleSheetRemoved'), - SourceMapAttached: Symbol('SourceMapAttached'), - SourceMapDetached: Symbol('SourceMapDetached'), - SourceMapChanged: Symbol('SourceMapChanged') + StyleSheetRemoved: Symbol('StyleSheetRemoved') }; SDK.CSSModel.MediaTypes = diff --git a/front_end/sdk/CSSStyleSheetHeader.js b/front_end/sdk/CSSStyleSheetHeader.js index e3863709b0..cc76e5beed 100644 --- a/front_end/sdk/CSSStyleSheetHeader.js +++ b/front_end/sdk/CSSStyleSheetHeader.js @@ -44,9 +44,7 @@ SDK.CSSStyleSheetHeader = class { * @param {string=} sourceMapURL */ setSourceMapURL(sourceMapURL) { - var completeSourceMapURL = - this.sourceURL && sourceMapURL ? Common.ParsedURL.completeURL(this.sourceURL, sourceMapURL) : sourceMapURL; - this.sourceMapURL = completeSourceMapURL; + this.sourceMapURL = sourceMapURL; } /** @@ -67,7 +65,7 @@ SDK.CSSStyleSheetHeader = class { * @return {boolean} */ isAnonymousInlineStyleSheet() { - return !this.resourceURL() && !this._cssModel.sourceMapForHeader(this); + return !this.resourceURL() && !this._cssModel.sourceMapManager().sourceMapForClient(this); } /** diff --git a/front_end/sdk/DebuggerModel.js b/front_end/sdk/DebuggerModel.js index 078ee22cfd..5d6fcff8b7 100644 --- a/front_end/sdk/DebuggerModel.js +++ b/front_end/sdk/DebuggerModel.js @@ -1184,6 +1184,36 @@ SDK.DebuggerModel.CallFrame = class { didEvaluateOnCallFrame); } + /** + * @param {string} code + * @param {string} objectGroup + * @param {boolean} includeCommandLineAPI + * @param {boolean} silent + * @param {boolean} returnByValue + * @param {boolean} generatePreview + * @return {!Promise} + */ + evaluatePromise(code, objectGroup, includeCommandLineAPI, silent, returnByValue, generatePreview) { + var fulfill; + var promise = new Promise(x => fulfill = x); + this.evaluate( + code, objectGroup, includeCommandLineAPI, silent, returnByValue, generatePreview, callback.bind(this)); + return promise; + + /** + * @param {?Protocol.Runtime.RemoteObject} result + * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails + * @param {string=} error + * @this {SDK.DebuggerModel.CallFrame} + */ + function callback(result, exceptionDetails, error) { + if (!result || exceptionDetails) + fulfill(null); + else + fulfill(this.debuggerModel._runtimeModel.createRemoteObject(result)); + } + } + /** * @param {function(?Protocol.Error=)=} callback */ diff --git a/front_end/sdk/ResourceTreeModel.js b/front_end/sdk/ResourceTreeModel.js index 40fe2a9603..19244b0e87 100644 --- a/front_end/sdk/ResourceTreeModel.js +++ b/front_end/sdk/ResourceTreeModel.js @@ -90,6 +90,17 @@ SDK.ResourceTreeModel = class extends SDK.SDKModel { return null; } + /** + * @param {boolean=} bypassCache + * @param {string=} scriptToEvaluateOnLoad + */ + static reloadAllPages(bypassCache, scriptToEvaluateOnLoad) { + for (var resourceTreeModel of SDK.targetManager.models(SDK.ResourceTreeModel)) { + if (!resourceTreeModel.target().parentTarget()) + resourceTreeModel.reloadPage(bypassCache, scriptToEvaluateOnLoad); + } + } + _fetchResourceTree() { /** @type {!Map} */ this._frames = new Map(); @@ -721,7 +732,8 @@ SDK.PageDispatcher = class { * @param {number} time */ loadEventFired(time) { - this._resourceTreeModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.Load, time); + this._resourceTreeModel.dispatchEventToListeners( + SDK.ResourceTreeModel.Events.Load, {resourceTreeModel: this._resourceTreeModel, loadTime: time}); } /** diff --git a/front_end/sdk/SourceMapManager.js b/front_end/sdk/SourceMapManager.js new file mode 100644 index 0000000000..b6ab08ac74 --- /dev/null +++ b/front_end/sdk/SourceMapManager.js @@ -0,0 +1,241 @@ +// Copyright 2017 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. + +/** + * @template T + */ +SDK.SourceMapManager = class extends SDK.SDKObject { + /** + * @param {!SDK.Target} target + */ + constructor(target) { + super(target); + + this._isEnabled = true; + + /** @type {!Map} */ + this._relativeSourceURL = new Map(); + /** @type {!Map} */ + this._relativeSourceMapURL = new Map(); + /** @type {!Map} */ + this._resolvedSourceMapURL = new Map(); + + /** @type {!Map} */ + this._sourceMapByURL = new Map(); + /** @type {!Multimap} */ + this._sourceMapURLToLoadingClients = new Multimap(); + /** @type {!Multimap} */ + this._sourceMapURLToClients = new Multimap(); + + SDK.targetManager.addEventListener(SDK.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this); + } + + /** + * @param {boolean} isEnabled + */ + setEnabled(isEnabled) { + if (isEnabled === this._isEnabled) + return; + this._isEnabled = isEnabled; + var clients = Array.from(this._resolvedSourceMapURL.keys()); + for (var client of clients) { + var relativeSourceURL = this._relativeSourceURL.get(client); + var relativeSourceMapURL = this._relativeSourceMapURL.get(client); + this.detachSourceMap(client); + if (isEnabled) + this.attachSourceMap(client, relativeSourceURL, relativeSourceMapURL); + } + } + + /** + * @param {!Common.Event} event + */ + _inspectedURLChanged(event) { + if (event.data !== this.target()) + return; + + var clients = Array.from(this._resolvedSourceMapURL.keys()); + for (var client of clients) { + var relativeSourceURL = this._relativeSourceURL.get(client); + var relativeSourceMapURL = this._relativeSourceMapURL.get(client); + var resolvedSourceMapURL = this._resolvedSourceMapURL.get(client); + var sourceMapURL = this._resolveRelativeURLs(relativeSourceURL, relativeSourceMapURL).sourceMapURL; + if (sourceMapURL !== resolvedSourceMapURL) { + this.detachSourceMap(client); + this.attachSourceMap(client, relativeSourceURL, relativeSourceMapURL); + } + } + } + + /** + * @param {!T} client + * @return {?SDK.SourceMap} + */ + sourceMapForClient(client) { + var sourceMapURL = this._resolvedSourceMapURL.get(client); + return sourceMapURL ? this._sourceMapByURL.get(sourceMapURL) || null : null; + } + + /** + * @param {!SDK.SourceMap} sourceMap + * @return {!Array} + */ + clientsForSourceMap(sourceMap) { + if (this._sourceMapURLToClients.has(sourceMap.url())) + return this._sourceMapURLToClients.get(sourceMap.url()).valuesArray(); + return this._sourceMapURLToLoadingClients.get(sourceMap.url()).valuesArray(); + } + + /** + * @param {!SDK.SourceMap.EditResult} editResult + */ + applySourceMapEdit(editResult) { + console.assert( + this._sourceMapByURL.has(editResult.map.url()), 'Cannot apply edit result for non-existing source map'); + this._sourceMapByURL.set(editResult.map.url(), editResult.map); + this.dispatchEventToListeners( + SDK.SourceMapManager.Events.SourceMapChanged, {sourceMap: editResult.map, newSources: editResult.newSources}); + } + + /** + * @param {string} sourceURL + * @param {string} sourceMapURL + * @return {!{sourceURL: ?string, sourceMapURL: ?string}} + */ + _resolveRelativeURLs(sourceURL, sourceMapURL) { + // |sourceURL| can be a random string, but is generally an absolute path. + // Complete it to inspected page url for relative links. + var resolvedSourceURL = Common.ParsedURL.completeURL(this.target().inspectedURL(), sourceURL); + var resolvedSourceMapURL = resolvedSourceURL ? Common.ParsedURL.completeURL(resolvedSourceURL, sourceMapURL) : null; + return {sourceURL: resolvedSourceURL, sourceMapURL: resolvedSourceMapURL}; + } + + /** + * @param {!T} client + * @param {string} sourceURL + * @param {?string} sourceMapURL + */ + attachSourceMap(client, sourceURL, sourceMapURL) { + if (!sourceMapURL) + return; + console.assert(!this._resolvedSourceMapURL.has(client), 'SourceMap is already attached to client'); + var resolvedURLs = this._resolveRelativeURLs(sourceURL, sourceMapURL); + if (!resolvedURLs.sourceURL || !resolvedURLs.sourceMapURL) + return; + this._relativeSourceURL.set(client, sourceURL); + this._relativeSourceMapURL.set(client, sourceMapURL); + this._resolvedSourceMapURL.set(client, resolvedURLs.sourceMapURL); + + sourceURL = resolvedURLs.sourceURL; + sourceMapURL = resolvedURLs.sourceMapURL; + if (!this._isEnabled) + return; + + if (this._sourceMapByURL.has(sourceMapURL)) { + attach.call(this, sourceMapURL, client); + return; + } + if (!this._sourceMapURLToLoadingClients.has(sourceMapURL)) { + SDK.TextSourceMap.load(sourceMapURL, sourceURL) + .then(onTextSourceMapLoaded.bind(this, sourceMapURL)) + .then(onSourceMap.bind(this, sourceMapURL)); + } + this._sourceMapURLToLoadingClients.set(sourceMapURL, client); + + /** + * @param {string} sourceMapURL + * @param {?SDK.TextSourceMap} sourceMap + * @return {!Promise} + * @this {SDK.SourceMapManager} + */ + function onTextSourceMapLoaded(sourceMapURL, sourceMap) { + if (!sourceMap) + return Promise.resolve(/** @type {?SDK.SourceMap} */ (null)); + var factoryExtension = this._factoryForSourceMap(sourceMap); + if (!factoryExtension) + return Promise.resolve(/** @type {?SDK.SourceMap} */ (sourceMap)); + return factoryExtension.instance() + .then(factory => factory.editableSourceMap(this.target(), sourceMap)) + .then(map => map || sourceMap) + .catchException(/** @type {?SDK.SourceMap} */ (null)); + } + + /** + * @param {string} sourceMapURL + * @param {?SDK.SourceMap} sourceMap + * @this {SDK.SourceMapManager} + */ + function onSourceMap(sourceMapURL, sourceMap) { + this._sourceMapLoadedForTest(); + var clients = this._sourceMapURLToLoadingClients.get(sourceMapURL); + this._sourceMapURLToLoadingClients.removeAll(sourceMapURL); + if (!sourceMap || !clients.size) + return; + this._sourceMapByURL.set(sourceMapURL, sourceMap); + for (var client of clients) + attach.call(this, sourceMapURL, client); + } + + /** + * @param {string} sourceMapURL + * @param {!T} client + * @this {SDK.SourceMapManager} + */ + function attach(sourceMapURL, client) { + this._sourceMapURLToClients.set(sourceMapURL, client); + this.dispatchEventToListeners(SDK.SourceMapManager.Events.SourceMapAttached, client); + } + } + + /** + * @param {!SDK.SourceMap} sourceMap + * @return {?Runtime.Extension} + */ + _factoryForSourceMap(sourceMap) { + var sourceExtensions = new Set(); + for (var url of sourceMap.sourceURLs()) + sourceExtensions.add(Common.ParsedURL.extractExtension(url)); + for (var runtimeExtension of self.runtime.extensions(SDK.SourceMapFactory)) { + var supportedExtensions = new Set(runtimeExtension.descriptor()['extensions']); + if (supportedExtensions.containsAll(sourceExtensions)) + return runtimeExtension; + } + return null; + } + + /** + * @param {!T} client + */ + detachSourceMap(client) { + var sourceMapURL = this._resolvedSourceMapURL.get(client); + this._relativeSourceURL.delete(client); + this._relativeSourceMapURL.delete(client); + this._resolvedSourceMapURL.delete(client); + + if (!sourceMapURL) + return; + if (!this._sourceMapURLToClients.hasValue(sourceMapURL, client)) { + this._sourceMapURLToLoadingClients.remove(sourceMapURL, client); + return; + } + this._sourceMapURLToClients.remove(sourceMapURL, client); + if (!this._sourceMapURLToClients.has(sourceMapURL)) + this._sourceMapByURL.delete(sourceMapURL); + this.dispatchEventToListeners(SDK.SourceMapManager.Events.SourceMapDetached, client); + } + + _sourceMapLoadedForTest() { + } + + dispose() { + SDK.targetManager.removeEventListener( + SDK.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this); + } +}; + +SDK.SourceMapManager.Events = { + SourceMapAttached: Symbol('SourceMapAttached'), + SourceMapDetached: Symbol('SourceMapDetached'), + SourceMapChanged: Symbol('SourceMapChanged') +}; diff --git a/front_end/sdk/Target.js b/front_end/sdk/Target.js index 174c9c9d7f..c0e4ec4b77 100644 --- a/front_end/sdk/Target.js +++ b/front_end/sdk/Target.js @@ -143,7 +143,7 @@ SDK.Target = class extends Protocol.TargetBase { if (!this._modelByConstructor.get(modelClass)) { var capabilities = SDK.SDKModel._capabilitiesByModelClass.get(modelClass); if (capabilities === undefined) - throw 'Model class is not registered'; + throw 'Model class is not registered @' + new Error().stack; if ((this._capabilitiesMask & capabilities) === capabilities) { var model = new modelClass(this); this._modelByConstructor.set(modelClass, model); diff --git a/front_end/sdk/TargetManager.js b/front_end/sdk/TargetManager.js index f779fe5741..90a9472d7d 100644 --- a/front_end/sdk/TargetManager.js +++ b/front_end/sdk/TargetManager.js @@ -99,21 +99,6 @@ SDK.TargetManager = class extends Common.Object { return this._targets[0] ? this._targets[0].inspectedURL() : ''; } - /** - * @param {boolean=} bypassCache - * @param {string=} injectedScript - */ - reloadPage(bypassCache, injectedScript) { - if (!this._targets.length) - return; - - var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(this._targets[0]); - if (!resourceTreeModel) - return; - - resourceTreeModel.reloadPage(bypassCache, injectedScript); - } - /** * @param {function(new:T,!SDK.Target)} modelClass * @param {!SDK.SDKModelObserver} observer @@ -245,7 +230,7 @@ SDK.TargetManager = class extends Common.Object { this._pendingTargets.add(target); target.model(SDK.NetworkManager); - var resourceTreeModel = target.model(SDK.ResourceTreeModel); + target.model(SDK.ResourceTreeModel); /** @type {!SDK.RuntimeModel} */ target.runtimeModel = /** @type {!SDK.RuntimeModel} */ (target.model(SDK.RuntimeModel)); target.model(SDK.DebuggerModel); @@ -256,7 +241,7 @@ SDK.TargetManager = class extends Common.Object { target.model(SDK.ServiceWorkerManager); if (target.hasTargetCapability()) - this._childTargetManagers.set(target, new SDK.ChildTargetManager(this, target, resourceTreeModel)); + this._childTargetManagers.set(target, new SDK.ChildTargetManager(this, target)); // Force creation of models which have observers. for (var modelClass of this._modelObservers.keys()) @@ -265,23 +250,6 @@ SDK.TargetManager = class extends Common.Object { this._targets.push(target); - if (resourceTreeModel && !target.parentTarget()) { - resourceTreeModel[SDK.TargetManager._listenersSymbol] = [ - resourceTreeModel.addEventListener( - SDK.ResourceTreeModel.Events.MainFrameNavigated, - event => this.dispatchEventToListeners(SDK.TargetManager.Events.MainFrameNavigated, event.data)), - resourceTreeModel.addEventListener( - SDK.ResourceTreeModel.Events.Load, - event => this.dispatchEventToListeners(SDK.TargetManager.Events.Load, event.data)), - resourceTreeModel.addEventListener( - SDK.ResourceTreeModel.Events.PageReloadRequested, - event => this.dispatchEventToListeners(SDK.TargetManager.Events.PageReloadRequested, event.data)), - resourceTreeModel.addEventListener( - SDK.ResourceTreeModel.Events.WillReloadPage, - event => this.dispatchEventToListeners(SDK.TargetManager.Events.WillReloadPage, event.data)), - ]; - } - var copy = this._observersForTarget(target); for (var i = 0; i < copy.length; ++i) copy[i].targetAdded(target); @@ -323,10 +291,6 @@ SDK.TargetManager = class extends Common.Object { childTargetManager.dispose(); this._targets.remove(target); - var resourceTreeModel = SDK.ResourceTreeModel.fromTarget(target); - var treeModelListeners = resourceTreeModel && resourceTreeModel[SDK.TargetManager._listenersSymbol]; - if (treeModelListeners) - Common.EventTarget.removeEventListeners(treeModelListeners); for (var modelClass of target.models().keys()) this._modelRemoved(target, modelClass, target.models().get(modelClass)); @@ -391,7 +355,7 @@ SDK.TargetManager = class extends Common.Object { this, 'main', Common.UIString('Node'), SDK.Target.Capability.Target, this._createMainConnection.bind(this), null); target.setInspectedURL('Node'); - this._childTargetManagers.set(target, new SDK.ChildTargetManager(this, target, null)); + this._childTargetManagers.set(target, new SDK.ChildTargetManager(this, target)); Host.userMetrics.actionTaken(Host.UserMetrics.Action.ConnectToNodeJSFromFrontend); return; } @@ -454,9 +418,8 @@ SDK.ChildTargetManager = class { /** * @param {!SDK.TargetManager} targetManager * @param {!SDK.Target} parentTarget - * @param {?SDK.ResourceTreeModel} resourceTreeModel */ - constructor(targetManager, parentTarget, resourceTreeModel) { + constructor(targetManager, parentTarget) { this._targetManager = targetManager; this._parentTarget = parentTarget; this._targetAgent = parentTarget.targetAgent(); @@ -638,17 +601,11 @@ SDK.ChildConnection = class { /** @enum {symbol} */ SDK.TargetManager.Events = { InspectedURLChanged: Symbol('InspectedURLChanged'), - Load: Symbol('Load'), - MainFrameNavigated: Symbol('MainFrameNavigated'), NameChanged: Symbol('NameChanged'), - PageReloadRequested: Symbol('PageReloadRequested'), - WillReloadPage: Symbol('WillReloadPage'), - TargetDisposed: Symbol('TargetDisposed'), SuspendStateChanged: Symbol('SuspendStateChanged'), AvailableNodeTargetsChanged: Symbol('AvailableNodeTargetsChanged') }; -SDK.TargetManager._listenersSymbol = Symbol('SDK.TargetManager.Listeners'); SDK.TargetManager._isWorkerSymbol = Symbol('SDK.TargetManager.IsWorker'); /** diff --git a/front_end/sdk/module.json b/front_end/sdk/module.json index 84b4b79382..a98364b371 100644 --- a/front_end/sdk/module.json +++ b/front_end/sdk/module.json @@ -151,6 +151,7 @@ "ResourceTreeModel.js", "SecurityOriginManager.js", "SourceMap.js", + "SourceMapManager.js", "NetworkManager.js", "NetworkRequest.js", "PaintProfiler.js", diff --git a/front_end/security/SecurityModel.js b/front_end/security/SecurityModel.js index 157f4182ef..273c3a6ba0 100644 --- a/front_end/security/SecurityModel.js +++ b/front_end/security/SecurityModel.js @@ -108,4 +108,14 @@ Security.SecurityDispatcher = class { securityState, schemeIsCryptographic, explanations, insecureContentStatus, summary || null); this._model.dispatchEventToListeners(Security.SecurityModel.Events.SecurityStateChanged, pageSecurityState); } + + + /** + * @override + * @param {number} eventId + * @param {string} errorType + * @param {string} requestURL + */ + certificateError(eventId, errorType, requestURL) { + } }; diff --git a/front_end/security/SecurityPanel.js b/front_end/security/SecurityPanel.js index 0937c3ef87..b33cdad847 100644 --- a/front_end/security/SecurityPanel.js +++ b/front_end/security/SecurityPanel.js @@ -198,6 +198,9 @@ Security.SecurityPanel = class extends UI.PanelWithSidebar { var oldSecurityState = originState.securityState; originState.securityState = this._securityStateMin(oldSecurityState, securityState); if (oldSecurityState !== originState.securityState) { + let securityDetails = /** @type {?Protocol.Network.SecurityDetails} */ (request.securityDetails()); + if (securityDetails) + originState.securityDetails = securityDetails; this._sidebarTree.updateOrigin(origin, securityState); if (originState.originView) originState.originView.setSecurityState(securityState); @@ -347,11 +350,14 @@ Security.SecurityPanel = class extends UI.PanelWithSidebar { // explanations to reflect the new counts. this._mainView.refreshExplanations(); - if (request) { - var origin = Common.ParsedURL.extractOrigin(request.url()); - this._sidebarTree.setMainOrigin(origin); + // If we could not find a matching request (as in the case of clicking + // through an interstitial, see crbug.com/669309), set the origin based upon + // the url data from the MainFrameNavigated event itself. + let origin = Common.ParsedURL.extractOrigin(request ? request.url() : frame.url); + this._sidebarTree.setMainOrigin(origin); + + if (request) this._processRequest(request); - } } _onInterstitialShown() { diff --git a/front_end/source_frame/SourceCodeDiff.js b/front_end/source_frame/SourceCodeDiff.js index 1bf24458ab..546aab6840 100644 --- a/front_end/source_frame/SourceCodeDiff.js +++ b/front_end/source_frame/SourceCodeDiff.js @@ -7,19 +7,31 @@ SourceFrame.SourceCodeDiff = class { /** * @param {!WorkspaceDiff.WorkspaceDiff} workspaceDiff - * @param {!Workspace.UISourceCode} uiSourceCode * @param {!TextEditor.CodeMirrorTextEditor} textEditor */ - constructor(workspaceDiff, uiSourceCode, textEditor) { + constructor(workspaceDiff, textEditor) { this._textEditor = textEditor; this._decorations = []; this._textEditor.installGutter(SourceFrame.SourceCodeDiff.DiffGutterType, true); - this._uiSourceCode = uiSourceCode; + this._uiSourceCode = null; this._workspaceDiff = workspaceDiff; /** @type {!Array}*/ this._animatedLines = []; - this._workspaceDiff.subscribeToDiffChange(this._uiSourceCode, this._update, this); + this._update(); + } + + /** + * @param {?Workspace.UISourceCode} uiSourceCode + */ + setUISourceCode(uiSourceCode) { + if (uiSourceCode === this._uiSourceCode) + return; + if (this._uiSourceCode) + this._workspaceDiff.unsubscribeFromDiffChange(this._uiSourceCode, this._update, this); + if (uiSourceCode) + this._workspaceDiff.subscribeToDiffChange(uiSourceCode, this._update, this); + this._uiSourceCode = uiSourceCode; this._update(); } @@ -155,15 +167,21 @@ SourceFrame.SourceCodeDiff = class { } _update() { - this._workspaceDiff.requestDiff(this._uiSourceCode).then(this._innerUpdate.bind(this)); + if (this._uiSourceCode) + this._workspaceDiff.requestDiff(this._uiSourceCode).then(this._innerUpdate.bind(this)); + else + this._innerUpdate(null); } /** * @param {?Diff.Diff.DiffArray} lineDiff */ _innerUpdate(lineDiff) { - if (!lineDiff) + if (!lineDiff) { + this._updateDecorations(this._decorations, []); + this._decorations = []; return; + } /** @type {!Map} */ var oldDecorations = new Map(); @@ -201,7 +219,8 @@ SourceFrame.SourceCodeDiff = class { } dispose() { - WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(this._uiSourceCode, this._update, this); + if (this._uiSourceCode) + WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(this._uiSourceCode, this._update, this); } }; diff --git a/front_end/source_frame/UISourceCodeFrame.js b/front_end/source_frame/UISourceCodeFrame.js index 444b062228..38935d669c 100644 --- a/front_end/source_frame/UISourceCodeFrame.js +++ b/front_end/source_frame/UISourceCodeFrame.js @@ -39,7 +39,7 @@ SourceFrame.UISourceCodeFrame = class extends SourceFrame.SourceFrame { this.setEditable(this._canEditSource()); if (Runtime.experiments.isEnabled('sourceDiff')) - this._diff = new SourceFrame.SourceCodeDiff(WorkspaceDiff.workspaceDiff(), uiSourceCode, this.textEditor); + this._diff = new SourceFrame.SourceCodeDiff(WorkspaceDiff.workspaceDiff(), this.textEditor); /** @type {?UI.AutocompleteConfig} */ @@ -72,9 +72,9 @@ SourceFrame.UISourceCodeFrame = class extends SourceFrame.SourceFrame { () => UI.context.setFlavor(SourceFrame.UISourceCodeFrame, this)); this._updateStyle(); + this._updateDiffUISourceCode(); - this._errorPopoverHelper = new UI.PopoverHelper(this.element); - this._errorPopoverHelper.initializeCallbacks(this._getErrorAnchor.bind(this), this._showErrorPopover.bind(this)); + this._errorPopoverHelper = new UI.PopoverHelper(this.element, this._getErrorPopoverContent.bind(this)); this._errorPopoverHelper.setHasPadding(true); this._errorPopoverHelper.setTimeout(100, 100); @@ -241,6 +241,7 @@ SourceFrame.UISourceCodeFrame = class extends SourceFrame.SourceFrame { this._installMessageAndDecorationListeners(); this._updateStyle(); this._decorateAllTypes(); + this._updateDiffUISourceCode(); this.onBindingChanged(); } @@ -251,6 +252,17 @@ SourceFrame.UISourceCodeFrame = class extends SourceFrame.SourceFrame { // Overriden in subclasses. } + _updateDiffUISourceCode() { + if (!this._diff) + return; + if (this._persistenceBinding) + this._diff.setUISourceCode(this._persistenceBinding.network); + else if (this._uiSourceCode.project().type() === Workspace.projectTypes.Network) + this._diff.setUISourceCode(this._uiSourceCode); + else + this._diff.setUISourceCode(null); + } + _updateStyle() { this.element.classList.toggle( 'source-frame-unsaved-committed-changes', @@ -388,33 +400,26 @@ SourceFrame.UISourceCodeFrame = class extends SourceFrame.SourceFrame { } /** - * @param {!Element} target * @param {!Event} event - * @return {(!Element|undefined)} + * @return {?UI.PopoverRequest} */ - _getErrorAnchor(target, event) { - var element = target.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon') || - target.enclosingNodeOrSelfWithClass('text-editor-line-decoration-wave'); + _getErrorPopoverContent(event) { + var element = event.target.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon') || + event.target.enclosingNodeOrSelfWithClass('text-editor-line-decoration-wave'); if (!element) - return; - this._errorWavePopoverAnchor = new AnchorBox(event.clientX, event.clientY, 1, 1); - return element; - } - - /** - * @param {!Element|!AnchorBox} anchor - * @param {!UI.GlassPane} popover - * @return {!Promise} - */ - _showErrorPopover(anchor, popover) { - var element = /** @type {!Element} */ (anchor); - var messageBucket = element.enclosingNodeOrSelfWithClass('text-editor-line-decoration')._messageBucket; - var messagesOutline = messageBucket.messagesDescription(); - popover.setContentAnchorBox( - element.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon') ? element.boxInWindow() : - this._errorWavePopoverAnchor); - popover.contentElement.appendChild(messagesOutline); - return Promise.resolve(true); + return null; + var anchor = element.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon') ? + element.boxInWindow() : + new AnchorBox(event.clientX, event.clientY, 1, 1); + return { + box: anchor, + show: popover => { + var messageBucket = element.enclosingNodeOrSelfWithClass('text-editor-line-decoration')._messageBucket; + var messagesOutline = messageBucket.messagesDescription(); + popover.contentElement.appendChild(messagesOutline); + return Promise.resolve(true); + } + }; } _updateBucketDecorations() { diff --git a/front_end/sources/JavaScriptSourceFrame.js b/front_end/sources/JavaScriptSourceFrame.js index 8cd68688cc..e76e19ec47 100644 --- a/front_end/sources/JavaScriptSourceFrame.js +++ b/front_end/sources/JavaScriptSourceFrame.js @@ -44,9 +44,8 @@ Sources.JavaScriptSourceFrame = class extends SourceFrame.UISourceCodeFrame { if (uiSourceCode.project().type() === Workspace.projectTypes.Debugger) this.element.classList.add('source-frame-debugger-script'); - this._popoverHelper = new UI.PopoverHelper(this._scriptsPanel.element, true); - this._popoverHelper.initializeCallbacks( - this._getPopoverAnchor.bind(this), this._showObjectPopover.bind(this), this._onHidePopover.bind(this)); + this._popoverHelper = new UI.PopoverHelper(this._scriptsPanel.element, this._getPopoverRequest.bind(this)); + this._popoverHelper.setDisableOnClick(true); this._popoverHelper.setTimeout(250, 250); this._popoverHelper.setHasPadding(true); this._scriptsPanel.element.addEventListener( @@ -376,150 +375,99 @@ Sources.JavaScriptSourceFrame = class extends SourceFrame.UISourceCodeFrame { return tokenType.startsWith('js-variable') || tokenType.startsWith('js-property') || tokenType === 'js-def'; } - _getPopoverAnchor(element, event) { + /** + * @param {!Event} event + * @return {?UI.PopoverRequest} + */ + _getPopoverRequest(event) { var target = UI.context.flavor(SDK.Target); var debuggerModel = target ? target.model(SDK.DebuggerModel) : null; if (!debuggerModel || !debuggerModel.isPaused()) - return; + return null; var textPosition = this.textEditor.coordinatesToCursorPosition(event.x, event.y); if (!textPosition) - return; + return null; + var mouseLine = textPosition.startLine; var mouseColumn = textPosition.startColumn; var textSelection = this.textEditor.selection().normalize(); + var anchorBox; + var lineNumber; + var startHighlight; + var endHighlight; + if (textSelection && !textSelection.isEmpty()) { if (textSelection.startLine !== textSelection.endLine || textSelection.startLine !== mouseLine || mouseColumn < textSelection.startColumn || mouseColumn > textSelection.endColumn) - return; + return null; var leftCorner = this.textEditor.cursorPositionToCoordinates(textSelection.startLine, textSelection.startColumn); var rightCorner = this.textEditor.cursorPositionToCoordinates(textSelection.endLine, textSelection.endColumn); - var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height); - anchorBox.highlight = { - lineNumber: textSelection.startLine, - startColumn: textSelection.startColumn, - endColumn: textSelection.endColumn - 1 - }; - anchorBox.forSelection = true; - return anchorBox; - } - - var token = this.textEditor.tokenAtTextPosition(textPosition.startLine, textPosition.startColumn); - if (!token || !token.type) - return; - var lineNumber = textPosition.startLine; - var line = this.textEditor.line(lineNumber); - var tokenContent = line.substring(token.startColumn, token.endColumn); - - var isIdentifier = this._isIdentifier(token.type); - if (!isIdentifier && (token.type !== 'js-keyword' || tokenContent !== 'this')) - return; - - var leftCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.startColumn); - var rightCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.endColumn - 1); - var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height); - - anchorBox.highlight = {lineNumber: lineNumber, startColumn: token.startColumn, endColumn: token.endColumn - 1}; - - return anchorBox; - } - - /** - * @param {!AnchorBox} anchorBox - * @return {!Promise} - */ - _resolveObjectForPopover(anchorBox) { - var selectedCallFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame); - if (!selectedCallFrame) - return Promise.resolve(/** @type {?SDK.RemoteObject} */ (null)); - var lineNumber = anchorBox.highlight.lineNumber; - var startHighlight = anchorBox.highlight.startColumn; - var endHighlight = anchorBox.highlight.endColumn; - var line = this.textEditor.line(lineNumber); - if (!anchorBox.forSelection) { + anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height); + lineNumber = textSelection.startLine; + startHighlight = textSelection.startColumn; + endHighlight = textSelection.endColumn - 1; + } else { + var token = this.textEditor.tokenAtTextPosition(textPosition.startLine, textPosition.startColumn); + if (!token || !token.type) + return null; + lineNumber = textPosition.startLine; + var line = this.textEditor.line(lineNumber); + var tokenContent = line.substring(token.startColumn, token.endColumn); + + var isIdentifier = this._isIdentifier(token.type); + if (!isIdentifier && (token.type !== 'js-keyword' || tokenContent !== 'this')) + return null; + + var leftCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.startColumn); + var rightCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.endColumn - 1); + anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height); + + startHighlight = token.startColumn; + endHighlight = token.endColumn - 1; while (startHighlight > 1 && line.charAt(startHighlight - 1) === '.') { - var token = this.textEditor.tokenAtTextPosition(lineNumber, startHighlight - 2); - if (!token || !token.type) - return Promise.resolve(/** @type {?SDK.RemoteObject} */ (null)); - startHighlight = token.startColumn; + var tokenBefore = this.textEditor.tokenAtTextPosition(lineNumber, startHighlight - 2); + if (!tokenBefore || !tokenBefore.type) + return null; + startHighlight = tokenBefore.startColumn; } } - var evaluationText = line.substring(startHighlight, endHighlight + 1); - return Sources.SourceMapNamesResolver - .resolveExpression( - selectedCallFrame, evaluationText, this._debuggerSourceCode, lineNumber, startHighlight, endHighlight) - .then(onResolve.bind(this)); - - /** - * @param {?string=} text - * @return {!Promise} - * @this {Sources.JavaScriptSourceFrame} - */ - function onResolve(text) { - var fulfill; - var promise = new Promise(x => fulfill = x); - selectedCallFrame.evaluate( - text || evaluationText, 'popover', false, true, false, false, showObjectPopover.bind(this, fulfill)); - return promise; - } - /** - * @param {function(?SDK.RemoteObject)} fulfill - * @param {?Protocol.Runtime.RemoteObject} result - * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails - * @this {Sources.JavaScriptSourceFrame} - */ - function showObjectPopover(fulfill, result, exceptionDetails) { - var target = UI.context.flavor(SDK.Target); - var potentiallyUpdatedCallFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame); - if (selectedCallFrame !== potentiallyUpdatedCallFrame || !result || exceptionDetails) { - fulfill(null); - return; - } - this._popoverAnchorBox = anchorBox; - this._popoverTarget = target; - var highlightRange = new Common.TextRange(lineNumber, startHighlight, lineNumber, endHighlight); - this._popoverAnchorBox._highlightDescriptor = - this.textEditor.highlightRange(highlightRange, 'source-frame-eval-expression'); - fulfill(target.runtimeModel.createRemoteObject(result)); - } - } + var objectPopoverHelper; + var highlightDescriptor; - /** - * @param {!AnchorBox|!Element} anchorBox - * @param {!UI.GlassPane} popover - * @return {!Promise} - */ - _showObjectPopover(anchorBox, popover) { - return this._resolveObjectForPopover(/** @type {!AnchorBox} */ (anchorBox)).then(object => { - if (!object) - return false; - return ObjectUI.ObjectPopoverHelper.buildObjectPopover(object, popover).then(objectPopoverHelper => { - if (!objectPopoverHelper) { - this._onHidePopover(); // Cleanup artifacts from _resolveObjectForPopover. + return { + box: anchorBox, + show: async popover => { + var selectedCallFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame); + if (!selectedCallFrame) + return false; + var evaluationText = this.textEditor.line(lineNumber).substring(startHighlight, endHighlight + 1); + var resolvedText = await Sources.SourceMapNamesResolver.resolveExpression( + selectedCallFrame, evaluationText, this._debuggerSourceCode, lineNumber, startHighlight, endHighlight); + var remoteObject = await selectedCallFrame.evaluatePromise( + resolvedText || evaluationText, 'popover', false, true, false, false); + if (!remoteObject) + return false; + objectPopoverHelper = await ObjectUI.ObjectPopoverHelper.buildObjectPopover(remoteObject, popover); + var potentiallyUpdatedCallFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame); + if (!objectPopoverHelper || selectedCallFrame !== potentiallyUpdatedCallFrame) { + target.runtimeModel.releaseObjectGroup('popover'); + if (objectPopoverHelper) + objectPopoverHelper.dispose(); return false; } - this._objectPopoverHelper = objectPopoverHelper; + var highlightRange = new Common.TextRange(lineNumber, startHighlight, lineNumber, endHighlight); + highlightDescriptor = this.textEditor.highlightRange(highlightRange, 'source-frame-eval-expression'); return true; - }); - }); - } - - _onHidePopover() { - if (this._objectPopoverHelper) { - this._objectPopoverHelper.dispose(); - delete this._objectPopoverHelper; - } - if (this._popoverTarget) { - this._popoverTarget.runtimeModel.releaseObjectGroup('popover'); - delete this._popoverTarget; - } - if (this._popoverAnchorBox) { - if (this._popoverAnchorBox._highlightDescriptor) - this.textEditor.removeHighlight(this._popoverAnchorBox._highlightDescriptor); - delete this._popoverAnchorBox; - } + }, + hide: () => { + objectPopoverHelper.dispose(); + target.runtimeModel.releaseObjectGroup('popover'); + this.textEditor.removeHighlight(highlightDescriptor); + } + }; } _onKeyDown(event) { diff --git a/front_end/timeline/TimelinePanel.js b/front_end/timeline/TimelinePanel.js index ee00e525df..7ba8493cf7 100644 --- a/front_end/timeline/TimelinePanel.js +++ b/front_end/timeline/TimelinePanel.js @@ -102,8 +102,10 @@ Timeline.TimelinePanel = class extends UI.Panel { this._createFileSelector(); - SDK.targetManager.addEventListener(SDK.TargetManager.Events.PageReloadRequested, this._pageReloadRequested, this); - SDK.targetManager.addEventListener(SDK.TargetManager.Events.Load, this._loadEventFired, this); + SDK.targetManager.addModelListener( + SDK.ResourceTreeModel, SDK.ResourceTreeModel.Events.PageReloadRequested, this._pageReloadRequested, this); + SDK.targetManager.addModelListener( + SDK.ResourceTreeModel, SDK.ResourceTreeModel.Events.Load, this._loadEventFired, this); if (Runtime.experiments.isEnabled('timelineMultipleMainViews')) { var viewMode = Timeline.TimelinePanel.ViewMode; @@ -501,18 +503,16 @@ Timeline.TimelinePanel = class extends UI.Panel { } /** + * @param {!SDK.TracingManager} tracingManager * @param {boolean} userInitiated * @return {!Promise} */ - _startRecording(userInitiated) { + _startRecording(tracingManager, userInitiated) { console.assert(!this._statusPane, 'Status pane is already opened.'); - var tracingManagers = SDK.targetManager.models(SDK.TracingManager); - if (!tracingManagers.length) - return Promise.resolve(); this._setState(Timeline.TimelinePanel.State.StartPending); this._showRecordingStarted(); - this._autoRecordGeneration = userInitiated ? null : Symbol('Generation'); + this._autoRecordGeneration = userInitiated ? null : {tracingManager: tracingManager}; var enabledTraceProviders = Extensions.extensionServer.traceProviders().filter( provider => Timeline.TimelinePanel._settingForTraceProvider(provider).get()); @@ -523,7 +523,7 @@ Timeline.TimelinePanel = class extends UI.Panel { }; this._pendingPerformanceModel = new Timeline.PerformanceModel(); - this._controller = new Timeline.TimelineController(tracingManagers[0], this._pendingPerformanceModel, this); + this._controller = new Timeline.TimelineController(tracingManager, this._pendingPerformanceModel, this); Host.userMetrics.actionTaken( userInitiated ? Host.UserMetrics.Action.TimelineStarted : Host.UserMetrics.Action.TimelinePageReloadStarted); this._setUIControlsEnabled(false); @@ -558,10 +558,13 @@ Timeline.TimelinePanel = class extends UI.Panel { } _toggleRecording() { - if (this._state === Timeline.TimelinePanel.State.Idle) - this._startRecording(true); - else if (this._state === Timeline.TimelinePanel.State.Recording) + if (this._state === Timeline.TimelinePanel.State.Idle) { + var tracingManagers = SDK.targetManager.models(SDK.TracingManager); + if (tracingManagers.length) + this._startRecording(tracingManagers[0], true); + } else if (this._state === Timeline.TimelinePanel.State.Recording) { this._stopRecording(); + } } _clear() { @@ -785,15 +788,20 @@ Timeline.TimelinePanel = class extends UI.Panel { if (this._state !== Timeline.TimelinePanel.State.Idle || !this.isShowing()) return; var resourceTreeModel = /** @type {!SDK.ResourceTreeModel} */ (event.data); + var tracingManager = resourceTreeModel.target().model(SDK.TracingManager); + if (resourceTreeModel.target() !== SDK.targetManager.mainTarget() || !tracingManager) + return; + resourceTreeModel.suspendReload(); - this._startRecording(false).then(() => resourceTreeModel.resumeReload()); + this._startRecording(tracingManager, false).then(() => resourceTreeModel.resumeReload()); } /** * @param {!Common.Event} event */ _loadEventFired(event) { - if (this._state !== Timeline.TimelinePanel.State.Recording || !this._autoRecordGeneration) + if (this._state !== Timeline.TimelinePanel.State.Recording || !this._autoRecordGeneration || + this._autoRecordGeneration.tracingManager.target() !== event.data.resourceTreeModel.target()) return; setTimeout(stopRecordingOnReload.bind(this, this._autoRecordGeneration), this._millisecondsToRecordAfterLoadEvent); diff --git a/front_end/timeline/TimelineUIUtils.js b/front_end/timeline/TimelineUIUtils.js index 614b509974..ee01b9a3a9 100644 --- a/front_end/timeline/TimelineUIUtils.js +++ b/front_end/timeline/TimelineUIUtils.js @@ -694,7 +694,7 @@ Timeline.TimelineUIUtils = class { var url = TimelineModel.TimelineData.forEvent(event).url; event[Timeline.TimelineUIUtils._previewElementSymbol] = await new Promise(fulfill => { if (url) - Components.DOMPresentationUtils.buildImagePreviewContents(target, url, false, fulfill); + Components.DOMPresentationUtils.buildImagePreviewContents(target, url, false).then(fulfill); else if (TimelineModel.TimelineData.forEvent(event).picture) Timeline.TimelineUIUtils.buildPicturePreviewContent(event, target, fulfill); else @@ -1131,13 +1131,15 @@ Timeline.TimelineUIUtils = class { * @param {function(?Element)} fulfill */ function action(fulfill) { - Components.DOMPresentationUtils.buildImagePreviewContents( - /** @type {!SDK.Target} */ (target), request.url, false, saveImage); + Components.DOMPresentationUtils + .buildImagePreviewContents( + /** @type {!SDK.Target} */ (target), request.url, false) + .then(saveImage); /** - * @param {!Element=} element + * @param {?Element} element */ function saveImage(element) { - request.previewElement = element || null; + request.previewElement = element; fulfill(request.previewElement); } } diff --git a/front_end/ui/Popover.js b/front_end/ui/Popover.js index 5bc0a5f6d3..e96c3d01c3 100644 --- a/front_end/ui/Popover.js +++ b/front_end/ui/Popover.js @@ -33,29 +33,22 @@ */ UI.PopoverHelper = class { /** - * @param {!Element} panelElement - * @param {boolean=} disableOnClick + * @param {!Element} container + * @param {function(!Event):?UI.PopoverRequest} getRequest */ - constructor(panelElement, disableOnClick) { - this._disableOnClick = !!disableOnClick; + constructor(container, getRequest) { + this._disableOnClick = false; this._hasPadding = false; - panelElement.addEventListener('mousedown', this._mouseDown.bind(this), false); - panelElement.addEventListener('mousemove', this._mouseMove.bind(this), false); - panelElement.addEventListener('mouseout', this._mouseOut.bind(this), false); + this._getRequest = getRequest; + this._scheduledRequest = null; + /** @type {?function()} */ + this._hidePopoverCallback = null; + container.addEventListener('mousedown', this._mouseDown.bind(this), false); + container.addEventListener('mousemove', this._mouseMove.bind(this), false); + container.addEventListener('mouseout', this._mouseOut.bind(this), false); this.setTimeout(1000, 500); } - /** - * @param {function(!Element, !Event):(!Element|!AnchorBox|undefined)} getAnchor - * @param {function((!Element|!AnchorBox), !UI.GlassPane):!Promise} showPopover - * @param {function()=} onHide - */ - initializeCallbacks(getAnchor, showPopover, onHide) { - this._getAnchor = getAnchor; - this._showPopover = showPopover; - this._onHide = onHide; - } - /** * @param {number} timeout * @param {number=} hideTimeout @@ -76,147 +69,182 @@ UI.PopoverHelper = class { } /** - * @param {!MouseEvent} event + * @param {boolean} disableOnClick + */ + setDisableOnClick(disableOnClick) { + this._disableOnClick = disableOnClick; + } + + /** + * @param {!Event} event * @return {boolean} */ - _eventInHoverElement(event) { - if (!this._hoverElement) - return false; - var box = this._hoverElement instanceof AnchorBox ? this._hoverElement : this._hoverElement.boxInWindow(); - return ( - box.x <= event.clientX && event.clientX <= box.x + box.width && box.y <= event.clientY && - event.clientY <= box.y + box.height); + _eventInScheduledContent(event) { + return this._scheduledRequest ? this._scheduledRequest.box.contains(event.clientX, event.clientY) : false; } + /** + * @param {!Event} event + */ _mouseDown(event) { - if (this._disableOnClick || !this._eventInHoverElement(event)) { + if (this._disableOnClick || !this._eventInScheduledContent(event)) { this.hidePopover(); } else { - this._killHidePopoverTimer(); - this._handleMouseAction(event, true); + this._stopHidePopoverTimer(); + this._stopShowPopoverTimer(); + this._startShowPopoverTimer(event, 0); } } + /** + * @param {!Event} event + */ _mouseMove(event) { // Pretend that nothing has happened. - if (this._eventInHoverElement(event)) + if (this._eventInScheduledContent(event)) return; this._startHidePopoverTimer(); - this._handleMouseAction(event, false); + this._stopShowPopoverTimer(); + if (event.which && this._disableOnClick) + return; + this._startShowPopoverTimer( + event, this.isPopoverVisible() ? Math.max(this._timeout * 0.6, this._hideTimeout) : this._timeout); } - _popoverMouseOut(event) { - if (!this.isPopoverVisible()) + /** + * @param {!Event} event + */ + _popoverMouseMove(event) { + this._stopHidePopoverTimer(); + } + + /** + * @param {!UI.GlassPane} popover + * @param {!Event} event + */ + _popoverMouseOut(popover, event) { + if (!popover.isShowing()) return; - if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(this._popover.contentElement)) + if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(popover.contentElement)) this._startHidePopoverTimer(); } + /** + * @param {!Event} event + */ _mouseOut(event) { if (!this.isPopoverVisible()) return; - if (!this._eventInHoverElement(event)) + if (!this._eventInScheduledContent(event)) this._startHidePopoverTimer(); } _startHidePopoverTimer() { - // User has 500ms (this._hideTimeout) to reach the popup. - if (!this._popover || this._hidePopoverTimer) + // User has this._hideTimeout to reach the popup. + if (!this._hidePopoverCallback || this._hidePopoverTimer) return; - /** - * @this {UI.PopoverHelper} - */ - function doHide() { + this._hidePopoverTimer = setTimeout(() => { this._hidePopover(); delete this._hidePopoverTimer; - } - this._hidePopoverTimer = setTimeout(doHide.bind(this), this._hideTimeout); + }, this._hideTimeout); } - _handleMouseAction(event, isMouseDown) { - this._resetHoverTimer(); - if (event.which && this._disableOnClick) - return; - this._hoverElement = this._getAnchor(event.target, event); - if (!this._hoverElement) + /** + * @param {!Event} event + * @param {number} timeout + */ + _startShowPopoverTimer(event, timeout) { + this._scheduledRequest = this._getRequest.call(null, event); + if (!this._scheduledRequest) return; - const toolTipDelay = isMouseDown ? 0 : (this._popup ? this._timeout * 0.6 : this._timeout); - this._hoverTimer = - setTimeout(this._mouseHover.bind(this, this._hoverElement, event.target.ownerDocument), toolTipDelay); + + this._showPopoverTimer = setTimeout(() => { + delete this._showPopoverTimer; + this._showPopover(event.target.ownerDocument); + }, timeout); } - _resetHoverTimer() { - if (this._hoverTimer) { - clearTimeout(this._hoverTimer); - delete this._hoverTimer; - } + _stopShowPopoverTimer() { + if (!this._showPopoverTimer) + return; + clearTimeout(this._showPopoverTimer); + delete this._showPopoverTimer; } /** * @return {boolean} */ isPopoverVisible() { - return !!this._popover; + return !!this._hidePopoverCallback; } hidePopover() { - this._resetHoverTimer(); + this._stopShowPopoverTimer(); this._hidePopover(); } _hidePopover() { - if (!this._popover) + if (!this._hidePopoverCallback) return; - - delete UI.PopoverHelper._popover; - if (this._onHide) - this._onHide(); - - if (this._popover.isShowing()) - this._popover.hide(); - delete this._popover; - this._hoverElement = null; + this._hidePopoverCallback.call(null); + this._hidePopoverCallback = null; } - _mouseHover(element, document) { - delete this._hoverTimer; - this._hidePopover(); - this._hoverElement = element; - - this._popover = new UI.GlassPane(); - this._popover.registerRequiredCSS('ui/popover.css'); - this._popover.setBlockPointerEvents(false); - this._popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent); - this._popover.setShowArrow(true); - this._popover.contentElement.classList.toggle('has-padding', this._hasPadding); - this._popover.contentElement.addEventListener('mousemove', this._killHidePopoverTimer.bind(this), true); - this._popover.contentElement.addEventListener('mouseout', this._popoverMouseOut.bind(this), true); - this._popover.setContentAnchorBox( - this._hoverElement instanceof AnchorBox ? this._hoverElement : this._hoverElement.boxInWindow()); - - // This should not happen, but we hide previous popover to be on the safe side. - if (UI.PopoverHelper._popover) { - console.error('One popover is already visible'); - UI.PopoverHelper._popover.hide(); - } - UI.PopoverHelper._popover = this._popover; - var popover = this._popover; - this._showPopover(element, this._popover).then(success => { - if (success && this._popover === popover && this._hoverElement === element) - popover.show(document); + /** + * @param {!Document} document + */ + _showPopover(document) { + var popover = new UI.GlassPane(); + popover.registerRequiredCSS('ui/popover.css'); + popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent); + popover.setBlockPointerEvents(false); + popover.setShowArrow(true); + var request = this._scheduledRequest; + request.show.call(null, popover).then(success => { + if (!success) + return; + + if (this._scheduledRequest !== request) { + if (request.hide) + request.hide.call(null); + return; + } + + // This should not happen, but we hide previous popover to be on the safe side. + if (UI.PopoverHelper._popoverHelper) { + console.error('One popover is already visible'); + UI.PopoverHelper._popoverHelper.hidePopover(); + } + UI.PopoverHelper._popoverHelper = this; + + popover.contentElement.classList.toggle('has-padding', this._hasPadding); + popover.contentElement.addEventListener('mousemove', this._popoverMouseMove.bind(this), true); + popover.contentElement.addEventListener('mouseout', this._popoverMouseOut.bind(this, popover), true); + popover.setContentAnchorBox(request.box); + popover.show(document); + + this._hidePopoverCallback = () => { + if (request.hide) + request.hide.call(null); + popover.hide(); + delete UI.PopoverHelper._popoverHelper; + }; }); } - _killHidePopoverTimer() { - if (this._hidePopoverTimer) { - clearTimeout(this._hidePopoverTimer); - delete this._hidePopoverTimer; + _stopHidePopoverTimer() { + if (!this._hidePopoverTimer) + return; + clearTimeout(this._hidePopoverTimer); + delete this._hidePopoverTimer; - // We know that we reached the popup, but we might have moved over other elements. - // Discard pending command. - this._resetHoverTimer(); - } + // We know that we reached the popup, but we might have moved over other elements. + // Discard pending command. + this._stopShowPopoverTimer(); } }; + +/** @typedef {{box: !AnchorBox, show:(function(!UI.GlassPane):!Promise), hide:(function()|undefined)}} */ +UI.PopoverRequest;