chromium/tools/perf/page_sets/rendering/tough_scrollbar_scrolling_cases.py

# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import time

from telemetry.internal.actions import page_action
from telemetry.page import shared_page_state

from page_sets.rendering import rendering_story
from page_sets.rendering import story_tags

TOUGH_SCROLLBAR_UMA = [
    'Graphics.Smoothness.Checkerboarding3.ScrollbarScroll',
]


class ToughFastScrollbarScrollingPage(rendering_story.RenderingStory):
  ABSTRACT_STORY = True
  SPEED_IN_PIXELS_PER_SECOND = None
  SYNTHETIC_GESTURE_SOURCE = page_action.GESTURE_SOURCE_MOUSE
  TAGS = [story_tags.GPU_RASTERIZATION, story_tags.TOUGH_SCROLLBAR_SCROLLING]

  def __init__(self,
               page_set,
               shared_page_state_class=shared_page_state.SharedPageState,
               name_suffix='',
               extra_browser_args=None):
    super(ToughFastScrollbarScrollingPage,
          self).__init__(page_set=page_set,
                         shared_page_state_class=shared_page_state_class,
                         name_suffix=name_suffix,
                         extra_browser_args=extra_browser_args)

  def RunPageInteractions(self, action_runner):
    scrollbar_x, scrollbar_top, scrollbar_bottom, top_button, bottom_button = \
      self._ScrollBarRatios(action_runner)
    start = time.time()
    with action_runner.CreateGestureInteraction('DragAction'):
      direction = 'down'
      while time.time() - start < 15:
        if direction == 'down':
          action_runner.DragPage(
              left_start_ratio=scrollbar_x,
              top_start_ratio=scrollbar_top,
              left_end_ratio=scrollbar_x,
              top_end_ratio=bottom_button,
              speed_in_pixels_per_second=self.SPEED_IN_PIXELS_PER_SECOND)
        else:
          action_runner.DragPage(
              left_start_ratio=scrollbar_x,
              top_start_ratio=scrollbar_bottom,
              left_end_ratio=scrollbar_x,
              top_end_ratio=top_button,
              speed_in_pixels_per_second=self.SPEED_IN_PIXELS_PER_SECOND)
        direction = 'up' if direction == 'down' else 'down'

  def _ScrollBarRatios(self, action_runner):
    # Calculate scrollbar thickness by adding an element to the page.
    # Record the client width of that element, and then add a long child
    # element so that the first element includes a scrollbar. Record the
    # client width again and then calculate the difference to get the
    # scrollbar thickness.
    scrollbar_thickness = float(
        action_runner.EvaluateJavaScript('''
        (function() {
          window.__scrollableElementForTelemetry = document.scrollingElement;
          var container = document.createElement("div");
          container.style.width = "100px";
          container.style.height = "100px";
          container.style.position = "absolute";
          container.style.visibility = "hidden";
          container.style.overflow = "auto";

          document.body.appendChild(container);

          var widthBefore = container.clientWidth;
          var longContent = document.createElement("div");
          longContent.style.height = "1000px";
          container.appendChild(longContent);

          var widthAfter = container.clientWidth;

          container.remove();

          window.__scrollbarThickness = widthBefore - widthAfter;
          return window.__scrollbarThickness;
        })();'''))
    window_width = float(action_runner.EvaluateJavaScript('window.innerWidth'))
    window_height = float(
        action_runner.EvaluateJavaScript('window.innerHeight'))
    # Works only when there is no horizontally overflowing content.
    left_margin = float(
        action_runner.EvaluateJavaScript(
            'document.body.getBoundingClientRect().x'))
    body_width = float(
        action_runner.EvaluateJavaScript(
            'document.body.getBoundingClientRect().width'))
    top_margin = float(
        action_runner.EvaluateJavaScript(
            'document.body.getBoundingClientRect().y'))
    window_height = window_height - top_margin

    # For regular scrollbars the minimal thumb length is one and two times the
    # track's width in W10 and W11 respectively. For Fluent scrollbars the
    # minimal thumb length is 17px. The constant was chosen such that:
    # (Button Length) <= (Track width * |track_to_arrow_button_ratio|) <=
    # (Button Length + Minimum thumb length) for all scrollbars.
    track_to_arrow_button_ratio = 1.8
    top = (scrollbar_thickness * track_to_arrow_button_ratio -
           top_margin) / (window_height)
    bottom = 1 - (scrollbar_thickness *
                  track_to_arrow_button_ratio) / window_height
    scrollbar_mid_x = (window_width - left_margin -
                       (scrollbar_thickness / 2)) / body_width
    # Ensure the thumb goes through the full range of motion. These
    # variables make the the mouse drag until the middle of the scrollbar
    # buttons.
    top_button = ((scrollbar_thickness / 2) - top_margin) / window_height
    bottom_button = 1 - (scrollbar_thickness / 2) / window_height
    return scrollbar_mid_x, top, bottom, top_button, bottom_button

  def WillStartTracing(self, chrome_trace_config):
    chrome_trace_config.EnableUMAHistograms(*TOUGH_SCROLLBAR_UMA)


class ScrollbarScrollingText100Page(ToughFastScrollbarScrollingPage):
  BASE_NAME = 'text_scrollbar_100_pixels_per_second'
  URL = 'file://../tough_scrolling_cases/text.html'
  SPEED_IN_PIXELS_PER_SECOND = 100


class ScrollbarScrollingText200Page(ToughFastScrollbarScrollingPage):
  BASE_NAME = 'text_scrollbar_200_pixels_per_second'
  URL = 'file://../tough_scrolling_cases/text.html'
  SPEED_IN_PIXELS_PER_SECOND = 400


class ScrollbarScrollingText300Page(ToughFastScrollbarScrollingPage):
  BASE_NAME = 'text_scrollbar_700_pixels_per_second'
  URL = 'file://../tough_scrolling_cases/text.html'
  SPEED_IN_PIXELS_PER_SECOND = 700


class ScrollbarScrollingText1200Page(ToughFastScrollbarScrollingPage):
  BASE_NAME = 'text_scrollbar_1200_pixels_per_second'
  URL = 'file://../tough_scrolling_cases/text.html'
  SPEED_IN_PIXELS_PER_SECOND = 1200


class ScrollbarScrollingText2300Page(ToughFastScrollbarScrollingPage):
  BASE_NAME = 'text_scrollbar_2300_pixels_per_second'
  URL = 'file://../tough_scrolling_cases/text.html'
  SPEED_IN_PIXELS_PER_SECOND = 2300