Don't load CSS resources for non-rendered elements.

The current situation was that we triggered loading of all resources,
like background-images, for an element we computed style for. Instead,
align with Firefox and Safari to skip fetching for:

* display:none elements
* elements in display:none subtrees
* display:contents elements
* elements outside the flat tree

Note that we still trigger fetches for visibility:hidden, which is in
line with what Firefox and Safari does.

Also removed an unnecessary StyleResolver::LoadPendingResources wrapper.

Bug: 765095
Change-Id: I958b5d22bac942408957398bf5761269908466b5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2190650
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/master@{#767327}
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 8e83060..a383649 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -764,11 +764,6 @@
   return viewport_style;
 }
 
-// Start loading resources referenced by this style.
-void StyleResolver::LoadPendingResources(StyleResolverState& state) {
-  state.GetElementStyleResources().LoadPendingResources(state.Style());
-}
-
 static ElementAnimations* GetElementAnimations(StyleResolverState& state) {
   if (!state.GetAnimatingElement())
     return nullptr;
@@ -1265,7 +1260,7 @@
         state, result.AllRules(), false, inherited_only, needs_apply_pass);
   }
 
-  LoadPendingResources(state);
+  state.LoadPendingResources();
 
   // Now return the style.
   return state.TakeStyle();
@@ -1459,7 +1454,7 @@
   }
 
   // Start loading resources used by animations.
-  LoadPendingResources(state);
+  state.LoadPendingResources();
 
   DCHECK(!state.GetFontBuilder().FontDirty());
 
@@ -2083,7 +2078,7 @@
         state, match_result, apply_inherited_only, needs_apply_pass);
   }
 
-  LoadPendingResources(state);
+  state.LoadPendingResources();
   MaybeAddToMatchedPropertiesCache(state, cache_success, match_result);
 
   DCHECK(!state.GetFontBuilder().FontDirty());
@@ -2185,7 +2180,7 @@
 
   CascadeAndApplyForcedColors(state, result);
 
-  LoadPendingResources(state);
+  state.LoadPendingResources();
   MaybeAddToMatchedPropertiesCache(state, cache_success, result);
 
   DCHECK(!state.GetFontBuilder().FontDirty());
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h
index d61c1de..7c259ec 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -153,8 +153,6 @@
 
   void AddMatchedRulesToTracker(const ElementRuleCollector&);
 
-  void LoadPendingResources(StyleResolverState&);
-
   void CollectPseudoRulesForElement(const Element&,
                                     ElementRuleCollector&,
                                     PseudoId,
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
index 46d980e..cbd2473 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
@@ -156,6 +156,12 @@
 }
 
 void StyleResolverState::LoadPendingResources() {
+  if ((ParentStyle() && ParentStyle()->IsEnsuredInDisplayNone()) ||
+      StyleRef().Display() == EDisplay::kNone ||
+      StyleRef().Display() == EDisplay::kContents ||
+      StyleRef().IsEnsuredOutsideFlatTree())
+    return;
+
   element_style_resources_.LoadPendingResources(Style());
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index 46d01c3a..d431324 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -8,12 +8,17 @@
 #include "third_party/blink/renderer/core/animation/animation_test_helper.h"
 #include "third_party/blink/renderer/core/animation/document_timeline.h"
 #include "third_party/blink/renderer/core/animation/element_animations.h"
+#include "third_party/blink/renderer/core/css/css_image_value.h"
+#include "third_party/blink/renderer/core/css/css_value_list.h"
+#include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
+#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/dom/text.h"
+#include "third_party/blink/renderer/core/html/html_style_element.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
@@ -304,4 +309,84 @@
                          StyleResolverFontRelativeUnitTest,
                          testing::Values("em", "rem", "ex", "ch"));
 
+namespace {
+
+const CSSImageValue& GetBackgroundImageValue(Element* element) {
+  DCHECK(element);
+  const auto* style = element->GetComputedStyle();
+  DCHECK(style);
+  const CSSValue* computed_value = ComputedStyleUtils::ComputedPropertyValue(
+      GetCSSPropertyBackgroundImage(), *style);
+
+  const CSSValueList* bg_img_list = To<CSSValueList>(computed_value);
+  return To<CSSImageValue>(bg_img_list->Item(0));
+}
+
+}  // namespace
+
+TEST_F(StyleResolverTest, BackgroundImageFetch) {
+  GetDocument().documentElement()->setInnerHTML(R"HTML(
+    <style id="sheet">
+      #none {
+        display: none;
+        background-image: url(img-none.png);
+      }
+      #inside-none {
+        background-image: url(img-inside-none.png);
+      }
+      #hidden {
+        visibility: hidden;
+        background-image: url(img-hidden.png);
+      }
+      #inside-hidden {
+        background-image: url(img-inside-hidden.png);
+      }
+      #contents {
+        display: contents;
+        background-image: url(img-contents.png);
+      }
+      #non-slotted {
+        background-image: url(img-non-slotted.png);
+      }
+    </style>
+    <div id="none">
+      <div id="inside-none"></div>
+    </div>
+    <div id="hidden">
+      <div id="inside-hidden"></div>
+    </div>
+    <div id="contents"></div>
+    <div id="host">
+      <div id="non-slotted"></div>
+    </div>
+  )HTML");
+
+  GetDocument().getElementById("host")->AttachShadowRootInternal(
+      ShadowRootType::kOpen);
+  UpdateAllLifecyclePhasesForTest();
+
+  auto* none = GetDocument().getElementById("none");
+  auto* inside_none = GetDocument().getElementById("inside-none");
+  auto* hidden = GetDocument().getElementById("hidden");
+  auto* inside_hidden = GetDocument().getElementById("inside-hidden");
+  auto* contents = GetDocument().getElementById("contents");
+  auto* non_slotted = GetDocument().getElementById("non-slotted");
+
+  inside_none->EnsureComputedStyle();
+  non_slotted->EnsureComputedStyle();
+
+  EXPECT_TRUE(GetBackgroundImageValue(none).IsCachePending())
+      << "No fetch for display:none";
+  EXPECT_TRUE(GetBackgroundImageValue(inside_none).IsCachePending())
+      << "No fetch inside display:none";
+  EXPECT_FALSE(GetBackgroundImageValue(hidden).IsCachePending())
+      << "Fetch for visibility:hidden";
+  EXPECT_FALSE(GetBackgroundImageValue(inside_hidden).IsCachePending())
+      << "Fetch for inherited visibility:hidden";
+  EXPECT_TRUE(GetBackgroundImageValue(contents).IsCachePending())
+      << "No fetch for display:contents";
+  EXPECT_TRUE(GetBackgroundImageValue(non_slotted).IsCachePending())
+      << "No fetch for element outside the flat tree";
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/style/BUILD.gn b/third_party/blink/renderer/core/style/BUILD.gn
index 61caa6d6..73358b0 100644
--- a/third_party/blink/renderer/core/style/BUILD.gn
+++ b/third_party/blink/renderer/core/style/BUILD.gn
@@ -85,6 +85,7 @@
     "style_offset_rotation.h",
     "style_path.cc",
     "style_path.h",
