-
Notifications
You must be signed in to change notification settings - Fork 453
Screenshots track #1198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
gregtatum
merged 9 commits into
firefox-devtools:master
from
gregtatum:screenshot-track
Aug 30, 2018
Merged
Screenshots track #1198
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
b78c69a
Update CSP to allow for data-uri images
gregtatum fff94bf
Extract screenshot tracks from the profile
gregtatum 8c67618
Add a TimelineTrackScreenshots component
gregtatum 892714f
Add tests for screenshot tracks
gregtatum eaea52d
Add the snapshots for TrackScreenshots.test.js
gregtatum a84bb7b
Address review for TrackScreenshots
gregtatum 3b00379
Fix make profile function to include the correct number of samples
gregtatum 0601711
Change screenshot markers into TracingMarkers
gregtatum 13d9dd0
Address review for Screenshots with TracingMarkers
gregtatum File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /* This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
|
||
| .timelineTrackScreenshot { | ||
| position: relative; | ||
| background: var(--grey-20); | ||
| overflow: hidden; | ||
| } | ||
|
|
||
| .timelineTrackScreenshotImgContainer { | ||
| /* This positioning is defined through JavaScript */ | ||
| position: absolute; | ||
| overflow: hidden; | ||
| display: flex; | ||
| justify-content: center; | ||
| } | ||
|
|
||
| .timelineTrackScreenshotImgContainer::after { | ||
| /* Create a border on the right of the container */ | ||
| content: ""; | ||
| position: absolute; | ||
| top: 0; | ||
| width: 1px; | ||
| height: 100%; | ||
| background: var(--grey-30); | ||
| right: 0; | ||
| } | ||
|
|
||
| .timelineTrackScreenshotHover { | ||
| /* This positioning is defined through JavaScript */ | ||
| position: absolute; | ||
| pointer-events: none; | ||
| z-index: 4; /* Ensure this is higher than any other timeline element. */ | ||
| } | ||
|
|
||
| .timelineTrackScreenshotHoverImg { | ||
| position: relative; | ||
| left: -50%; | ||
| border-radius: 5px; | ||
| padding: 0.5px; | ||
| border: 0.5px solid rgba(0,0,0,.2); | ||
| box-shadow:0 2px 4px rgba(0,0,0,.2); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,253 @@ | ||
| /* This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
|
||
| // @flow | ||
|
|
||
| import React, { PureComponent } from 'react'; | ||
| import { createPortal } from 'react-dom'; | ||
| import explicitConnect from '../../utils/connect'; | ||
| import { | ||
| selectorsForThread, | ||
| getCommittedRange, | ||
| getPreviewSelection, | ||
| } from '../../reducers/profile-view'; | ||
| import { withSize, type SizeProps } from '../shared/WithSize'; | ||
|
|
||
| import type { ThreadIndex, Thread } from '../../types/profile'; | ||
| import type { ScreenshotPayload } from '../../types/markers'; | ||
| import type { TracingMarker } from '../../types/profile-derived'; | ||
| import type { Milliseconds } from '../../types/units'; | ||
| import type { | ||
| ExplicitConnectOptions, | ||
| ConnectedProps, | ||
| } from '../../utils/connect'; | ||
|
|
||
| import { ensureExists } from '../../utils/flow'; | ||
| import './TrackScreenshots.css'; | ||
|
|
||
| type OwnProps = {| | ||
| +threadIndex: ThreadIndex, | ||
| +windowId: string, | ||
| ...SizeProps, | ||
| |}; | ||
| type StateProps = {| | ||
| +thread: Thread, | ||
| +rangeStart: Milliseconds, | ||
| +rangeEnd: Milliseconds, | ||
| +screenshots: TracingMarker[], | ||
| +threadName: string, | ||
| +isMakingPreviewSelection: boolean, | ||
| |}; | ||
| type DispatchProps = {||}; | ||
| type Props = ConnectedProps<OwnProps, StateProps, DispatchProps>; | ||
| type State = {| | ||
| offsetX: null | number, | ||
| pageX: null | number, | ||
| containerTop: null | number, | ||
| |}; | ||
|
|
||
| // Export the value for tests. | ||
| export const TRACK_HEIGHT = 50; | ||
| const HOVER_HEIGHT = 100; | ||
| const HOVER_MAX_WIDTH_RATIO = 1.75; | ||
|
|
||
| class Screenshots extends PureComponent<Props, State> { | ||
| state = { | ||
| offsetX: null, | ||
| pageX: null, | ||
| containerTop: null, | ||
| }; | ||
|
|
||
| _overlayElement = ensureExists( | ||
| document.querySelector('#root-overlay'), | ||
| 'Expected to find a root overlay element.' | ||
| ); | ||
|
|
||
| findScreenshotAtMouse(offsetX: number): number | null { | ||
| const { width, rangeStart, rangeEnd, screenshots } = this.props; | ||
| const rangeLength = rangeEnd - rangeStart; | ||
| const mouseTime = offsetX / width * rangeLength + rangeStart; | ||
|
|
||
| // Loop backwards to find the latest screenshot that has a time less | ||
| // than the current time at the mouse position. | ||
| for (let i = screenshots.length - 1; i >= 0; i--) { | ||
| const screenshotTime = screenshots[i].start; | ||
| if (mouseTime >= screenshotTime) { | ||
| return i; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * This function runs through all of the screenshots, and then samples the last known | ||
| * screenshot, and places it on the screen, making a film strip. | ||
| */ | ||
| renderScreenshotStrip() { | ||
| const { | ||
| thread, | ||
| width: outerContainerWidth, | ||
| rangeStart, | ||
| rangeEnd, | ||
| screenshots, | ||
| } = this.props; | ||
|
|
||
| if (screenshots.length === 0) { | ||
| return null; | ||
| } | ||
|
|
||
| const images = []; | ||
| const rangeLength = rangeEnd - rangeStart; | ||
| const imageContainerWidth = TRACK_HEIGHT * 0.75; | ||
| const timeToPixel = time => | ||
| outerContainerWidth * (time - rangeStart) / rangeLength; | ||
|
|
||
| let screenshotIndex = 0; | ||
This comment was marked as outdated.
Sorry, something went wrong. |
||
| for ( | ||
| let left = timeToPixel(screenshots[0].start); | ||
| left < outerContainerWidth; | ||
| left += imageContainerWidth | ||
| ) { | ||
| // Try to find the next screenshot to fit in, or re-use the existing one. | ||
| for (let i = screenshotIndex; i < screenshots.length; i++) { | ||
| if (timeToPixel(screenshots[i].start) <= left) { | ||
| screenshotIndex = i; | ||
| } else { | ||
| break; | ||
| } | ||
| } | ||
| // Coerce the payload into a screenshot one. | ||
| const payload: ScreenshotPayload = (screenshots[screenshotIndex] | ||
| .data: any); | ||
| const { url: urlStringIndex, windowWidth, windowHeight } = payload; | ||
| const scaledImageWidth = TRACK_HEIGHT * windowWidth / windowHeight; | ||
| images.push( | ||
| <div | ||
| className="timelineTrackScreenshotImgContainer" | ||
| style={{ left, width: imageContainerWidth }} | ||
| key={left} | ||
| > | ||
| {/* The following image is centered and cropped by the outer container. */} | ||
| <img | ||
| className="timelineTrackScreenshotImg" | ||
| src={thread.stringTable.getString(urlStringIndex)} | ||
| style={{ | ||
| width: scaledImageWidth, | ||
| height: TRACK_HEIGHT, | ||
| }} | ||
| /> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| return images; | ||
| } | ||
|
|
||
| renderHoverPreview() { | ||
| const { pageX, offsetX, containerTop } = this.state; | ||
| const { screenshots, thread, isMakingPreviewSelection, width } = this.props; | ||
|
|
||
| if (isMakingPreviewSelection || offsetX === null || pageX === null) { | ||
| return null; | ||
| } | ||
|
|
||
| const screenshotIndex = this.findScreenshotAtMouse(offsetX); | ||
| if (screenshotIndex === null) { | ||
| return null; | ||
| } | ||
|
|
||
| // Coerce the payload into a screenshot one. | ||
| const payload: ScreenshotPayload = (screenshots[screenshotIndex].data: any); | ||
| const { url, windowWidth, windowHeight } = payload; | ||
|
|
||
| // Compute the hover image's thumbnail size. | ||
| let hoverHeight = HOVER_HEIGHT; | ||
| let hoverWidth = HOVER_HEIGHT / windowHeight * windowWidth; | ||
|
|
||
| if (hoverWidth > HOVER_HEIGHT * HOVER_MAX_WIDTH_RATIO) { | ||
| // This is a really wide image, limit the height so it lays out reasonably. | ||
| hoverWidth = HOVER_HEIGHT * HOVER_MAX_WIDTH_RATIO; | ||
| hoverHeight = hoverWidth / windowWidth * windowHeight; | ||
| } | ||
julienw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Set the top so it centers around the track. | ||
| const top = containerTop + (TRACK_HEIGHT - hoverHeight) * 0.5; | ||
| const left = | ||
| offsetX + hoverWidth * 0.5 > width | ||
| ? // Stick the hover image on to the right side of the container. | ||
| pageX - offsetX + width - hoverWidth * 0.5 | ||
| : // Center the hover image around the mouse. | ||
| pageX; | ||
|
|
||
| return createPortal( | ||
julienw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <div className="timelineTrackScreenshotHover" style={{ left, top }}> | ||
| <img | ||
| className="timelineTrackScreenshotHoverImg" | ||
| src={thread.stringTable.getString(url)} | ||
| style={{ | ||
| height: hoverHeight, | ||
| width: hoverWidth, | ||
| }} | ||
| /> | ||
| </div>, | ||
| this._overlayElement | ||
| ); | ||
| } | ||
|
|
||
| _handleMouseLeave = () => { | ||
| this.setState({ | ||
| offsetX: null, | ||
| pageX: null, | ||
| containerTop: null, | ||
| }); | ||
| }; | ||
|
|
||
| _handleMouseMove = (event: SyntheticMouseEvent<HTMLDivElement>) => { | ||
| const { top, left } = event.currentTarget.getBoundingClientRect(); | ||
| this.setState({ | ||
| pageX: event.pageX, | ||
| offsetX: event.pageX - left, | ||
julienw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| containerTop: top, | ||
| }); | ||
| }; | ||
|
|
||
| render() { | ||
| return ( | ||
| <div | ||
| className="timelineTrackScreenshot" | ||
| style={{ height: TRACK_HEIGHT }} | ||
| onMouseLeave={this._handleMouseLeave} | ||
| onMouseMove={this._handleMouseMove} | ||
| > | ||
| {this.renderScreenshotStrip()} | ||
| {this.renderHoverPreview()} | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| const options: ExplicitConnectOptions<OwnProps, StateProps, DispatchProps> = { | ||
| mapStateToProps: (state, ownProps) => { | ||
| const { threadIndex, windowId } = ownProps; | ||
| const selectors = selectorsForThread(threadIndex); | ||
| const { start, end } = getCommittedRange(state); | ||
| const previewSelection = getPreviewSelection(state); | ||
| return { | ||
| thread: selectors.getRangeFilteredThread(state), | ||
| screenshots: ensureExists( | ||
| selectors.getRangeFilteredScreenshotsById(state).get(windowId), | ||
| 'Expected to find screenshots for the given pid' | ||
| ), | ||
| threadName: selectors.getFriendlyThreadName(state), | ||
| rangeStart: start, | ||
| rangeEnd: end, | ||
| isMakingPreviewSelection: | ||
| previewSelection.hasSelection && previewSelection.isModifying, | ||
| }; | ||
| }, | ||
| component: Screenshots, | ||
| }; | ||
|
|
||
| export default withSize(explicitConnect(options)); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just wondering why you didn't use a border ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It wasn't showing up for some reason. I think it's somehow getting covered up by the sibling containers.