Bug 1201327 - Don't repaint the whole frame subtree when background-position changes. r=dbaron
authorMarkus Stange <mstange@themasta.com>
Mon, 02 Nov 2015 17:36:35 +0100
changeset 270770 21cabf6ab3e9a254cdc9370986f300213b346cb2
parent 270769 b0a00d6915bb7c858ae7e1d94c7507b76e23eddd
child 270771 9e3455c9aa3c17d8a0c488071766396150ef7136
push id67450
push usermstange@themasta.com
push dateMon, 02 Nov 2015 16:37:28 +0000
treeherdermozilla-inbound@21cabf6ab3e9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1201327
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1201327 - Don't repaint the whole frame subtree when background-position changes. r=dbaron
layout/reftests/invalidation/background-position-1-ref.html
layout/reftests/invalidation/background-position-1.html
layout/reftests/invalidation/reftest.list
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/background-position-1-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Changes to background-position should not cause things to repaint that don't intersect the background image.</title>
+
+<style>
+
+body {
+  margin: 0;
+}
+
+#background {
+  height: 512px;
+  background-image: url(image_rgrg-256x256.png);
+  background-repeat: no-repeat;
+  background-position: 300px 100px;
+}
+
+#not-intersecting-background {
+  box-sizing: border-box;
+  width: 200px;
+  height: 200px;
+  margin: 50px;
+  border: 1px solid lime;
+}
+
+</style>
+
+<div id="background">
+  <div id="not-intersecting-background"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/background-position-1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Changes to background-position should not cause things to repaint that don't intersect the background image.</title>
+
+<style>
+
+body {
+  margin: 0;
+}
+
+#background {
+  height: 512px;
+  background-image: url(image_rgrg-256x256.png);
+  background-repeat: no-repeat;
+  background-position: 300px 50px;
+}
+
+#not-intersecting-background {
+  box-sizing: border-box;
+  width: 200px;
+  height: 200px;
+  margin: 50px;
+  border: 1px solid lime;
+}
+
+</style>
+
+<div id="background">
+  <div id="not-intersecting-background" class="reftest-no-paint"></div>
+</div>
+
+<script>
+
+function doTest() {
+  document.querySelector("#background").style.backgroundPosition = "300px 100px";
+  document.documentElement.removeAttribute("class");
+}
+document.addEventListener("MozReftestInvalidate", doTest);
+
+</script>
--- a/layout/reftests/invalidation/reftest.list
+++ b/layout/reftests/invalidation/reftest.list
@@ -65,8 +65,9 @@ pref(layout.animated-image-layers.enable
 != layer-splitting-6.html about:blank
 != layer-splitting-7.html about:blank
 fuzzy-if(gtkWidget,2,4) fuzzy-if(asyncPan,2,3955) fuzzy-if(OSX,179,30) == image-scrolling-zoom-1.html image-scrolling-zoom-1-ref.html
 != image-scrolling-zoom-1-ref.html image-scrolling-zoom-1-notref.html
 != fast-scrolling.html about:blank
 != fractional-transform-1.html about:blank
 != fractional-transform-2.html about:blank
 != fractional-transform-3.html about:blank
+== background-position-1.html background-position-1-ref.html
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2246,37 +2246,42 @@ nsStyleBackground::Destroy(nsPresContext
 
 nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const
 {
   const nsStyleBackground* moreLayers =
     mImageCount > aOther.mImageCount ? this : &aOther;
   const nsStyleBackground* lessLayers =
     mImageCount > aOther.mImageCount ? &aOther : this;
 
-  bool hasVisualDifference = false;
+  nsChangeHint hint = nsChangeHint(0);
 
   NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, moreLayers) {
     if (i < lessLayers->mImageCount) {
-      if (moreLayers->mLayers[i] != lessLayers->mLayers[i]) {
-        if ((moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) ||
-            (lessLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element))
-          return NS_CombineHint(nsChangeHint_UpdateEffects,
-                                nsChangeHint_RepaintFrame);
-        hasVisualDifference = true;
+      nsChangeHint layerDifference = moreLayers->mLayers[i].CalcDifference(lessLayers->mLayers[i]);
+      hint |= layerDifference;
+      if (layerDifference &&
+          ((moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) ||
+           (lessLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element))) {
+        hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
       }
     } else {
-      if (moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element)
-        return NS_CombineHint(nsChangeHint_UpdateEffects,
-                              nsChangeHint_RepaintFrame);
-      hasVisualDifference = true;
+      hint |= nsChangeHint_RepaintFrame;
+      if (moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) {
+        hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
+      }
     }
   }
 
-  if (hasVisualDifference || mBackgroundColor != aOther.mBackgroundColor)
-    return nsChangeHint_RepaintFrame;
+  if (mBackgroundColor != aOther.mBackgroundColor) {
+    hint |= nsChangeHint_RepaintFrame;
+  }
+
+  if (hint) {
+    return hint;
+  }
 
   if (mAttachmentCount != aOther.mAttachmentCount ||
       mClipCount != aOther.mClipCount ||
       mOriginCount != aOther.mOriginCount ||
       mRepeatCount != aOther.mRepeatCount ||
       mPositionCount != aOther.mPositionCount ||
       mSizeCount != aOther.mSizeCount) {
     return nsChangeHint_NeutralChange;
@@ -2477,16 +2482,35 @@ nsStyleBackground::Layer::operator==(con
          mOrigin == aOther.mOrigin &&
          mRepeat == aOther.mRepeat &&
          mBlendMode == aOther.mBlendMode &&
          mPosition == aOther.mPosition &&
          mSize == aOther.mSize &&
          mImage == aOther.mImage;
 }
 
+nsChangeHint
+nsStyleBackground::Layer::CalcDifference(const Layer& aOther) const
+{
+  nsChangeHint hint = nsChangeHint(0);
+  if (mAttachment != aOther.mAttachment ||
+      mClip != aOther.mClip ||
+      mOrigin != aOther.mOrigin ||
+      mRepeat != aOther.mRepeat ||
+      mBlendMode != aOther.mBlendMode ||
+      mSize != aOther.mSize ||
+      mImage != aOther.mImage) {
+    hint |= nsChangeHint_RepaintFrame;
+  }
+  if (mPosition != aOther.mPosition) {
+    hint |= nsChangeHint_SchedulePaint;
+  }
+  return hint;
+}
+
 // --------------------
 // nsStyleDisplay
 //
 void nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType)
 {
   switch (aTimingFunctionType) {
     case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START:
       mType = Type::StepStart;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -391,19 +391,20 @@ struct nsStyleBackground {
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBackground, sz);
   }
   void Destroy(nsPresContext* aContext);
 
   nsChangeHint CalcDifference(const nsStyleBackground& aOther) const;
   static nsChangeHint MaxDifference() {
-    return NS_CombineHint(nsChangeHint_UpdateEffects,
-                          NS_CombineHint(nsChangeHint_RepaintFrame,
-                                         nsChangeHint_NeutralChange));
+    return nsChangeHint_UpdateEffects |
+           nsChangeHint_RepaintFrame |
+           nsChangeHint_SchedulePaint |
+           nsChangeHint_NeutralChange;
   }
   static nsChangeHint DifferenceAlwaysHandledForDescendants() {
     // CalcDifference never returns the reflow hints that are sometimes
     // handled for descendants at all.
     return nsChangeHint(0);
   }
 
   struct Position;
@@ -547,16 +548,19 @@ struct nsStyleBackground {
 
     // True if the rendering of this layer might change when the size
     // of the background positioning area changes.  This is true for any
     // non-solid-color background whose position or size depends on
     // the size of the positioning area.  It's also true for SVG images
     // whose root <svg> node has a viewBox.
     bool RenderingMightDependOnPositioningAreaSizeChange() const;
 
+    // Compute the change hint required by changes in just this layer.
+    nsChangeHint CalcDifference(const Layer& aOther) const;
+
     // An equality operator that compares the images using URL-equality
     // rather than pointer-equality.
     bool operator==(const Layer& aOther) const;
     bool operator!=(const Layer& aOther) const {
       return !(*this == aOther);
     }
   };