+    "style_pending_image.cc",
     "style_pending_image.h",
     "style_ray.cc",
     "style_ray.h",
diff --git a/third_party/blink/renderer/core/style/style_pending_image.cc b/third_party/blink/renderer/core/style/style_pending_image.cc
new file mode 100644
index 0000000..68468ad
--- /dev/null
+++ b/third_party/blink/renderer/core/style/style_pending_image.cc
@@ -0,0 +1,26 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/style/style_pending_image.h"
+
+#include "third_party/blink/renderer/core/style/computed_style.h"
+
+namespace blink {
+
+CSSValue* StylePendingImage::ComputedCSSValue(const ComputedStyle& style,
+                                              bool allow_visited_style) const {
+  DCHECK(style.IsEnsuredInDisplayNone() ||
+         style.Display() == EDisplay::kContents);
+
+  if (CSSImageValue* image_value = CssImageValue())
+    return image_value->ValueWithURLMadeAbsolute();
+  if (CSSImageSetValue* image_set_value = CssImageSetValue())
+    return image_set_value->ValueWithURLsMadeAbsolute();
+  if (CSSImageGeneratorValue* image_generator_value = CssImageGeneratorValue())
+    return image_generator_value->ComputedCSSValue(style, allow_visited_style);
+  NOTREACHED();
+  return CssValue();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/style/style_pending_image.h b/third_party/blink/renderer/core/style/style_pending_image.h
index 5d15580..d9102f3 100644
--- a/third_party/blink/renderer/core/style/style_pending_image.h
+++ b/third_party/blink/renderer/core/style/style_pending_image.h
@@ -40,8 +40,9 @@
 
 // StylePendingImage is a placeholder StyleImage that is entered into the
 // ComputedStyle during style resolution, in order to avoid loading images that
-// are not referenced by the final style.  They should never exist in a
-// ComputedStyle after it has been returned from the style selector.
+// are not referenced by the final style.  They should only exist in a
+// ComputedStyle for non-rendered elements created with EnsureComputedStyle or
+// display:contents.
 class StylePendingImage final : public StyleImage {
  public:
   explicit StylePendingImage(const CSSValue& value)
@@ -53,11 +54,8 @@
 
   CSSValue* CssValue() const override { return value_; }
 
-  CSSValue* ComputedCSSValue(const ComputedStyle&,
-                             bool allow_visited_style) const override {
-    NOTREACHED();
-    return nullptr;
-  }
+  CSSValue* ComputedCSSValue(const ComputedStyle& style,
+                             bool allow_visited_style) const override;
 
   CSSImageValue* CssImageValue() const {
     return DynamicTo<CSSImageValue>(value_.Get());
@@ -100,8 +98,6 @@
  private:
   bool IsEqual(const StyleImage& other) const override;
 
-  // TODO(sashab): Replace this with <const CSSValue> once Member<>
-  // supports const types.
   Member<CSSValue> value_;
 };
 
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-cross-fade.html b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-cross-fade.html
index 93b3bfb..9237df7 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-cross-fade.html
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-cross-fade.html
@@ -3,6 +3,7 @@
 <head>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
+<base href="https://basedomain">
 </head>
 <body>
 <script type="text/javascript">
@@ -31,6 +32,7 @@
   assert_equals(testCrossfade("background-image: -webkit-cross-fade(url(dummy://example.png), url(dummy://example.png), 700%)", "background-image"), '-webkit-cross-fade(url("dummy://example.png"), url("dummy://example.png"), 1)');
 
   assert_equals(testCrossfade("background-image: -webkit-cross-fade(url(dummy://example.png), url(dummy://example.png), -20)", "background-image"), '-webkit-cross-fade(url("dummy://example.png"), url("dummy://example.png"), 0)');
+  assert_equals(testCrossfade("display:none;background-image: -webkit-cross-fade(url(example.png), url(example.png), 20%)", "background-image"), '-webkit-cross-fade(url("https://basedomain/example.png"), url("https://basedomain/example.png"), 0.2)');
 }, 'Valid -webkit-cross-fade');
 
 test(() => {