Bug 1002332 - Make AnimationTimeline record the last refresh time and use that when there is no refresh driver; r=bz
authorBrian Birtles <birtles@gmail.com>
Thu, 24 Jul 2014 13:37:38 +0900
changeset 217447 9345f94814621b287c734d60d07c14988bb56b2d
parent 217446 810b3536cb50995e807269b4349fc1e2b952e65b
child 217448 34235900c3a0e300afb1911489fa6e0c8d67df76
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1002332
milestone34.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 1002332 - Make AnimationTimeline record the last refresh time and use that when there is no refresh driver; r=bz A document that belongs to an iframe that is display:none as no associated pres context from which to get a refresh driver. However, in this case document.timeline.currentTime should still never go backwards (since document timelines are supposed to be monotonically increasing). This patch makes AnimationTimeline record the last value returned for the current time so that if the document becomes display:none we can still return the most recently used time.
dom/animation/AnimationTimeline.cpp
dom/animation/AnimationTimeline.h
dom/animation/test/animation-timeline/test_animation-timeline.html
layout/style/AnimationCommon.cpp
--- a/dom/animation/AnimationTimeline.cpp
+++ b/dom/animation/AnimationTimeline.cpp
@@ -1,16 +1,15 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AnimationTimeline.h"
 #include "mozilla/dom/AnimationTimelineBinding.h"
