Don't create unneeded layers for scrollbar-gutter: force

This CL prevents the creation of unnecessary layers for elements
that use scrollbar-gutter's "force" keyword.

Now those layers are created only when the element's style uses
custom scrollbars, because a PaintLayerScrollableArea is required
in order to calculate their thickness.

When the element uses default scrollbars, their thickness can be
obtained directly from the page's scrollbar theme.

Bug: 710214
Change-Id: Ia319e5601745247189be58da8b1e992a34a17c99
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2738895
Reviewed-by: Philip Rogers <pdr@chromium.org>
Commit-Queue: Felipe Erias <felipeerias@igalia.com>
Cr-Commit-Position: refs/heads/master@{#864254}
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index dfb4b7b..df80e37 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1489,8 +1489,7 @@
 
 LayoutBox* Element::GetLayoutBoxForScrolling() const {
   LayoutBox* box = GetLayoutBox();
-  if (!box || (!box->IsScrollContainer() &&
-               !box->StyleRef().IsScrollbarGutterForce())) {
+  if (!box || !box->IsScrollContainer()) {
     return nullptr;
   }
   return box;
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index d0c6adc4..0e75300 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -90,6 +90,7 @@
 #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h"
 #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
+#include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
 #include "third_party/blink/renderer/core/paint/background_image_geometry.h"
@@ -99,6 +100,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/paint/rounded_border_geometry.h"
+#include "third_party/blink/renderer/core/style/computed_style_base_constants.h"
 #include "third_party/blink/renderer/core/style/shadow_list.h"
 #include "third_party/blink/renderer/platform/geometry/double_rect.h"
 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
@@ -366,6 +368,30 @@
   }
 }
 
+int HypotheticalScrollbarThickness(const LayoutBox& box,
+                                   ScrollbarOrientation scrollbar_orientation,
+                                   bool should_include_overlay_thickness) {
+  box.CheckIsNotDestroyed();
+
+  if (PaintLayerScrollableArea* scrollable_area = box.GetScrollableArea()) {
+    return scrollable_area->HypotheticalScrollbarThickness(
+        scrollbar_orientation, should_include_overlay_thickness);
+  } else {
+    Page* page = box.GetFrame()->GetPage();
+    ScrollbarTheme& theme = page->GetScrollbarTheme();
+
+    if (theme.UsesOverlayScrollbars() && !should_include_overlay_thickness) {
+      return 0;
+    } else {
+      ChromeClient& chrome_client = page->GetChromeClient();
+      Document& document = box.GetDocument();
+      float scale_from_dip =
+          chrome_client.WindowToViewportScalar(document.GetFrame(), 1.0f);
+      return theme.ScrollbarThickness(scale_from_dip);
+    }
+  }
+}
+
 }  // namespace
 
 BoxLayoutExtraInput::BoxLayoutExtraInput(LayoutBox& box) : box(box) {
@@ -416,7 +442,8 @@
   if (HasNonVisibleOverflow())
     return kOverflowClipPaintLayer;
 
-  if (StyleRef().IsScrollbarGutterForce())
+  if (StyleRef().IsScrollbarGutterForce() &&
+      StyleRef().HasPseudoElementStyle(kPseudoIdScrollbar))
     return kNormalPaintLayer;
 
   return kNoPaintLayer;
@@ -1000,8 +1027,14 @@
 
 LayoutUnit LayoutBox::ScrollWidth() const {
   NOT_DESTROYED();
-  if (IsScrollContainer() || StyleRef().IsScrollbarGutterForce())
+  if (IsScrollContainer())
     return GetScrollableArea()->ScrollWidth();
+  if (StyleRef().IsScrollbarGutterForce()) {
+    if (auto* scrollable_area = GetScrollableArea())
+      return scrollable_area->ScrollWidth();
+    else
+      return PhysicalLayoutOverflowRect().Width();
+  }
   // For objects with visible overflow, this matches IE.
   // FIXME: Need to work right with writing modes.
   if (StyleRef().IsLeftToRightDirection())
@@ -1012,8 +1045,14 @@
 
 LayoutUnit LayoutBox::ScrollHeight() const {
   NOT_DESTROYED();
-  if (IsScrollContainer() || StyleRef().IsScrollbarGutterForce())
+  if (IsScrollContainer())
     return GetScrollableArea()->ScrollHeight();
+  if (StyleRef().IsScrollbarGutterForce()) {
+    if (auto* scrollable_area = GetScrollableArea())
+      return scrollable_area->ScrollHeight();
+    else
+      return PhysicalLayoutOverflowRect().Height();
+  }
   // For objects with visible overflow, this matches IE.
   // FIXME: Need to work right with writing modes.
   return std::max(ClientHeight(), LayoutOverflowRect().MaxY() - BorderTop());
@@ -1584,14 +1623,11 @@
   NOT_DESTROYED();
   NGPhysicalBoxStrut scrollbars;
   PaintLayerScrollableArea* scrollable_area = GetScrollableArea();
-  if (!scrollable_area)
-    return scrollbars;
 
   if (include_scrollbar_gutter == kIncludeScrollbarGutter &&
       HasScrollbarGutters(kVerticalScrollbar)) {
-    LayoutUnit gutter_size =
-        LayoutUnit(scrollable_area->HypotheticalScrollbarThickness(
-            kVerticalScrollbar, /* should_include_overlay_thickness */ true));
+    LayoutUnit gutter_size = LayoutUnit(HypotheticalScrollbarThickness(
+        *this, kVerticalScrollbar, /* include_overlay_thickness */ true));
     if (ShouldPlaceVerticalScrollbarOnLeft()) {
       scrollbars.left = gutter_size;
       if (StyleRef().IsScrollbarGutterBoth())
@@ -1601,23 +1637,25 @@
       if (StyleRef().IsScrollbarGutterBoth())
         scrollbars.left = gutter_size;
     }
-  } else if (ShouldPlaceVerticalScrollbarOnLeft()) {
-    scrollbars.left = LayoutUnit(scrollable_area->VerticalScrollbarWidth(
-        overlay_scrollbar_clip_behavior));
-  } else {
-    scrollbars.right = LayoutUnit(scrollable_area->VerticalScrollbarWidth(
-        overlay_scrollbar_clip_behavior));
+  } else if (scrollable_area) {
+    if (ShouldPlaceVerticalScrollbarOnLeft()) {
+      scrollbars.left = LayoutUnit(scrollable_area->VerticalScrollbarWidth(
+          overlay_scrollbar_clip_behavior));
+    } else {
+      scrollbars.right = LayoutUnit(scrollable_area->VerticalScrollbarWidth(
+          overlay_scrollbar_clip_behavior));
+    }
   }
 
   if (include_scrollbar_gutter == kIncludeScrollbarGutter &&
       HasScrollbarGutters(kHorizontalScrollbar)) {
-    LayoutUnit gutter_size =
-        LayoutUnit(scrollable_area->HypotheticalScrollbarThickness(
-            kHorizontalScrollbar, /* should_include_overlay_thickness */ true));
+    LayoutUnit gutter_size = LayoutUnit(
+        HypotheticalScrollbarThickness(*this, kHorizontalScrollbar,
+                                       /* include_overlay_thickness */ true));
     scrollbars.bottom = gutter_size;
     if (StyleRef().IsScrollbarGutterBoth())
       scrollbars.top = gutter_size;
-  } else {
+  } else if (scrollable_area) {
     scrollbars.bottom = LayoutUnit(scrollable_area->HorizontalScrollbarHeight(
         overlay_scrollbar_clip_behavior));
   }
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 2f9146d..a8ecd5fb 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1683,10 +1683,12 @@
   // PaintLayerScrollableArea.
   if (GetLayoutBox()->CanResize())
     return true;
-  // When scrollbar-gutter is "force" we need a PaintLayerScrollableArea
-  // in order to calculate the size of scrollbar gutters.
-  if (GetLayoutObject().StyleRef().IsScrollbarGutterForce())
+  // With custom scrollbars, we need a PaintLayerScrollableArea
+  // so we can calculate the size of scrollbar gutters.
+  if (GetLayoutObject().StyleRef().IsScrollbarGutterForce() &&
+      GetLayoutObject().StyleRef().HasPseudoElementStyle(kPseudoIdScrollbar)) {
     return true;
+  }
   return false;
 }
 
diff --git a/third_party/blink/web_tests/compositing/overflow/overflow-scrollbar-gutter-layers-expected.txt b/third_party/blink/web_tests/compositing/overflow/overflow-scrollbar-gutter-layers-expected.txt
new file mode 100644
index 0000000..c543542
--- /dev/null
+++ b/third_party/blink/web_tests/compositing/overflow/overflow-scrollbar-gutter-layers-expected.txt
@@ -0,0 +1,11 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/compositing/overflow/overflow-scrollbar-gutter-layers.html b/third_party/blink/web_tests/compositing/overflow/overflow-scrollbar-gutter-layers.html
new file mode 100644
index 0000000..ccf5a15
--- /dev/null
+++ b/third_party/blink/web_tests/compositing/overflow/overflow-scrollbar-gutter-layers.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<style>
+  body {
+    display: flex;
+    flex-wrap: wrap;
+  }
+
+  .container {
+    width: 100px;
+    height: 100px;
+    margin: 5px;
+    background-color: green;
+    border: solid;
+  }
+
+  .content {
+    width: 100%;
+    height: 105px;
+    background-color: orange;
+  }
+
+  /* overflow */
+  .container.visible { overflow:   visible; }
+  .container.hidden  { overflow-y: hidden;  }
+  .container.clip    { overflow:   clip;  }
+
+  /* scrollbar-gutter */
+  .stable            { scrollbar-gutter: stable; }
+  .stable_both       { scrollbar-gutter: stable both; }
+  .stable_force      { scrollbar-gutter: stable force; }
+  .stable_both_force { scrollbar-gutter: stable both force; }
+  .always            { scrollbar-gutter: always; }
+  .always_both       { scrollbar-gutter: always both; }
+  .always_force      { scrollbar-gutter: always force; }
+  .always_both_force { scrollbar-gutter: always both force; }
+
+</style>
+<pre id="layerTree"></pre>
+<!-- These elements should not create new layers. -->
+<div class="container visible stable_force">
+  <div class="content"></div>
+</div>
+<div class="container visible stable_both_force">
+  <div class="content"></div>
+</div>
+<div class="container visible always_force">
+  <div class="content"></div>
+</div>
+<div class="container visible always_both_force">
+  <div class="content"></div>
+</div>
+<div class="container hidden stable_force">
+  <div class="content"></div>
+</div>
+<div class="container hidden stable_both_force">
+  <div class="content"></div>
+</div>
+<div class="container hidden always_force">
+  <div class="content"></div>
+</div>
+<div class="container hidden always_both_force">
+  <div class="content"></div>
+</div>
+<div class="container clip stable_force">
+  <div class="content"></div>
+</div>
+<div class="container clip stable_both_force">
+  <div class="content"></div>
+</div>
+<div class="container clip always_force">
+  <div class="content"></div>
+</div>
+<div class="container clip always_both_force">
+  <div class="content"></div>
+</div>
+<script>
+  if (window.testRunner) {
+    testRunner.dumpAsText();
+    document.getElementById("layerTree").innerText = internals.layerTreeAsText(document);
+  }
+</script>
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/overflow-scrollbar-gutter-layers-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/overflow-scrollbar-gutter-layers-expected.txt
new file mode 100644
index 0000000..82c7392
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/compositing/overflow/overflow-scrollbar-gutter-layers-expected.txt
@@ -0,0 +1,11 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+