author | Daisuke Akatsuka <dakatsuka@mozilla.com> |
Sat, 28 Apr 2018 10:48:23 +0900 | |
changeset 416196 | cd392b05865ea0059a8134e57a8db6e6cdf71a8b |
parent 416195 | a1666927ab16389f635786c38ad16a91acef5494 |
child 416197 | d7008cb7d66a3a7984873d1b6b9201d76f31a0f1 |
push id | 33918 |
push user | nerli@mozilla.com |
push date | Sun, 29 Apr 2018 09:47:13 +0000 |
treeherder | mozilla-central@afbec7f03bd8 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | gl |
bugs | 1453010 |
milestone | 61.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/devtools/client/inspector/animation/components/AnimationTarget.js +++ b/devtools/client/inspector/animation/components/AnimationTarget.js @@ -49,17 +49,17 @@ class AnimationTarget extends Component } shouldComponentUpdate(nextProps, nextState) { return this.state.nodeFront !== nextState.nodeFront || this.props.highlightedNode !== nextState.highlightedNode; } async updateNodeFront(animation) { - const { emitEventForTest, getNodeFromActor } = this.props; + const { getNodeFromActor } = this.props; // Try and get it from the playerFront directly. let nodeFront = animation.animationTargetNodeFront; // Next, get it from the walkerActor if it wasn't found. if (!nodeFront) { try { nodeFront = await getNodeFromActor(animation.actorID); @@ -68,21 +68,21 @@ class AnimationTarget extends Component // attributed to the panel having been destroyed in the meantime, this // error needs to be logged and render needs to stop. console.error(e); return; } } this.setState({ nodeFront }); - emitEventForTest("animation-target-rendered"); } render() { const { + emitEventForTest, onHideBoxModelHighlighter, onShowBoxModelHighlighterForNode, highlightedNode, setHighlightedNode, setSelectedNode, } = this.props; const { nodeFront } = this.state; @@ -90,16 +90,18 @@ class AnimationTarget extends Component if (!nodeFront) { return dom.div( { className: "animation-target" } ); } + emitEventForTest("animation-target-rendered"); + const isHighlighted = nodeFront.actorID === highlightedNode; return dom.div( { className: "animation-target" + (isHighlighted ? " highlighting" : ""), }, Rep(
--- a/devtools/client/inspector/animation/test/browser.ini +++ b/devtools/client/inspector/animation/test/browser.ini @@ -19,16 +19,17 @@ support-files = [browser_animation_animated-property-list.js] [browser_animation_animated-property-list_unchanged-items.js] [browser_animation_animated-property-name.js] [browser_animation_animation-detail_close-button.js] [browser_animation_animation-detail_title.js] [browser_animation_animation-detail_visibility.js] [browser_animation_animation-list.js] [browser_animation_animation-target.js] +[browser_animation_animation-target_highlight.js] [browser_animation_animation-timeline-tick.js] [browser_animation_current-time-label.js] [browser_animation_current-time-scrubber.js] [browser_animation_empty_on_invalid_nodes.js] [browser_animation_inspector_exists.js] [browser_animation_keyframes-graph_computed-value-path.js] [browser_animation_keyframes-graph_computed-value-path_easing-hint.js] [browser_animation_keyframes-graph_keyframe-marker.js]
--- a/devtools/client/inspector/animation/test/browser_animation_animation-target.js +++ b/devtools/client/inspector/animation/test/browser_animation_animation-target.js @@ -2,34 +2,42 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; // Test for following AnimationTarget component works. // * element existance // * number of elements // * content of element +// * select an animated node by clicking on inspect node +// * title of inspect icon add_task(async function() { await addTab(URL_ROOT + "doc_simple_animation.html"); await removeAnimatedElementsExcept([".animated", ".long"]); - const { animationInspector, inspector, panel } = await openAnimationInspector(); + const { animationInspector, panel } = await openAnimationInspector(); info("Checking the animation target elements existance"); const animationItemEls = panel.querySelectorAll(".animation-list .animation-item"); is(animationItemEls.length, animationInspector.state.animations.length, "Number of animation target element should be same to number of animations " + "that displays"); for (const animationItemEl of animationItemEls) { const animationTargetEl = animationItemEl.querySelector(".animation-target"); ok(animationTargetEl, "The animation target element should be in each animation item element"); } + info("Checking the selecting an animated node by clicking the target node"); + await clickOnTargetNode(animationInspector, panel, 0); + is(panel.querySelectorAll(".animation-target").length, 1, + "The length of animations should be 1"); + info("Checking the content of animation target"); - await selectNodeAndWaitForAnimations(".animated", inspector); const animationTargetEl = panel.querySelector(".animation-list .animation-item .animation-target"); is(animationTargetEl.textContent, "div.ball.animated", "The target element's content is correct"); ok(animationTargetEl.querySelector(".objectBox"), "objectBox is in the page exists"); + ok(animationTargetEl.querySelector(".open-inspector").title, + INSPECTOR_L10N.getStr("inspector.nodePreview.highlightNodeLabel")); });
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/animation/test/browser_animation_animation-target_highlight.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for following highlighting related. +// * highlight when mouse over on a target node +// * unhighlight when mouse out from the above element +// * lock highlighting when click on the inspect icon in animation target component +// * add 'highlighting' class to animation target component during locking +// * unlock highlighting when click on the above icon +// * lock highlighting when click on the other inspect icon +// * if the locked node has multi animations, +// the class will add to those animation target as well + +add_task(async function() { + await addTab(URL_ROOT + "doc_simple_animation.html"); + await removeAnimatedElementsExcept([".animated", ".multi"]); + const { animationInspector, panel, toolbox } = await openAnimationInspector(); + + info("Check highlighting when mouse over on a target node"); + let onHighlight = toolbox.once("node-highlight"); + mouseOverOnTargetNode(animationInspector, panel, 0); + let nodeFront = await onHighlight; + assertNodeFront(nodeFront, "DIV", "ball animated"); + + info("Check unhighlighting when mouse out on a target node"); + let onUnhighlight = toolbox.once("node-unhighlight"); + mouseOutOnTargetNode(animationInspector, panel, 0); + await onUnhighlight; + ok(true, "Unhighlighted the targe node"); + + info("Check node is highlighted when the inspect icon is clicked"); + onHighlight = toolbox.once("node-highlight"); + await clickOnInspectIcon(animationInspector, panel, 0); + nodeFront = await onHighlight; + assertNodeFront(nodeFront, "DIV", "ball animated"); + ok(panel.querySelectorAll(".animation-target")[0].classList.contains("highlighting"), + "The highlighted animation target element should have 'highlighting' class"); + + info("Check if the animation target is still highlighted on mouse out"); + mouseOutOnTargetNode(animationInspector, panel, 0); + await wait(500); + ok(panel.querySelectorAll(".animation-target")[0].classList.contains("highlighting"), + "The highlighted element still should have 'highlighting' class"); + + info("Highlighting another animation target"); + onHighlight = toolbox.once("node-highlight"); + await clickOnInspectIcon(animationInspector, panel, 1); + nodeFront = await onHighlight; + assertNodeFront(nodeFront, "DIV", "ball multi"); + + info("Check the highlighted state of the animation targets"); + const animationTargetEls = panel.querySelectorAll(".animation-target"); + ok(!animationTargetEls[0].classList.contains("highlighting"), + "The animation target[0] should not have 'highlighting' class"); + ok(animationTargetEls[1].classList.contains("highlighting"), + "The animation target[1] should have 'highlighting' class"); + ok(animationTargetEls[2].classList.contains("highlighting"), + "The animation target[2] should have 'highlighting' class"); +}); + +function assertNodeFront(nodeFront, tagName, classValue) { + is(nodeFront.tagName, "DIV", + "The highlighted node has the correct tagName"); + is(nodeFront.attributes[0].name, "class", + "The highlighted node has the correct attributes"); + is(nodeFront.attributes[0].value, classValue, + "The highlighted node has the correct class"); +}
--- a/devtools/client/inspector/animation/test/head.js +++ b/devtools/client/inspector/animation/test/head.js @@ -93,97 +93,98 @@ addTab = async function(url) { const removeAnimatedElementsExcept = async function(selectors) { return executeInContent("Test:RemoveAnimatedElementsExcept", { selectors }); }; /** * Click on an animation in the timeline to select it. * * @param {AnimationInspector} animationInspector. - * @param {AnimationsPanel} panel - * The panel instance. + * @param {DOMElement} panel + * #animation-container element. * @param {Number} index * The index of the animation to click on. */ const clickOnAnimation = async function(animationInspector, panel, index) { info("Click on animation " + index + " in the timeline"); const summaryGraphEl = panel.querySelectorAll(".animation-summary-graph")[index]; await clickOnSummaryGraph(animationInspector, panel, summaryGraphEl); }; /** * Click on an animation by given selector of node which is target element of animation. * * @param {AnimationInspector} animationInspector. - * @param {AnimationsPanel} panel - * The panel instance. + * @param {DOMElement} panel + * #animation-container element. * @param {String} selector * Selector of node which is target element of animation. */ const clickOnAnimationByTargetSelector = async function(animationInspector, panel, selector) { info(`Click on animation whose selector of target element is '${ selector }'`); const animationItemEl = findAnimationItemElementsByTargetSelector(panel, selector); const summaryGraphEl = animationItemEl.querySelector(".animation-summary-graph"); await clickOnSummaryGraph(animationInspector, panel, summaryGraphEl); }; /** * Click on close button for animation detail pane. * - * @param {AnimationsPanel} panel - * The panel instance. + * @param {DOMElement} panel + * #animation-container element. */ const clickOnDetailCloseButton = function(panel) { info("Click on close button for animation detail pane"); const buttonEl = panel.querySelector(".animation-detail-close-button"); const bounds = buttonEl.getBoundingClientRect(); const x = bounds.width / 2; const y = bounds.height / 2; EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal); }; /** * Click on pause/resume button. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel - * The panel instance. + * @param {DOMElement} panel + * #animation-container element. */ const clickOnPauseResumeButton = async function(animationInspector, panel) { info("Click on pause/resume button"); const buttonEl = panel.querySelector(".pause-resume-button"); const bounds = buttonEl.getBoundingClientRect(); const x = bounds.width / 2; const y = bounds.height / 2; EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal); await waitForSummaryAndDetail(animationInspector); }; /** * Click on rewind button. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel - * The panel instance. + * @param {DOMElement} panel + * #animation-container element. */ const clickOnRewindButton = async function(animationInspector, panel) { info("Click on rewind button"); const buttonEl = panel.querySelector(".rewind-button"); const bounds = buttonEl.getBoundingClientRect(); const x = bounds.width / 2; const y = bounds.height / 2; EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal); await waitForSummaryAndDetail(animationInspector); }; /** * Click on the scrubber controller pane to update the animation current time. * - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. * @param {Number} mouseDownPosition * rate on scrubber controller pane. * This method calculates * `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane` * as the clientX of MouseEvent. */ const clickOnCurrentTimeScrubberController = async function(animationInspector, panel, @@ -194,20 +195,40 @@ const clickOnCurrentTimeScrubberControll const mousedonwX = bounds.width * mouseDownPosition; info(`Click ${ mousedonwX } on scrubber controller`); EventUtils.synthesizeMouse(controllerEl, mousedonwX, 0, {}, controllerEl.ownerGlobal); await waitForSummaryAndDetail(animationInspector); }; /** + * Click on the inspect icon for the given AnimationTargetComponent. + * + * @param {AnimationInspector} animationInspector. + * @param {DOMElement} panel + * #animation-container element. + * @param {Number} index + * The index of the AnimationTargetComponent to click on. + */ +const clickOnInspectIcon = async function(animationInspector, panel, index) { + info(`Click on an inspect icon in animation target component[${ index }]`); + const iconEl = + panel.querySelectorAll(".animation-target .objectBox .open-inspector")[index]; + iconEl.scrollIntoView(false); + EventUtils.synthesizeMouseAtCenter(iconEl, {}, iconEl.ownerGlobal); + // We wait just one time, because the components are updated synchronously. + await animationInspector.once("animation-target-rendered"); +}; + +/** * Click on playback rate selector to select given rate. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. * @param {Number} rate */ const clickOnPlaybackRateSelector = async function(animationInspector, panel, rate) { info(`Click on playback rate selector to select ${rate}`); const selectEl = panel.querySelector(".playback-rate-selector"); const optionEl = [...selectEl.options].filter(o => Number(o.value) === rate)[0]; if (!optionEl) { @@ -221,35 +242,56 @@ const clickOnPlaybackRateSelector = asyn EventUtils.synthesizeMouseAtCenter(optionEl, { type: "mouseup" }, win); await waitForSummaryAndDetail(animationInspector); }; /** * Click on given summary graph element. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. * @param {Element} summaryGraphEl */ const clickOnSummaryGraph = async function(animationInspector, panel, summaryGraphEl) { // Disable pointer-events of the scrubber in order to avoid to click accidently. const scrubberEl = panel.querySelector(".current-time-scrubber"); scrubberEl.style.pointerEvents = "none"; // Scroll to show the timeBlock since the element may be out of displayed area. summaryGraphEl.scrollIntoView(false); EventUtils.synthesizeMouseAtCenter(summaryGraphEl, {}, summaryGraphEl.ownerGlobal); await waitForAnimationDetail(animationInspector); // Restore the scrubber style. scrubberEl.style.pointerEvents = "unset"; }; /** + * Click on the target node for the given AnimationTargetComponent index. + * + * @param {AnimationInspector} animationInspector. + * @param {DOMElement} panel + * #animation-container element. + * @param {Number} index + * The index of the AnimationTargetComponent to click on. + */ +const clickOnTargetNode = async function(animationInspector, panel, index) { + info(`Click on a target node in animation target component[${ index }]`); + const targetEl = panel.querySelectorAll(".animation-target .objectBox")[index]; + targetEl.scrollIntoView(false); + const onHighlight = animationInspector.inspector.toolbox.once("node-highlight"); + EventUtils.synthesizeMouseAtCenter(targetEl, {}, targetEl.ownerGlobal); + await waitForRendering(animationInspector); + await onHighlight; +}; + +/** * Drag on the scrubber to update the animation current time. * - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. * @param {Number} mouseDownPosition * rate on scrubber controller pane. * This method calculates * `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane` * as the clientX of MouseEvent. * @param {Number} mouseMovePosition * Dispatch mousemove event with mouseMovePosition after mousedown. * Calculation for clinetX is same to above. @@ -275,17 +317,18 @@ const dragOnCurrentTimeScrubber = async EventUtils.synthesizeMouse(controllerEl, mousemoveX, mouseYPixel, { type: "mouseup" }, controllerEl.ownerGlobal); await waitForSummaryAndDetail(animationInspector); }; /** * Drag on the scrubber controller pane to update the animation current time. * - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. * @param {Number} mouseDownPosition * rate on scrubber controller pane. * This method calculates * `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane` * as the clientX of MouseEvent. * @param {Number} mouseMovePosition * Dispatch mousemove event with mouseMovePosition after mousedown. * Calculation for clinetX is same to above. @@ -310,17 +353,18 @@ const dragOnCurrentTimeScrubberControlle await waitForSummaryAndDetail(animationInspector); }; /** * Get current animation duration and rate of * clickOrDragOnCurrentTimeScrubberController in given pixels. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. * @param {Number} pixels * @return {Object} * { * duration, * rate, * } */ const getDurationAndRate = function(animationInspector, panel, pixels) { @@ -328,16 +372,48 @@ const getDurationAndRate = function(anim const bounds = controllerEl.getBoundingClientRect(); const duration = animationInspector.state.timeScale.getDuration() / bounds.width * pixels; const rate = 1 / bounds.width * pixels; return { duration, rate }; }; /** + * Mouse over the target node for the given AnimationTargetComponent index. + * + * @param {AnimationInspector} animationInspector. + * @param {DOMElement} panel + * #animation-container element. + * @param {Number} index + * The index of the AnimationTargetComponent to click on. + */ +const mouseOverOnTargetNode = function(animationInspector, panel, index) { + info(`Mouse over on a target node in animation target component[${ index }]`); + const el = panel.querySelectorAll(".animation-target .objectBox")[index]; + el.scrollIntoView(false); + EventUtils.synthesizeMouse(el, 10, 5, { type: "mouseover" }, el.ownerGlobal); +}; + +/** + * Mouse out of the target node for the given AnimationTargetComponent index. + * + * @param {AnimationInspector} animationInspector. + * @param {DOMElement} panel + * #animation-container element. + * @param {Number} index + * The index of the AnimationTargetComponent to click on. + */ +const mouseOutOnTargetNode = function(animationInspector, panel, index) { + info(`Mouse out on a target node in animation target component[${ index }]`); + const el = panel.querySelectorAll(".animation-target .objectBox")[index]; + el.scrollIntoView(false); + EventUtils.synthesizeMouse(el, -1, -1, { type: "mouseout" }, el.ownerGlobal); +}; + +/** * Select animation inspector in sidebar and toolbar. * * @param {InspectorPanel} inspector */ const selectAnimationInspector = async function(inspector) { await inspector.toolbox.selectTool("inspector"); const onUpdated = inspector.once("inspector-updated"); inspector.sidebar.select("newanimationinspector"); @@ -367,17 +443,18 @@ const selectNodeAndWaitForAnimations = a await onUpdated; await waitForRendering(inspector.animationinspector); }; /** * Send keyboard event of space to given panel. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. */ const sendSpaceKeyEvent = async function(animationInspector, panel) { panel.focus(); EventUtils.sendKey("SPACE", panel.ownerGlobal); await waitForSummaryAndDetail(animationInspector); }; /** @@ -494,40 +571,43 @@ const waitForSummaryAndDetail = async fu waitForAnimationDetail(animationInspector), ]); }; /** * Check whether current time of all animations and UI are given specified time. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. * @param {Number} time */ function assertAnimationsCurrentTime(animationInspector, time) { const isTimeEqual = animationInspector.state.animations.every(({state}) => state.currentTime === time); ok(isTimeEqual, `Current time of animations should be ${ time }`); } /** * Check whether the animations are pausing. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. */ function assertAnimationsPausing(animationInspector, panel) { assertAnimationsPausingOrRunning(animationInspector, panel, true); } /** * Check whether the animations are pausing/running. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. * @param {boolean} shouldPause */ function assertAnimationsPausingOrRunning(animationInspector, panel, shouldPause) { const hasRunningAnimation = animationInspector.state.animations.some(({state}) => state.playState === "running"); if (shouldPause) { is(hasRunningAnimation, false, "All animations should be paused"); @@ -535,17 +615,18 @@ function assertAnimationsPausingOrRunnin is(hasRunningAnimation, true, "Animations should be running at least one"); } } /** * Check whether the animations are running. * * @param {AnimationInspector} animationInspector - * @param {AnimationsPanel} panel + * @param {DOMElement} panel + * #animation-container element. */ function assertAnimationsRunning(animationInspector, panel) { assertAnimationsPausingOrRunning(animationInspector, panel, false); } /** * Check the <stop> element in the given linearGradientEl for the correct offset * and color attributes. @@ -628,19 +709,22 @@ function isPassingThrough(pathSegList, x return false; } /** * Return animation item element by target node selector. * This function compares betweem animation-target textContent and given selector. * Then returns matched first item. * - * @param {Element} panel - root element of animation inspector. - * @param {String} selector - selector of tested element. - * @return {Element} animation item element. + * @param {DOMElement} panel + * #animation-container element. + * @param {String} selector + * Selector of tested element. + * @return {DOMElement} + * Animation item element. */ function findAnimationItemElementsByTargetSelector(panel, selector) { const attrNameEls = panel.querySelectorAll(".animation-target .attrName"); const regexp = new RegExp(`\\${ selector }(\\.|$)`, "gi"); for (const attrNameEl of attrNameEls) { if (regexp.exec(attrNameEl.textContent)) { return attrNameEl.closest(".animation-item");