-#include "mozilla/TimeStamp.h"
 #include "nsContentUtils.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsRefreshDriver.h"
 #include "nsDOMNavigationTiming.h"
 
 namespace mozilla {
 namespace dom {
@@ -31,29 +30,45 @@ AnimationTimeline::GetCurrentTime() cons
 {
   return ToTimelineTime(GetCurrentTimeStamp());
 }
 
 TimeStamp
 AnimationTimeline::GetCurrentTimeStamp() const
 {
   // Always return the same object to benefit from return-value optimization.
-  TimeStamp result; // Initializes to null timestamp
+  TimeStamp result = mLastCurrentTime;
+
+  // If we've never been sampled, initialize the current time to the timeline's
+  // zero time since that is the time we'll use if we don't have a refresh
+  // driver.
+  if (result.IsNull()) {
+    nsRefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
+    if (!timing) {
+      return result;
+    }
+    result = timing->GetNavigationStartTimeStamp();
+  }
 
   nsIPresShell* presShell = mDocument->GetShell();
   if (MOZ_UNLIKELY(!presShell)) {
     return result;
   }
 
   nsPresContext* presContext = presShell->GetPresContext();
   if (MOZ_UNLIKELY(!presContext)) {
     return result;
   }
 
   result = presContext->RefreshDriver()->MostRecentRefresh();
+  // FIXME: We would like to assert that:
+  //   mLastCurrentTime.IsNull() || result >= mLastCurrentTime
+  // but due to bug 1043078 this will not be the case when the refresh driver
+  // is restored from test control.
+  mLastCurrentTime = result;
   return result;
 }
 
 Nullable<double>
 AnimationTimeline::ToTimelineTime(const mozilla::TimeStamp& aTimeStamp) const
 {
   Nullable<double> result; // Initializes to null
   if (aTimeStamp.IsNull()) {
--- a/dom/animation/AnimationTimeline.h
+++ b/dom/animation/AnimationTimeline.h
@@ -4,25 +4,23 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_AnimationTimeline_h
 #define mozilla_dom_AnimationTimeline_h
 
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/TimeStamp.h"
 #include "js/TypeDecls.h"
 #include "nsIDocument.h"
 
 struct JSContext;
 
 namespace mozilla {
-
-class TimeStamp;
-
 namespace dom {
 
 class AnimationTimeline MOZ_FINAL : public nsWrapperCache
 {
 public:
   AnimationTimeline(nsIDocument* aDocument)
     : mDocument(aDocument)
   {
@@ -39,14 +37,19 @@ public:
   mozilla::TimeStamp GetCurrentTimeStamp() const;
 
   Nullable<double> ToTimelineTime(const mozilla::TimeStamp& aTimeStamp) const;
 
 protected:
   virtual ~AnimationTimeline() { }
 
   nsCOMPtr<nsIDocument> mDocument;
+
+  // Store the most recently returned value of current time. This is used
+  // in cases where we don't have a refresh driver (e.g. because we are in
+  // a display:none iframe).
+  mutable mozilla::TimeStamp mLastCurrentTime;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AnimationTimeline_h
--- a/dom/animation/test/animation-timeline/test_animation-timeline.html
+++ b/dom/animation/test/animation-timeline/test_animation-timeline.html
@@ -2,16 +2,17 @@
 <meta charset=utf-8>
 <title>Web Animations API: AnimationTimeline tests</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/WebIDLParser.js"></script>
 <script src="/resources/idlharness.js"></script>
 <div id="log"></div>
 <iframe src="data:text/html;charset=utf-8," width="10" height="10" id="iframe"></iframe>
+<iframe src="data:text/html;charset=utf-8,%3Chtml%20style%3D%22display%3Anone%22%3E%3C%2Fhtml%3E" width="10" height="10" id="hidden-iframe"></iframe>
 <script type="text/plain" id="AnimationTimeline-IDL">
 interface AnimationTimeline {
   readonly attribute double? currentTime;
 };
 </script>
 <script>
 'use strict';
 
@@ -97,9 +98,55 @@ async_test(function(t) {
 'document.timeline.currentTime liveness tests',
 {
   help: 'http://dev.w3.org/fxtf/web-animations/#script-execution-and-live-updates-to-the-model',
   assert: [ 'The value returned by the currentTime attribute of a' +
             ' document timeline will not change within a script block' ],
   author: 'Brian Birtles'
 });
 
+test(function() {
+  var hiddenIFrame = document.getElementById('hidden-iframe');
+  assert_equals(typeof hiddenIFrame.contentDocument.timeline.currentTime,
+    'number',
+    'currentTime of an initially hidden subframe\'s timeline is a number');
+  assert_true(hiddenIFrame.contentDocument.timeline.currentTime >= 0,
+    'currentTime of an initially hidden subframe\'s timeline is >= 0');
+}, 'document.timeline.currentTime hidden subframe test');
+
+async_test(function(t) {
+  var hiddenIFrame = document.getElementById('hidden-iframe');
+
+  // Don't run the test until after the iframe has completed loading or else the
+  // contentDocument may change.
+  var testToRunOnLoad = t.step_func(function() {
+    // Remove display:none
+    hiddenIFrame.style.display = 'block';
+    window.getComputedStyle(hiddenIFrame).display;
+
+    window.requestAnimationFrame(t.step_func(function() {
+      assert_true(hiddenIFrame.contentDocument.timeline.currentTime > 0,
+        'document.timeline.currentTime is positive after removing'
+        + ' display:none');
+      var previousValue = hiddenIFrame.contentDocument.timeline.currentTime;
+
+      // Re-introduce display:none
+      hiddenIFrame.style.display = 'none';
+      window.getComputedStyle(hiddenIFrame).display;
+
+      window.requestAnimationFrame(t.step_func(function() {
+        assert_true(
+          hiddenIFrame.contentDocument.timeline.currentTime >= previousValue,
+          'document.timeline.currentTime does not go backwards after'
+          + ' re-setting display:none');
+        t.done();
+      }));
+    }));
+  });
+
+  if (hiddenIFrame.contentDocument.readyState === 'complete') {
+    testToRunOnLoad();
+  } else {
+    hiddenIFrame.addEventListener("load", testToRunOnLoad);
+  }
+}, 'document.timeline.currentTime hidden subframe dynamic test');
+
 </script>
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -430,18 +430,18 @@ double
 ElementAnimation::CurrentTime() const
 {
   // In Web Animations, AnimationPlayers have a *current* time and Animations
   // have a *local* time. However, since we have a 1:1 correspondence between
   // AnimationPlayers and Animations, and since the startTime of *Animations*
   // (but not AnimationPlayers) is always 0, these are currently identical.
   Nullable<TimeDuration> currentTime = GetLocalTime();
 
-  // The current time is currently only going to be null when don't have a
-  // refresh driver (e.g. because we are in a display:none iframe).
+  // The current time is only going to be null when we don't have a refresh
+  // driver or navigation timing object and never did.
   //
   // Web Animations says that in this case we should use a timeline time of
   // 0 (the "effective timeline time") and calculate the current time from that.
   // Doing that, however, requires storing the start time as an offset rather
   // than a timestamp so for now we just return 0.
   //
   // FIXME: Store player start time and pause start as offsets rather than
   // timestamps and return the appropriate current time when the timeline time