• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!DOCTYPE html>
2<!--
3Copyright (c) 2014 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7<link rel="import" href="/tracing/base/utils.html">
8<script>
9'use strict';
10
11tr.exportTo('tr.b', function() {
12  var ESTIMATED_IDLE_PERIOD_LENGTH_MILLISECONDS = 10;
13  // The maximum amount of time that we allow for a task to get scheduled
14  // in idle time before forcing the task to run.
15  var REQUEST_IDLE_CALLBACK_TIMEOUT_MILLISECONDS = 100;
16
17  // Setting this to true will cause stack traces to get dumped into the
18  // tasks. When an exception happens the original stack will be printed.
19  //
20  // NOTE: This should never be set committed as true.
21  var recordRAFStacks = false;
22
23  var pendingPreAFs = [];
24  var pendingRAFs = [];
25  var pendingIdleCallbacks = [];
26  var currentRAFDispatchList = undefined;
27
28  var rafScheduled = false;
29  var idleWorkScheduled = false;
30
31  function scheduleRAF() {
32    if (rafScheduled)
33      return;
34    rafScheduled = true;
35    if (tr.isHeadless) {
36      Promise.resolve().then(function() {
37        processRequests(false, 0);
38      }, function(e) {
39        console.log(e.stack);
40        throw e;
41      });
42    } else {
43      if (window.requestAnimationFrame) {
44        window.requestAnimationFrame(processRequests.bind(this, false));
45      } else {
46        var delta = Date.now() - window.performance.now();
47        window.webkitRequestAnimationFrame(function(domTimeStamp) {
48          processRequests(false, domTimeStamp - delta);
49        });
50      }
51    }
52  }
53
54  function nativeRequestIdleCallbackSupported() {
55    return !tr.isHeadless && window.requestIdleCallback;
56  }
57
58  function scheduleIdleWork() {
59    if (idleWorkScheduled)
60      return;
61    if (!nativeRequestIdleCallbackSupported()) {
62      scheduleRAF();
63      return;
64    }
65    idleWorkScheduled = true;
66    window.requestIdleCallback(function(deadline, didTimeout) {
67      processIdleWork(false /* forceAllTasksToRun */, deadline);
68    }, { timeout: REQUEST_IDLE_CALLBACK_TIMEOUT_MILLISECONDS });
69  }
70
71  function onAnimationFrameError(e, opt_stack) {
72    console.log(e.stack);
73    if (tr.isHeadless)
74      throw e;
75
76    if (opt_stack)
77      console.log(opt_stack);
78
79    if (e.message)
80      console.error(e.message, e.stack);
81    else
82      console.error(e);
83  }
84
85  function runTask(task, frameBeginTime) {
86    try {
87      task.callback.call(task.context, frameBeginTime);
88    } catch (e) {
89      tr.b.onAnimationFrameError(e, task.stack);
90    }
91  }
92
93  function processRequests(forceAllTasksToRun, frameBeginTime) {
94    rafScheduled = false;
95
96    var currentPreAFs = pendingPreAFs;
97    currentRAFDispatchList = pendingRAFs;
98    pendingPreAFs = [];
99    pendingRAFs = [];
100    var hasRAFTasks = currentPreAFs.length || currentRAFDispatchList.length;
101
102    for (var i = 0; i < currentPreAFs.length; i++)
103      runTask(currentPreAFs[i], frameBeginTime);
104
105    while (currentRAFDispatchList.length > 0)
106      runTask(currentRAFDispatchList.shift(), frameBeginTime);
107    currentRAFDispatchList = undefined;
108
109    if ((!hasRAFTasks && !nativeRequestIdleCallbackSupported()) ||
110        forceAllTasksToRun) {
111      // We assume that we want to do a fixed maximum amount of optional work
112      // per frame. Hopefully rAF will eventually pass this in for us.
113      var rafCompletionDeadline =
114          frameBeginTime + ESTIMATED_IDLE_PERIOD_LENGTH_MILLISECONDS;
115      processIdleWork(
116          forceAllTasksToRun, {
117            timeRemaining: function() {
118              return rafCompletionDeadline - window.performance.now();
119            }
120          }
121      );
122    }
123
124    if (pendingIdleCallbacks.length > 0)
125      scheduleIdleWork();
126  }
127
128  function processIdleWork(forceAllTasksToRun, deadline) {
129    idleWorkScheduled = false;
130    while (pendingIdleCallbacks.length > 0) {
131      runTask(pendingIdleCallbacks.shift());
132      // Check timer after running at least one idle task to avoid buggy
133      // window.performance.now() on some platforms from blocking the idle
134      // task queue.
135      if (!forceAllTasksToRun &&
136          (tr.isHeadless || deadline.timeRemaining() <= 0)) {
137        break;
138      }
139    }
140
141    if (pendingIdleCallbacks.length > 0)
142      scheduleIdleWork();
143  }
144
145  function getStack_() {
146    if (!recordRAFStacks)
147      return '';
148
149    var stackLines = tr.b.stackTrace();
150    // Strip off getStack_.
151    stackLines.shift();
152    return stackLines.join('\n');
153  }
154
155  function requestPreAnimationFrame(callback, opt_this) {
156    pendingPreAFs.push({
157      callback: callback,
158      context: opt_this || global,
159      stack: getStack_()});
160    scheduleRAF();
161  }
162
163  function requestAnimationFrameInThisFrameIfPossible(callback, opt_this) {
164    if (!currentRAFDispatchList) {
165      requestAnimationFrame(callback, opt_this);
166      return;
167    }
168    currentRAFDispatchList.push({
169      callback: callback,
170      context: opt_this || global,
171      stack: getStack_()});
172    return;
173  }
174
175  function requestAnimationFrame(callback, opt_this) {
176    pendingRAFs.push({
177      callback: callback,
178      context: opt_this || global,
179      stack: getStack_()});
180    scheduleRAF();
181  }
182
183  function requestIdleCallback(callback, opt_this) {
184    pendingIdleCallbacks.push({
185      callback: callback,
186      context: opt_this || global,
187      stack: getStack_()});
188    scheduleIdleWork();
189  }
190
191  function forcePendingRAFTasksToRun(frameBeginTime) {
192    if (!rafScheduled)
193      return;
194    processRequests(false, frameBeginTime);
195  }
196
197  function forceAllPendingTasksToRunForTest() {
198    if (!rafScheduled && !idleWorkScheduled)
199      return;
200    processRequests(true, 0);
201  }
202
203  return {
204    onAnimationFrameError: onAnimationFrameError,
205    requestPreAnimationFrame: requestPreAnimationFrame,
206    requestAnimationFrame: requestAnimationFrame,
207    requestAnimationFrameInThisFrameIfPossible:
208        requestAnimationFrameInThisFrameIfPossible,
209    requestIdleCallback: requestIdleCallback,
210    forcePendingRAFTasksToRun: forcePendingRAFTasksToRun,
211    forceAllPendingTasksToRunForTest: forceAllPendingTasksToRunForTest
212  };
213});
214</script>
215