Skip to content
Open
8 changes: 8 additions & 0 deletions locales/en-US/app.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,14 @@ BottomBox--assembly-code-not-available-title = Assembly code not available
BottomBox--assembly-code-not-available-text =
See <a>issue #4520</a> for supported scenarios and planned improvements.

# The toggle button for making the bottom box fullscreen.
BottomBox--hide-fullscreen =
.title = Exit fullscreen

# The toggle button for making the bottom box fullscreen.
BottomBox--show-fullscreen =
.title = Fullscreen

SourceView--close-button =
.title = Close the source view

Expand Down
6 changes: 6 additions & 0 deletions res/img/svg/fullscreen-exit.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions res/img/svg/fullscreen.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/actions/profile-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,14 @@ export function closeBottomBox(): ThunkAction<void> {
};
}

export function toggleBottomBoxFullscreen(): ThunkAction<void> {
return (dispatch) => {
dispatch({
type: 'TOGGLE_BOTTOM_BOX_FULLSCREEN',
});
};
}

export function handleCallNodeTransformShortcut(
event: React.KeyboardEvent<HTMLElement>,
threadsKey: ThreadsKey,
Expand Down
13 changes: 11 additions & 2 deletions src/app-logic/url-handling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ type Query = BaseQuery & {
transforms?: string;
sourceViewIndex?: number;
assemblyView?: string;
bottomFullscreen?: boolean;

// StackChart specific
showUserTimings?: null | undefined;
Expand Down Expand Up @@ -352,8 +353,12 @@ export function getQueryStringFromUrlState(urlState: UrlState): string {
'timing'
? undefined
: urlState.profileSpecific.lastSelectedCallTreeSummaryStrategy;
const { sourceView, assemblyView, isBottomBoxOpenPerPanel } =
urlState.profileSpecific;
const {
sourceView,
assemblyView,
isBottomBoxOpenPerPanel,
isBottomBoxFullscreen,
} = urlState.profileSpecific;

if (isBottomBoxOpenPerPanel[selectedTab]) {
if (sourceView.sourceIndex !== null) {
Expand All @@ -365,6 +370,9 @@ export function getQueryStringFromUrlState(urlState: UrlState): string {
nativeSymbols[currentNativeSymbol]
);
}
if (isBottomBoxFullscreen) {
query.bottomFullscreen = true;
}
}
break;
}
Expand Down Expand Up @@ -593,6 +601,7 @@ export function stateFromLocation(
sourceView,
assemblyView,
isBottomBoxOpenPerPanel,
isBottomBoxFullscreen: query.bottomFullscreen || false,
timelineType: validateTimelineType(query.timelineType),
showJsTracerSummary: query.summary === undefined ? false : true,
globalTrackOrder: convertGlobalTrackOrderFromString(
Expand Down
19 changes: 19 additions & 0 deletions src/components/app/BottomBox.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
* 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/. */

.bottom-box-fullscreen {
position: fixed;
z-index: 100;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

.bottom-box-pane {
--internal-sourceview-background-color: var(--grey-20);
--internal-close-icon: url(../../../res/img/svg/close-dark.svg);
Expand Down Expand Up @@ -73,6 +82,8 @@

.bottom-close-button,
.bottom-assembly-button,
.bottom-fullscreen-hide-button,
.bottom-fullscreen-show-button,
.bottom-prev-button,
.bottom-next-button {
width: 24px;
Expand Down Expand Up @@ -101,6 +112,14 @@
background-image: var(--internal-assembly-icon);
}

.bottom-fullscreen-show-button {
background-image: url(firefox-profiler-res/img/svg/fullscreen.svg);
}

.bottom-fullscreen-hide-button {
background-image: url(firefox-profiler-res/img/svg/fullscreen-exit.svg);
}

.codeLoadingOverlay,
.sourceCodeErrorOverlay,
.assemblyCodeErrorOverlay {
Expand Down
40 changes: 37 additions & 3 deletions src/components/app/BottomBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import classNames from 'classnames';

import { SourceView } from '../shared/SourceView';
import { AssemblyView } from '../shared/AssemblyView';
import { FullscreenToggleButton } from './FullscreenToggleButton';
import { AssemblyViewToggleButton } from './AssemblyViewToggleButton';
import { AssemblyViewNativeSymbolNavigator } from './AssemblyViewNativeSymbolNavigator';
import { IonGraphView } from '../shared/IonGraphView';
Expand All @@ -22,9 +23,13 @@ import {
getAssemblyViewScrollGeneration,
getAssemblyViewScrollToInstructionAddress,
getAssemblyViewHighlightedInstruction,
getIsBottomBoxFullscreen,
} from 'firefox-profiler/selectors/url-state';
import {
closeBottomBox,
toggleBottomBoxFullscreen,
} from 'firefox-profiler/actions/profile-view';
import { selectedThreadSelectors } from 'firefox-profiler/selectors/per-thread';
import { closeBottomBox } from 'firefox-profiler/actions/profile-view';
import { parseFileNameFromSymbolication } from 'firefox-profiler/utils/special-paths';
import {
getSourceViewCode,
Expand Down Expand Up @@ -53,6 +58,7 @@ import { Localized } from '@fluent/react';
import './BottomBox.css';

type StateProps = {
readonly isFullscreen: boolean;
readonly sourceViewFile: string | null;
readonly sourceViewCode: SourceCodeStatus | void;
readonly sourceViewScrollGeneration: number;
Expand All @@ -71,6 +77,7 @@ type StateProps = {

type DispatchProps = {
readonly closeBottomBox: typeof closeBottomBox;
readonly toggleBottomBoxFullscreen: typeof toggleBottomBoxFullscreen;
};

type Props = ConnectedProps<{}, StateProps, DispatchProps>;
Expand Down Expand Up @@ -153,12 +160,31 @@ class BottomBoxImpl extends React.PureComponent<Props> {
_sourceView = React.createRef<SourceView>();
_assemblyView = React.createRef<AssemblyView>();

override componentDidMount() {
document.addEventListener('keydown', this._onKeyDown);
}

override componentWillUnmount() {
document.removeEventListener('keydown', this._onKeyDown);
}

_onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && this.props.isFullscreen) {
this.props.toggleBottomBoxFullscreen();
}
};

_onClickCloseButton = () => {
this.props.closeBottomBox();
// Close the fullscreen if we're closing the bottom box
if (this.props.isFullscreen) {
this.props.toggleBottomBoxFullscreen();
}
};

override render() {
const {
isFullscreen,
sourceViewFile,
sourceViewCode,
globalLineTimings,
Expand Down Expand Up @@ -200,6 +226,7 @@ class BottomBoxImpl extends React.PureComponent<Props> {
<div className="bottom-box-header-trailing-buttons">
{assemblyViewIsOpen ? <AssemblyViewNativeSymbolNavigator /> : null}
<AssemblyViewToggleButton />
<FullscreenToggleButton />
<Localized id="SourceView--close-button" attrs={{ title: true }}>
<button
className={classNames(
Expand All @@ -216,8 +243,13 @@ class BottomBoxImpl extends React.PureComponent<Props> {
);

return (
<div className="bottom-box">
<SplitterLayout customClassName="bottom-box" percentage>
<div
className={classNames(
'bottom-box',
isFullscreen ? 'bottom-box-fullscreen' : null
)}
>
<SplitterLayout percentage>
<div className="bottom-box-pane">
<div className="bottom-box-bar">
<h3 className="bottom-box-title">{path ?? '(no source file)'}</h3>
Expand Down Expand Up @@ -303,6 +335,7 @@ function convertErrors(errors: ApiQueryError[]): SourceCodeLoadingError[] {

export const BottomBox = explicitConnect<{}, StateProps, DispatchProps>({
mapStateToProps: (state) => ({
isFullscreen: getIsBottomBoxFullscreen(state),
sourceViewFile: getSourceViewFile(state),
sourceViewCode: getSourceViewCode(state),
globalLineTimings: selectedThreadSelectors.getSourceViewLineTimings(state),
Expand All @@ -323,6 +356,7 @@ export const BottomBox = explicitConnect<{}, StateProps, DispatchProps>({
}),
mapDispatchToProps: {
closeBottomBox,
toggleBottomBoxFullscreen,
},
component: BottomBoxImpl,
});
77 changes: 77 additions & 0 deletions src/components/app/FullscreenToggleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* 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/. */

import React from 'react';
import classNames from 'classnames';

import { getIsBottomBoxFullscreen } from 'firefox-profiler/selectors/url-state';
import { toggleBottomBoxFullscreen } from 'firefox-profiler/actions/profile-view';
import explicitConnect from 'firefox-profiler/utils/connect';

import type { ConnectedProps } from 'firefox-profiler/utils/connect';

import { Localized } from '@fluent/react';

type StateProps = {
readonly isBottomBoxFullscreen: boolean;
};

type DispatchProps = {
readonly toggleBottomBoxFullscreen: typeof toggleBottomBoxFullscreen;
};

type Props = ConnectedProps<{}, StateProps, DispatchProps>;

class FullscreenToggleButtonImpl extends React.PureComponent<Props> {
_onClick = () => {
this.props.toggleBottomBoxFullscreen();
};

override render() {
const { isBottomBoxFullscreen } = this.props;

return isBottomBoxFullscreen ? (
<Localized id="BottomBox--hide-fullscreen" attrs={{ title: true }}>
<button
className={classNames(
'bottom-fullscreen-hide-button',
'photon-button',
'photon-button-ghost',
'photon-button-ghost--checked'
)}
title="Exit fullscreen"
type="button"
onClick={this._onClick}
/>
</Localized>
) : (
<Localized id="BottomBox--show-fullscreen" attrs={{ title: true }}>
<button
className={classNames(
'bottom-fullscreen-show-button',
'photon-button',
'photon-button-ghost'
)}
title="Show fullscreen"
type="button"
onClick={this._onClick}
/>
</Localized>
);
}
}

export const FullscreenToggleButton = explicitConnect<
{},
StateProps,
DispatchProps
>({
mapStateToProps: (state) => ({
isBottomBoxFullscreen: getIsBottomBoxFullscreen(state),
}),
mapDispatchToProps: {
toggleBottomBoxFullscreen,
},
component: FullscreenToggleButtonImpl,
});
11 changes: 11 additions & 0 deletions src/reducers/url-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,16 @@ const isBottomBoxOpenPerPanel: Reducer<IsOpenPerPanelState> = (
}
};

const isBottomBoxFullscreen: Reducer<boolean> = (state = false, action) => {
switch (action.type) {
case 'TOGGLE_BOTTOM_BOX_FULLSCREEN': {
return !state;
}
default:
return state;
}
};

/**
* This value is only set from the URL and never changed.
*/
Expand Down Expand Up @@ -750,6 +760,7 @@ const profileSpecific = combineReducers({
sourceView,
assemblyView,
isBottomBoxOpenPerPanel,
isBottomBoxFullscreen,
timelineType,
globalTrackOrder,
hiddenGlobalTracks,
Expand Down
4 changes: 4 additions & 0 deletions src/selectors/url-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ export const getIsBottomBoxOpen: Selector<boolean> = (state) => {
return getProfileSpecificState(state).isBottomBoxOpenPerPanel[tab];
};

export const getIsBottomBoxFullscreen: Selector<boolean> = (state) => {
return getProfileSpecificState(state).isBottomBoxFullscreen;
};

/**
* The URL predictor is used to generate a link for an uploaded profile, to predict
* what the URL will be.
Expand Down
3 changes: 3 additions & 0 deletions src/types/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ type ProfileAction =
| {
readonly type: 'CLOSE_BOTTOM_BOX_FOR_TAB';
readonly tab: TabSlug;
}
| {
readonly type: 'TOGGLE_BOTTOM_BOX_FULLSCREEN';
};

type ReceiveProfileAction =
Expand Down
1 change: 1 addition & 0 deletions src/types/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ export type ProfileSpecificUrlState = {
sourceView: SourceViewState;
assemblyView: AssemblyViewState;
isBottomBoxOpenPerPanel: IsOpenPerPanelState;
isBottomBoxFullscreen: boolean;
globalTrackOrder: TrackIndex[];
hiddenGlobalTracks: Set<TrackIndex>;
hiddenLocalTracksByPid: Map<Pid, Set<TrackIndex>>;
Expand Down
Loading