<commit_before>/* 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 explicitConnect from '../../utils/connect';
import ThreadStackGraph from './ThreadStackGraph';
import { selectorsForThread } from '../../reducers/profile-view';
import { getSelectedThreadIndex } from '../../reducers/url-state';
import {
  getSampleIndexClosestToTime,
  getCallNodePath,
} from '../../profile-logic/profile-data';
import ContextMenuTrigger from '../shared/ContextMenuTrigger';
import ProfileThreadJankOverview from './ProfileThreadJankOverview';
import ProfileThreadTracingMarkerOverview from './ProfileThreadTracingMarkerOverview';
import {
  changeSelectedThread,
  updateProfileSelection,
  changeRightClickedThread,
  changeSelectedCallNode,
  focusCallTree,
} from '../../actions/profile-view';
import EmptyThreadIndicator from './EmptyThreadIndicator';

import type { Thread, ThreadIndex } from '../../types/profile';
import type { Milliseconds, StartEndRange } from '../../types/units';
import type {
  CallNodeInfo,
  IndexIntoCallNodeTable,
} from '../../types/profile-derived';
import type { State } from '../../types/reducers';
import type {
  ExplicitConnectOptions,
  ConnectedProps,
} from '../../utils/connect';

type OwnProps = {|
  +threadIndex: ThreadIndex,
  +interval: Milliseconds,
  +rangeStart: Milliseconds,
  +rangeEnd: Milliseconds,
  +isHidden: boolean,
  +isModifyingSelection: boolean,
  +style?: Object /* This is used by Reorderable */,
|};

type StateProps = {|
  +thread: Thread,
  +threadName: string,
  +processDetails: string,
  +callNodeInfo: CallNodeInfo,
  +selectedCallNodeIndex: IndexIntoCallNodeTable | null,
  +isSelected: boolean,
  +unfilteredSamplesRange: StartEndRange | null,
|};

type DispatchProps = {|
  +changeSelectedThread: typeof changeSelectedThread,
  +changeRightClickedThread: typeof changeRightClickedThread,
  +updateProfileSelection: typeof updateProfileSelection,
  +changeSelectedCallNode: typeof changeSelectedCallNode,
  +focusCallTree: typeof focusCallTree,
|};

type Props = ConnectedProps<OwnProps, StateProps, DispatchProps>;

class ProfileThreadHeaderBar extends PureComponent<Props> {
  constructor(props) {
    super(props);
    (this: any)._onLabelMouseDown = this._onLabelMouseDown.bind(this);
    (this: any)._onStackClick = this._onStackClick.bind(this);
    (this: any)._onLineClick = this._onLineClick.bind(this);
    (this: any)._onIntervalMarkerSelect = this._onIntervalMarkerSelect.bind(
      this
    );
  }

  _onLabelMouseDown(event: MouseEvent) {
    const {
      changeSelectedThread,
      changeRightClickedThread,
      threadIndex,
    } = this.props;
    if (event.button === 0) {
      changeSelectedThread(threadIndex);

      // Don't allow clicks on the threads list to steal focus from the tree view.
      event.preventDefault();
    } else if (event.button === 2) {
      // This is needed to allow the context menu to know what was right clicked without
      // actually changing the current selection.
      changeRightClickedThread(threadIndex);
    }
  }

  _onLineClick() {
    const { threadIndex, changeSelectedThread } = this.props;
    changeSelectedThread(threadIndex);
  }

  _onStackClick(time: number) {
    const { threadIndex, interval } = this.props;
    const {
      thread,
      callNodeInfo,
      changeSelectedCallNode,
      focusCallTree,
    } = this.props;
    const sampleIndex = getSampleIndexClosestToTime(
      thread.samples,
      time,
      interval
    );
    const newSelectedStack = thread.samples.stack[sampleIndex];
    const newSelectedCallNode =
      newSelectedStack === null
        ? -1
        : callNodeInfo.stackIndexToCallNodeIndex[newSelectedStack];
    changeSelectedCallNode(
      threadIndex,
      getCallNodePath(newSelectedCallNode, callNodeInfo.callNodeTable)
    );
    focusCallTree();
  }

