Skip to content

Commit

Permalink
[DevTools] Rework Popover API
Browse files Browse the repository at this point in the history
- Client now provides a method which resolves event to UI.PopoverRequest.
- We can be sure that show/hide methods are called in pairs.
- We can now scope additional work and local variables instead of passing them strangely through the AnchorBox.

BUG=none

Review-Url: https://codereview.chromium.org/2747553002
Cr-Commit-Position: refs/heads/master@{#458663}
  • Loading branch information
dgozman authored and Commit bot committed Mar 22, 2017
1 parent bfea3be commit 8713e2d
Show file tree
Hide file tree
Showing 15 changed files with 460 additions and 541 deletions.
84 changes: 39 additions & 45 deletions front_end/animation/AnimationTimeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -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...');
Expand Down Expand Up @@ -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<boolean>}
*/
_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.<!Image>} 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.<!Image>} screenshots
*/
function onFirstScreenshotLoaded(screenshots) {
new Animation.AnimationScreenshotPopover(screenshots).show(popover.contentElement);
fulfill(true);
}
}
};
}

_togglePauseAll() {
Expand Down
28 changes: 11 additions & 17 deletions front_end/components/DOMPresentationUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<?Element>}
*/
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
Expand Down Expand Up @@ -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);
}
};

Expand Down
9 changes: 9 additions & 0 deletions front_end/dom_extension/DOMExtension.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};

/**
Expand Down
51 changes: 19 additions & 32 deletions front_end/elements/ElementsPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>}
*/
_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) {
Expand Down Expand Up @@ -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) {
Expand Down
70 changes: 29 additions & 41 deletions front_end/elements/ElementsTreeOutline.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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<!Object|undefined>}
*/
_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();

/**
Expand All @@ -571,32 +585,6 @@ Elements.ElementsTreeOutline = class extends UI.TreeOutline {
}
}

/**
* @param {!Element|!AnchorBox} link
* @param {!UI.GlassPane} popover
* @return {!Promise<boolean>}
*/
_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);

Expand Down
Loading

0 comments on commit 8713e2d

Please sign in to comment.