scheduler
Advanced tools
Comparing version 0.0.0-9ebe1768a to 0.0.0-9f395904c
{ | ||
"branch": "master", | ||
"buildNumber": "14456", | ||
"checksum": "ec7ecb1", | ||
"commit": "9ebe1768a", | ||
"buildNumber": "28552", | ||
"checksum": "9794260", | ||
"commit": "9f395904c", | ||
"environment": "ci", | ||
"reactVersion": "16.8.6-canary-9ebe1768a" | ||
"reactVersion": "16.8.6-canary-9f395904c" | ||
} |
@@ -1,2 +0,2 @@ | ||
/** @license React v0.0.0-9ebe1768a | ||
/** @license React v0.0.0-9f395904c | ||
* scheduler-tracing.development.js | ||
@@ -54,5 +54,2 @@ * | ||
// Disables yielding during render in Concurrent Mode. Used for debugging only. | ||
// React Fire: prevent the value and checked attributes from syncing | ||
@@ -72,3 +69,3 @@ // with their related DOM properties | ||
// Experimental React Events support. Only used in www builds for now. | ||
// Experimental React Flare event system and event components support. | ||
@@ -78,2 +75,13 @@ | ||
// We will enforce mocking scheduler with scheduler/unstable_mock at some point. (v17?) | ||
// Till then, we warn about the missing mock, but still fallback to a sync mode compatible version | ||
// Temporary flag to revert the fix in #15650 | ||
// Changes priority of some events like mousemove to user-blocking priority, | ||
// but without making them discrete. The flag exists in case it causes | ||
// starvation problems. | ||
var DEFAULT_THREAD_ID = 0; | ||
@@ -80,0 +88,0 @@ |
@@ -1,2 +0,2 @@ | ||
/** @license React v0.0.0-9ebe1768a | ||
/** @license React v0.0.0-9f395904c | ||
* scheduler-tracing.production.min.js | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
/** @license React v0.0.0-9ebe1768a | ||
/** @license React v0.0.0-9f395904c | ||
* scheduler-tracing.profiling.min.js | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
/** @license React v0.0.0-9ebe1768a | ||
/** @license React v0.0.0-9f395904c | ||
* scheduler-unstable_mock.development.js | ||
@@ -24,3 +24,4 @@ * | ||
var scheduledCallback = null; | ||
var scheduledCallbackExpiration = -1; | ||
var scheduledTimeout = null; | ||
var timeoutTime = -1; | ||
var yieldedValues = null; | ||
@@ -30,15 +31,23 @@ var expectedNumberOfYields = -1; | ||
var isFlushing = false; | ||
var needsPaint = false; | ||
var shouldYieldForPaint = false; | ||
function requestHostCallback(callback, expiration) { | ||
function requestHostCallback(callback) { | ||
scheduledCallback = callback; | ||
scheduledCallbackExpiration = expiration; | ||
} | ||
function cancelHostCallback() { | ||
scheduledCallback = null; | ||
scheduledCallbackExpiration = -1; | ||
function requestHostTimeout(callback, ms) { | ||
scheduledTimeout = callback; | ||
timeoutTime = currentTime + ms; | ||
} | ||
function cancelHostTimeout() { | ||
scheduledTimeout = null; | ||
timeoutTime = -1; | ||
} | ||
function shouldYieldToHost() { | ||
if (expectedNumberOfYields !== -1 && yieldedValues !== null && yieldedValues.length >= expectedNumberOfYields || scheduledCallbackExpiration !== -1 && scheduledCallbackExpiration <= currentTime) { | ||
if (expectedNumberOfYields !== -1 && yieldedValues !== null && yieldedValues.length >= expectedNumberOfYields || shouldYieldForPaint && needsPaint) { | ||
// We yielded at least as many values as expected. Stop flushing. | ||
@@ -55,4 +64,8 @@ didStop = true; | ||
function forceFrameRate() { | ||
// No-op | ||
} | ||
// Should only be used via an assertion helper that inspects the yielded values. | ||
@@ -63,19 +76,23 @@ function unstable_flushNumberOfYields(count) { | ||
} | ||
expectedNumberOfYields = count; | ||
isFlushing = true; | ||
try { | ||
while (scheduledCallback !== null && !didStop) { | ||
var cb = scheduledCallback; | ||
scheduledCallback = null; | ||
var didTimeout = scheduledCallbackExpiration !== -1 && scheduledCallbackExpiration <= currentTime; | ||
cb(didTimeout); | ||
if (scheduledCallback !== null) { | ||
var cb = scheduledCallback; | ||
expectedNumberOfYields = count; | ||
isFlushing = true; | ||
try { | ||
var hasMoreWork = true; | ||
do { | ||
hasMoreWork = cb(true, currentTime); | ||
} while (hasMoreWork && !didStop); | ||
if (!hasMoreWork) { | ||
scheduledCallback = null; | ||
} | ||
} finally { | ||
expectedNumberOfYields = -1; | ||
didStop = false; | ||
isFlushing = false; | ||
} | ||
} finally { | ||
expectedNumberOfYields = -1; | ||
didStop = false; | ||
isFlushing = false; | ||
} | ||
} | ||
function unstable_flushExpired() { | ||
function unstable_flushUntilNextPaint() { | ||
if (isFlushing) { | ||
@@ -86,7 +103,16 @@ throw new Error('Already flushing work.'); | ||
var cb = scheduledCallback; | ||
scheduledCallback = null; | ||
shouldYieldForPaint = true; | ||
needsPaint = false; | ||
isFlushing = true; | ||
try { | ||
cb(true); | ||
var hasMoreWork = true; | ||
do { | ||
hasMoreWork = cb(true, currentTime); | ||
} while (hasMoreWork && !didStop); | ||
if (!hasMoreWork) { | ||
scheduledCallback = null; | ||
} | ||
} finally { | ||
shouldYieldForPaint = false; | ||
didStop = false; | ||
isFlushing = false; | ||
@@ -97,21 +123,44 @@ } | ||
function unstable_flushWithoutYielding() { | ||
function unstable_flushExpired() { | ||
if (isFlushing) { | ||
throw new Error('Already flushing work.'); | ||
} | ||
isFlushing = true; | ||
try { | ||
while (scheduledCallback !== null) { | ||
var cb = scheduledCallback; | ||
scheduledCallback = null; | ||
var didTimeout = scheduledCallbackExpiration !== -1 && scheduledCallbackExpiration <= currentTime; | ||
cb(didTimeout); | ||
if (scheduledCallback !== null) { | ||
isFlushing = true; | ||
try { | ||
var hasMoreWork = scheduledCallback(false, currentTime); | ||
if (!hasMoreWork) { | ||
scheduledCallback = null; | ||
} | ||
} finally { | ||
isFlushing = false; | ||
} | ||
} finally { | ||
expectedNumberOfYields = -1; | ||
didStop = false; | ||
isFlushing = false; | ||
} | ||
} | ||
function unstable_flushAllWithoutAsserting() { | ||
// Returns false if no work was flushed. | ||
if (isFlushing) { | ||
throw new Error('Already flushing work.'); | ||
} | ||
if (scheduledCallback !== null) { | ||
var cb = scheduledCallback; | ||
isFlushing = true; | ||
try { | ||
var hasMoreWork = true; | ||
do { | ||
hasMoreWork = cb(true, currentTime); | ||
} while (hasMoreWork); | ||
if (!hasMoreWork) { | ||
scheduledCallback = null; | ||
} | ||
return true; | ||
} finally { | ||
isFlushing = false; | ||
} | ||
} else { | ||
return false; | ||
} | ||
} | ||
function unstable_clearYields() { | ||
@@ -126,7 +175,7 @@ if (yieldedValues === null) { | ||
function flushAll() { | ||
function unstable_flushAll() { | ||
if (yieldedValues !== null) { | ||
throw new Error('Log is not empty. Assert on the log of yielded values before ' + 'flushing additional work.'); | ||
} | ||
unstable_flushWithoutYielding(); | ||
unstable_flushAllWithoutAsserting(); | ||
if (yieldedValues !== null) { | ||
@@ -137,3 +186,3 @@ throw new Error('While flushing work, something yielded a value. Use an ' + 'assertion helper to assert on the log of yielded values, e.g. ' + 'expect(Scheduler).toFlushAndYield([...])'); | ||
function yieldValue(value) { | ||
function unstable_yieldValue(value) { | ||
if (yieldedValues === null) { | ||
@@ -146,6 +195,10 @@ yieldedValues = [value]; | ||
function advanceTime(ms) { | ||
function unstable_advanceTime(ms) { | ||
currentTime += ms; | ||
// If the host callback timed out, flush the expired work. | ||
if (!isFlushing && scheduledCallbackExpiration !== -1 && scheduledCallbackExpiration <= currentTime) { | ||
if (!isFlushing) { | ||
if (scheduledTimeout !== null && timeoutTime <= currentTime) { | ||
scheduledTimeout(currentTime); | ||
timeoutTime = -1; | ||
scheduledTimeout = null; | ||
} | ||
unstable_flushExpired(); | ||
@@ -155,2 +208,6 @@ } | ||
function requestPaint() { | ||
needsPaint = true; | ||
} | ||
/* eslint-disable no-var */ | ||
@@ -179,12 +236,11 @@ | ||
// Callbacks are stored as a circular, doubly linked list. | ||
var firstCallbackNode = null; | ||
// Tasks are stored as a circular, doubly linked list. | ||
var firstTask = null; | ||
var firstDelayedTask = null; | ||
var currentHostCallbackDidTimeout = false; | ||
// Pausing the scheduler is useful for debugging. | ||
var isSchedulerPaused = false; | ||
var currentTask = null; | ||
var currentPriorityLevel = NormalPriority; | ||
var currentEventStartTime = -1; | ||
var currentExpirationTime = -1; | ||
@@ -195,53 +251,66 @@ // This is set while performing work, to prevent re-entrancy. | ||
var isHostCallbackScheduled = false; | ||
var isHostTimeoutScheduled = false; | ||
function scheduleHostCallbackIfNeeded() { | ||
if (isPerformingWork) { | ||
// Don't schedule work yet; wait until the next time we yield. | ||
return; | ||
} | ||
if (firstCallbackNode !== null) { | ||
// Schedule the host callback using the earliest expiration in the list. | ||
var expirationTime = firstCallbackNode.expirationTime; | ||
if (isHostCallbackScheduled) { | ||
// Cancel the existing host callback. | ||
cancelHostCallback(); | ||
} else { | ||
isHostCallbackScheduled = true; | ||
} | ||
requestHostCallback(flushWork, expirationTime); | ||
} | ||
function scheduler_flushTaskAtPriority_Immediate(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function scheduler_flushTaskAtPriority_UserBlocking(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function scheduler_flushTaskAtPriority_Normal(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function scheduler_flushTaskAtPriority_Low(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function scheduler_flushTaskAtPriority_Idle(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function flushFirstCallback() { | ||
var currentlyFlushingCallback = firstCallbackNode; | ||
// Remove the node from the list before calling the callback. That way the | ||
function flushTask(task, currentTime) { | ||
// Remove the task from the list before calling the callback. That way the | ||
// list is in a consistent state even if the callback throws. | ||
var next = firstCallbackNode.next; | ||
if (firstCallbackNode === next) { | ||
// This is the last callback in the list. | ||
firstCallbackNode = null; | ||
next = null; | ||
var next = task.next; | ||
if (next === task) { | ||
// This is the only scheduled task. Clear the list. | ||
firstTask = null; | ||
} else { | ||
var lastCallbackNode = firstCallbackNode.previous; | ||
firstCallbackNode = lastCallbackNode.next = next; | ||
next.previous = lastCallbackNode; | ||
// Remove the task from its position in the list. | ||
if (task === firstTask) { | ||
firstTask = next; | ||
} | ||
var previous = task.previous; | ||
previous.next = next; | ||
next.previous = previous; | ||
} | ||
task.next = task.previous = null; | ||
currentlyFlushingCallback.next = currentlyFlushingCallback.previous = null; | ||
// Now it's safe to call the callback. | ||
var callback = currentlyFlushingCallback.callback; | ||
var expirationTime = currentlyFlushingCallback.expirationTime; | ||
var priorityLevel = currentlyFlushingCallback.priorityLevel; | ||
// Now it's safe to execute the task. | ||
var callback = task.callback; | ||
var previousPriorityLevel = currentPriorityLevel; | ||
var previousExpirationTime = currentExpirationTime; | ||
currentPriorityLevel = priorityLevel; | ||
currentExpirationTime = expirationTime; | ||
var previousTask = currentTask; | ||
currentPriorityLevel = task.priorityLevel; | ||
currentTask = task; | ||
var continuationCallback; | ||
try { | ||
var didUserCallbackTimeout = currentHostCallbackDidTimeout || | ||
// Immediate priority callbacks are always called as if they timed out | ||
priorityLevel === ImmediatePriority; | ||
continuationCallback = callback(didUserCallbackTimeout); | ||
var didUserCallbackTimeout = task.expirationTime <= currentTime; | ||
// Add an extra function to the callstack. Profiling tools can use this | ||
// to infer the priority of work that appears higher in the stack. | ||
switch (currentPriorityLevel) { | ||
case ImmediatePriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_Immediate(callback, didUserCallbackTimeout); | ||
break; | ||
case UserBlockingPriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_UserBlocking(callback, didUserCallbackTimeout); | ||
break; | ||
case NormalPriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_Normal(callback, didUserCallbackTimeout); | ||
break; | ||
case LowPriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_Low(callback, didUserCallbackTimeout); | ||
break; | ||
case IdlePriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_Idle(callback, didUserCallbackTimeout); | ||
break; | ||
} | ||
} catch (error) { | ||
@@ -251,3 +320,3 @@ throw error; | ||
currentPriorityLevel = previousPriorityLevel; | ||
currentExpirationTime = previousExpirationTime; | ||
currentTask = previousTask; | ||
} | ||
@@ -258,5 +327,7 @@ | ||
if (typeof continuationCallback === 'function') { | ||
var continuationNode = { | ||
var expirationTime = task.expirationTime; | ||
var continuationTask = { | ||
callback: continuationCallback, | ||
priorityLevel: priorityLevel, | ||
priorityLevel: task.priorityLevel, | ||
startTime: task.startTime, | ||
expirationTime: expirationTime, | ||
@@ -267,36 +338,34 @@ next: null, | ||
// Insert the new callback into the list, sorted by its expiration. This is | ||
// Insert the new callback into the list, sorted by its timeout. This is | ||
// almost the same as the code in `scheduleCallback`, except the callback | ||
// is inserted into the list *before* callbacks of equal expiration instead | ||
// is inserted into the list *before* callbacks of equal timeout instead | ||
// of after. | ||
if (firstCallbackNode === null) { | ||
if (firstTask === null) { | ||
// This is the first callback in the list. | ||
firstCallbackNode = continuationNode.next = continuationNode.previous = continuationNode; | ||
firstTask = continuationTask.next = continuationTask.previous = continuationTask; | ||
} else { | ||
var nextAfterContinuation = null; | ||
var node = firstCallbackNode; | ||
var t = firstTask; | ||
do { | ||
if (node.expirationTime >= expirationTime) { | ||
// This callback expires at or after the continuation. We will insert | ||
// the continuation *before* this callback. | ||
nextAfterContinuation = node; | ||
if (expirationTime <= t.expirationTime) { | ||
// This task times out at or after the continuation. We will insert | ||
// the continuation *before* this task. | ||
nextAfterContinuation = t; | ||
break; | ||
} | ||
node = node.next; | ||
} while (node !== firstCallbackNode); | ||
t = t.next; | ||
} while (t !== firstTask); | ||
if (nextAfterContinuation === null) { | ||
// No equal or lower priority callback was found, which means the new | ||
// callback is the lowest priority callback in the list. | ||
nextAfterContinuation = firstCallbackNode; | ||
} else if (nextAfterContinuation === firstCallbackNode) { | ||
// The new callback is the highest priority callback in the list. | ||
firstCallbackNode = continuationNode; | ||
scheduleHostCallbackIfNeeded(); | ||
// No equal or lower priority task was found, which means the new task | ||
// is the lowest priority task in the list. | ||
nextAfterContinuation = firstTask; | ||
} else if (nextAfterContinuation === firstTask) { | ||
// The new task is the highest priority task in the list. | ||
firstTask = continuationTask; | ||
} | ||
var previous = nextAfterContinuation.previous; | ||
previous.next = nextAfterContinuation.previous = continuationNode; | ||
continuationNode.next = nextAfterContinuation; | ||
continuationNode.previous = previous; | ||
var _previous = nextAfterContinuation.previous; | ||
_previous.next = nextAfterContinuation.previous = continuationTask; | ||
continuationTask.next = nextAfterContinuation; | ||
continuationTask.previous = _previous; | ||
} | ||
@@ -306,3 +375,37 @@ } | ||
function flushWork(didUserCallbackTimeout) { | ||
function advanceTimers(currentTime) { | ||
// Check for tasks that are no longer delayed and add them to the queue. | ||
if (firstDelayedTask !== null && firstDelayedTask.startTime <= currentTime) { | ||
do { | ||
var task = firstDelayedTask; | ||
var next = task.next; | ||
if (task === next) { | ||
firstDelayedTask = null; | ||
} else { | ||
firstDelayedTask = next; | ||
var previous = task.previous; | ||
previous.next = next; | ||
next.previous = previous; | ||
} | ||
task.next = task.previous = null; | ||
insertScheduledTask(task, task.expirationTime); | ||
} while (firstDelayedTask !== null && firstDelayedTask.startTime <= currentTime); | ||
} | ||
} | ||
function handleTimeout(currentTime) { | ||
isHostTimeoutScheduled = false; | ||
advanceTimers(currentTime); | ||
if (!isHostCallbackScheduled) { | ||
if (firstTask !== null) { | ||
isHostCallbackScheduled = true; | ||
requestHostCallback(flushWork); | ||
} else if (firstDelayedTask !== null) { | ||
requestHostTimeout(handleTimeout, firstDelayedTask.startTime - currentTime); | ||
} | ||
} | ||
} | ||
function flushWork(hasTimeRemaining, initialTime) { | ||
// Exit right away if we're currently paused | ||
@@ -313,41 +416,45 @@ if (enableSchedulerDebugging && isSchedulerPaused) { | ||
// We'll need a new host callback the next time work is scheduled. | ||
// We'll need a host callback the next time work is scheduled. | ||
isHostCallbackScheduled = false; | ||
if (isHostTimeoutScheduled) { | ||
// We scheduled a timeout but it's no longer needed. Cancel it. | ||
isHostTimeoutScheduled = false; | ||
cancelHostTimeout(); | ||
} | ||
var currentTime = initialTime; | ||
advanceTimers(currentTime); | ||
isPerformingWork = true; | ||
var previousDidTimeout = currentHostCallbackDidTimeout; | ||
currentHostCallbackDidTimeout = didUserCallbackTimeout; | ||
try { | ||
if (didUserCallbackTimeout) { | ||
if (!hasTimeRemaining) { | ||
// Flush all the expired callbacks without yielding. | ||
while (firstCallbackNode !== null && !(enableSchedulerDebugging && isSchedulerPaused)) { | ||
// TODO Wrap in feature flag | ||
// Read the current time. Flush all the callbacks that expire at or | ||
// earlier than that time. Then read the current time again and repeat. | ||
// This optimizes for as few performance.now calls as possible. | ||
var currentTime = getCurrentTime(); | ||
if (firstCallbackNode.expirationTime <= currentTime) { | ||
do { | ||
flushFirstCallback(); | ||
} while (firstCallbackNode !== null && firstCallbackNode.expirationTime <= currentTime && !(enableSchedulerDebugging && isSchedulerPaused)); | ||
continue; | ||
} | ||
break; | ||
// TODO: Split flushWork into two separate functions instead of using | ||
// a boolean argument? | ||
while (firstTask !== null && firstTask.expirationTime <= currentTime && !(enableSchedulerDebugging && isSchedulerPaused)) { | ||
flushTask(firstTask, currentTime); | ||
currentTime = getCurrentTime(); | ||
advanceTimers(currentTime); | ||
} | ||
} else { | ||
// Keep flushing callbacks until we run out of time in the frame. | ||
if (firstCallbackNode !== null) { | ||
if (firstTask !== null) { | ||
do { | ||
if (enableSchedulerDebugging && isSchedulerPaused) { | ||
break; | ||
} | ||
flushFirstCallback(); | ||
} while (firstCallbackNode !== null && !shouldYieldToHost()); | ||
flushTask(firstTask, currentTime); | ||
currentTime = getCurrentTime(); | ||
advanceTimers(currentTime); | ||
} while (firstTask !== null && !shouldYieldToHost() && !(enableSchedulerDebugging && isSchedulerPaused)); | ||
} | ||
} | ||
// Return whether there's additional work | ||
if (firstTask !== null) { | ||
return true; | ||
} else { | ||
if (firstDelayedTask !== null) { | ||
requestHostTimeout(handleTimeout, firstDelayedTask.startTime - currentTime); | ||
} | ||
return false; | ||
} | ||
} finally { | ||
isPerformingWork = false; | ||
currentHostCallbackDidTimeout = previousDidTimeout; | ||
// There's still work remaining. Request another callback. | ||
scheduleHostCallbackIfNeeded(); | ||
} | ||
@@ -369,15 +476,8 @@ } | ||
var previousPriorityLevel = currentPriorityLevel; | ||
var previousEventStartTime = currentEventStartTime; | ||
currentPriorityLevel = priorityLevel; | ||
currentEventStartTime = getCurrentTime(); | ||
try { | ||
return eventHandler(); | ||
} catch (error) { | ||
// There's still work remaining. Request another callback. | ||
scheduleHostCallbackIfNeeded(); | ||
throw error; | ||
} finally { | ||
currentPriorityLevel = previousPriorityLevel; | ||
currentEventStartTime = previousEventStartTime; | ||
} | ||
@@ -387,3 +487,3 @@ } | ||
function unstable_next(eventHandler) { | ||
var priorityLevel = void 0; | ||
var priorityLevel; | ||
switch (currentPriorityLevel) { | ||
@@ -403,15 +503,8 @@ case ImmediatePriority: | ||
var previousPriorityLevel = currentPriorityLevel; | ||
var previousEventStartTime = currentEventStartTime; | ||
currentPriorityLevel = priorityLevel; | ||
currentEventStartTime = getCurrentTime(); | ||
try { | ||
return eventHandler(); | ||
} catch (error) { | ||
// There's still work remaining. Request another callback. | ||
scheduleHostCallbackIfNeeded(); | ||
throw error; | ||
} finally { | ||
currentPriorityLevel = previousPriorityLevel; | ||
currentEventStartTime = previousEventStartTime; | ||
} | ||
@@ -425,15 +518,8 @@ } | ||
var previousPriorityLevel = currentPriorityLevel; | ||
var previousEventStartTime = currentEventStartTime; | ||
currentPriorityLevel = parentPriorityLevel; | ||
currentEventStartTime = getCurrentTime(); | ||
try { | ||
return callback.apply(this, arguments); | ||
} catch (error) { | ||
// There's still work remaining. Request another callback. | ||
scheduleHostCallbackIfNeeded(); | ||
throw error; | ||
} finally { | ||
currentPriorityLevel = previousPriorityLevel; | ||
currentEventStartTime = previousEventStartTime; | ||
} | ||
@@ -443,32 +529,42 @@ }; | ||
function unstable_scheduleCallback(priorityLevel, callback, deprecated_options) { | ||
var startTime = currentEventStartTime !== -1 ? currentEventStartTime : getCurrentTime(); | ||
function timeoutForPriorityLevel(priorityLevel) { | ||
switch (priorityLevel) { | ||
case ImmediatePriority: | ||
return IMMEDIATE_PRIORITY_TIMEOUT; | ||
case UserBlockingPriority: | ||
return USER_BLOCKING_PRIORITY; | ||
case IdlePriority: | ||
return IDLE_PRIORITY; | ||
case LowPriority: | ||
return LOW_PRIORITY_TIMEOUT; | ||
case NormalPriority: | ||
default: | ||
return NORMAL_PRIORITY_TIMEOUT; | ||
} | ||
} | ||
var expirationTime; | ||
if (typeof deprecated_options === 'object' && deprecated_options !== null && typeof deprecated_options.timeout === 'number') { | ||
// FIXME: Remove this branch once we lift expiration times out of React. | ||
expirationTime = startTime + deprecated_options.timeout; | ||
function unstable_scheduleCallback(priorityLevel, callback, options) { | ||
var currentTime = getCurrentTime(); | ||
var startTime; | ||
var timeout; | ||
if (typeof options === 'object' && options !== null) { | ||
var delay = options.delay; | ||
if (typeof delay === 'number' && delay > 0) { | ||
startTime = currentTime + delay; | ||
} else { | ||
startTime = currentTime; | ||
} | ||
timeout = typeof options.timeout === 'number' ? options.timeout : timeoutForPriorityLevel(priorityLevel); | ||
} else { | ||
switch (priorityLevel) { | ||
case ImmediatePriority: | ||
expirationTime = startTime + IMMEDIATE_PRIORITY_TIMEOUT; | ||
break; | ||
case UserBlockingPriority: | ||
expirationTime = startTime + USER_BLOCKING_PRIORITY; | ||
break; | ||
case IdlePriority: | ||
expirationTime = startTime + IDLE_PRIORITY; | ||
break; | ||
case LowPriority: | ||
expirationTime = startTime + LOW_PRIORITY_TIMEOUT; | ||
break; | ||
case NormalPriority: | ||
default: | ||
expirationTime = startTime + NORMAL_PRIORITY_TIMEOUT; | ||
} | ||
timeout = timeoutForPriorityLevel(priorityLevel); | ||
startTime = currentTime; | ||
} | ||
var newNode = { | ||
var expirationTime = startTime + timeout; | ||
var newTask = { | ||
callback: callback, | ||
priorityLevel: priorityLevel, | ||
startTime: startTime, | ||
expirationTime: expirationTime, | ||
@@ -479,38 +575,95 @@ next: null, | ||
// Insert the new callback into the list, ordered first by expiration, then | ||
// by insertion. So the new callback is inserted any other callback with | ||
// equal expiration. | ||
if (firstCallbackNode === null) { | ||
// This is the first callback in the list. | ||
firstCallbackNode = newNode.next = newNode.previous = newNode; | ||
scheduleHostCallbackIfNeeded(); | ||
if (startTime > currentTime) { | ||
// This is a delayed task. | ||
insertDelayedTask(newTask, startTime); | ||
if (firstTask === null && firstDelayedTask === newTask) { | ||
// All tasks are delayed, and this is the task with the earliest delay. | ||
if (isHostTimeoutScheduled) { | ||
// Cancel an existing timeout. | ||
cancelHostTimeout(); | ||
} else { | ||
isHostTimeoutScheduled = true; | ||
} | ||
// Schedule a timeout. | ||
requestHostTimeout(handleTimeout, startTime - currentTime); | ||
} | ||
} else { | ||
insertScheduledTask(newTask, expirationTime); | ||
// Schedule a host callback, if needed. If we're already performing work, | ||
// wait until the next time we yield. | ||
if (!isHostCallbackScheduled && !isPerformingWork) { | ||
isHostCallbackScheduled = true; | ||
requestHostCallback(flushWork); | ||
} | ||
} | ||
return newTask; | ||
} | ||
function insertScheduledTask(newTask, expirationTime) { | ||
// Insert the new task into the list, ordered first by its timeout, then by | ||
// insertion. So the new task is inserted after any other task the | ||
// same timeout | ||
if (firstTask === null) { | ||
// This is the first task in the list. | ||
firstTask = newTask.next = newTask.previous = newTask; | ||
} else { | ||
var next = null; | ||
var node = firstCallbackNode; | ||
var task = firstTask; | ||
do { | ||
if (node.expirationTime > expirationTime) { | ||
// The new callback expires before this one. | ||
next = node; | ||
if (expirationTime < task.expirationTime) { | ||
// The new task times out before this one. | ||
next = task; | ||
break; | ||
} | ||
node = node.next; | ||
} while (node !== firstCallbackNode); | ||
task = task.next; | ||
} while (task !== firstTask); | ||
if (next === null) { | ||
// No callback with a later expiration was found, which means the new | ||
// callback has the latest expiration in the list. | ||
next = firstCallbackNode; | ||
} else if (next === firstCallbackNode) { | ||
// The new callback has the earliest expiration in the entire list. | ||
firstCallbackNode = newNode; | ||
scheduleHostCallbackIfNeeded(); | ||
// No task with a later timeout was found, which means the new task has | ||
// the latest timeout in the list. | ||
next = firstTask; | ||
} else if (next === firstTask) { | ||
// The new task has the earliest expiration in the entire list. | ||
firstTask = newTask; | ||
} | ||
var previous = next.previous; | ||
previous.next = next.previous = newNode; | ||
newNode.next = next; | ||
newNode.previous = previous; | ||
previous.next = next.previous = newTask; | ||
newTask.next = next; | ||
newTask.previous = previous; | ||
} | ||
} | ||
return newNode; | ||
function insertDelayedTask(newTask, startTime) { | ||
// Insert the new task into the list, ordered by its start time. | ||
if (firstDelayedTask === null) { | ||
// This is the first task in the list. | ||
firstDelayedTask = newTask.next = newTask.previous = newTask; | ||
} else { | ||
var next = null; | ||
var task = firstDelayedTask; | ||
do { | ||
if (startTime < task.startTime) { | ||
// The new task times out before this one. | ||
next = task; | ||
break; | ||
} | ||
task = task.next; | ||
} while (task !== firstDelayedTask); | ||
if (next === null) { | ||
// No task with a later timeout was found, which means the new task has | ||
// the latest timeout in the list. | ||
next = firstDelayedTask; | ||
} else if (next === firstDelayedTask) { | ||
// The new task has the earliest expiration in the entire list. | ||
firstDelayedTask = newTask; | ||
} | ||
var previous = next.previous; | ||
previous.next = next.previous = newTask; | ||
newTask.next = next; | ||
newTask.previous = previous; | ||
} | ||
} | ||
@@ -524,4 +677,5 @@ | ||
isSchedulerPaused = false; | ||
if (firstCallbackNode !== null) { | ||
scheduleHostCallbackIfNeeded(); | ||
if (!isHostCallbackScheduled && !isPerformingWork) { | ||
isHostCallbackScheduled = true; | ||
requestHostCallback(flushWork); | ||
} | ||
@@ -531,7 +685,7 @@ } | ||
function unstable_getFirstCallbackNode() { | ||
return firstCallbackNode; | ||
return firstTask; | ||
} | ||
function unstable_cancelCallback(callbackNode) { | ||
var next = callbackNode.next; | ||
function unstable_cancelCallback(task) { | ||
var next = task.next; | ||
if (next === null) { | ||
@@ -542,11 +696,15 @@ // Already cancelled. | ||
if (next === callbackNode) { | ||
// This is the only scheduled callback. Clear the list. | ||
firstCallbackNode = null; | ||
if (task === next) { | ||
if (task === firstTask) { | ||
firstTask = null; | ||
} else if (task === firstDelayedTask) { | ||
firstDelayedTask = null; | ||
} | ||
} else { | ||
// Remove the callback from its position in the list. | ||
if (callbackNode === firstCallbackNode) { | ||
firstCallbackNode = next; | ||
if (task === firstTask) { | ||
firstTask = next; | ||
} else if (task === firstDelayedTask) { | ||
firstDelayedTask = next; | ||
} | ||
var previous = callbackNode.previous; | ||
var previous = task.previous; | ||
previous.next = next; | ||
@@ -556,3 +714,3 @@ next.previous = previous; | ||
callbackNode.next = callbackNode.previous = null; | ||
task.next = task.previous = null; | ||
} | ||
@@ -565,12 +723,17 @@ | ||
function unstable_shouldYield() { | ||
return !currentHostCallbackDidTimeout && (firstCallbackNode !== null && firstCallbackNode.expirationTime < currentExpirationTime || shouldYieldToHost()); | ||
var currentTime = getCurrentTime(); | ||
advanceTimers(currentTime); | ||
return currentTask !== null && firstTask !== null && firstTask.startTime <= currentTime && firstTask.expirationTime < currentTask.expirationTime || shouldYieldToHost(); | ||
} | ||
exports.unstable_flushWithoutYielding = unstable_flushWithoutYielding; | ||
var unstable_requestPaint = requestPaint; | ||
exports.unstable_flushAllWithoutAsserting = unstable_flushAllWithoutAsserting; | ||
exports.unstable_flushNumberOfYields = unstable_flushNumberOfYields; | ||
exports.unstable_flushExpired = unstable_flushExpired; | ||
exports.unstable_clearYields = unstable_clearYields; | ||
exports.flushAll = flushAll; | ||
exports.yieldValue = yieldValue; | ||
exports.advanceTime = advanceTime; | ||
exports.unstable_flushUntilNextPaint = unstable_flushUntilNextPaint; | ||
exports.unstable_flushAll = unstable_flushAll; | ||
exports.unstable_yieldValue = unstable_yieldValue; | ||
exports.unstable_advanceTime = unstable_advanceTime; | ||
exports.unstable_ImmediatePriority = ImmediatePriority; | ||
@@ -588,2 +751,3 @@ exports.unstable_UserBlockingPriority = UserBlockingPriority; | ||
exports.unstable_shouldYield = unstable_shouldYield; | ||
exports.unstable_requestPaint = unstable_requestPaint; | ||
exports.unstable_continueExecution = unstable_continueExecution; | ||
@@ -593,3 +757,4 @@ exports.unstable_pauseExecution = unstable_pauseExecution; | ||
exports.unstable_now = getCurrentTime; | ||
exports.unstable_forceFrameRate = forceFrameRate; | ||
})(); | ||
} |
@@ -1,2 +0,2 @@ | ||
/** @license React v0.0.0-9ebe1768a | ||
/** @license React v0.0.0-9f395904c | ||
* scheduler-unstable_mock.production.min.js | ||
@@ -10,11 +10,13 @@ * | ||
'use strict';Object.defineProperty(exports,"__esModule",{value:!0});var c=0,f=null,g=-1,h=null,k=-1,l=!1,m=!1;function n(){return-1!==k&&null!==h&&h.length>=k||-1!==g&&g<=c?l=!0:!1}function q(){if(m)throw Error("Already flushing work.");if(null!==f){var a=f;f=null;m=!0;try{a(!0)}finally{m=!1}}}function t(){if(m)throw Error("Already flushing work.");m=!0;try{for(;null!==f;){var a=f;f=null;a(-1!==g&&g<=c)}}finally{k=-1,m=l=!1}}var u=null,v=!1,w=3,x=-1,y=-1,z=!1,A=!1; | ||
function B(){if(!z&&null!==u){var a=u.expirationTime;A?(f=null,g=-1):A=!0;f=C;g=a}} | ||
function D(){var a=u,d=u.next;if(u===d)u=null;else{var b=u.previous;u=b.next=d;d.previous=b}a.next=a.previous=null;b=a.callback;d=a.expirationTime;a=a.priorityLevel;var e=w,r=y;w=a;y=d;try{var p=b(v||1===a)}catch(E){throw E;}finally{w=e,y=r}if("function"===typeof p)if(p={callback:p,priorityLevel:a,expirationTime:d,next:null,previous:null},null===u)u=p.next=p.previous=p;else{b=null;a=u;do{if(a.expirationTime>=d){b=a;break}a=a.next}while(a!==u);null===b?b=u:b===u&&(u=p,B());d=b.previous;d.next=b.previous= | ||
p;p.next=b;p.previous=d}}function C(a){A=!1;z=!0;var d=v;v=a;try{if(a)for(;null!==u;)if(a=c,u.expirationTime<=a){do D();while(null!==u&&u.expirationTime<=a)}else break;else if(null!==u){do D();while(null!==u&&!n())}}finally{z=!1,v=d,B()}}exports.unstable_flushWithoutYielding=t;exports.unstable_flushNumberOfYields=function(a){if(m)throw Error("Already flushing work.");k=a;m=!0;try{for(;null!==f&&!l;)a=f,f=null,a(-1!==g&&g<=c)}finally{k=-1,m=l=!1}};exports.unstable_flushExpired=q; | ||
exports.unstable_clearYields=function(){if(null===h)return[];var a=h;h=null;return a};exports.flushAll=function(){if(null!==h)throw Error("Log is not empty. Assert on the log of yielded values before flushing additional work.");t();if(null!==h)throw Error("While flushing work, something yielded a value. Use an assertion helper to assert on the log of yielded values, e.g. expect(Scheduler).toFlushAndYield([...])");};exports.yieldValue=function(a){null===h?h=[a]:h.push(a)}; | ||
exports.advanceTime=function(a){c+=a;!m&&-1!==g&&g<=c&&q()};exports.unstable_ImmediatePriority=1;exports.unstable_UserBlockingPriority=2;exports.unstable_NormalPriority=3;exports.unstable_IdlePriority=5;exports.unstable_LowPriority=4;exports.unstable_runWithPriority=function(a,d){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var b=w,e=x;w=a;x=c;try{return d()}catch(r){throw B(),r;}finally{w=b,x=e}}; | ||
exports.unstable_next=function(a){switch(w){case 1:case 2:case 3:var d=3;break;default:d=w}var b=w,e=x;w=d;x=c;try{return a()}catch(r){throw B(),r;}finally{w=b,x=e}}; | ||
exports.unstable_scheduleCallback=function(a,d,b){var e=-1!==x?x:c;if("object"===typeof b&&null!==b&&"number"===typeof b.timeout)b=e+b.timeout;else switch(a){case 1:b=e+-1;break;case 2:b=e+250;break;case 5:b=e+1073741823;break;case 4:b=e+1E4;break;default:b=e+5E3}a={callback:d,priorityLevel:a,expirationTime:b,next:null,previous:null};if(null===u)u=a.next=a.previous=a,B();else{d=null;e=u;do{if(e.expirationTime>b){d=e;break}e=e.next}while(e!==u);null===d?d=u:d===u&&(u=a,B());b=d.previous;b.next=d.previous= | ||
a;a.next=d;a.previous=b}return a};exports.unstable_cancelCallback=function(a){var d=a.next;if(null!==d){if(d===a)u=null;else{a===u&&(u=d);var b=a.previous;b.next=d;d.previous=b}a.next=a.previous=null}};exports.unstable_wrapCallback=function(a){var d=w;return function(){var b=w,e=x;w=d;x=c;try{return a.apply(this,arguments)}catch(r){throw B(),r;}finally{w=b,x=e}}};exports.unstable_getCurrentPriorityLevel=function(){return w}; | ||
exports.unstable_shouldYield=function(){return!v&&(null!==u&&u.expirationTime<y||n())};exports.unstable_continueExecution=function(){null!==u&&B()};exports.unstable_pauseExecution=function(){};exports.unstable_getFirstCallbackNode=function(){return u};exports.unstable_now=function(){return c}; | ||
'use strict';Object.defineProperty(exports,"__esModule",{value:!0});var d=0,e=null,g=null,k=-1,l=null,n=-1,q=!1,r=!1,t=!1,u=!1;function v(){return-1!==n&&null!==l&&l.length>=n||u&&t?q=!0:!1}function w(){if(r)throw Error("Already flushing work.");if(null!==e){r=!0;try{e(!1,d)||(e=null)}finally{r=!1}}}function x(){if(r)throw Error("Already flushing work.");if(null!==e){var a=e;r=!0;try{var b=!0;do b=a(!0,d);while(b);b||(e=null);return!0}finally{r=!1}}else return!1} | ||
var y=null,z=null,A=null,B=3,C=!1,D=!1,E=!1; | ||
function F(a,b){var c=a.next;if(c===a)y=null;else{a===y&&(y=c);var f=a.previous;f.next=c;c.previous=f}a.next=a.previous=null;c=a.callback;f=B;var p=A;B=a.priorityLevel;A=a;try{var h=a.expirationTime<=b;switch(B){case 1:var m=c(h);break;case 2:m=c(h);break;case 3:m=c(h);break;case 4:m=c(h);break;case 5:m=c(h)}}catch(L){throw L;}finally{B=f,A=p}if("function"===typeof m)if(b=a.expirationTime,a={callback:m,priorityLevel:a.priorityLevel,startTime:a.startTime,expirationTime:b,next:null,previous:null},null=== | ||
y)y=a.next=a.previous=a;else{m=null;h=y;do{if(b<=h.expirationTime){m=h;break}h=h.next}while(h!==y);null===m?m=y:m===y&&(y=a);b=m.previous;b.next=m.previous=a;a.next=m;a.previous=b}}function G(a){if(null!==z&&z.startTime<=a){do{var b=z,c=b.next;if(b===c)z=null;else{z=c;var f=b.previous;f.next=c;c.previous=f}b.next=b.previous=null;H(b,b.expirationTime)}while(null!==z&&z.startTime<=a)}}function I(a){E=!1;G(a);D||(null!==y?(D=!0,e=J):null!==z&&(a=z.startTime-a,g=I,k=d+a))} | ||
function J(a,b){D=!1;E&&(E=!1,g=null,k=-1);G(b);C=!0;try{if(!a)for(;null!==y&&y.expirationTime<=b;)F(y,b),b=d,G(b);else if(null!==y){do F(y,b),b=d,G(b);while(null!==y&&!v())}if(null!==y)return!0;if(null!==z){var c=z.startTime-b;g=I;k=d+c}return!1}finally{C=!1}}function K(a){switch(a){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1E4;default:return 5E3}} | ||
function H(a,b){if(null===y)y=a.next=a.previous=a;else{var c=null,f=y;do{if(b<f.expirationTime){c=f;break}f=f.next}while(f!==y);null===c?c=y:c===y&&(y=a);b=c.previous;b.next=c.previous=a;a.next=c;a.previous=b}}exports.unstable_flushAllWithoutAsserting=x;exports.unstable_flushNumberOfYields=function(a){if(r)throw Error("Already flushing work.");if(null!==e){var b=e;n=a;r=!0;try{a=!0;do a=b(!0,d);while(a&&!q);a||(e=null)}finally{n=-1,r=q=!1}}};exports.unstable_flushExpired=w; | ||
exports.unstable_clearYields=function(){if(null===l)return[];var a=l;l=null;return a};exports.unstable_flushUntilNextPaint=function(){if(r)throw Error("Already flushing work.");if(null!==e){var a=e;u=!0;t=!1;r=!0;try{var b=!0;do b=a(!0,d);while(b&&!q);b||(e=null)}finally{r=q=u=!1}}}; | ||
exports.unstable_flushAll=function(){if(null!==l)throw Error("Log is not empty. Assert on the log of yielded values before flushing additional work.");x();if(null!==l)throw Error("While flushing work, something yielded a value. Use an assertion helper to assert on the log of yielded values, e.g. expect(Scheduler).toFlushAndYield([...])");};exports.unstable_yieldValue=function(a){null===l?l=[a]:l.push(a)};exports.unstable_advanceTime=function(a){d+=a;r||(null!==g&&k<=d&&(g(d),k=-1,g=null),w())}; | ||
exports.unstable_ImmediatePriority=1;exports.unstable_UserBlockingPriority=2;exports.unstable_NormalPriority=3;exports.unstable_IdlePriority=5;exports.unstable_LowPriority=4;exports.unstable_runWithPriority=function(a,b){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var c=B;B=a;try{return b()}finally{B=c}};exports.unstable_next=function(a){switch(B){case 1:case 2:case 3:var b=3;break;default:b=B}var c=B;B=b;try{return a()}finally{B=c}}; | ||
exports.unstable_scheduleCallback=function(a,b,c){var f=d;if("object"===typeof c&&null!==c){var p=c.delay;p="number"===typeof p&&0<p?f+p:f;c="number"===typeof c.timeout?c.timeout:K(a)}else c=K(a),p=f;c=p+c;a={callback:b,priorityLevel:a,startTime:p,expirationTime:c,next:null,previous:null};if(p>f){c=p;if(null===z)z=a.next=a.previous=a;else{b=null;var h=z;do{if(c<h.startTime){b=h;break}h=h.next}while(h!==z);null===b?b=z:b===z&&(z=a);c=b.previous;c.next=b.previous=a;a.next=b;a.previous=c}null===y&&z=== | ||
a&&(E?(g=null,k=-1):E=!0,g=I,k=d+(p-f))}else H(a,c),D||C||(D=!0,e=J);return a};exports.unstable_cancelCallback=function(a){var b=a.next;if(null!==b){if(a===b)a===y?y=null:a===z&&(z=null);else{a===y?y=b:a===z&&(z=b);var c=a.previous;c.next=b;b.previous=c}a.next=a.previous=null}};exports.unstable_wrapCallback=function(a){var b=B;return function(){var c=B;B=b;try{return a.apply(this,arguments)}finally{B=c}}};exports.unstable_getCurrentPriorityLevel=function(){return B}; | ||
exports.unstable_shouldYield=function(){var a=d;G(a);return null!==A&&null!==y&&y.startTime<=a&&y.expirationTime<A.expirationTime||v()};exports.unstable_requestPaint=function(){t=!0};exports.unstable_continueExecution=function(){D||C||(D=!0,e=J)};exports.unstable_pauseExecution=function(){};exports.unstable_getFirstCallbackNode=function(){return y};exports.unstable_now=function(){return d};exports.unstable_forceFrameRate=function(){}; |
@@ -1,2 +0,2 @@ | ||
/** @license React v0.0.0-9ebe1768a | ||
/** @license React v0.0.0-9f395904c | ||
* scheduler.development.js | ||
@@ -21,2 +21,3 @@ * | ||
var enableSchedulerDebugging = false; | ||
var enableIsInputPending = false; | ||
@@ -32,5 +33,9 @@ // The DOM Scheduler implementation is similar to requestIdleCallback. It | ||
var requestHostCallback = void 0; | ||
var cancelHostCallback = void 0; | ||
var requestHostTimeout = void 0; | ||
var cancelHostTimeout = void 0; | ||
var shouldYieldToHost = void 0; | ||
var requestPaint = void 0; | ||
exports.unstable_now = void 0; | ||
exports.unstable_forceFrameRate = void 0; | ||
@@ -98,12 +103,17 @@ var hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function'; | ||
var _callback = null; | ||
var _flushCallback = function (didTimeout) { | ||
var _timeoutID = null; | ||
var _flushCallback = function () { | ||
if (_callback !== null) { | ||
try { | ||
_callback(didTimeout); | ||
} finally { | ||
var currentTime = exports.unstable_now(); | ||
var hasRemainingTime = true; | ||
_callback(hasRemainingTime, currentTime); | ||
_callback = null; | ||
} catch (e) { | ||
setTimeout(_flushCallback, 0); | ||
throw e; | ||
} | ||
} | ||
}; | ||
requestHostCallback = function (cb, ms) { | ||
requestHostCallback = function (cb) { | ||
if (_callback !== null) { | ||
@@ -114,11 +124,15 @@ // Protect against re-entrancy. | ||
_callback = cb; | ||
setTimeout(_flushCallback, 0, false); | ||
setTimeout(_flushCallback, 0); | ||
} | ||
}; | ||
cancelHostCallback = function () { | ||
_callback = null; | ||
requestHostTimeout = function (cb, ms) { | ||
_timeoutID = setTimeout(cb, ms); | ||
}; | ||
cancelHostTimeout = function () { | ||
clearTimeout(_timeoutID); | ||
}; | ||
shouldYieldToHost = function () { | ||
return false; | ||
}; | ||
requestPaint = exports.unstable_forceFrameRate = function () {}; | ||
} else { | ||
@@ -137,7 +151,6 @@ if (typeof console !== 'undefined') { | ||
var isMessageEventScheduled = false; | ||
var timeoutTime = -1; | ||
var isAnimationFrameScheduled = false; | ||
var isFlushingHostCallback = false; | ||
var timeoutID = -1; | ||
@@ -150,5 +163,62 @@ var frameDeadline = 0; | ||
var activeFrameTime = 33; | ||
var fpsLocked = false; | ||
shouldYieldToHost = function () { | ||
return frameDeadline <= exports.unstable_now(); | ||
// TODO: Make this configurable | ||
// TODO: Adjust this based on priority? | ||
var maxFrameLength = 300; | ||
var needsPaint = false; | ||
if (enableIsInputPending && navigator !== undefined && navigator.scheduling !== undefined && navigator.scheduling.isInputPending !== undefined) { | ||
var scheduling = navigator.scheduling; | ||
shouldYieldToHost = function () { | ||
var currentTime = exports.unstable_now(); | ||
if (currentTime >= frameDeadline) { | ||
// There's no time left in the frame. We may want to yield control of | ||
// the main thread, so the browser can perform high priority tasks. The | ||
// main ones are painting and user input. If there's a pending paint or | ||
// a pending input, then we should yield. But if there's neither, then | ||
// we can yield less often while remaining responsive. We'll eventually | ||
// yield regardless, since there could be a pending paint that wasn't | ||
// accompanied by a call to `requestPaint`, or other main thread tasks | ||
// like network events. | ||
if (needsPaint || scheduling.isInputPending()) { | ||
// There is either a pending paint or a pending input. | ||
return true; | ||
} | ||
// There's no pending input. Only yield if we've reached the max | ||
// frame length. | ||
return currentTime >= frameDeadline + maxFrameLength; | ||
} else { | ||
// There's still time left in the frame. | ||
return false; | ||
} | ||
}; | ||
requestPaint = function () { | ||
needsPaint = true; | ||
}; | ||
} else { | ||
// `isInputPending` is not available. Since we have no way of knowing if | ||
// there's pending input, always yield at the end of the frame. | ||
shouldYieldToHost = function () { | ||
return exports.unstable_now() >= frameDeadline; | ||
}; | ||
// Since we yield every frame regardless, `requestPaint` has no effect. | ||
requestPaint = function () {}; | ||
} | ||
exports.unstable_forceFrameRate = function (fps) { | ||
if (fps < 0 || fps > 125) { | ||
console.error('forceFrameRate takes a positive int between 0 and 125, ' + 'forcing framerates higher than 125 fps is not unsupported'); | ||
return; | ||
} | ||
if (fps > 0) { | ||
activeFrameTime = Math.floor(1000 / fps); | ||
fpsLocked = true; | ||
} else { | ||
// reset the framerate | ||
activeFrameTime = 33; | ||
fpsLocked = false; | ||
} | ||
}; | ||
@@ -161,40 +231,28 @@ | ||
isMessageEventScheduled = false; | ||
var prevScheduledCallback = scheduledHostCallback; | ||
var prevTimeoutTime = timeoutTime; | ||
scheduledHostCallback = null; | ||
timeoutTime = -1; | ||
var currentTime = exports.unstable_now(); | ||
var didTimeout = false; | ||
if (frameDeadline - currentTime <= 0) { | ||
// There's no time left in this idle period. Check if the callback has | ||
// a timeout and whether it's been exceeded. | ||
if (prevTimeoutTime !== -1 && prevTimeoutTime <= currentTime) { | ||
// Exceeded the timeout. Invoke the callback even though there's no | ||
// time left. | ||
didTimeout = true; | ||
} else { | ||
// No timeout. | ||
if (!isAnimationFrameScheduled) { | ||
// Schedule another animation callback so we retry later. | ||
isAnimationFrameScheduled = true; | ||
requestAnimationFrameWithTimeout(animationTick); | ||
if (scheduledHostCallback !== null) { | ||
var currentTime = exports.unstable_now(); | ||
var hasTimeRemaining = frameDeadline - currentTime > 0; | ||
try { | ||
var hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime); | ||
if (hasMoreWork) { | ||
// Ensure the next frame is scheduled. | ||
if (!isAnimationFrameScheduled) { | ||
isAnimationFrameScheduled = true; | ||
requestAnimationFrameWithTimeout(animationTick); | ||
} | ||
} else { | ||
scheduledHostCallback = null; | ||
} | ||
// Exit without invoking the callback. | ||
scheduledHostCallback = prevScheduledCallback; | ||
timeoutTime = prevTimeoutTime; | ||
return; | ||
} catch (error) { | ||
// If a scheduler task throws, exit the current browser task so the | ||
// error can be observed, and post a new task as soon as possible | ||
// so we can continue where we left off. | ||
isMessageEventScheduled = true; | ||
port.postMessage(undefined); | ||
throw error; | ||
} | ||
// Yielding to the browser will give it a chance to paint, so we can | ||
// reset this. | ||
needsPaint = false; | ||
} | ||
if (prevScheduledCallback !== null) { | ||
isFlushingHostCallback = true; | ||
try { | ||
prevScheduledCallback(didTimeout); | ||
} finally { | ||
isFlushingHostCallback = false; | ||
} | ||
} | ||
}; | ||
@@ -220,3 +278,3 @@ | ||
var nextFrameTime = rafTime - frameDeadline + activeFrameTime; | ||
if (nextFrameTime < activeFrameTime && previousFrameTime < activeFrameTime) { | ||
if (nextFrameTime < activeFrameTime && previousFrameTime < activeFrameTime && !fpsLocked) { | ||
if (nextFrameTime < 8) { | ||
@@ -245,23 +303,26 @@ // Defensive coding. We don't support higher frame rates than 120hz. | ||
requestHostCallback = function (callback, absoluteTimeout) { | ||
scheduledHostCallback = callback; | ||
timeoutTime = absoluteTimeout; | ||
if (isFlushingHostCallback || absoluteTimeout < 0) { | ||
// Don't wait for the next frame. Continue working ASAP, in a new event. | ||
port.postMessage(undefined); | ||
} else if (!isAnimationFrameScheduled) { | ||
// If rAF didn't already schedule one, we need to schedule a frame. | ||
// TODO: If this rAF doesn't materialize because the browser throttles, we | ||
// might want to still have setTimeout trigger rIC as a backup to ensure | ||
// that we keep performing work. | ||
isAnimationFrameScheduled = true; | ||
requestAnimationFrameWithTimeout(animationTick); | ||
requestHostCallback = function (callback) { | ||
if (scheduledHostCallback === null) { | ||
scheduledHostCallback = callback; | ||
if (!isAnimationFrameScheduled) { | ||
// If rAF didn't already schedule one, we need to schedule a frame. | ||
// TODO: If this rAF doesn't materialize because the browser throttles, | ||
// we might want to still have setTimeout trigger rIC as a backup to | ||
// ensure that we keep performing work. | ||
isAnimationFrameScheduled = true; | ||
requestAnimationFrameWithTimeout(animationTick); | ||
} | ||
} | ||
}; | ||
cancelHostCallback = function () { | ||
scheduledHostCallback = null; | ||
isMessageEventScheduled = false; | ||
timeoutTime = -1; | ||
requestHostTimeout = function (callback, ms) { | ||
timeoutID = localSetTimeout(function () { | ||
callback(exports.unstable_now()); | ||
}, ms); | ||
}; | ||
cancelHostTimeout = function () { | ||
localClearTimeout(timeoutID); | ||
timeoutID = -1; | ||
}; | ||
} | ||
@@ -292,12 +353,11 @@ | ||
// Callbacks are stored as a circular, doubly linked list. | ||
var firstCallbackNode = null; | ||
// Tasks are stored as a circular, doubly linked list. | ||
var firstTask = null; | ||
var firstDelayedTask = null; | ||
var currentHostCallbackDidTimeout = false; | ||
// Pausing the scheduler is useful for debugging. | ||
var isSchedulerPaused = false; | ||
var currentTask = null; | ||
var currentPriorityLevel = NormalPriority; | ||
var currentEventStartTime = -1; | ||
var currentExpirationTime = -1; | ||
@@ -308,53 +368,66 @@ // This is set while performing work, to prevent re-entrancy. | ||
var isHostCallbackScheduled = false; | ||
var isHostTimeoutScheduled = false; | ||
function scheduleHostCallbackIfNeeded() { | ||
if (isPerformingWork) { | ||
// Don't schedule work yet; wait until the next time we yield. | ||
return; | ||
} | ||
if (firstCallbackNode !== null) { | ||
// Schedule the host callback using the earliest expiration in the list. | ||
var expirationTime = firstCallbackNode.expirationTime; | ||
if (isHostCallbackScheduled) { | ||
// Cancel the existing host callback. | ||
cancelHostCallback(); | ||
} else { | ||
isHostCallbackScheduled = true; | ||
} | ||
requestHostCallback(flushWork, expirationTime); | ||
} | ||
function scheduler_flushTaskAtPriority_Immediate(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function scheduler_flushTaskAtPriority_UserBlocking(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function scheduler_flushTaskAtPriority_Normal(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function scheduler_flushTaskAtPriority_Low(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function scheduler_flushTaskAtPriority_Idle(callback, didTimeout) { | ||
return callback(didTimeout); | ||
} | ||
function flushFirstCallback() { | ||
var currentlyFlushingCallback = firstCallbackNode; | ||
// Remove the node from the list before calling the callback. That way the | ||
function flushTask(task, currentTime) { | ||
// Remove the task from the list before calling the callback. That way the | ||
// list is in a consistent state even if the callback throws. | ||
var next = firstCallbackNode.next; | ||
if (firstCallbackNode === next) { | ||
// This is the last callback in the list. | ||
firstCallbackNode = null; | ||
next = null; | ||
var next = task.next; | ||
if (next === task) { | ||
// This is the only scheduled task. Clear the list. | ||
firstTask = null; | ||
} else { | ||
var lastCallbackNode = firstCallbackNode.previous; | ||
firstCallbackNode = lastCallbackNode.next = next; | ||
next.previous = lastCallbackNode; | ||
// Remove the task from its position in the list. | ||
if (task === firstTask) { | ||
firstTask = next; | ||
} | ||
var previous = task.previous; | ||
previous.next = next; | ||
next.previous = previous; | ||
} | ||
task.next = task.previous = null; | ||
currentlyFlushingCallback.next = currentlyFlushingCallback.previous = null; | ||
// Now it's safe to call the callback. | ||
var callback = currentlyFlushingCallback.callback; | ||
var expirationTime = currentlyFlushingCallback.expirationTime; | ||
var priorityLevel = currentlyFlushingCallback.priorityLevel; | ||
// Now it's safe to execute the task. | ||
var callback = task.callback; | ||
var previousPriorityLevel = currentPriorityLevel; | ||
var previousExpirationTime = currentExpirationTime; | ||
currentPriorityLevel = priorityLevel; | ||
currentExpirationTime = expirationTime; | ||
var previousTask = currentTask; | ||
currentPriorityLevel = task.priorityLevel; | ||
currentTask = task; | ||
var continuationCallback; | ||
try { | ||
var didUserCallbackTimeout = currentHostCallbackDidTimeout || | ||
// Immediate priority callbacks are always called as if they timed out | ||
priorityLevel === ImmediatePriority; | ||
continuationCallback = callback(didUserCallbackTimeout); | ||
var didUserCallbackTimeout = task.expirationTime <= currentTime; | ||
// Add an extra function to the callstack. Profiling tools can use this | ||
// to infer the priority of work that appears higher in the stack. | ||
switch (currentPriorityLevel) { | ||
case ImmediatePriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_Immediate(callback, didUserCallbackTimeout); | ||
break; | ||
case UserBlockingPriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_UserBlocking(callback, didUserCallbackTimeout); | ||
break; | ||
case NormalPriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_Normal(callback, didUserCallbackTimeout); | ||
break; | ||
case LowPriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_Low(callback, didUserCallbackTimeout); | ||
break; | ||
case IdlePriority: | ||
continuationCallback = scheduler_flushTaskAtPriority_Idle(callback, didUserCallbackTimeout); | ||
break; | ||
} | ||
} catch (error) { | ||
@@ -364,3 +437,3 @@ throw error; | ||
currentPriorityLevel = previousPriorityLevel; | ||
currentExpirationTime = previousExpirationTime; | ||
currentTask = previousTask; | ||
} | ||
@@ -371,5 +444,7 @@ | ||
if (typeof continuationCallback === 'function') { | ||
var continuationNode = { | ||
var expirationTime = task.expirationTime; | ||
var continuationTask = { | ||
callback: continuationCallback, | ||
priorityLevel: priorityLevel, | ||
priorityLevel: task.priorityLevel, | ||
startTime: task.startTime, | ||
expirationTime: expirationTime, | ||
@@ -380,36 +455,34 @@ next: null, | ||
// Insert the new callback into the list, sorted by its expiration. This is | ||
// Insert the new callback into the list, sorted by its timeout. This is | ||
// almost the same as the code in `scheduleCallback`, except the callback | ||
// is inserted into the list *before* callbacks of equal expiration instead | ||
// is inserted into the list *before* callbacks of equal timeout instead | ||
// of after. | ||
if (firstCallbackNode === null) { | ||
if (firstTask === null) { | ||
// This is the first callback in the list. | ||
firstCallbackNode = continuationNode.next = continuationNode.previous = continuationNode; | ||
firstTask = continuationTask.next = continuationTask.previous = continuationTask; | ||
} else { | ||
var nextAfterContinuation = null; | ||
var node = firstCallbackNode; | ||
var t = firstTask; | ||
do { | ||
if (node.expirationTime >= expirationTime) { | ||
// This callback expires at or after the continuation. We will insert | ||
// the continuation *before* this callback. | ||
nextAfterContinuation = node; | ||
if (expirationTime <= t.expirationTime) { | ||
// This task times out at or after the continuation. We will insert | ||
// the continuation *before* this task. | ||
nextAfterContinuation = t; | ||
break; | ||
} | ||
node = node.next; | ||
} while (node !== firstCallbackNode); | ||
t = t.next; | ||
} while (t !== firstTask); | ||
if (nextAfterContinuation === null) { | ||
// No equal or lower priority callback was found, which means the new | ||
// callback is the lowest priority callback in the list. | ||
nextAfterContinuation = firstCallbackNode; | ||
} else if (nextAfterContinuation === firstCallbackNode) { | ||
// The new callback is the highest priority callback in the list. | ||
firstCallbackNode = continuationNode; | ||
scheduleHostCallbackIfNeeded(); | ||
// No equal or lower priority task was found, which means the new task | ||
// is the lowest priority task in the list. | ||
nextAfterContinuation = firstTask; | ||
} else if (nextAfterContinuation === firstTask) { | ||
// The new task is the highest priority task in the list. | ||
firstTask = continuationTask; | ||
} | ||
var previous = nextAfterContinuation.previous; | ||
previous.next = nextAfterContinuation.previous = continuationNode; | ||
continuationNode.next = nextAfterContinuation; | ||
continuationNode.previous = previous; | ||
var _previous = nextAfterContinuation.previous; | ||
_previous.next = nextAfterContinuation.previous = continuationTask; | ||
continuationTask.next = nextAfterContinuation; | ||
continuationTask.previous = _previous; | ||
} | ||
@@ -419,3 +492,37 @@ } | ||
function flushWork(didUserCallbackTimeout) { | ||
function advanceTimers(currentTime) { | ||
// Check for tasks that are no longer delayed and add them to the queue. | ||
if (firstDelayedTask !== null && firstDelayedTask.startTime <= currentTime) { | ||
do { | ||
var task = firstDelayedTask; | ||
var next = task.next; | ||
if (task === next) { | ||
firstDelayedTask = null; | ||
} else { | ||
firstDelayedTask = next; | ||
var previous = task.previous; | ||
previous.next = next; | ||
next.previous = previous; | ||
} | ||
task.next = task.previous = null; | ||
insertScheduledTask(task, task.expirationTime); | ||
} while (firstDelayedTask !== null && firstDelayedTask.startTime <= currentTime); | ||
} | ||
} | ||
function handleTimeout(currentTime) { | ||
isHostTimeoutScheduled = false; | ||
advanceTimers(currentTime); | ||
if (!isHostCallbackScheduled) { | ||
if (firstTask !== null) { | ||
isHostCallbackScheduled = true; | ||
requestHostCallback(flushWork); | ||
} else if (firstDelayedTask !== null) { | ||
requestHostTimeout(handleTimeout, firstDelayedTask.startTime - currentTime); | ||
} | ||
} | ||
} | ||
function flushWork(hasTimeRemaining, initialTime) { | ||
// Exit right away if we're currently paused | ||
@@ -426,41 +533,45 @@ if (enableSchedulerDebugging && isSchedulerPaused) { | ||
// We'll need a new host callback the next time work is scheduled. | ||
// We'll need a host callback the next time work is scheduled. | ||
isHostCallbackScheduled = false; | ||
if (isHostTimeoutScheduled) { | ||
// We scheduled a timeout but it's no longer needed. Cancel it. | ||
isHostTimeoutScheduled = false; | ||
cancelHostTimeout(); | ||
} | ||
var currentTime = initialTime; | ||
advanceTimers(currentTime); | ||
isPerformingWork = true; | ||
var previousDidTimeout = currentHostCallbackDidTimeout; | ||
currentHostCallbackDidTimeout = didUserCallbackTimeout; | ||
try { | ||
if (didUserCallbackTimeout) { | ||
if (!hasTimeRemaining) { | ||
// Flush all the expired callbacks without yielding. | ||
while (firstCallbackNode !== null && !(enableSchedulerDebugging && isSchedulerPaused)) { | ||
// TODO Wrap in feature flag | ||
// Read the current time. Flush all the callbacks that expire at or | ||
// earlier than that time. Then read the current time again and repeat. | ||
// This optimizes for as few performance.now calls as possible. | ||
var currentTime = exports.unstable_now(); | ||
if (firstCallbackNode.expirationTime <= currentTime) { | ||
do { | ||
flushFirstCallback(); | ||
} while (firstCallbackNode !== null && firstCallbackNode.expirationTime <= currentTime && !(enableSchedulerDebugging && isSchedulerPaused)); | ||
continue; | ||
} | ||
break; | ||
// TODO: Split flushWork into two separate functions instead of using | ||
// a boolean argument? | ||
while (firstTask !== null && firstTask.expirationTime <= currentTime && !(enableSchedulerDebugging && isSchedulerPaused)) { | ||
flushTask(firstTask, currentTime); | ||
currentTime = exports.unstable_now(); | ||
advanceTimers(currentTime); | ||
} | ||
} else { | ||
// Keep flushing callbacks until we run out of time in the frame. | ||
if (firstCallbackNode !== null) { | ||
if (firstTask !== null) { | ||
do { | ||
if (enableSchedulerDebugging && isSchedulerPaused) { | ||
break; | ||
} | ||
flushFirstCallback(); | ||
} while (firstCallbackNode !== null && !shouldYieldToHost()); | ||
flushTask(firstTask, currentTime); | ||
currentTime = exports.unstable_now(); | ||
advanceTimers(currentTime); | ||
} while (firstTask !== null && !shouldYieldToHost() && !(enableSchedulerDebugging && isSchedulerPaused)); | ||
} | ||
} | ||
// Return whether there's additional work | ||
if (firstTask !== null) { | ||
return true; | ||
} else { | ||
if (firstDelayedTask !== null) { | ||
requestHostTimeout(handleTimeout, firstDelayedTask.startTime - currentTime); | ||
} | ||
return false; | ||
} | ||
} finally { | ||
isPerformingWork = false; | ||
currentHostCallbackDidTimeout = previousDidTimeout; | ||
// There's still work remaining. Request another callback. | ||
scheduleHostCallbackIfNeeded(); | ||
} | ||
@@ -482,15 +593,8 @@ } | ||
var previousPriorityLevel = currentPriorityLevel; | ||
var previousEventStartTime = currentEventStartTime; | ||
currentPriorityLevel = priorityLevel; | ||
currentEventStartTime = exports.unstable_now(); | ||
try { | ||
return eventHandler(); | ||
} catch (error) { | ||
// There's still work remaining. Request another callback. | ||
scheduleHostCallbackIfNeeded(); | ||
throw error; | ||
} finally { | ||
currentPriorityLevel = previousPriorityLevel; | ||
currentEventStartTime = previousEventStartTime; | ||
} | ||
@@ -500,3 +604,3 @@ } | ||
function unstable_next(eventHandler) { | ||
var priorityLevel = void 0; | ||
var priorityLevel; | ||
switch (currentPriorityLevel) { | ||
@@ -516,15 +620,8 @@ case ImmediatePriority: | ||
var previousPriorityLevel = currentPriorityLevel; | ||
var previousEventStartTime = currentEventStartTime; | ||
currentPriorityLevel = priorityLevel; | ||
currentEventStartTime = exports.unstable_now(); | ||
try { | ||
return eventHandler(); | ||
} catch (error) { | ||
// There's still work remaining. Request another callback. | ||
scheduleHostCallbackIfNeeded(); | ||
throw error; | ||
} finally { | ||
currentPriorityLevel = previousPriorityLevel; | ||
currentEventStartTime = previousEventStartTime; | ||
} | ||
@@ -538,15 +635,8 @@ } | ||
var previousPriorityLevel = currentPriorityLevel; | ||
var previousEventStartTime = currentEventStartTime; | ||
currentPriorityLevel = parentPriorityLevel; | ||
currentEventStartTime = exports.unstable_now(); | ||
try { | ||
return callback.apply(this, arguments); | ||
} catch (error) { | ||
// There's still work remaining. Request another callback. | ||
scheduleHostCallbackIfNeeded(); | ||
throw error; | ||
} finally { | ||
currentPriorityLevel = previousPriorityLevel; | ||
currentEventStartTime = previousEventStartTime; | ||
} | ||
@@ -556,32 +646,42 @@ }; | ||
function unstable_scheduleCallback(priorityLevel, callback, deprecated_options) { | ||
var startTime = currentEventStartTime !== -1 ? currentEventStartTime : exports.unstable_now(); | ||
function timeoutForPriorityLevel(priorityLevel) { | ||
switch (priorityLevel) { | ||
case ImmediatePriority: | ||
return IMMEDIATE_PRIORITY_TIMEOUT; | ||
case UserBlockingPriority: | ||
return USER_BLOCKING_PRIORITY; | ||
case IdlePriority: | ||
return IDLE_PRIORITY; | ||
case LowPriority: | ||
return LOW_PRIORITY_TIMEOUT; | ||
case NormalPriority: | ||
default: | ||
return NORMAL_PRIORITY_TIMEOUT; | ||
} | ||
} | ||
var expirationTime; | ||
if (typeof deprecated_options === 'object' && deprecated_options !== null && typeof deprecated_options.timeout === 'number') { | ||
// FIXME: Remove this branch once we lift expiration times out of React. | ||
expirationTime = startTime + deprecated_options.timeout; | ||
function unstable_scheduleCallback(priorityLevel, callback, options) { | ||
var currentTime = exports.unstable_now(); | ||
var startTime; | ||
var timeout; | ||
if (typeof options === 'object' && options !== null) { | ||
var delay = options.delay; | ||
if (typeof delay === 'number' && delay > 0) { | ||
startTime = currentTime + delay; | ||
} else { | ||
startTime = currentTime; | ||
} | ||
timeout = typeof options.timeout === 'number' ? options.timeout : timeoutForPriorityLevel(priorityLevel); | ||
} else { | ||
switch (priorityLevel) { | ||
case ImmediatePriority: | ||
expirationTime = startTime + IMMEDIATE_PRIORITY_TIMEOUT; | ||
break; | ||
case UserBlockingPriority: | ||
expirationTime = startTime + USER_BLOCKING_PRIORITY; | ||
break; | ||
case IdlePriority: | ||
expirationTime = startTime + IDLE_PRIORITY; | ||
break; | ||
case LowPriority: | ||
expirationTime = startTime + LOW_PRIORITY_TIMEOUT; | ||
break; | ||
case NormalPriority: | ||
default: | ||
expirationTime = startTime + NORMAL_PRIORITY_TIMEOUT; | ||
} | ||
timeout = timeoutForPriorityLevel(priorityLevel); | ||
startTime = currentTime; | ||
} | ||
var newNode = { | ||
var expirationTime = startTime + timeout; | ||
var newTask = { | ||
callback: callback, | ||
priorityLevel: priorityLevel, | ||
startTime: startTime, | ||
expirationTime: expirationTime, | ||
@@ -592,38 +692,95 @@ next: null, | ||
// Insert the new callback into the list, ordered first by expiration, then | ||
// by insertion. So the new callback is inserted any other callback with | ||
// equal expiration. | ||
if (firstCallbackNode === null) { | ||
// This is the first callback in the list. | ||
firstCallbackNode = newNode.next = newNode.previous = newNode; | ||
scheduleHostCallbackIfNeeded(); | ||
if (startTime > currentTime) { | ||
// This is a delayed task. | ||
insertDelayedTask(newTask, startTime); | ||
if (firstTask === null && firstDelayedTask === newTask) { | ||
// All tasks are delayed, and this is the task with the earliest delay. | ||
if (isHostTimeoutScheduled) { | ||
// Cancel an existing timeout. | ||
cancelHostTimeout(); | ||
} else { | ||
isHostTimeoutScheduled = true; | ||
} | ||
// Schedule a timeout. | ||
requestHostTimeout(handleTimeout, startTime - currentTime); | ||
} | ||
} else { | ||
insertScheduledTask(newTask, expirationTime); | ||
// Schedule a host callback, if needed. If we're already performing work, | ||
// wait until the next time we yield. | ||
if (!isHostCallbackScheduled && !isPerformingWork) { | ||
isHostCallbackScheduled = true; | ||
requestHostCallback(flushWork); | ||
} | ||
} | ||
return newTask; | ||
} | ||
function insertScheduledTask(newTask, expirationTime) { | ||
// Insert the new task into the list, ordered first by its timeout, then by | ||
// insertion. So the new task is inserted after any other task the | ||
// same timeout | ||
if (firstTask === null) { | ||
// This is the first task in the list. | ||
firstTask = newTask.next = newTask.previous = newTask; | ||
} else { | ||
var next = null; | ||
var node = firstCallbackNode; | ||
var task = firstTask; | ||
do { | ||
if (node.expirationTime > expirationTime) { | ||
// The new callback expires before this one. | ||
next = node; | ||
if (expirationTime < task.expirationTime) { | ||
// The new task times out before this one. | ||
next = task; | ||
break; | ||
} | ||
node = node.next; | ||
} while (node !== firstCallbackNode); | ||
task = task.next; | ||
} while (task !== firstTask); | ||
if (next === null) { | ||
// No callback with a later expiration was found, which means the new | ||
// callback has the latest expiration in the list. | ||
next = firstCallbackNode; | ||
} else if (next === firstCallbackNode) { | ||
// The new callback has the earliest expiration in the entire list. | ||
firstCallbackNode = newNode; | ||
scheduleHostCallbackIfNeeded(); | ||
// No task with a later timeout was found, which means the new task has | ||
// the latest timeout in the list. | ||
next = firstTask; | ||
} else if (next === firstTask) { | ||
// The new task has the earliest expiration in the entire list. | ||
firstTask = newTask; | ||
} | ||
var previous = next.previous; | ||
previous.next = next.previous = newNode; | ||
newNode.next = next; | ||
newNode.previous = previous; | ||
previous.next = next.previous = newTask; | ||
newTask.next = next; | ||
newTask.previous = previous; | ||
} | ||
} | ||
return newNode; | ||
function insertDelayedTask(newTask, startTime) { | ||
// Insert the new task into the list, ordered by its start time. | ||
if (firstDelayedTask === null) { | ||
// This is the first task in the list. | ||
firstDelayedTask = newTask.next = newTask.previous = newTask; | ||
} else { | ||
var next = null; | ||
var task = firstDelayedTask; | ||
do { | ||
if (startTime < task.startTime) { | ||
// The new task times out before this one. | ||
next = task; | ||
break; | ||
} | ||
task = task.next; | ||
} while (task !== firstDelayedTask); | ||
if (next === null) { | ||
// No task with a later timeout was found, which means the new task has | ||
// the latest timeout in the list. | ||
next = firstDelayedTask; | ||
} else if (next === firstDelayedTask) { | ||
// The new task has the earliest expiration in the entire list. | ||
firstDelayedTask = newTask; | ||
} | ||
var previous = next.previous; | ||
previous.next = next.previous = newTask; | ||
newTask.next = next; | ||
newTask.previous = previous; | ||
} | ||
} | ||
@@ -637,4 +794,5 @@ | ||
isSchedulerPaused = false; | ||
if (firstCallbackNode !== null) { | ||
scheduleHostCallbackIfNeeded(); | ||
if (!isHostCallbackScheduled && !isPerformingWork) { | ||
isHostCallbackScheduled = true; | ||
requestHostCallback(flushWork); | ||
} | ||
@@ -644,7 +802,7 @@ } | ||
function unstable_getFirstCallbackNode() { | ||
return firstCallbackNode; | ||
return firstTask; | ||
} | ||
function unstable_cancelCallback(callbackNode) { | ||
var next = callbackNode.next; | ||
function unstable_cancelCallback(task) { | ||
var next = task.next; | ||
if (next === null) { | ||
@@ -655,11 +813,15 @@ // Already cancelled. | ||
if (next === callbackNode) { | ||
// This is the only scheduled callback. Clear the list. | ||
firstCallbackNode = null; | ||
if (task === next) { | ||
if (task === firstTask) { | ||
firstTask = null; | ||
} else if (task === firstDelayedTask) { | ||
firstDelayedTask = null; | ||
} | ||
} else { | ||
// Remove the callback from its position in the list. | ||
if (callbackNode === firstCallbackNode) { | ||
firstCallbackNode = next; | ||
if (task === firstTask) { | ||
firstTask = next; | ||
} else if (task === firstDelayedTask) { | ||
firstDelayedTask = next; | ||
} | ||
var previous = callbackNode.previous; | ||
var previous = task.previous; | ||
previous.next = next; | ||
@@ -669,3 +831,3 @@ next.previous = previous; | ||
callbackNode.next = callbackNode.previous = null; | ||
task.next = task.previous = null; | ||
} | ||
@@ -678,5 +840,9 @@ | ||
function unstable_shouldYield() { | ||
return !currentHostCallbackDidTimeout && (firstCallbackNode !== null && firstCallbackNode.expirationTime < currentExpirationTime || shouldYieldToHost()); | ||
var currentTime = exports.unstable_now(); | ||
advanceTimers(currentTime); | ||
return currentTask !== null && firstTask !== null && firstTask.startTime <= currentTime && firstTask.expirationTime < currentTask.expirationTime || shouldYieldToHost(); | ||
} | ||
var unstable_requestPaint = requestPaint; | ||
exports.unstable_ImmediatePriority = ImmediatePriority; | ||
@@ -694,2 +860,3 @@ exports.unstable_UserBlockingPriority = UserBlockingPriority; | ||
exports.unstable_shouldYield = unstable_shouldYield; | ||
exports.unstable_requestPaint = unstable_requestPaint; | ||
exports.unstable_continueExecution = unstable_continueExecution; | ||
@@ -696,0 +863,0 @@ exports.unstable_pauseExecution = unstable_pauseExecution; |
@@ -1,2 +0,2 @@ | ||
/** @license React v0.0.0-9ebe1768a | ||
/** @license React v0.0.0-9f395904c | ||
* scheduler.production.min.js | ||
@@ -10,12 +10,15 @@ * | ||
'use strict';Object.defineProperty(exports,"__esModule",{value:!0});var d=void 0,f=void 0,g=void 0;exports.unstable_now=void 0;var k=Date,m="function"===typeof setTimeout?setTimeout:void 0,n="function"===typeof clearTimeout?clearTimeout:void 0,p="function"===typeof requestAnimationFrame?requestAnimationFrame:void 0,q="function"===typeof cancelAnimationFrame?cancelAnimationFrame:void 0,r=void 0,t=void 0;function u(a){r=p(function(b){n(t);a(b)});t=m(function(){q(r);a(exports.unstable_now())},100)} | ||
if("object"===typeof performance&&"function"===typeof performance.now){var v=performance;exports.unstable_now=function(){return v.now()}}else exports.unstable_now=function(){return k.now()}; | ||
if("undefined"===typeof window||"function"!==typeof MessageChannel){var w=null,x=function(a){if(null!==w)try{w(a)}finally{w=null}};d=function(a){null!==w?setTimeout(d,0,a):(w=a,setTimeout(x,0,!1))};f=function(){w=null};g=function(){return!1}}else{"undefined"!==typeof console&&("function"!==typeof p&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!==typeof q&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")); | ||
var y=null,z=!1,A=-1,B=!1,C=!1,D=0,E=33,F=33;g=function(){return D<=exports.unstable_now()};var G=new MessageChannel,H=G.port2;G.port1.onmessage=function(){z=!1;var a=y,b=A;y=null;A=-1;var c=exports.unstable_now(),e=!1;if(0>=D-c)if(-1!==b&&b<=c)e=!0;else{B||(B=!0,u(I));y=a;A=b;return}if(null!==a){C=!0;try{a(e)}finally{C=!1}}};var I=function(a){if(null!==y){u(I);var b=a-D+F;b<F&&E<F?(8>b&&(b=8),F=b<E?E:b):E=b;D=a+F;z||(z=!0,H.postMessage(void 0))}else B=!1};d=function(a,b){y=a;A=b;C||0>b?H.postMessage(void 0): | ||
B||(B=!0,u(I))};f=function(){y=null;z=!1;A=-1}}var J=null,K=!1,L=3,M=-1,N=-1,O=!1,P=!1;function Q(){if(!O&&null!==J){var a=J.expirationTime;P?f():P=!0;d(R,a)}} | ||
function S(){var a=J,b=J.next;if(J===b)J=null;else{var c=J.previous;J=c.next=b;b.previous=c}a.next=a.previous=null;c=a.callback;b=a.expirationTime;a=a.priorityLevel;var e=L,l=N;L=a;N=b;try{var h=c(K||1===a)}catch(T){throw T;}finally{L=e,N=l}if("function"===typeof h)if(h={callback:h,priorityLevel:a,expirationTime:b,next:null,previous:null},null===J)J=h.next=h.previous=h;else{c=null;a=J;do{if(a.expirationTime>=b){c=a;break}a=a.next}while(a!==J);null===c?c=J:c===J&&(J=h,Q());b=c.previous;b.next=c.previous= | ||
h;h.next=c;h.previous=b}}function R(a){P=!1;O=!0;var b=K;K=a;try{if(a)for(;null!==J;){var c=exports.unstable_now();if(J.expirationTime<=c){do S();while(null!==J&&J.expirationTime<=c)}else break}else if(null!==J){do S();while(null!==J&&!g())}}finally{O=!1,K=b,Q()}}exports.unstable_ImmediatePriority=1;exports.unstable_UserBlockingPriority=2;exports.unstable_NormalPriority=3;exports.unstable_IdlePriority=5;exports.unstable_LowPriority=4; | ||
exports.unstable_runWithPriority=function(a,b){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var c=L,e=M;L=a;M=exports.unstable_now();try{return b()}catch(l){throw Q(),l;}finally{L=c,M=e}};exports.unstable_next=function(a){switch(L){case 1:case 2:case 3:var b=3;break;default:b=L}var c=L,e=M;L=b;M=exports.unstable_now();try{return a()}catch(l){throw Q(),l;}finally{L=c,M=e}}; | ||
exports.unstable_scheduleCallback=function(a,b,c){var e=-1!==M?M:exports.unstable_now();if("object"===typeof c&&null!==c&&"number"===typeof c.timeout)c=e+c.timeout;else switch(a){case 1:c=e+-1;break;case 2:c=e+250;break;case 5:c=e+1073741823;break;case 4:c=e+1E4;break;default:c=e+5E3}a={callback:b,priorityLevel:a,expirationTime:c,next:null,previous:null};if(null===J)J=a.next=a.previous=a,Q();else{b=null;e=J;do{if(e.expirationTime>c){b=e;break}e=e.next}while(e!==J);null===b?b=J:b===J&&(J=a,Q());c= | ||
b.previous;c.next=b.previous=a;a.next=b;a.previous=c}return a};exports.unstable_cancelCallback=function(a){var b=a.next;if(null!==b){if(b===a)J=null;else{a===J&&(J=b);var c=a.previous;c.next=b;b.previous=c}a.next=a.previous=null}};exports.unstable_wrapCallback=function(a){var b=L;return function(){var c=L,e=M;L=b;M=exports.unstable_now();try{return a.apply(this,arguments)}catch(l){throw Q(),l;}finally{L=c,M=e}}};exports.unstable_getCurrentPriorityLevel=function(){return L}; | ||
exports.unstable_shouldYield=function(){return!K&&(null!==J&&J.expirationTime<N||g())};exports.unstable_continueExecution=function(){null!==J&&Q()};exports.unstable_pauseExecution=function(){};exports.unstable_getFirstCallbackNode=function(){return J}; | ||
'use strict';Object.defineProperty(exports,"__esModule",{value:!0});var d=void 0,e=void 0,g=void 0,m=void 0,n=void 0;exports.unstable_now=void 0;exports.unstable_forceFrameRate=void 0;var p=Date,q="function"===typeof setTimeout?setTimeout:void 0,r="function"===typeof clearTimeout?clearTimeout:void 0,t="function"===typeof requestAnimationFrame?requestAnimationFrame:void 0,u="function"===typeof cancelAnimationFrame?cancelAnimationFrame:void 0,v=void 0,w=void 0; | ||
function x(a){v=t(function(b){r(w);a(b)});w=q(function(){u(v);a(exports.unstable_now())},100)}if("object"===typeof performance&&"function"===typeof performance.now){var y=performance;exports.unstable_now=function(){return y.now()}}else exports.unstable_now=function(){return p.now()}; | ||
if("undefined"===typeof window||"function"!==typeof MessageChannel){var z=null,A=null,B=function(){if(null!==z)try{var a=exports.unstable_now();z(!0,a);z=null}catch(b){throw setTimeout(B,0),b;}};d=function(a){null!==z?setTimeout(d,0,a):(z=a,setTimeout(B,0))};e=function(a,b){A=setTimeout(a,b)};g=function(){clearTimeout(A)};m=function(){return!1};n=exports.unstable_forceFrameRate=function(){}}else{"undefined"!==typeof console&&("function"!==typeof t&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"), | ||
"function"!==typeof u&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"));var C=null,D=!1,E=!1,F=-1,G=0,H=33,I=33,J=!1;m=function(){return exports.unstable_now()>=G};n=function(){};exports.unstable_forceFrameRate=function(a){0>a||125<a?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"):0<a?(I=Math.floor(1E3/a),J=!0):(I= | ||
33,J=!1)};var K=new MessageChannel,L=K.port2;K.port1.onmessage=function(){D=!1;if(null!==C){var a=exports.unstable_now(),b=0<G-a;try{C(b,a)?E||(E=!0,x(M)):C=null}catch(c){throw D=!0,L.postMessage(void 0),c;}}};var M=function(a){if(null!==C){x(M);var b=a-G+I;b<I&&H<I&&!J?(8>b&&(b=8),I=b<H?H:b):H=b;G=a+I;D||(D=!0,L.postMessage(void 0))}else E=!1};d=function(a){null===C&&(C=a,E||(E=!0,x(M)))};e=function(a,b){F=q(function(){a(exports.unstable_now())},b)};g=function(){r(F);F=-1}} | ||
var N=null,O=null,P=null,Q=3,R=!1,S=!1,T=!1; | ||
function U(a,b){var c=a.next;if(c===a)N=null;else{a===N&&(N=c);var f=a.previous;f.next=c;c.previous=f}a.next=a.previous=null;c=a.callback;f=Q;var l=P;Q=a.priorityLevel;P=a;try{var h=a.expirationTime<=b;switch(Q){case 1:var k=c(h);break;case 2:k=c(h);break;case 3:k=c(h);break;case 4:k=c(h);break;case 5:k=c(h)}}catch(aa){throw aa;}finally{Q=f,P=l}if("function"===typeof k)if(b=a.expirationTime,a={callback:k,priorityLevel:a.priorityLevel,startTime:a.startTime,expirationTime:b,next:null,previous:null}, | ||
null===N)N=a.next=a.previous=a;else{k=null;h=N;do{if(b<=h.expirationTime){k=h;break}h=h.next}while(h!==N);null===k?k=N:k===N&&(N=a);b=k.previous;b.next=k.previous=a;a.next=k;a.previous=b}}function V(a){if(null!==O&&O.startTime<=a){do{var b=O,c=b.next;if(b===c)O=null;else{O=c;var f=b.previous;f.next=c;c.previous=f}b.next=b.previous=null;W(b,b.expirationTime)}while(null!==O&&O.startTime<=a)}}function X(a){T=!1;V(a);S||(null!==N?(S=!0,d(Y)):null!==O&&e(X,O.startTime-a))} | ||
function Y(a,b){S=!1;T&&(T=!1,g());V(b);R=!0;try{if(!a)for(;null!==N&&N.expirationTime<=b;)U(N,b),b=exports.unstable_now(),V(b);else if(null!==N){do U(N,b),b=exports.unstable_now(),V(b);while(null!==N&&!m())}if(null!==N)return!0;null!==O&&e(X,O.startTime-b);return!1}finally{R=!1}}function Z(a){switch(a){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1E4;default:return 5E3}} | ||
function W(a,b){if(null===N)N=a.next=a.previous=a;else{var c=null,f=N;do{if(b<f.expirationTime){c=f;break}f=f.next}while(f!==N);null===c?c=N:c===N&&(N=a);b=c.previous;b.next=c.previous=a;a.next=c;a.previous=b}}var ba=n;exports.unstable_ImmediatePriority=1;exports.unstable_UserBlockingPriority=2;exports.unstable_NormalPriority=3;exports.unstable_IdlePriority=5;exports.unstable_LowPriority=4; | ||
exports.unstable_runWithPriority=function(a,b){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var c=Q;Q=a;try{return b()}finally{Q=c}};exports.unstable_next=function(a){switch(Q){case 1:case 2:case 3:var b=3;break;default:b=Q}var c=Q;Q=b;try{return a()}finally{Q=c}}; | ||
exports.unstable_scheduleCallback=function(a,b,c){var f=exports.unstable_now();if("object"===typeof c&&null!==c){var l=c.delay;l="number"===typeof l&&0<l?f+l:f;c="number"===typeof c.timeout?c.timeout:Z(a)}else c=Z(a),l=f;c=l+c;a={callback:b,priorityLevel:a,startTime:l,expirationTime:c,next:null,previous:null};if(l>f){c=l;if(null===O)O=a.next=a.previous=a;else{b=null;var h=O;do{if(c<h.startTime){b=h;break}h=h.next}while(h!==O);null===b?b=O:b===O&&(O=a);c=b.previous;c.next=b.previous=a;a.next=b;a.previous= | ||
c}null===N&&O===a&&(T?g():T=!0,e(X,l-f))}else W(a,c),S||R||(S=!0,d(Y));return a};exports.unstable_cancelCallback=function(a){var b=a.next;if(null!==b){if(a===b)a===N?N=null:a===O&&(O=null);else{a===N?N=b:a===O&&(O=b);var c=a.previous;c.next=b;b.previous=c}a.next=a.previous=null}};exports.unstable_wrapCallback=function(a){var b=Q;return function(){var c=Q;Q=b;try{return a.apply(this,arguments)}finally{Q=c}}};exports.unstable_getCurrentPriorityLevel=function(){return Q}; | ||
exports.unstable_shouldYield=function(){var a=exports.unstable_now();V(a);return null!==P&&null!==N&&N.startTime<=a&&N.expirationTime<P.expirationTime||m()};exports.unstable_requestPaint=ba;exports.unstable_continueExecution=function(){S||R||(S=!0,d(Y))};exports.unstable_pauseExecution=function(){};exports.unstable_getFirstCallbackNode=function(){return N}; |
{ | ||
"name": "scheduler", | ||
"version": "0.0.0-9ebe1768a", | ||
"version": "0.0.0-9f395904c", | ||
"description": "Cooperative scheduler for the browser environment.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -50,2 +50,9 @@ /** | ||
function unstable_requestPaint() { | ||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_requestPaint.apply( | ||
this, | ||
arguments | ||
); | ||
} | ||
function unstable_runWithPriority() { | ||
@@ -100,2 +107,9 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_runWithPriority.apply( | ||
function unstable_forceFrameRate() { | ||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_forceFrameRate.apply( | ||
this, | ||
arguments | ||
); | ||
} | ||
return Object.freeze({ | ||
@@ -106,2 +120,3 @@ unstable_now: unstable_now, | ||
unstable_shouldYield: unstable_shouldYield, | ||
unstable_requestPaint: unstable_requestPaint, | ||
unstable_runWithPriority: unstable_runWithPriority, | ||
@@ -114,2 +129,3 @@ unstable_next: unstable_next, | ||
unstable_getFirstCallbackNode: unstable_getFirstCallbackNode, | ||
unstable_forceFrameRate: unstable_forceFrameRate, | ||
get unstable_IdlePriority() { | ||
@@ -116,0 +132,0 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED |
@@ -50,2 +50,9 @@ /** | ||
function unstable_requestPaint() { | ||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_requestPaint.apply( | ||
this, | ||
arguments | ||
); | ||
} | ||
function unstable_runWithPriority() { | ||
@@ -94,2 +101,9 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_runWithPriority.apply( | ||
function unstable_forceFrameRate() { | ||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_forceFrameRate.apply( | ||
this, | ||
arguments | ||
); | ||
} | ||
return Object.freeze({ | ||
@@ -100,2 +114,3 @@ unstable_now: unstable_now, | ||
unstable_shouldYield: unstable_shouldYield, | ||
unstable_requestPaint: unstable_requestPaint, | ||
unstable_runWithPriority: unstable_runWithPriority, | ||
@@ -108,2 +123,3 @@ unstable_next: unstable_next, | ||
unstable_getFirstCallbackNode: unstable_getFirstCallbackNode, | ||
unstable_forceFrameRate: unstable_forceFrameRate, | ||
get unstable_IdlePriority() { | ||
@@ -110,0 +126,0 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED |
@@ -50,2 +50,9 @@ /** | ||
function unstable_requestPaint() { | ||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_requestPaint.apply( | ||
this, | ||
arguments | ||
); | ||
} | ||
function unstable_runWithPriority() { | ||
@@ -94,2 +101,9 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_runWithPriority.apply( | ||
function unstable_forceFrameRate() { | ||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_forceFrameRate.apply( | ||
this, | ||
arguments | ||
); | ||
} | ||
return Object.freeze({ | ||
@@ -100,2 +114,3 @@ unstable_now: unstable_now, | ||
unstable_shouldYield: unstable_shouldYield, | ||
unstable_requestPaint: unstable_requestPaint, | ||
unstable_runWithPriority: unstable_runWithPriority, | ||
@@ -108,2 +123,3 @@ unstable_next: unstable_next, | ||
unstable_getFirstCallbackNode: unstable_getFirstCallbackNode, | ||
unstable_forceFrameRate: unstable_forceFrameRate, | ||
get unstable_IdlePriority() { | ||
@@ -110,0 +126,0 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
126979
23
3162