Bug 1335644 - (intersection-observer) Always send an initial notification after .observe() is called. r=mstange
authorTobias Schneider <schneider@jancona.com>
Wed, 24 May 2017 17:20:57 -0700
changeset 409761 b7f8c279b92268625473c916e9f34b307c924625
parent 409760 3f686a688272acc7889ae55daae5976cd361cec6
child 409762 933ff638ea341d44700c4a89eda866c53c8ee2dd
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1335644
milestone55.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 1335644 - (intersection-observer) Always send an initial notification after .observe() is called. r=mstange
dom/base/Element.cpp
dom/base/test/test_intersectionobservers.html
testing/web-platform/meta/intersection-observer/cross-origin-iframe.html.ini
testing/web-platform/meta/intersection-observer/disconnect.html.ini
testing/web-platform/meta/intersection-observer/edge-inclusive-intersection.html.ini
testing/web-platform/meta/intersection-observer/iframe-no-root.html.ini
testing/web-platform/meta/intersection-observer/multiple-targets.html.ini
testing/web-platform/meta/intersection-observer/multiple-thresholds.html.ini
testing/web-platform/meta/intersection-observer/observer-without-js-reference.html.ini
testing/web-platform/meta/intersection-observer/remove-element.html.ini
testing/web-platform/meta/intersection-observer/root-margin.html.ini
testing/web-platform/meta/intersection-observer/same-document-no-root.html.ini
testing/web-platform/meta/intersection-observer/same-document-root.html.ini
testing/web-platform/meta/intersection-observer/same-document-zero-size-target.html.ini
testing/web-platform/meta/intersection-observer/timestamp.html.ini
testing/web-platform/meta/intersection-observer/unclipped-root.html.ini
testing/web-platform/meta/intersection-observer/zero-area-element-hidden.html.ini
testing/web-platform/meta/intersection-observer/zero-area-element-visible.html.ini
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3950,25 +3950,36 @@ Element::ClearDataset()
 
 nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
 Element::RegisteredIntersectionObservers()
 {
   nsDOMSlots* slots = DOMSlots();
   return &slots->mRegisteredIntersectionObservers;
 }
 
+enum nsPreviousIntersectionThreshold {
+  eUninitialized = -2,
+  eNonIntersecting = -1
+};
+
 void
 Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver)
 {
   nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>* observers =
     RegisteredIntersectionObservers();
   if (observers->Contains(aObserver)) {
     return;
   }
-  RegisteredIntersectionObservers()->Put(aObserver, -1);
+
+  // Value can be:
+  //   -2:   Makes sure next calculated threshold always differs, leading to a
+  //         notification task being scheduled.
+  //   -1:   Non-intersecting.
+  //   >= 0: Intersecting, valid index of aObserver->mThresholds.
+  RegisteredIntersectionObservers()->Put(aObserver, eUninitialized);
 }
 
 void
 Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver)
 {
   nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>* observers =
     RegisteredIntersectionObservers();
   observers->Remove(aObserver);
--- a/dom/base/test/test_intersectionobservers.html
+++ b/dom/base/test/test_intersectionobservers.html
@@ -317,26 +317,26 @@ limitations under the License.
           expect(observer).to.be(io);
           expect(this).to.be(io);
           done();
         }, {root: rootEl});
         io.observe(targetEl1);
       });
 
 