  _onIntervalMarkerSelect(
    threadIndex: ThreadIndex,
    start: Milliseconds,
    end: Milliseconds
  ) {
    const {
      rangeStart,
      rangeEnd,
      updateProfileSelection,
      changeSelectedThread,
    } = this.props;
    updateProfileSelection({
      hasSelection: true,
      isModifying: false,
      selectionStart: Math.max(rangeStart, start),
      selectionEnd: Math.min(rangeEnd, end),
    });
    changeSelectedThread(threadIndex);
  }

  render() {
    const {
      thread,
      threadIndex,
      interval,
      rangeStart,
      rangeEnd,
      callNodeInfo,
      selectedCallNodeIndex,
      isSelected,
      threadName,
      processDetails,
      isHidden,
      isModifyingSelection,
      unfilteredSamplesRange,
      style,
    } = this.props;

    if (isHidden) {
      // If this thread is hidden, render out a stub element so that the Reorderable
      // Component still works across all the threads.
      return <li className="profileThreadHeaderBarHidden" />;
    }

    const processType = thread.processType;
    const displayJank = thread.name === 'GeckoMain' && processType !== 'plugin';
    const displayTracingMarkers =
      (thread.name === 'GeckoMain' ||
        thread.name === 'Compositor' ||
        thread.name === 'Renderer') &&
      processType !== 'plugin';
    const className = 'profileThreadHeaderBar';

    return (
      <li
        className={'profileThreadHeaderBar' + (isSelected ? ' selected' : '')}
        onClick={this._onLineClick}
        style={style}
      >
        <ContextMenuTrigger
          id={'ProfileThreadHeaderContextMenu'}
          renderTag="div"
          attributes={{
            title: processDetails,
            className: 'grippy profileThreadHeaderBarThreadLabel',
            onMouseDown: this._onLabelMouseDown,
          }}
        >
          <h1 className="profileThreadHeaderBarThreadName">
            {threadName}
          </h1>
        </ContextMenuTrigger>
        <div className="profileThreadHeaderBarThreadDetails">
          {displayJank
            ? <ProfileThreadJankOverview
                className={`${className}IntervalMarkerOverview ${className}IntervalMarkerOverviewJank`}<commit_msg>Display thread responsiveness (jank) for all threads on non-plugin processes.<commit_after>/* 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 explicitConnect from '../../utils/connect';
import ThreadStackGraph from './ThreadStackGraph';
import { selectorsForThread } from '../../reducers/profile-view';
import { getSelectedThreadIndex } from '../../reducers/url-state';
import {
  getSampleIndexClosestToTime,
  getCallNodePath,
} from '../../profile-logic/profile-data';
import ContextMenuTrigger from '../shared/ContextMenuTrigger';
import ProfileThreadJankOverview from './ProfileThreadJankOverview';
import ProfileThreadTracingMarkerOverview from './ProfileThreadTracingMarkerOverview';
import {
  changeSelectedThread,
  updateProfileSelection,
  changeRightClickedThread,
  changeSelectedCallNode,
  focusCallTree,
} from '../../actions/profile-view';
import EmptyThreadIndicator from './EmptyThreadIndicator';

import type { Thread, ThreadIndex } from '../../types/profile';
import type { Milliseconds, StartEndRange } from '../../types/units';
import type {
  CallNodeInfo,
  IndexIntoCallNodeTable,
} from '../../types/profile-derived';
import type { State } from '../../types/reducers';
import type {
  ExplicitConnectOptions,
  ConnectedProps,
} from '../../utils/connect';

type OwnProps = {|
  +threadIndex: ThreadIndex,
  +interval: Milliseconds,
  +rangeStart: Milliseconds,
  +rangeEnd: Milliseconds,
  +isHidden: boolean,
  +isModifyingSelection: boolean,
  +style?: Object /* This is used by Reorderable */,
|};

type StateProps = {|
  +thread: Thread,
  +threadName: string,
  +processDetails: string,
  +callNodeInfo: CallNodeInfo,
  +selectedCallNodeIndex: IndexIntoCallNodeTable | null,
  +isSelected: boolean,
  +unfilteredSamplesRange: StartEndRange | null,
|};

type DispatchProps = {|
  +changeSelectedThread: typeof changeSelectedThread,
  +changeRightClickedThread: typeof changeRightClickedThread,
  +updateProfileSelection: typeof updateProfileSelection,
  +changeSelectedCallNode: typeof changeSelectedCallNode,
  +focusCallTree: typeof focusCallTree,
|};

type Props = ConnectedProps<OwnProps, StateProps, DispatchProps>;

class ProfileThreadHeaderBar extends PureComponent<Props> {
  constructor(props) {
    super(props);
    (this: any)._onLabelMouseDown = this._onLabelMouseDown.bind(this);
    (this: any)._onStackClick = this._onStackClick.bind(this);
    (this: any)._onLineClick = this._onLineClick.bind(this);
    (this: any)._onIntervalMarkerSelect = this._onIntervalMarkerSelect.bind(
      this
    );
  }

  _onLabelMouseDown(event: MouseEvent) {
    const {
      changeSelectedThread,
      changeRightClickedThread,
      threadIndex,
    } = this.props;
    if (event.button === 0) {
      changeSelectedThread(threadIndex);

      // Don't allow clicks on the threads list to steal focus from the tree view.
      event.preventDefault();
    } else if (event.button === 2) {
      // This is needed to allow the context menu to know what was right clicked without
      // actually changing the current selection.
      changeRightClickedThread(threadIndex);
    }
  }

  _onLineClick() {
    const { threadIndex, changeSelectedThread } = this.props;
    changeSelectedThread(threadIndex);
  }

  _onStackClick(time: number) {
    const { threadIndex, interval } = this.props;
    const {
      thread,
      callNodeInfo,
      changeSelectedCallNode,
      focusCallTree,
    } = this.props;
    const sampleIndex = getSampleIndexClosestToTime(
      thread.samples,
      time,
      interval
    );
    const newSelectedStack = thread.samples.stack[sampleIndex];
    const newSelectedCallNode =
      newSelectedStack === null
        ? -1
        : callNodeInfo.stackIndexToCallNodeIndex[newSelectedStack];
    changeSelectedCallNode(
      threadIndex,
      getCallNodePath(newSelectedCallNode, callNodeInfo.callNodeTable)
    );
    focusCallTree();
  }

  _onIntervalMarkerSelect(
    threadIndex: ThreadIndex,
    start: Milliseconds,
    end: Milliseconds
  ) {
    const {
      rangeStart,
      rangeEnd,
      updateProfileSelection,
      changeSelectedThread,
    } = this.props;
    updateProfileSelection({
      hasSelection: true,
      isModifying: false,
      selectionStart: Math.max(rangeStart, start),
      selectionEnd: Math.min(rangeEnd, end),
    });
    changeSelectedThread(threadIndex);
  }

  render() {
    const {
      thread,
      threadIndex,
      interval,
      rangeStart,
      rangeEnd,
      callNodeInfo,
      selectedCallNodeIndex,
      isSelected,
      threadName,
      processDetails,
      isHidden,
      isModifyingSelection,
      unfilteredSamplesRange,
      style,
    } = this.props;

    if (isHidden) {
      // If this thread is hidden, render out a stub element so that the Reorderable
      // Component still works across all the threads.
      return <li className="profileThreadHeaderBarHidden" />;
    }

    const processType = thread.processType;
    const displayJank = processType !== 'plugin';
    const displayTracingMarkers =
      (thread.name === 'GeckoMain' ||
        thread.name === 'Compositor' ||
        thread.name === 'Renderer') &&
      processType !== 'plugin';
    const className = 'profileThreadHeaderBar';

    return (
      <li
        className={'profileThreadHeaderBar' + (isSelected ? ' selected' : '')}
        onClick={this._onLineClick}
        style={style}
      >
        <ContextMenuTrigger
          id={'ProfileThreadHeaderContextMenu'}
          renderTag="div"
          attributes={{
            title: processDetails,
            className: 'grippy profileThreadHeaderBarThreadLabel',
            onMouseDown: this._onLabelMouseDown,
          }}
        >
          <h1 className="profileThreadHeaderBarThreadName">
            {threadName}
          </h1>
        </ContextMenuTrigger>
        <div className="profileThreadHeaderBarThreadDetails">
          {displayJank
            ? <ProfileThreadJankOverview
                className={`${className}IntervalMarkerOverview ${className}IntervalMarkerOverviewJank`}
{'fraction_non_alphanumeric': 0.10567358955995862, 'fraction_numerical': 0.0014323227500596801, 'mean_word_length': 3.2617836554764326, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6712295', 'n_tokens_mistral': 3495, 'n_tokens_neox': 3447, 'n_words': 812}