UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

117 lines (115 loc) 4.46 kB
import { isPerformanceAPIAvailable } from './is-performance-api-available'; /** * Monitors if a pages enters a visibility state which will lead to * distorted duration measurements (where the measurement uses the * requestAnimationFrame api). */ export var getDistortedDurationMonitor = function getDistortedDurationMonitor() { if (typeof document === 'undefined') { return { distortedDuration: false, cleanup: function cleanup() {} }; } // If an editor is rendered when the document is not visible -- the callback passed to // requestAnimationFrame will not fire until the document becomes visible again. // // For the purposes of using performance measurement -- this behaviour means the events // which have been fired in the background are not useful -- and lead to other events // being hard to draw conclusions from. // // To mitigate this -- we detect this state -- and fire a separate callback when the // measurement has occurred when the render was in the background var distortedDuration = document.visibilityState !== 'visible'; function handleVisibilityChange() { if (document.visibilityState !== 'visible') { distortedDuration = true; } } // Ignored via go/ees005 // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners document.addEventListener('visibilitychange', handleVisibilityChange); return { distortedDuration: distortedDuration, /** * Cleans up the document visibility event listener */ cleanup: function cleanup() { // Ignored via go/ees005 // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners document.removeEventListener('visibilitychange', handleVisibilityChange); } }; }; /** * Measures time it takes to render a frame including -> style, paint, layout and composition. * * How does it work: * 1. We mark the beginning of a render with the `startMark` * 2. We schedule `requestAnimationFrame` callback for the next frame * 3. Framework (e.g. prosemirror) does its magic and mounts dom nodes "synchronously" * 4. When the main thread is unblocked our callback gets executed and onMeasureComplete is being called * * Why does it work: * | javascript (framework) | style | layout | paint | composite | javascript | ... * | startMark + scheduling rAF | | rAF callback, endMark */ export function measureRender( /** * Unique name for the measurement * * Important: if multiple measureRender events are fired at the same time * with the same measure name -- the result will not be correct. */ measureName, /** * Call back fired when the measurement completes. * * Note: when this function is called when the Document.visibilityState is not * visible -- the duration is likely to be misleading/inaccurate. This is due * to the measurements use of the `requestAnimationFrame` api which only fires * when the Document.visibilityState is visible. */ onMeasureComplete) { if (!isPerformanceAPIAvailable()) { return; } var startMark = "[START]: ".concat(measureName); var endMark = "[END]: ".concat(measureName); var startTime = performance.now(); performance.mark(startMark); var distortedDurationMonitor = getDistortedDurationMonitor(); requestAnimationFrame(function () { requestAnimationFrame(function () { performance.mark(endMark); distortedDurationMonitor.cleanup(); var duration = performance.now() - startTime; try { performance.measure(measureName, startMark, endMark); var entry = performance.getEntriesByName(measureName).pop(); if (!entry) { onMeasureComplete({ duration: duration, startTime: startTime, distortedDuration: distortedDurationMonitor.distortedDuration }); } else { onMeasureComplete({ duration: entry.duration, startTime: entry.startTime, distortedDuration: distortedDurationMonitor.distortedDuration }); } } catch (e) { onMeasureComplete({ duration: duration, startTime: startTime, distortedDuration: distortedDurationMonitor.distortedDuration }); } performance.clearMeasures(measureName); performance.clearMarks(startMark); performance.clearMarks(endMark); }); }); }