-      it('does not trigger if target does not intersect when observing begins',
+      it('does trigger if target does not intersect when observing begins',
           function(done) {
 
         var spy = sinon.spy();
         io = new IntersectionObserver(spy, {root: rootEl});
 
         targetEl2.style.top = '-40px';
         io.observe(targetEl2);
         callDelayed(function() {
-          expect(spy.callCount).to.be(0);
+          expect(spy.callCount).to.be(1);
           done();
         }, ASYNC_TIMEOUT);
       });
 
 
       it('does not trigger if target is not a descendant of the intersection root in the containing block chain',
           function(done) {
 
@@ -520,17 +520,17 @@ limitations under the License.
             targetEl3.style.top = '0px';
             targetEl3.style.left = '205px';
             io.observe(targetEl1);
             io.observe(targetEl2);
             io.observe(targetEl3);
             spy.waitForNotification(function() {
               expect(spy.callCount).to.be(1);
               var records = sortRecords(spy.lastCall.args[0]);
-              expect(records.length).to.be(2);
+              expect(records.length).to.be(3);
               expect(records[0].target).to.be(targetEl1);
               expect(records[0].intersectionRatio).to.be(0.25);
               expect(records[1].target).to.be(targetEl2);
               expect(records[1].intersectionRatio).to.be(0.75);
               done();
             });
           },
           function(done) {
@@ -625,59 +625,59 @@ limitations under the License.
             io.observe(targetEl1);
             io.observe(targetEl2);
             io.observe(targetEl3);
             io.observe(targetEl4);
           },
           function(done) {
             io = new IntersectionObserver(function(records) {
               records = sortRecords(records);
-              expect(records.length).to.be(3);
+              expect(records.length).to.be(4);
               expect(records[0].target).to.be(targetEl1);
               expect(records[0].intersectionRatio).to.be(0.5);
-              expect(records[1].target).to.be(targetEl3);
-              expect(records[1].intersectionRatio).to.be(0.5);
-              expect(records[2].target).to.be(targetEl4);
+              expect(records[2].target).to.be(targetEl3);
               expect(records[2].intersectionRatio).to.be(0.5);
+              expect(records[3].target).to.be(targetEl4);
+              expect(records[3].intersectionRatio).to.be(0.5);
               io.disconnect();
               done();
             }, {root: rootEl, rootMargin: '-10px 10%'});
 
             io.observe(targetEl1);
             io.observe(targetEl2);
             io.observe(targetEl3);
             io.observe(targetEl4);
           },
           function(done) {
             io = new IntersectionObserver(function(records) {
               records = sortRecords(records);
-              expect(records.length).to.be(2);
+              expect(records.length).to.be(4);
               expect(records[0].target).to.be(targetEl1);
               expect(records[0].intersectionRatio).to.be(0.5);
-              expect(records[1].target).to.be(targetEl4);
-              expect(records[1].intersectionRatio).to.be(0.5);
+              expect(records[3].target).to.be(targetEl4);
+              expect(records[3].intersectionRatio).to.be(0.5);
               io.disconnect();
               done();
             }, {root: rootEl, rootMargin: '-5% -2.5% 0px'});
 
             io.observe(targetEl1);
             io.observe(targetEl2);
             io.observe(targetEl3);
             io.observe(targetEl4);
           },
           function(done) {
             io = new IntersectionObserver(function(records) {
               records = sortRecords(records);
-              expect(records.length).to.be(3);
+              expect(records.length).to.be(4);
               expect(records[0].target).to.be(targetEl1);
               expect(records[0].intersectionRatio).to.be(0.5);
               expect(records[1].target).to.be(targetEl2);
               expect(records[1].intersectionRatio).to.be(0.5);
-              expect(records[2].target).to.be(targetEl4);
-              expect(records[2].intersectionRatio).to.be(0.25);
+              expect(records[3].target).to.be(targetEl4);
+              expect(records[3].intersectionRatio).to.be(0.25);
               io.disconnect();
               done();
             }, {root: rootEl, rootMargin: '5% -2.5% -10px -190px'});
 
             io.observe(targetEl1);
             io.observe(targetEl2);
             io.observe(targetEl3);
             io.observe(targetEl4);
@@ -697,19 +697,19 @@ limitations under the License.
             targetEl1.style.left = '-21px';
             targetEl2.style.top = '-20px';
             targetEl2.style.left = '0px';
             io.observe(targetEl1);
             io.observe(targetEl2);
             spy.waitForNotification(function() {
               expect(spy.callCount).to.be(1);
               var records = sortRecords(spy.lastCall.args[0]);
-              expect(records.length).to.be(1);
-              expect(records[0].intersectionRatio).to.be(0);
-              expect(records[0].target).to.be(targetEl2);
+              expect(records.length).to.be(2);
+              expect(records[1].intersectionRatio).to.be(0);
+              expect(records[1].target).to.be(targetEl2);
               done();
             });
           },
           function(done) {
             targetEl1.style.top = '0px';
             targetEl1.style.left = '-20px';
             targetEl2.style.top = '-21px';
             targetEl2.style.left = '0px';
@@ -809,57 +809,57 @@ limitations under the License.
         runSequence([
           function(done) {
             io.observe(targetEl1);
             callDelayed(done, 0);
           },
           function(done) {
             document.getElementById('fixtures').appendChild(rootEl);
             callDelayed(function() {
-              expect(spy.callCount).to.be(0);
+              expect(spy.callCount).to.be(1);
               done();
             }, ASYNC_TIMEOUT);
           },
           function(done) {
             parentEl.insertBefore(targetEl1, targetEl2);
             spy.waitForNotification(function() {
-              expect(spy.callCount).to.be(1);
+              expect(spy.callCount).to.be(2);
               var records = sortRecords(spy.lastCall.args[0]);
               expect(records.length).to.be(1);
               expect(records[0].intersectionRatio).to.be(1);
               expect(records[0].target).to.be(targetEl1);
               done();
             });
           },
           function(done) {
             grandParentEl.remove();
             spy.waitForNotification(function() {
-              expect(spy.callCount).to.be(2);
+              expect(spy.callCount).to.be(3);
               var records = sortRecords(spy.lastCall.args[0]);
               expect(records.length).to.be(1);
               expect(records[0].intersectionRatio).to.be(0);
               expect(records[0].target).to.be(targetEl1);
               done();
             });
           },
           function(done) {
             rootEl.appendChild(targetEl1);
             spy.waitForNotification(function() {
-              expect(spy.callCount).to.be(3);
+              expect(spy.callCount).to.be(4);
               var records = sortRecords(spy.lastCall.args[0]);
               expect(records.length).to.be(1);
               expect(records[0].intersectionRatio).to.be(1);
               expect(records[0].target).to.be(targetEl1);
               done();
             });
           },
           function(done) {
             rootEl.remove();
             spy.waitForNotification(function() {
-              expect(spy.callCount).to.be(4);
+              expect(spy.callCount).to.be(5);
               var records = sortRecords(spy.lastCall.args[0]);
               expect(records.length).to.be(1);
               expect(records[0].intersectionRatio).to.be(0);
               expect(records[0].target).to.be(targetEl1);
               done();
             });
           }
         ], done);
@@ -880,17 +880,23 @@ limitations under the License.
       });
 
 
       it('supports CSS transitions and transforms', function(done) {
 
         targetEl1.style.top = '220px';
         targetEl1.style.left = '220px';
 
+        var callCount = 0;
+
         io = new IntersectionObserver(function(records) {
+          callCount++;
+          if (callCount <= 1) {
+            return;
+          }
           expect(records.length).to.be(1);
           expect(records[0].intersectionRatio).to.be(1);
           done();
         }, {root: rootEl, threshold: [1]});
 
         io.observe(targetEl1);
         callDelayed(function() {
           targetEl1.style.transform = 'translateX(-40px) translateY(-40px)';
--- a/testing/web-platform/meta/intersection-observer/cross-origin-iframe.html.ini
+++ b/testing/web-platform/meta/intersection-observer/cross-origin-iframe.html.ini
@@ -1,3 +1,2 @@
 [cross-origin-iframe.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/disconnect.html.ini
+++ b/testing/web-platform/meta/intersection-observer/disconnect.html.ini
@@ -1,3 +1,2 @@
 [disconnect.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/edge-inclusive-intersection.html.ini
+++ b/testing/web-platform/meta/intersection-observer/edge-inclusive-intersection.html.ini
@@ -1,6 +1,4 @@
 [edge-inclusive-intersection.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359317
-  [First rAF.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
 
--- a/testing/web-platform/meta/intersection-observer/iframe-no-root.html.ini
+++ b/testing/web-platform/meta/intersection-observer/iframe-no-root.html.ini
@@ -1,6 +1,4 @@
 [iframe-no-root.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
-  [First rAF.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
 
--- a/testing/web-platform/meta/intersection-observer/multiple-targets.html.ini
+++ b/testing/web-platform/meta/intersection-observer/multiple-targets.html.ini
@@ -1,6 +1,4 @@
 [multiple-targets.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359311
-  [First rAF.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
 
--- a/testing/web-platform/meta/intersection-observer/multiple-thresholds.html.ini
+++ b/testing/web-platform/meta/intersection-observer/multiple-thresholds.html.ini
@@ -1,5 +1,3 @@
 [multiple-thresholds.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
-  [First rAF.]
-    https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/observer-without-js-reference.html.ini
+++ b/testing/web-platform/meta/intersection-observer/observer-without-js-reference.html.ini
@@ -1,3 +1,2 @@
 [observer-without-js-reference.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/remove-element.html.ini
+++ b/testing/web-platform/meta/intersection-observer/remove-element.html.ini
@@ -1,6 +1,4 @@
 [remove-element.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1363650
-  [First rAF.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
 
--- a/testing/web-platform/meta/intersection-observer/root-margin.html.ini
+++ b/testing/web-platform/meta/intersection-observer/root-margin.html.ini
@@ -1,5 +1,3 @@
 [root-margin.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
-  [First rAF.]
-    https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/same-document-no-root.html.ini
+++ b/testing/web-platform/meta/intersection-observer/same-document-no-root.html.ini
@@ -1,5 +1,3 @@
 [same-document-no-root.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
-  [First rAF.]
-    https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/same-document-root.html.ini
+++ b/testing/web-platform/meta/intersection-observer/same-document-root.html.ini
@@ -1,6 +1,4 @@
 [same-document-root.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1363650
-  [First rAF.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
 
--- a/testing/web-platform/meta/intersection-observer/same-document-zero-size-target.html.ini
+++ b/testing/web-platform/meta/intersection-observer/same-document-zero-size-target.html.ini
@@ -1,5 +1,3 @@
 [same-document-zero-size-target.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
-  [First rAF.]
-    https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/timestamp.html.ini
+++ b/testing/web-platform/meta/intersection-observer/timestamp.html.ini
@@ -1,3 +1,2 @@
 [timestamp.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/unclipped-root.html.ini
+++ b/testing/web-platform/meta/intersection-observer/unclipped-root.html.ini
@@ -1,6 +1,4 @@
 [unclipped-root.html]
   type: testharness
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359317
-  [First rAF.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
 
--- a/testing/web-platform/meta/intersection-observer/zero-area-element-hidden.html.ini
+++ b/testing/web-platform/meta/intersection-observer/zero-area-element-hidden.html.ini
@@ -1,3 +1,2 @@
 [zero-area-element-hidden.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644
--- a/testing/web-platform/meta/intersection-observer/zero-area-element-visible.html.ini
+++ b/testing/web-platform/meta/intersection-observer/zero-area-element-visible.html.ini
@@ -1,4 +1,3 @@
 [zero-area-element-visible.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1335644