mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 23:35:18 +00:00
8637eba4b3
- remove poisondata stuff (cf5b5d29e5) - Bug 1218488 - clarify buffer ownership for nsICanvasRenderingContextInternal::GetBuffer; r=Bas,baku (dba4272b73) - Bug 1167215 - Re-apply CompositeUntil calls when we get a new batch of textures. r=roc (d57a8ac9e9) - Bug 1220923 - Make nsIntRegion a typedef for IntRegionTyped<UnknownUnits>. r=nical (a5f4e1e283) - Bug 1220898 - Make nsIntMargin a typedef for mozilla::gfx::IntMargin. r=nical (8bdb2f191a) - Bug 1205913 - Differentiate YCbCr, NV12 and RGB textures when drawing layer borders. r=nical (ff5d3dc7df) - Bug 1203376 - Honor filter region settings for lighting filters. r=mstange (8d0565e99a) - Bug 1220512 - ensure next frame status is updated before notifying MediaDecoder::PlaybackEnded. See bug 1220512 comment 1 for the detail. r=cpearce. (c2f434b5d4) - Bug 1207964 - Remove workaround from bug 1080461. r=jwwang (19bff98c14) - Bug 1199904: Only start decoding ahead after explicitly requesting data. r=gerald (8712637b49) - Bug 1197664: Report the total number of decoded frames. CLOSED TREE r=kentuckyfriedtakahe (422827d1af) - Bug 1178596: Reset frame size queue after flushing. r=cpearce (dd40ac4e6b) - Bug 1205911: P1. Cancel pending demux request when searching for next keyframe. r=edwin (8a03e6904f) - Bug 1188313: P1. Attempt to seek audio near video. r=cpearce (a8b6465d97) - Bug 1205911: P2. Ensure demuxer is reset before performing a seek. r=edwin (e93534a1d1) - bit of Bug 1185886: P2 (c2e89be4a3) - bit of Bug 1211443 (98fbb4a4a2) - Bug 1089586: Abort pending seeks. r=jwwang (e81f453204) - Bug 1195094: P1. Properly detects SPS changes for decoders requiring Annex B. r=cpearce (976febcd7c) - Bug 1195094: P2. Ensure TrackInfo object passed to constructor is never modified. r=cpearce (d5c15b2368) - Bug 1218577: Use only Blank PDM if enabled. r=kamidphish (a7e06549cc) - Bug 1210231 - Enable unencrypted <video> playback using Adobe's GMP for decoding. r=jya (7f80702b89) - Bug 1214932 - Add media.wmf.enabled pref. r=jya (8c183c0dd1) - Bug 1213173: Always use FFmpeg regardless of version. r=kentuckyfriedtakahe (e5af2c0c91) - Bug 1211787 - Improve the accuracy of MediaDecoderStateMachine::GetDecodedAudioDuration(). r=roc. (16d755c555) - Bug 1193614 - Schedule State Machine when VideoQueue() is low. r=cpearce (fe608da5fd) - Bug 1219984. Part 1 - remove EventPassMode::Both. In order to support multiple arguments, all arguments must be either moved or copied. r=kinetik. (2ee049008c) - Bug 1219984. Part 2 - add support for multiple arguments. r=kinetik. (1c70d5d69b) - Bug 1219974 - Add DisconnectIfExists() to MediaEventListener. r=kinetik. (43f6ae90b0) - Bug 1192109 - Fix insufficient includes in MediaEventSource.h. r=kinetik. (09eca240a1) - mData, aData (5e87f70853) - Bug 1219330 - Handle PlanaYCbCrImage::SetData failure. r=jya, jesup (144f3c266f) - Bug 1222308. Assume frames that are very old will never be composited. r=nical (1da9be2527) - space (45b67caa85) - Bug 1182426 - Set PlanarYCbCrImage's size in VP8TrackEncoder GTest. r=roc (d70a4d20c4) - Bug 1217080. Move recycling functionality into RecyclingPlanarYCbCrImage. r=nical (e2b2f650d7) - Bug 1216644 - part 1 - simple s/nsAutoArrayPtr/UniquePtr/ changes in gfx/; r=jrmuizel (d19bc51f94) - Bug 1216644 - part 2 - make gfxFontEntry::mUVSData a UniquePtr; r=jrmuizel (605a1bc6b1) - Bug 1216644 - part 3 - make BufferRecycleBin store UniquePtrs; r=jrmuizel (7781281266) - Add an RAII class to lock and unlock textures. (bug 1222863 part 1, r=nical) (abdec485f3) - Use RAII for more TextureClient locking cases. (bug 1222863 part 2, r=nical) (b35486ed3d) - Bug 1212288 - Make ImageContainer::AllocateProducerID callable on all threads; r=roc (fc569b8e48) - Bug 1140947 - Correct some logging in SourceBufferResource.cpp. r=cajbir (fcbb38056c) - Bug 1199573: [MSE] Properly handle partial media header received prior a discontinuity. r=gerald (f1cda54fa3) - Bug 1213726 - Remove AbstractMediaDecoder::HasInitializationData(). r=kinetik. (62f09f816e) - Bug 1034081 - Never seek before startTime. r=rillian Only adjust seek target up to startTime (44139b20ec) - Bug 1196353 - Use standard Xiph extradata format to pass headers from demuxers to decoders. r=jya (41f8dee5af) - Bug 1202332 - XiphExtradataToHeaders miscalculates final header length. r=derf (eec20f90f0) - Bug 1208799: [webm] Use first track found. r=kinetik (7196355e31) - Bug 1190472 - part 1 - improve MediaRawDataQueue's reference-counting behavior; r=kinetik (47b066b8e8) - Bug 1190472 - part 2 - delete unused MediaRawDataQueue::Push method; r=kinetik (f8246efe1b) - Bug 1190472 - part 3 - optimize pushing an entire queue onto MediaRawDataQueue; r=kinetik (045b389bf8) - Bug 1213024 - Comment the structured clone reading and writing, r=fitzgen (1b64f9f502) - Bug 1201620 - Make SavedFrame stacks structured cloneable; r=sfink (87e9b0a04b) - Bug 1221456 - Avoid C4819 warning spam. r=glandium (3dde3cb10a) - Bug 1213752 - IonMonkey: MIPS: Enable MIPS64 support. r=glandium (36da4cbd99) - Bug 1214175 - Make hash table manipulation infallible in Shape::fixupGetterSetterForBarrier() r=terrence (98e5e601ec) - Bug 1215337 - Cache slotSpan(), r=terrence (3e992f9f06) - No Bug - Include jswin.h in jsobj.cpp to unbreak windows non-unified bustage. (r=terrence over IRC) (92cba3254f) - Bug 1052139 - Make prototype-setting first create Object.prototype so that subsequent prototype chain-splicing will work correctly. r=bz (34a8d00d9f) - Bug 1172076 - Improve js::DumpBacktrace to include raw frame pointers and types. r=jandem (f11eb1763b) - Bug 1052139 - Change the boolean constant controlling whether the global object prototype chain is immutable, to enable immutable-prototype enforcement generally. r=duh (b7a1124feb) - Bug 1215363 - Fix a couple of OOM handling issues and make JS_sprintf funcions crash when passed illegal format strings r=terrence (c778891dd3) - Bug 1211331 - Ensure that GC slices are terminated such that we can safely iterate the heap. r=terrence (440308a333) - Bug 1224048 - Use stable hashing for the temporary tables in the JSON parser; r=jonco (7d058f44c9) - Bug 1215678 - Nuke cross compartment wrappers if we fail to add them to the wrapper map r=terrence (8e9e535a11) - Bug 1200732 - Use stable hashing for AutoCycleDetectorSet; r=jonco (68237bdea4) - more poisondata removal (6479b20220) - Bug 1218488 - clarify buffer ownership for nsICanvasRenderingContextInternal::GetBuffer; r=Bas,baku (ae89d929b2) - Bug 1207378 (Part 1) - Add support for a frame rect to Downscaler. r=tn (234866c32f) - Bug 1207378 (Part 2) - Use Downscaler to remove first-frame padding when downscaling GIFs. r=tn (5e995bdca2) - Bug 1207378 - Add FrameRect to non-skia version of BeginFrame. r=seth (939f0510eb) - Bug 1204394 (part 1) - Using StreamingLexer in the BMP decoder. r=seth. (77eaa1e125) - Bug 1204394 (part 2) - Add bmpsuite to the BMP reftests. r=seth. (237a902461) - Bug 1214476 - Remove unused code for encoding BMPv2 files. r=seth. (b8382357d7) - Bug 1213613 (part 1) - Formatting cleanups for nsBMPEncoder.h. r=seth. (4ee65bc173) - Bug 1213613 (part 2) - Move some BMP-related structs. r=seth. (15289784ce) - Bug 1215156 - move SetPixel* functions into nsBMPDecoder.cpp; r=seth (a143c843d4) - Bug 1215763 - part 1 - remove unnecessary nsAutoPtr.h includes; r=seth (f8a3a1f6b0) - Bug 1180715 (1/4) - Track image LoadTime to compare with file mtime. review=seth (bdbd25752c) - Bug 1180715 (2/4) - Provide a nsIFileURL interface for thumbnails. review=ttaubert (24c506569d) - Bug 1147562 - Update remaining callsites of newChannel before landing the shim in toolkit/ (r=gijs) (f7f2ab798f) - Bug 1163866 - Set originalURI correctly for moz-page-thumb:// channels. r=adw (3c7c97928e) - Bug 1180715 (3/4) - Drop parseURI from newChannel2. review=ttaubert (5b409e7d4b) - Bug 1180715 (4/4) - Use nsIURL methods instead of RegExp. review=ttaubert (1f7a026e06) - Bug 1209705 - Propagate the DrawResult for temporary surfaces to the caller in ClippedImage. r=tn (c5be1c7df4) - Bug 1215763 - part 2 - s/nsAutoPtr/UniquePtr/ in image/; r=seth (58c5da3bd2) - Bug 1215763 - part 3 - s/nsAutoArrayPtr/UniquePtr/ in nsBMPEncoder; r=seth (62bc939286) - Bug 1213613 (part 3) - Fix color-scaling of 16bpp BMP images. r=seth. (d06d511564) - Bug 1214072 (part 1) - Read BMP bitfields during metadata decoding. r=seth. (56cc2c02cd) - Bug 1214072 (part 2) - Implement transparency properly for BMP images. r=seth. (b6b07c2d90) - Bug 1218823 - use UniquePtr<> in preference to delete[] in image/; r=seth (5cd21f89a2) - Bug 1215334 (part 1) - Avoid creating a fake header for BMP files in ICO files. r=seth. (67506fc53e) - Bug 1215334 (part 2) - Avoid creating a fake header for BMP files in ICO files. r=seth. (cf6c3f4553) - spacing (b91c58ec87) - Bug 1196494 - part 1: remove unnecessary GetClientBounds call in CompositorParent. r=jrmuziel (685f49ae9c) - Bug 1180008 - Define gtk_window_get_window_type in mozgtk. r=karlt (e7ca7db4d3) - Bug 863512 - Fixing xul dnd panels for linux. r=enndeakin (5d0faaf0dc) - Bug 1195002 - draw to MozContainer window to allow GTK to draw decorations, r=karlt (7a84272fe9) - Bug 1210249 - don't mess with toplevel widget when client side decorations are enabled. r=karlt (464e9ce3fb) - bug 1180008 use mGdkWindow instead of finding it from gtk_widget_get_window(mShell) r=acomminos (024f8bd5a7) - bug 1180008 don't measure size of decorations for override-redirect windows r=acomminos (416ffda363) - Bug 1026803 part 1 - Factor out a common utility class for converting wrapping native times to TimeStamps; r=karlt (915ecdfc40) - Bug 1026803 part 2 - Add an assertion that SystemTimeConverter's template parameter is unsigned; r=karlt (68ccf1569d) - Bug 1026803 part 3 - Make some simplifications to SystemTimeConverter; r=karlt (c38719b7de) - Bug 1026803 part 4 - Convert GTK native event times to timestamps; r=karlt (74ef903992) - Bug 1026803 part 5 - Convert CurrentXXXTimeGetter classes from functors to helper classes; r=karlt (06d49e5965) - Bug 1026803 part 6 - Make SystemTimeConverter detect clock skew; r=karlt (d663ab72b2) - Bug 1026803 part 7 - Store the CurrentX11TimeGetter as a member object on nsWindow; r=karlt (f6a04e6e9c) - Bug 1026803 part 8 - Add ability for CurrentX11TimeGetter to perform an asynchronous request of the current time; r=karlt (961dab1d91) - Bug 1026803 part 8b - Factor out an IsTimeNewerThanTimeStamp method; r=karlt (1afb9f511a) - Bug 1026803 part 9 - Return DOMHighResTimeStamp objects from Event.timeStamp on Linux for Nightly/Aurora builds; r=karlt (975432dbd8) - Fix bustage from changeset 5c5dc6f367ac (bug 1026803) r=bustage on CLOSED TREE (c0be358f84) - fix bustage (e5e97018b6) - Bug 1196494 - part 2: only update nsWindow client offset when _NET_FRAME_EXTENTS property actually changes. r=eihrul (dd7e913d49) - Bug 1170342 - Disable XInput2 by default on GTK3. r=karlt (6a52d65a98) - Bug 1208904 - Fix a condition in nsWindow::SetNonClientMargins; r=roc (fe8c7b3e77) - Bug 1199892 - "Mouse cursor flickers in Flash object with wmode opaque/transparent". r=roc (6d50037e14) - Bug 1171101 - Remove pointer events and gesture scrolling dependencies. r=smaug (3aa5c89ea4) - Bug 978679. Implement touch events for GTK3. r=karlt (adafe58640) - Bug 1209774 - Transform from GDK coords to layout device pixels before calling DispatchEvent. r=karlt (4c81cf74ea) - Bug 1191293. Remove harmless assertion that is triggered by GTK3. r=masayuki (e0b9eb2c80) - Bug 978679. Convert GDK touch event coordinates properly. r=karlt (dfeae08f47) - restore some XP vs Vista differences, Bug 925599 bits (88ff57f06f) - Bug 1220392 - use UniquePtr<T[]> instead of nsAutoArrayPtr<T> in widget/; r=roc (35026aa4c9) - Bug 1214616 - Remove encoding conversion methods from nsPrimitiveHelpers. r=emk. (3a4bdbbc8e) - Bug 1216611 - add mozilla::MakeUniqueFallible and convert uses throughout the tree; r=Waldo (8672c2e3d9) - Bug 1216964 - remove nsAutoArrayPtr use from ActorsParent; r=khuey (ddff59241a) - Bug 1219903 - use UniquePtr<T[]> instead of delete[] calls in layout/generic/; r=dholbert (8b96067a6e) - Bug 1220190 - use UniquePtr<T[]> instead of delete[] calls in layout/xul/; r=dholbert (065b3c521c) - Bug 1220714 - use UniquePtr<T[]> instead of nsAutoArrayPtr<T> in layout/; r=dholbert (6a8245751c) - Bug 1221550 - use UniquePtr<T[]> instead of nsAutoArrayPtr<T> in intl/; r=smontagu (67868889e2) - Bug 1232374 - remove nsAutoArrayPtr usages from toolkit/; r=froydnj (d65586df26) - Bug 1170522 - expose whether or not we're in tablet mode to xul/js/css, r=jimm,ted (5cb134a8d4) - bug 1163872 - Fix a unified build issue in nsXPLookAndFeel.cpp. r=jimm (925e38abed) - bug 1217602 - remove nsIPKIParamBlock r=Cykesiopka (b5f0fc8dfd) - missing bit of old bug (fdcb4fe143) - Bug 1221453 - Use ObjDirPaths for GENERATED_INCLUDES and merge with LOCAL_INCLUDES. r=gps (a0537fff83) - Bug 1194948 - Build gfx/ipc in unified mode and mark as FAIL_ON_WARNINGS. r=BenWa (c88b356ac8) - Bug 815952 - Stop clearing clipboard data originating from a private window after closing private windows. r=ehsan (b94fea061d) - Bug 943296 - widget/gtk/nsDragService.cpp should assume Gtk uses UTF-8. r=karlt. (3613e87354) - Bug 1186661 - Draw drag and drop alpha pixmap correctly on GTK3. r=karlt (ca884af03b) - Bug 983843 - Switch to GtkOffscreenWindow for drag source widget, fixing ghost tabs on some GTK versions. r=karlt (ab56f9d764) - bug 1216916 clean up when InvokeDragSession() fails r=roc (30c811c33e) - Bug 1198128 - Fix -Wshadow warnings in widget/gtk. r=karlt (06bc60349e) - Merge branch 'dev' of https://github.com/rmottola/Arctic-Fox into dev (c79378b7c9) - Bug 1206915 - Move dumping of compositor textures under its own environment variable. r=mattwoodrow (b8ba4f0fbf) - Bug 1213007 - Part 1. Implementing gfxCrash. r=dvander (e307f9d543) - Bug 1208661 - Show display list and layer textures in-line in the HTML paint dump. r=BenWa (7047c68964) - Relax the driver crash guard on nightly and e10s builds. (bug 1200825, r=jgilbert) (1dd81b1257) - Bug 1214802 - gfxEnv - consolidate environment variables used by the graphics code in one place. r=botond (afb61356c6) - Bug 1217192 - Use gfxCriticalNote where we're already using the non-default construction parameter. r=mchang (d04ca17de5) - addback some crash stuff (8a78973a71) - Fix layers.acceleration.force-enabled not working. (bug 1212659, r=jrmuizel) (5eb85d8f64) - Bug 1142516 - Improve assertions and logging on the compositor side. r=Bas (cca63735e5) - Bug 1194335. Use a StackArray for RECTS so we see them in the minidumps. r=mattwoodrow (1a83a134e8) - Bug 1192058 - For DXGI_PRESENT_PARAMETERS, set pDirtyRects to nullptr if DirtyRectsCount == 0. r=BenWa (f78ff0df24) - Bug 1204922 - More information about crashes. r=bas (53cbd02c12) - Bug 1222033 - Rename gfxCrash to gfxDevCrash. r=jrmuizel (32351d0bc7) - Bug 1209812 (part 1) - Remove casts between cairo_format_t and gfxImageFormat. r=nical. (c1bc5cd74c) - Bug 1209812 (part 2) - Remove gfxImageFormat::A1. r=nical. (99f665ad80) - Bug 1182426 - Sort includes in VP8TrackEncoder.cpp alphabetically. r=roc (5f10334ba8) - Bug 1182426 - Don't try to encode new frames of a size other than the initial in VP8TrackEncoder. r=roc (8fb0b8f0d9) - Bug 1182426 - Flatten YUV formats conversion code in VP8TrackEncoder. r=roc (0853d098f7) - Bug 1182426 - Convert non-PlanarYCbCRImages in VP8TrackEncoder. r=roc (d2d78fa94a) - Bug 1204106 - Use correct alpha blend modes for OVER in CompositorOGL. r=jrmuizel (5cc211b9d6) - Bug 1207326 - Part 1: Correct projection clipping rectangle,r=matt.woodrow (8329afb6a7) - Bug 1207326 - Part 2: Add reftest,r=jmuizelaar (d17d6c5d4f) - Bug 1209446 - Make sure mFrameInProgress flag is set to true only when we actually begin drawing new frame. r=nical (4ff48c4149) - Disable screen and multiply mix-blend-mode support in the D3D11 and OGL compositors. (bug 1135271, r=mattwoodrow) (9b4c11a289) - Bug 1210189 - Use nsScreenGonk in nsWindow::StartRemoteDrawing() r=mwu (2653a33972) - Bug 1210514 - Fix color inversion when BasicCompositor is used on gonk r=nical (141fee3bfb) - Bug 1209812 (part 3) - Rename SurfaceFormat::R5G6B5 as R5G6B5_UINT16. r=Bas. (915e7eaba3) - Bug 1209812 (part 4) - Add comments to SurfaceFormat. r=jrmuizel,Bas. (ef1977582f) - Bug 1209812 (part 5) - Add endian-neutral variants to SurfaceFormat. r=nical,Bas. (93c49df8c5) - Bug 1171671 - Simplify Boot Animation control r=mwu (94c4f89b45) - Bug 1210182 - Implement GrallocTextureHostBasic r=nical (4e5ea5b92c) - Bug 1209812 (follow-up) - Android bustage fix on a CLOSED TREE. r=me. (8b2fa6268d) - Bug 1178513 - Added RGBA8888 to RGB565 converter. r=mattwoodrow (8ba5dbd3c9) - Bug 1160689 - thumbnail image corruption on certain videos. r=sotaro (278a2e29f6) - Bug 1204922: When ResizeBuffers fails, make no attempt to do subsequent paints until it succeeds again. r=milan (0c040d8228) - Bug 1215027 - Fix EndFrameForExternalComposition() r=nical (bab4690e54) - Bug 1215364 - Implement BasicCOmpositor::EndFrameForExternalComposition r=nical (c8b9c7bfb9) - Bug 1213968 - Renew the surface on iOS when resuming the compositor r=kats (73489dc21c) - Bug 1201318 - Factor out AddFamily. r=jdaggett (3f2556b8b4) - Bug 1201318 - revise OSX system font handling. r=mstange (f8a8f5f562) - Bug 1163877 - Part 1: Add storage for other FontFaceSets a FontFace is in. r=jdaggett (ab3a16b597) - Bug 1163877 - Part 2: Allow FontFaces to be added to multiple FontFaceSets. r=jdaggett (9b2dd7e5c9) - Bug 1163877 - Part 3: Update state on, and reflow documents for, all FontFaceSets that contain a FontFace whose user font entry updated. r=jdaggett (215db30569) - Bug 1192986 Fix test_interfaces.html to expect Cache API and font loading to be released. r=ehsan (8db9ef1df8) - Bug 1193019 - Rename CSSFontFaceLoadEvent to FontFaceSetLoadEvent. r=khuey (53f373c53d) - Bug 1163877 - Part 4: Tests. r=jdaggett (c6053ca8b4) - Bug 1180415 - initialize downloadable font pattern from FTFace. r=karlt (04aa59ba79) - Bug 1163491 - map local fontnames to fontconfig patterns. r=karlt (ad10ebde2a) - Bug 543715 p1 - distinguish between italic and oblique. r=jfkthame (4c9a0abf64) - Bug 543715 p2 - italic/oblique reftests. r=jfkthame (90fb927148) - bug 1178733 - enable APZ for iOS. r=kats (e41702d9cd) - Bug 1169956. Backout bug 1073209 for tiled image regressions on OS X. r=jrmuizel (a80a29aaa3) - Bug 1073209 - Eliminate usage of CreateSamplingRestrictedDrawable on d2d backends. r=jrmuizel (9ac8781a52) - Bug 1204136 - Align DisplayPort on non-tiling platform. r=botond (1d8be17663) - Bug 1122918 - Put the logical values for 'float' and 'clear' behind a pref, and enable them only on nightly builds and for B2G. r=heycam (a428b34d66) - Bug 1183484 - Cycle collect FontFaceSetIterator. r=bzbarsky (dc49f3f098) - Bug 1027579 - Do not load fonts from the cache if LOAD_BYPASS_CACHE is set. r=jfkthame r=bz (6cf20c9119) - Bug 1201318 - Use nsAutoReleasePool from nsCocoaUtils.h. r=jdaggett (bfe5f05086) - Bug 1201403 - streamline MacOSFontEntry::HasFontTable implementation. r=jfkthame (da06064b86) - Bug 1165611 - fix font smoothing under Linux. r=karlt (166a96603b) - Bug 1160506 - support intra-family font fallback. r=heycam (f56f0507d0) - Bug 1165693 - Cache the result of calling FcConfigSubstitute for our sentinel font name, to make gfxFcPlatformFontList::FindFamily less expensive. r=jdaggett (09ace74bc1) - Bug 1165693 - patch 2 - Cache family-name lookups in gfxFcPlatformFontList::FindFamily, to avoid repeating expensive calls to FcConfigSubstitute. r=jdaggett (ab34bf45c0) - Bug 1174946 - Back out the (incorrectly-implemented) caching of sentinelFirstFamily from bug 1165693, which should be largely overshadowed by the mFcSubstituteCache anyway. r=jdaggett (9b7784b7a6) - Bug 1165766 - Crash in AddFontSetFamilies() r=jtd (13cba8e6c7) - Bug 1170421 - return first font suggested by fontconfig as the default font. r=karlt (426c6bd348) - Bug 1008169 - Font selection and font size dropdowns are reacting very slowly on press up/down,r=jaws (8e395026e1) - Bug 1166161 - Display available font from font.name-list.{family}.{lang} as fallback default font, instead of empty string. (045855761a) - Bug 1187680 - Use NSVisualEffectMaterialMenu for menus if it's available. r=smichaud (0cede2f295) - Bug 1190257. Use the previous vsync timestamp on windows 10. r=jrmuizel (7cf7e2644f) - Bug 1220699 - Add telemetry probe to measure vsync latency in the parent refresh driver. r=avih (347936dffb) - Bug 1221674 - Add telemetry probe in the content process to measure the time between refresh driver ticks. r=kats (1d1b885f10) - Bug 1198362 - Delete PreciseRefreshDriverTimer. r=roc (8fdcca758f) - Bug 1197898 - Delete vsync refresh driver preference. r=kats (eab85ba8dd) - Bug 1210250. Fallback to GDI fonts with a skia backend. r=jwatt (8c374b4bd3) - Bug 1208927 - Initialize queryD3DKMTStatistics so that it can't be accessed uninitialized; r=jrmuizel (69c6781a82) - Bug 1144946 - Delete PreciseRefreshDriverTimerWindowsDwmVsync refresh driver timer. r=roc (a78ccb3d42) - Bug 1187784 (part 4) - Replace nsBaseHashtable::EnumerateRead() calls in layout/ with iterators. r=heycam. (6a09016e6d) - Bug 1187784 (part 3) - Replace nsBaseHashtable::EnumerateRead() calls in layout/ with iterators. r=heycam. (1ce8abd863) - Bug 1187784 (part 2) - Replace nsBaseHashtable::EnumerateRead() calls in layout/ with iterators. r=heycam. (cd98be2f19) - Bug 1187784 (part 1) - Replace nsBaseHashtable::EnumerateRead() calls in layout/ with iterators. r=heycam. (09022514a9) - Bug 1217230 - Set mNeedToRecomputeVisibility true only when style or layout flush. r=seth (8f3edd57c0) - Bug 1167281 - sort scalable fonts first when resolving generic families under Linux. r=karlt (e2ecb89f31) - Bug 1186875 - check if FcFontSort returns non-null. r=jtd (087905da51) - Bug 1218617 - Invalidate whole widget area after external composition r=mattwoodrow (3901f416f5) - Bug 1153499 - Enable push and sw prefs. r=dougt,ehsan (ca0f3a105d) - Bug 1141415 - add expire setting of permission to SpecialPowers. r=jmaher (372bc0c930) - partial Bug 1196665 - Add originAttributes into SpecialPowers. r=bholley (b8407a1bcc) - Bug 1213151 - Part 1: Add a SpecialPowers API for cleaning up the STS data that works in both e10s and non-e10s modes; r=jdm (04bba17fd3) - Bug 1213151 - Part 2: Use SpecialPowers.cleanUpSTSData() in a few tests; r=jdm (cce5f23dac) - Bug 1214593 - Remove service worker periodic updater. r=ehsan (87cfebcd0d) - fix tests, fix spaces (9cba57e7ff) - Bug 1207499 - Part 10: Remove use of expression closure from testing/. r=jmaher (652faa8963) - Bug 1204154 - Clean up jar manifests that needlessly specify the source file. r=dao (f116e33bed) - Bug 1222943 (part 1) - Change Touch::mRadius from nsIntPoint to LayoutDeviceIntPoint. r=kats. (dcc6c15797) - Bug 1222943 (part 2) - Remove an unnecessary call to ToUnknownSize(). r=botond. (08f644f194) - Bug 1220925 - Event::GetScreenCoords should return CSSIntPoint instead of LayoutDevicePoint. r=botond (4e4f54e8e7) - Bug 943294 - Leave dealing with legacy codepages for clipboard data to Windows itself. r=jmathies. (124ecbfa3e) - Bug 1243507 - Reimplement non-Unicode clipboard formats as bug 943294 broke drag and drop between some Unicode-unaware apps. r=jimm (02edc31ed9) - ported Bug 1254980 - Ensure that text/html is still written to the clipboard. r=enndeakin a=sylvestre (9cd4c0e41a) - nsWindow: build fix (1c3e798a89)
2036 lines
62 KiB
C++
2036 lines
62 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* Code to notify things that animate before a refresh, at an appropriate
|
|
* refresh rate. (Perhaps temporary, until replaced by compositor.)
|
|
*
|
|
* Chrome and each tab have their own RefreshDriver, which in turn
|
|
* hooks into one of a few global timer based on RefreshDriverTimer,
|
|
* defined below. There are two main global timers -- one for active
|
|
* animations, and one for inactive ones. These are implemented as
|
|
* subclasses of RefreshDriverTimer; see below for a description of
|
|
* their implementations. In the future, additional timer types may
|
|
* implement things like blocking on vsync.
|
|
*/
|
|
|
|
#ifdef XP_WIN
|
|
#include <windows.h>
|
|
// mmsystem isn't part of WIN32_LEAN_AND_MEAN, so we have
|
|
// to manually include it
|
|
#include <mmsystem.h>
|
|
#include "WinUtils.h"
|
|
#endif
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/AutoRestore.h"
|
|
#include "nsHostObjectProtocolHandler.h"
|
|
#include "nsRefreshDriver.h"
|
|
#include "nsITimer.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "nsAutoPtr.h"
|
|
#include "nsIDocument.h"
|
|
#include "jsapi.h"
|
|
#include "nsContentUtils.h"
|
|
#include "mozilla/PendingAnimationTracker.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "nsViewManager.h"
|
|
#include "GeckoProfiler.h"
|
|
#include "nsNPAPIPluginInstance.h"
|
|
#include "nsPerformance.h"
|
|
#include "mozilla/dom/WindowBinding.h"
|
|
#include "RestyleManager.h"
|
|
#include "Layers.h"
|
|
#include "imgIContainer.h"
|
|
#include "mozilla/dom/ScriptSettings.h"
|
|
#include "nsDocShell.h"
|
|
#include "nsISimpleEnumerator.h"
|
|
#include "nsJSEnvironment.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "gfxPrefs.h"
|
|
#include "BackgroundChild.h"
|
|
#include "mozilla/ipc/PBackgroundChild.h"
|
|
#include "nsIIPCBackgroundChildCreateCallback.h"
|
|
#include "mozilla/layout/VsyncChild.h"
|
|
#include "VsyncSource.h"
|
|
#include "mozilla/VsyncDispatcher.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/unused.h"
|
|
#include "mozilla/TimelineConsumers.h"
|
|
#include "nsAnimationManager.h"
|
|
|
|
#ifdef MOZ_NUWA_PROCESS
|
|
#include "ipc/Nuwa.h"
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::widget;
|
|
using namespace mozilla::ipc;
|
|
using namespace mozilla::layout;
|
|
|
|
static PRLogModuleInfo *gLog = nullptr;
|
|
#define LOG(...) MOZ_LOG(gLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
|
|
#define DEFAULT_THROTTLED_FRAME_RATE 1
|
|
#define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
|
|
// after 10 minutes, stop firing off inactive timers
|
|
#define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
|
|
|
|
namespace mozilla {
|
|
|
|
/*
|
|
* The base class for all global refresh driver timers. It takes care
|
|
* of managing the list of refresh drivers attached to them and
|
|
* provides interfaces for querying/setting the rate and actually
|
|
* running a timer 'Tick'. Subclasses must implement StartTimer(),
|
|
* StopTimer(), and ScheduleNextTick() -- the first two just
|
|
* start/stop whatever timer mechanism is in use, and ScheduleNextTick
|
|
* is called at the start of the Tick() implementation to set a time
|
|
* for the next tick.
|
|
*/
|
|
class RefreshDriverTimer {
|
|
public:
|
|
RefreshDriverTimer()
|
|
: mLastFireEpoch(0)
|
|
{
|
|
}
|
|
|
|
virtual ~RefreshDriverTimer()
|
|
{
|
|
NS_ASSERTION(mRefreshDrivers.Length() == 0, "Should have removed all refresh drivers from here by now!");
|
|
}
|
|
|
|
virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
|
|
{
|
|
LOG("[%p] AddRefreshDriver %p", this, aDriver);
|
|
|
|
NS_ASSERTION(!mRefreshDrivers.Contains(aDriver), "AddRefreshDriver for a refresh driver that's already in the list!");
|
|
mRefreshDrivers.AppendElement(aDriver);
|
|
|
|
if (mRefreshDrivers.Length() == 1) {
|
|
StartTimer();
|
|
}
|
|
}
|
|
|
|
virtual void RemoveRefreshDriver(nsRefreshDriver* aDriver)
|
|
{
|
|
LOG("[%p] RemoveRefreshDriver %p", this, aDriver);
|
|
|
|
NS_ASSERTION(mRefreshDrivers.Contains(aDriver), "RemoveRefreshDriver for a refresh driver that's not in the list!");
|
|
mRefreshDrivers.RemoveElement(aDriver);
|
|
|
|
if (mRefreshDrivers.Length() == 0) {
|
|
StopTimer();
|
|
}
|
|
}
|
|
|
|
TimeStamp MostRecentRefresh() const { return mLastFireTime; }
|
|
int64_t MostRecentRefreshEpochTime() const { return mLastFireEpoch; }
|
|
|
|
void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
for (nsRefreshDriver* driver : mRefreshDrivers) {
|
|
aNewTimer->AddRefreshDriver(driver);
|
|
driver->mActiveTimer = aNewTimer;
|
|
}
|
|
mRefreshDrivers.Clear();
|
|
|
|
aNewTimer->mLastFireEpoch = mLastFireEpoch;
|
|
aNewTimer->mLastFireTime = mLastFireTime;
|
|
}
|
|
|
|
protected:
|
|
virtual void StartTimer() = 0;
|
|
virtual void StopTimer() = 0;
|
|
virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
|
|
|
|
/*
|
|
* Actually runs a tick, poking all the attached RefreshDrivers.
|
|
* Grabs the "now" time via JS_Now and TimeStamp::Now().
|
|
*/
|
|
void Tick()
|
|
{
|
|
int64_t jsnow = JS_Now();
|
|
TimeStamp now = TimeStamp::Now();
|
|
Tick(jsnow, now);
|
|
}
|
|
|
|
/*
|
|
* Tick the refresh drivers based on the given timestamp.
|
|
*/
|
|
void Tick(int64_t jsnow, TimeStamp now)
|
|
{
|
|
ScheduleNextTick(now);
|
|
|
|
mLastFireEpoch = jsnow;
|
|
mLastFireTime = now;
|
|
|
|
LOG("[%p] ticking drivers...", this);
|
|
nsTArray<RefPtr<nsRefreshDriver> > drivers(mRefreshDrivers);
|
|
// RD is short for RefreshDriver
|
|
profiler_tracing("Paint", "RD", TRACING_INTERVAL_START);
|
|
for (nsRefreshDriver* driver : drivers) {
|
|
// don't poke this driver if it's in test mode
|
|
if (driver->IsTestControllingRefreshesEnabled()) {
|
|
continue;
|
|
}
|
|
|
|
TickDriver(driver, jsnow, now);
|
|
}
|
|
profiler_tracing("Paint", "RD", TRACING_INTERVAL_END);
|
|
LOG("[%p] done.", this);
|
|
}
|
|
|
|
static void TickDriver(nsRefreshDriver* driver, int64_t jsnow, TimeStamp now)
|
|
{
|
|
LOG(">> TickDriver: %p (jsnow: %lld)", driver, jsnow);
|
|
driver->Tick(jsnow, now);
|
|
}
|
|
|
|
int64_t mLastFireEpoch;
|
|
TimeStamp mLastFireTime;
|
|
TimeStamp mTargetTime;
|
|
|
|
nsTArray<RefPtr<nsRefreshDriver> > mRefreshDrivers;
|
|
|
|
// useful callback for nsITimer-based derived classes, here
|
|
// bacause of c++ protected shenanigans
|
|
static void TimerTick(nsITimer* aTimer, void* aClosure)
|
|
{
|
|
RefreshDriverTimer *timer = static_cast<RefreshDriverTimer*>(aClosure);
|
|
timer->Tick();
|
|
}
|
|
};
|
|
|
|
/*
|
|
* A RefreshDriverTimer that uses a nsITimer as the underlying timer. Note that
|
|
* this is a ONE_SHOT timer, not a repeating one! Subclasses are expected to
|
|
* implement ScheduleNextTick and intelligently calculate the next time to tick,
|
|
* and to reset mTimer. Using a repeating nsITimer gets us into a lot of pain
|
|
* with its attempt at intelligent slack removal and such, so we don't do it.
|
|
*/
|
|
class SimpleTimerBasedRefreshDriverTimer :
|
|
public RefreshDriverTimer
|
|
{
|
|
public:
|
|
/*
|
|
* aRate -- the delay, in milliseconds, requested between timer firings
|
|
*/
|
|
explicit SimpleTimerBasedRefreshDriverTimer(double aRate)
|
|
{
|
|
SetRate(aRate);
|
|
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
|
}
|
|
|
|
virtual ~SimpleTimerBasedRefreshDriverTimer()
|
|
{
|
|
StopTimer();
|
|
}
|
|
|
|
// will take effect at next timer tick
|
|
virtual void SetRate(double aNewRate)
|
|
{
|
|
mRateMilliseconds = aNewRate;
|
|
mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
|
|
}
|
|
|
|
double GetRate() const
|
|
{
|
|
return mRateMilliseconds;
|
|
}
|
|
|
|
protected:
|
|
|
|
virtual void StartTimer()
|
|
{
|
|
// pretend we just fired, and we schedule the next tick normally
|
|
mLastFireEpoch = JS_Now();
|
|
mLastFireTime = TimeStamp::Now();
|
|
|
|
mTargetTime = mLastFireTime + mRateDuration;
|
|
|
|
uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
|
|
mTimer->InitWithFuncCallback(TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT);
|
|
}
|
|
|
|
virtual void StopTimer()
|
|
{
|
|
mTimer->Cancel();
|
|
}
|
|
|
|
double mRateMilliseconds;
|
|
TimeDuration mRateDuration;
|
|
RefPtr<nsITimer> mTimer;
|
|
};
|
|
|
|
/*
|
|
* A refresh driver that listens to vsync events and ticks the refresh driver
|
|
* on vsync intervals. We throttle the refresh driver if we get too many
|
|
* vsync events and wait to catch up again.
|
|
*/
|
|
class VsyncRefreshDriverTimer : public RefreshDriverTimer
|
|
{
|
|
public:
|
|
VsyncRefreshDriverTimer()
|
|
: mVsyncChild(nullptr)
|
|
{
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
mVsyncObserver = new RefreshDriverVsyncObserver(this);
|
|
RefPtr<mozilla::gfx::VsyncSource> vsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
|
|
MOZ_ALWAYS_TRUE(mVsyncDispatcher = vsyncSource->GetRefreshTimerVsyncDispatcher());
|
|
mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
|
|
}
|
|
|
|
explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild)
|
|
: mVsyncChild(aVsyncChild)
|
|
{
|
|
MOZ_ASSERT(!XRE_IsParentProcess());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mVsyncChild);
|
|
mVsyncObserver = new RefreshDriverVsyncObserver(this);
|
|
mVsyncChild->SetVsyncObserver(mVsyncObserver);
|
|
}
|
|
|
|
private:
|
|
// Since VsyncObservers are refCounted, but the RefreshDriverTimer are
|
|
// explicitly shutdown. We create an inner class that has the VsyncObserver
|
|
// and is shutdown when the RefreshDriverTimer is deleted. The alternative is
|
|
// to (a) make all RefreshDriverTimer RefCounted or (b) use different
|
|
// VsyncObserver types.
|
|
class RefreshDriverVsyncObserver final : public VsyncObserver
|
|
{
|
|
public:
|
|
explicit RefreshDriverVsyncObserver(VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer)
|
|
: mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer)
|
|
, mRefreshTickLock("RefreshTickLock")
|
|
, mRecentVsync(TimeStamp::Now())
|
|
, mLastChildTick(TimeStamp::Now())
|
|
, mVsyncRate(TimeDuration::Forever())
|
|
, mProcessedVsync(true)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override
|
|
{
|
|
// IMPORTANT: All paths through this method MUST hold a strong ref on
|
|
// |this| for the duration of the TickRefreshDriver callback.
|
|
|
|
if (!NS_IsMainThread()) {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
// Compress vsync notifications such that only 1 may run at a time
|
|
// This is so that we don't flood the refresh driver with vsync messages
|
|
// if the main thread is blocked for long periods of time
|
|
{ // scope lock
|
|
MonitorAutoLock lock(mRefreshTickLock);
|
|
mRecentVsync = aVsyncTimestamp;
|
|
if (!mProcessedVsync) {
|
|
return true;
|
|
}
|
|
mProcessedVsync = false;
|
|
}
|
|
|
|
nsCOMPtr<nsIRunnable> vsyncEvent =
|
|
NS_NewRunnableMethodWithArg<TimeStamp>(this,
|
|
&RefreshDriverVsyncObserver::TickRefreshDriver,
|
|
aVsyncTimestamp);
|
|
NS_DispatchToMainThread(vsyncEvent);
|
|
} else {
|
|
RefPtr<RefreshDriverVsyncObserver> kungFuDeathGrip(this);
|
|
TickRefreshDriver(aVsyncTimestamp);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
mVsyncRefreshDriverTimer = nullptr;
|
|
}
|
|
|
|
void OnTimerStart()
|
|
{
|
|
if (!XRE_IsParentProcess()) {
|
|
mLastChildTick = TimeStamp::Now();
|
|
}
|
|
}
|
|
|
|
private:
|
|
virtual ~RefreshDriverVsyncObserver() {}
|
|
|
|
void RecordTelemetryProbes(TimeStamp aVsyncTimestamp)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
#ifndef ANDROID /* bug 1142079 */
|
|
if (XRE_IsParentProcess()) {
|
|
TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
|
|
Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS,
|
|
vsyncLatency.ToMilliseconds());
|
|
} else if (mVsyncRate != TimeDuration::Forever()) {
|
|
TimeDuration contentDelay = (TimeStamp::Now() - mLastChildTick) - mVsyncRate;
|
|
Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS,
|
|
contentDelay.ToMilliseconds());
|
|
} else {
|
|
// Request the vsync rate from the parent process. Might be a few vsyncs
|
|
// until the parent responds.
|
|
mVsyncRate = mVsyncRefreshDriverTimer->mVsyncChild->GetVsyncRate();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void TickRefreshDriver(TimeStamp aVsyncTimestamp)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
RecordTelemetryProbes(aVsyncTimestamp);
|
|
if (XRE_IsParentProcess()) {
|
|
MonitorAutoLock lock(mRefreshTickLock);
|
|
aVsyncTimestamp = mRecentVsync;
|
|
mProcessedVsync = true;
|
|
} else {
|
|
mLastChildTick = TimeStamp::Now();
|
|
}
|
|
MOZ_ASSERT(aVsyncTimestamp <= TimeStamp::Now());
|
|
|
|
// We might have a problem that we call ~VsyncRefreshDriverTimer() before
|
|
// the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
|
|
// before use.
|
|
if (mVsyncRefreshDriverTimer) {
|
|
mVsyncRefreshDriverTimer->RunRefreshDrivers(aVsyncTimestamp);
|
|
}
|
|
}
|
|
|
|
// VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
|
|
// be always available before Shutdown(). We can just use the raw pointer
|
|
// here.
|
|
VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer;
|
|
Monitor mRefreshTickLock;
|
|
TimeStamp mRecentVsync;
|
|
TimeStamp mLastChildTick;
|
|
TimeDuration mVsyncRate;
|
|
bool mProcessedVsync;
|
|
}; // RefreshDriverVsyncObserver
|
|
|
|
virtual ~VsyncRefreshDriverTimer()
|
|
{
|
|
if (XRE_IsParentProcess()) {
|
|
mVsyncDispatcher->SetParentRefreshTimer(nullptr);
|
|
mVsyncDispatcher = nullptr;
|
|
} else {
|
|
// Since the PVsyncChild actors live through the life of the process, just
|
|
// send the unobserveVsync message to disable vsync event. We don't need
|
|
// to handle the cleanup stuff of this actor. PVsyncChild::ActorDestroy()
|
|
// will be called and clean up this actor.
|
|
Unused << mVsyncChild->SendUnobserve();
|
|
mVsyncChild->SetVsyncObserver(nullptr);
|
|
mVsyncChild = nullptr;
|
|
}
|
|
|
|
// Detach current vsync timer from this VsyncObserver. The observer will no
|
|
// longer tick this timer.
|
|
mVsyncObserver->Shutdown();
|
|
mVsyncObserver = nullptr;
|
|
}
|
|
|
|
virtual void StartTimer() override
|
|
{
|
|
mLastFireEpoch = JS_Now();
|
|
mLastFireTime = TimeStamp::Now();
|
|
|
|
if (XRE_IsParentProcess()) {
|
|
mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
|
|
} else {
|
|
Unused << mVsyncChild->SendObserve();
|
|
mVsyncObserver->OnTimerStart();
|
|
}
|
|
}
|
|
|
|
virtual void StopTimer() override
|
|
{
|
|
if (XRE_IsParentProcess()) {
|
|
mVsyncDispatcher->SetParentRefreshTimer(nullptr);
|
|
} else {
|
|
Unused << mVsyncChild->SendUnobserve();
|
|
}
|
|
}
|
|
|
|
virtual void ScheduleNextTick(TimeStamp aNowTime) override
|
|
{
|
|
// Do nothing since we just wait for the next vsync from
|
|
// RefreshDriverVsyncObserver.
|
|
}
|
|
|
|
void RunRefreshDrivers(TimeStamp aTimeStamp)
|
|
{
|
|
int64_t jsnow = JS_Now();
|
|
TimeDuration diff = TimeStamp::Now() - aTimeStamp;
|
|
int64_t vsyncJsNow = jsnow - diff.ToMicroseconds();
|
|
Tick(vsyncJsNow, aTimeStamp);
|
|
}
|
|
|
|
RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
|
|
// Used for parent process.
|
|
RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
|
|
// Used for child process.
|
|
// The mVsyncChild will be always available before VsncChild::ActorDestroy().
|
|
// After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
|
|
RefPtr<VsyncChild> mVsyncChild;
|
|
}; // VsyncRefreshDriverTimer
|
|
|
|
/**
|
|
* Since the content process takes some time to setup
|
|
* the vsync IPC connection, this timer is used
|
|
* during the intial startup process.
|
|
* During initial startup, the refresh drivers
|
|
* are ticked off this timer, and are swapped out once content
|
|
* vsync IPC connection is established.
|
|
*/
|
|
class StartupRefreshDriverTimer :
|
|
public SimpleTimerBasedRefreshDriverTimer
|
|
{
|
|
public:
|
|
explicit StartupRefreshDriverTimer(double aRate)
|
|
: SimpleTimerBasedRefreshDriverTimer(aRate)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
virtual void ScheduleNextTick(TimeStamp aNowTime)
|
|
{
|
|
// Since this is only used for startup, it isn't super critical
|
|
// that we tick at consistent intervals.
|
|
TimeStamp newTarget = aNowTime + mRateDuration;
|
|
uint32_t delay = static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
|
|
mTimer->InitWithFuncCallback(TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT);
|
|
mTargetTime = newTarget;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* A RefreshDriverTimer for inactive documents. When a new refresh driver is
|
|
* added, the rate is reset to the base (normally 1s/1fps). Every time
|
|
* it ticks, a single refresh driver is poked. Once they have all been poked,
|
|
* the duration between ticks doubles, up to mDisableAfterMilliseconds. At that point,
|
|
* the timer is quiet and doesn't tick (until something is added to it again).
|
|
*
|
|
* When a timer is removed, there is a possibility of another timer
|
|
* being skipped for one cycle. We could avoid this by adjusting
|
|
* mNextDriverIndex in RemoveRefreshDriver, but there's little need to
|
|
* add that complexity. All we want is for inactive drivers to tick
|
|
* at some point, but we don't care too much about how often.
|
|
*/
|
|
class InactiveRefreshDriverTimer final :
|
|
public SimpleTimerBasedRefreshDriverTimer
|
|
{
|
|
public:
|
|
explicit InactiveRefreshDriverTimer(double aRate)
|
|
: SimpleTimerBasedRefreshDriverTimer(aRate),
|
|
mNextTickDuration(aRate),
|
|
mDisableAfterMilliseconds(-1.0),
|
|
mNextDriverIndex(0)
|
|
{
|
|
}
|
|
|
|
InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
|
|
: SimpleTimerBasedRefreshDriverTimer(aRate),
|
|
mNextTickDuration(aRate),
|
|
mDisableAfterMilliseconds(aDisableAfterMilliseconds),
|
|
mNextDriverIndex(0)
|
|
{
|
|
}
|
|
|
|
virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
|
|
{
|
|
RefreshDriverTimer::AddRefreshDriver(aDriver);
|
|
|
|
LOG("[%p] inactive timer got new refresh driver %p, resetting rate",
|
|
this, aDriver);
|
|
|
|
// reset the timer, and start with the newly added one next time.
|
|
mNextTickDuration = mRateMilliseconds;
|
|
|
|
// we don't really have to start with the newly added one, but we may as well
|
|
// not tick the old ones at the fastest rate any more than we need to.
|
|
mNextDriverIndex = mRefreshDrivers.Length() - 1;
|
|
|
|
StopTimer();
|
|
StartTimer();
|
|
}
|
|
|
|
protected:
|
|
virtual void StartTimer()
|
|
{
|
|
mLastFireEpoch = JS_Now();
|
|
mLastFireTime = TimeStamp::Now();
|
|
|
|
mTargetTime = mLastFireTime + mRateDuration;
|
|
|
|
uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
|
|
mTimer->InitWithFuncCallback(TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT);
|
|
}
|
|
|
|
virtual void StopTimer()
|
|
{
|
|
mTimer->Cancel();
|
|
}
|
|
|
|
virtual void ScheduleNextTick(TimeStamp aNowTime)
|
|
{
|
|
if (mDisableAfterMilliseconds > 0.0 &&
|
|
mNextTickDuration > mDisableAfterMilliseconds)
|
|
{
|
|
// We hit the time after which we should disable
|
|
// inactive window refreshes; don't schedule anything
|
|
// until we get kicked by an AddRefreshDriver call.
|
|
return;
|
|
}
|
|
|
|
// double the next tick time if we've already gone through all of them once
|
|
if (mNextDriverIndex >= mRefreshDrivers.Length()) {
|
|
mNextTickDuration *= 2.0;
|
|
mNextDriverIndex = 0;
|
|
}
|
|
|
|
// this doesn't need to be precise; do a simple schedule
|
|
uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
|
|
mTimer->InitWithFuncCallback(TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT);
|
|
|
|
LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this, mNextTickDuration,
|
|
mNextDriverIndex, mRefreshDrivers.Length());
|
|
}
|
|
|
|
/* Runs just one driver's tick. */
|
|
void TickOne()
|
|
{
|
|
int64_t jsnow = JS_Now();
|
|
TimeStamp now = TimeStamp::Now();
|
|
|
|
ScheduleNextTick(now);
|
|
|
|
mLastFireEpoch = jsnow;
|
|
mLastFireTime = now;
|
|
|
|
nsTArray<RefPtr<nsRefreshDriver> > drivers(mRefreshDrivers);
|
|
if (mNextDriverIndex < drivers.Length() &&
|
|
!drivers[mNextDriverIndex]->IsTestControllingRefreshesEnabled())
|
|
{
|
|
TickDriver(drivers[mNextDriverIndex], jsnow, now);
|
|
}
|
|
|
|
mNextDriverIndex++;
|
|
}
|
|
|
|
static void TimerTickOne(nsITimer* aTimer, void* aClosure)
|
|
{
|
|
InactiveRefreshDriverTimer *timer = static_cast<InactiveRefreshDriverTimer*>(aClosure);
|
|
timer->TickOne();
|
|
}
|
|
|
|
double mNextTickDuration;
|
|
double mDisableAfterMilliseconds;
|
|
uint32_t mNextDriverIndex;
|
|
};
|
|
|
|
// The PBackground protocol connection callback. It will be called when
|
|
// PBackground is ready. Then we create the PVsync sub-protocol for our
|
|
// vsync-base RefreshTimer.
|
|
class VsyncChildCreateCallback final : public nsIIPCBackgroundChildCreateCallback
|
|
{
|
|
NS_DECL_ISUPPORTS
|
|
|
|
public:
|
|
VsyncChildCreateCallback()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
static void CreateVsyncActor(PBackgroundChild* aPBackgroundChild)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aPBackgroundChild);
|
|
|
|
layout::PVsyncChild* actor = aPBackgroundChild->SendPVsyncConstructor();
|
|
layout::VsyncChild* child = static_cast<layout::VsyncChild*>(actor);
|
|
nsRefreshDriver::PVsyncActorCreated(child);
|
|
}
|
|
|
|
private:
|
|
virtual ~VsyncChildCreateCallback() {}
|
|
|
|
virtual void ActorCreated(PBackgroundChild* aPBackgroundChild) override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aPBackgroundChild);
|
|
CreateVsyncActor(aPBackgroundChild);
|
|
}
|
|
|
|
virtual void ActorFailed() override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_CRASH("Failed To Create VsyncChild Actor");
|
|
}
|
|
}; // VsyncChildCreateCallback
|
|
NS_IMPL_ISUPPORTS(VsyncChildCreateCallback, nsIIPCBackgroundChildCreateCallback)
|
|
|
|
} // namespace mozilla
|
|
|
|
static RefreshDriverTimer* sRegularRateTimer;
|
|
static InactiveRefreshDriverTimer* sThrottledRateTimer;
|
|
|
|
#ifdef XP_WIN
|
|
static int32_t sHighPrecisionTimerRequests = 0;
|
|
// a bare pointer to avoid introducing a static constructor
|
|
static nsITimer *sDisableHighPrecisionTimersTimer = nullptr;
|
|
#endif
|
|
|
|
static void
|
|
CreateContentVsyncRefreshTimer(void*)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!XRE_IsParentProcess());
|
|
|
|
// Create the PVsync actor child for vsync-base refresh timer.
|
|
// PBackgroundChild is created asynchronously. If PBackgroundChild is still
|
|
// unavailable, setup VsyncChildCreateCallback callback to handle the async
|
|
// connect. We will still use software timer before PVsync ready, and change
|
|
// to use hw timer when the connection is done. Please check
|
|
// VsyncChildCreateCallback::CreateVsyncActor() and
|
|
// nsRefreshDriver::PVsyncActorCreated().
|
|
PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
|
|
if (backgroundChild) {
|
|
// If we already have PBackgroundChild, create the
|
|
// child VsyncRefreshDriverTimer here.
|
|
VsyncChildCreateCallback::CreateVsyncActor(backgroundChild);
|
|
return;
|
|
}
|
|
// Setup VsyncChildCreateCallback callback
|
|
RefPtr<nsIIPCBackgroundChildCreateCallback> callback = new VsyncChildCreateCallback();
|
|
if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
|
|
MOZ_CRASH("PVsync actor create failed!");
|
|
}
|
|
}
|
|
|
|
static void
|
|
CreateVsyncRefreshTimer()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
|
|
// ready.
|
|
gfxPrefs::GetSingleton();
|
|
|
|
if (gfxPlatform::IsInLayoutAsapMode()) {
|
|
return;
|
|
}
|
|
|
|
if (XRE_IsParentProcess()) {
|
|
// Make sure all vsync systems are ready.
|
|
gfxPlatform::GetPlatform();
|
|
// In parent process, we don't need to use ipc. We can create the
|
|
// VsyncRefreshDriverTimer directly.
|
|
sRegularRateTimer = new VsyncRefreshDriverTimer();
|
|
return;
|
|
}
|
|
|
|
#ifdef MOZ_NUWA_PROCESS
|
|
// NUWA process will just use software timer. Use NuwaAddFinalConstructor()
|
|
// to register a callback to create the vsync-base refresh timer after a
|
|
// process is created.
|
|
if (IsNuwaProcess()) {
|
|
NuwaAddFinalConstructor(&CreateContentVsyncRefreshTimer, nullptr);
|
|
return;
|
|
}
|
|
#endif
|
|
// If this process is not created by NUWA, just create the vsync timer here.
|
|
CreateContentVsyncRefreshTimer(nullptr);
|
|
}
|
|
|
|
static uint32_t
|
|
GetFirstFrameDelay(imgIRequest* req)
|
|
{
|
|
nsCOMPtr<imgIContainer> container;
|
|
if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
|
|
return 0;
|
|
}
|
|
|
|
// If this image isn't animated, there isn't a first frame delay.
|
|
int32_t delay = container->GetFirstFrameDelay();
|
|
if (delay < 0)
|
|
return 0;
|
|
|
|
return static_cast<uint32_t>(delay);
|
|
}
|
|
|
|
/* static */ void
|
|
nsRefreshDriver::InitializeStatics()
|
|
{
|
|
if (!gLog) {
|
|
gLog = PR_NewLogModule("nsRefreshDriver");
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
nsRefreshDriver::Shutdown()
|
|
{
|
|
// clean up our timers
|
|
delete sRegularRateTimer;
|
|
delete sThrottledRateTimer;
|
|
|
|
sRegularRateTimer = nullptr;
|
|
sThrottledRateTimer = nullptr;
|
|
|
|
#ifdef XP_WIN
|
|
if (sDisableHighPrecisionTimersTimer) {
|
|
sDisableHighPrecisionTimersTimer->Cancel();
|
|
NS_RELEASE(sDisableHighPrecisionTimersTimer);
|
|
timeEndPeriod(1);
|
|
} else if (sHighPrecisionTimerRequests) {
|
|
timeEndPeriod(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* static */ int32_t
|
|
nsRefreshDriver::DefaultInterval()
|
|
{
|
|
return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
|
|
}
|
|
|
|
// Compute the interval to use for the refresh driver timer, in milliseconds.
|
|
// outIsDefault indicates that rate was not explicitly set by the user
|
|
// so we might choose other, more appropriate rates (e.g. vsync, etc)
|
|
// layout.frame_rate=0 indicates "ASAP mode".
|
|
// In ASAP mode rendering is iterated as fast as possible (typically for stress testing).
|
|
// A target rate of 10k is used internally instead of special-handling 0.
|
|
// Backends which block on swap/present/etc should try to not block
|
|
// when layout.frame_rate=0 - to comply with "ASAP" as much as possible.
|
|
double
|
|
nsRefreshDriver::GetRegularTimerInterval(bool *outIsDefault) const
|
|
{
|
|
int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
|
|
if (rate < 0) {
|
|
rate = gfxPlatform::GetDefaultFrameRate();
|
|
if (outIsDefault) {
|
|
*outIsDefault = true;
|
|
}
|
|
} else {
|
|
if (outIsDefault) {
|
|
*outIsDefault = false;
|
|
}
|
|
}
|
|
|
|
if (rate == 0) {
|
|
rate = 10000;
|
|
}
|
|
|
|
return 1000.0 / rate;
|
|
}
|
|
|
|
/* static */ double
|
|
nsRefreshDriver::GetThrottledTimerInterval()
|
|
{
|
|
int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
|
|
if (rate <= 0) {
|
|
rate = DEFAULT_THROTTLED_FRAME_RATE;
|
|
}
|
|
return 1000.0 / rate;
|
|
}
|
|
|
|
/* static */ mozilla::TimeDuration
|
|
nsRefreshDriver::GetMinRecomputeVisibilityInterval()
|
|
{
|
|
int32_t interval =
|
|
Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1);
|
|
if (interval <= 0) {
|
|
interval = DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS;
|
|
}
|
|
return TimeDuration::FromMilliseconds(interval);
|
|
}
|
|
|
|
double
|
|
nsRefreshDriver::GetRefreshTimerInterval() const
|
|
{
|
|
return mThrottled ? GetThrottledTimerInterval() : GetRegularTimerInterval();
|
|
}
|
|
|
|
RefreshDriverTimer*
|
|
nsRefreshDriver::ChooseTimer() const
|
|
{
|
|
if (mThrottled) {
|
|
if (!sThrottledRateTimer)
|
|
sThrottledRateTimer = new InactiveRefreshDriverTimer(GetThrottledTimerInterval(),
|
|
DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
|
|
return sThrottledRateTimer;
|
|
}
|
|
|
|
if (!sRegularRateTimer) {
|
|
bool isDefault = true;
|
|
double rate = GetRegularTimerInterval(&isDefault);
|
|
|
|
// Try to use vsync-base refresh timer first for sRegularRateTimer.
|
|
CreateVsyncRefreshTimer();
|
|
|
|
if (!sRegularRateTimer) {
|
|
sRegularRateTimer = new StartupRefreshDriverTimer(rate);
|
|
}
|
|
}
|
|
return sRegularRateTimer;
|
|
}
|
|
|
|
nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
|
|
: mActiveTimer(nullptr),
|
|
mReflowCause(nullptr),
|
|
mStyleCause(nullptr),
|
|
mPresContext(aPresContext),
|
|
mRootRefresh(nullptr),
|
|
mPendingTransaction(0),
|
|
mCompletedTransaction(0),
|
|
mFreezeCount(0),
|
|
mThrottledFrameRequestInterval(TimeDuration::FromMilliseconds(
|
|
GetThrottledTimerInterval())),
|
|
mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
|
|
mThrottled(false),
|
|
mNeedToRecomputeVisibility(false),
|
|
mTestControllingRefreshes(false),
|
|
mViewManagerFlushIsPending(false),
|
|
mRequestedHighPrecision(false),
|
|
mInRefresh(false),
|
|
mWaitingForTransaction(false),
|
|
mSkippedPaints(false)
|
|
{
|
|
mMostRecentRefreshEpochTime = JS_Now();
|
|
mMostRecentRefresh = TimeStamp::Now();
|
|
mMostRecentTick = mMostRecentRefresh;
|
|
mNextThrottledFrameRequestTick = mMostRecentTick;
|
|
mNextRecomputeVisibilityTick = mMostRecentTick;
|
|
}
|
|
|
|
nsRefreshDriver::~nsRefreshDriver()
|
|
{
|
|
MOZ_ASSERT(ObserverCount() == 0,
|
|
"observers should have unregistered");
|
|
MOZ_ASSERT(!mActiveTimer, "timer should be gone");
|
|
|
|
if (mRootRefresh) {
|
|
mRootRefresh->RemoveRefreshObserver(this, Flush_Style);
|
|
mRootRefresh = nullptr;
|
|
}
|
|
for (nsIPresShell* shell : mPresShellsToInvalidateIfHidden) {
|
|
shell->InvalidatePresShellIfHidden();
|
|
}
|
|
mPresShellsToInvalidateIfHidden.Clear();
|
|
|
|
profiler_free_backtrace(mStyleCause);
|
|
profiler_free_backtrace(mReflowCause);
|
|
}
|
|
|
|
// Method for testing. See nsIDOMWindowUtils.advanceTimeAndRefresh
|
|
// for description.
|
|
void
|
|
nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds)
|
|
{
|
|
// ensure that we're removed from our driver
|
|
StopTimer();
|
|
|
|
if (!mTestControllingRefreshes) {
|
|
mMostRecentRefreshEpochTime = JS_Now();
|
|
mMostRecentRefresh = TimeStamp::Now();
|
|
|
|
mTestControllingRefreshes = true;
|
|
if (mWaitingForTransaction) {
|
|
// Disable any refresh driver throttling when entering test mode
|
|
mWaitingForTransaction = false;
|
|
mSkippedPaints = false;
|
|
}
|
|
}
|
|
|
|
mMostRecentRefreshEpochTime += aMilliseconds * 1000;
|
|
mMostRecentRefresh += TimeDuration::FromMilliseconds((double) aMilliseconds);
|
|
|
|
mozilla::dom::AutoNoJSAPI nojsapi;
|
|
DoTick();
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::RestoreNormalRefresh()
|
|
{
|
|
mTestControllingRefreshes = false;
|
|
EnsureTimerStarted(eAllowTimeToGoBackwards);
|
|
mCompletedTransaction = mPendingTransaction;
|
|
}
|
|
|
|
TimeStamp
|
|
nsRefreshDriver::MostRecentRefresh() const
|
|
{
|
|
const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
|
|
|
|
return mMostRecentRefresh;
|
|
}
|
|
|
|
int64_t
|
|
nsRefreshDriver::MostRecentRefreshEpochTime() const
|
|
{
|
|
const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
|
|
|
|
return mMostRecentRefreshEpochTime;
|
|
}
|
|
|
|
bool
|
|
nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
|
|
mozFlushType aFlushType)
|
|
{
|
|
ObserverArray& array = ArrayFor(aFlushType);
|
|
bool success = array.AppendElement(aObserver) != nullptr;
|
|
EnsureTimerStarted();
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
|
|
mozFlushType aFlushType)
|
|
{
|
|
ObserverArray& array = ArrayFor(aFlushType);
|
|
return array.RemoveElement(aObserver);
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
|
|
{
|
|
mPostRefreshObservers.AppendElement(aObserver);
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
|
|
{
|
|
mPostRefreshObservers.RemoveElement(aObserver);
|
|
}
|
|
|
|
bool
|
|
nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
|
|
{
|
|
uint32_t delay = GetFirstFrameDelay(aRequest);
|
|
if (delay == 0) {
|
|
if (!mRequests.PutEntry(aRequest)) {
|
|
return false;
|
|
}
|
|
} else {
|
|
ImageStartData* start = mStartTable.Get(delay);
|
|
if (!start) {
|
|
start = new ImageStartData();
|
|
mStartTable.Put(delay, start);
|
|
}
|
|
start->mEntries.PutEntry(aRequest);
|
|
}
|
|
|
|
EnsureTimerStarted();
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
|
|
{
|
|
// Try to remove from both places, just in case, because we can't tell
|
|
// whether RemoveEntry() succeeds.
|
|
mRequests.RemoveEntry(aRequest);
|
|
uint32_t delay = GetFirstFrameDelay(aRequest);
|
|
if (delay != 0) {
|
|
ImageStartData* start = mStartTable.Get(delay);
|
|
if (start) {
|
|
start->mEntries.RemoveEntry(aRequest);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags)
|
|
{
|
|
if (mTestControllingRefreshes)
|
|
return;
|
|
|
|
// will it already fire, and no other changes needed?
|
|
if (mActiveTimer && !(aFlags & eAdjustingTimer))
|
|
return;
|
|
|
|
if (IsFrozen() || !mPresContext) {
|
|
// If we don't want to start it now, or we've been disconnected.
|
|
StopTimer();
|
|
return;
|
|
}
|
|
|
|
if (mPresContext->Document()->IsBeingUsedAsImage()) {
|
|
// Image documents receive ticks from clients' refresh drivers.
|
|
// XXXdholbert Exclude SVG-in-opentype fonts from this optimization, until
|
|
// they receive refresh-driver ticks from their client docs (bug 1107252).
|
|
nsIURI* uri = mPresContext->Document()->GetDocumentURI();
|
|
if (!uri || !IsFontTableURI(uri)) {
|
|
MOZ_ASSERT(!mActiveTimer,
|
|
"image doc refresh driver should never have its own timer");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We got here because we're either adjusting the time *or* we're
|
|
// starting it for the first time. Add to the right timer,
|
|
// prehaps removing it from a previously-set one.
|
|
RefreshDriverTimer *newTimer = ChooseTimer();
|
|
if (newTimer != mActiveTimer) {
|
|
if (mActiveTimer)
|
|
mActiveTimer->RemoveRefreshDriver(this);
|
|
mActiveTimer = newTimer;
|
|
mActiveTimer->AddRefreshDriver(this);
|
|
}
|
|
|
|
// Since the different timers are sampled at different rates, when switching
|
|
// timers, the most recent refresh of the new timer may be *before* the
|
|
// most recent refresh of the old timer. However, the refresh driver time
|
|
// should not go backwards so we clamp the most recent refresh time.
|
|
//
|
|
// The one exception to this is when we are restoring the refresh driver
|
|
// from test control in which case the time is expected to go backwards
|
|
// (see bug 1043078).
|
|
mMostRecentRefresh =
|
|
aFlags & eAllowTimeToGoBackwards
|
|
? mActiveTimer->MostRecentRefresh()
|
|
: std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
|
|
mMostRecentRefreshEpochTime =
|
|
aFlags & eAllowTimeToGoBackwards
|
|
? mActiveTimer->MostRecentRefreshEpochTime()
|
|
: std::max(mActiveTimer->MostRecentRefreshEpochTime(),
|
|
mMostRecentRefreshEpochTime);
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::StopTimer()
|
|
{
|
|
if (!mActiveTimer)
|
|
return;
|
|
|
|
mActiveTimer->RemoveRefreshDriver(this);
|
|
mActiveTimer = nullptr;
|
|
|
|
if (mRequestedHighPrecision) {
|
|
SetHighPrecisionTimersEnabled(false);
|
|
}
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
static void
|
|
DisableHighPrecisionTimersCallback(nsITimer *aTimer, void *aClosure)
|
|
{
|
|
timeEndPeriod(1);
|
|
NS_RELEASE(sDisableHighPrecisionTimersTimer);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
nsRefreshDriver::ConfigureHighPrecision()
|
|
{
|
|
bool haveUnthrottledFrameRequestCallbacks =
|
|
mFrameRequestCallbackDocs.Length() > 0;
|
|
|
|
// if the only change that's needed is that we need high precision,
|
|
// then just set that
|
|
if (!mThrottled && !mRequestedHighPrecision &&
|
|
haveUnthrottledFrameRequestCallbacks) {
|
|
SetHighPrecisionTimersEnabled(true);
|
|
} else if (mRequestedHighPrecision && !haveUnthrottledFrameRequestCallbacks) {
|
|
SetHighPrecisionTimersEnabled(false);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::SetHighPrecisionTimersEnabled(bool aEnable)
|
|
{
|
|
LOG("[%p] SetHighPrecisionTimersEnabled (%s)", this, aEnable ? "true" : "false");
|
|
|
|
if (aEnable) {
|
|
NS_ASSERTION(!mRequestedHighPrecision, "SetHighPrecisionTimersEnabled(true) called when already requested!");
|
|
#ifdef XP_WIN
|
|
if (++sHighPrecisionTimerRequests == 1) {
|
|
// If we had a timer scheduled to disable it, that means that it's already
|
|
// enabled; just cancel the timer. Otherwise, really enable it.
|
|
if (sDisableHighPrecisionTimersTimer) {
|
|
sDisableHighPrecisionTimersTimer->Cancel();
|
|
NS_RELEASE(sDisableHighPrecisionTimersTimer);
|
|
} else {
|
|
timeBeginPeriod(1);
|
|
}
|
|
}
|
|
#endif
|
|
mRequestedHighPrecision = true;
|
|
} else {
|
|
NS_ASSERTION(mRequestedHighPrecision, "SetHighPrecisionTimersEnabled(false) called when not requested!");
|
|
#ifdef XP_WIN
|
|
if (--sHighPrecisionTimerRequests == 0) {
|
|
// Don't jerk us around between high precision and low precision
|
|
// timers; instead, only allow leaving high precision timers
|
|
// after 90 seconds. This is arbitrary, but hopefully good
|
|
// enough.
|
|
NS_ASSERTION(!sDisableHighPrecisionTimersTimer, "We shouldn't have an outstanding disable-high-precision timer !");
|
|
|
|
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
|
if (timer) {
|
|
timer.forget(&sDisableHighPrecisionTimersTimer);
|
|
sDisableHighPrecisionTimersTimer->InitWithFuncCallback(DisableHighPrecisionTimersCallback,
|
|
nullptr,
|
|
90 * 1000,
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
} else {
|
|
// might happen if we're shutting down XPCOM; just drop the time period down
|
|
// immediately
|
|
timeEndPeriod(1);
|
|
}
|
|
}
|
|
#endif
|
|
mRequestedHighPrecision = false;
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
nsRefreshDriver::ObserverCount() const
|
|
{
|
|
uint32_t sum = 0;
|
|
for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
|
|
sum += mObservers[i].Length();
|
|
}
|
|
|
|
// Even while throttled, we need to process layout and style changes. Style
|
|
// changes can trigger transitions which fire events when they complete, and
|
|
// layout changes can affect media queries on child documents, triggering
|
|
// style changes, etc.
|
|
sum += mStyleFlushObservers.Length();
|
|
sum += mLayoutFlushObservers.Length();
|
|
sum += mFrameRequestCallbackDocs.Length();
|
|
sum += mThrottledFrameRequestCallbackDocs.Length();
|
|
sum += mViewManagerFlushIsPending;
|
|
return sum;
|
|
}
|
|
|
|
uint32_t
|
|
nsRefreshDriver::ImageRequestCount() const
|
|
{
|
|
uint32_t count = 0;
|
|
for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) {
|
|
count += iter.UserData()->mEntries.Count();
|
|
}
|
|
return count + mRequests.Count();
|
|
}
|
|
|
|
nsRefreshDriver::ObserverArray&
|
|
nsRefreshDriver::ArrayFor(mozFlushType aFlushType)
|
|
{
|
|
switch (aFlushType) {
|
|
case Flush_Style:
|
|
return mObservers[0];
|
|
case Flush_Layout:
|
|
return mObservers[1];
|
|
case Flush_Display:
|
|
return mObservers[2];
|
|
default:
|
|
MOZ_ASSERT(false, "bad flush type");
|
|
return *static_cast<ObserverArray*>(nullptr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* nsITimerCallback implementation
|
|
*/
|
|
|
|
void
|
|
nsRefreshDriver::DoTick()
|
|
{
|
|
NS_PRECONDITION(!IsFrozen(), "Why are we notified while frozen?");
|
|
NS_PRECONDITION(mPresContext, "Why are we notified after disconnection?");
|
|
NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
|
|
"Shouldn't have a JSContext on the stack");
|
|
|
|
if (mTestControllingRefreshes) {
|
|
Tick(mMostRecentRefreshEpochTime, mMostRecentRefresh);
|
|
} else {
|
|
Tick(JS_Now(), TimeStamp::Now());
|
|
}
|
|
}
|
|
|
|
struct DocumentFrameCallbacks {
|
|
explicit DocumentFrameCallbacks(nsIDocument* aDocument) :
|
|
mDocument(aDocument)
|
|
{}
|
|
|
|
nsCOMPtr<nsIDocument> mDocument;
|
|
nsIDocument::FrameRequestCallbackList mCallbacks;
|
|
};
|
|
|
|
static nsDocShell* GetDocShell(nsPresContext* aPresContext)
|
|
{
|
|
return static_cast<nsDocShell*>(aPresContext->GetDocShell());
|
|
}
|
|
|
|
static bool
|
|
HasPendingAnimations(nsIPresShell* aShell)
|
|
{
|
|
nsIDocument* doc = aShell->GetDocument();
|
|
if (!doc) {
|
|
return false;
|
|
}
|
|
|
|
PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();
|
|
return tracker && tracker->HasPendingAnimations();
|
|
}
|
|
|
|
/**
|
|
* Return a list of all the child docShells in a given root docShell that are
|
|
* visible and are recording markers for the profilingTimeline
|
|
*/
|
|
static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
|
|
nsTArray<nsDocShell*>& aShells)
|
|
{
|
|
if (!aRootDocShell) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
|
if (!timelines || timelines->IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsISimpleEnumerator> enumerator;
|
|
nsresult rv = aRootDocShell->GetDocShellEnumerator(
|
|
nsIDocShellTreeItem::typeAll,
|
|
nsIDocShell::ENUMERATE_BACKWARDS,
|
|
getter_AddRefs(enumerator));
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShell> curItem;
|
|
bool hasMore = false;
|
|
while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
|
|
nsCOMPtr<nsISupports> curSupports;
|
|
enumerator->GetNext(getter_AddRefs(curSupports));
|
|
curItem = do_QueryInterface(curSupports);
|
|
|
|
if (!curItem || !curItem->GetRecordProfileTimelineMarkers()) {
|
|
continue;
|
|
}
|
|
|
|
nsDocShell* shell = static_cast<nsDocShell*>(curItem.get());
|
|
bool isVisible = false;
|
|
shell->GetVisibility(&isVisible);
|
|
if (!isVisible) {
|
|
continue;
|
|
}
|
|
|
|
aShells.AppendElement(shell);
|
|
};
|
|
}
|
|
|
|
static void
|
|
TakeFrameRequestCallbacksFrom(nsIDocument* aDocument,
|
|
nsTArray<DocumentFrameCallbacks>& aTarget)
|
|
{
|
|
aTarget.AppendElement(aDocument);
|
|
aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks);
|
|
}
|
|
|
|
static bool
|
|
DispatchAnimationEventsOnSubDocuments(nsIDocument* aDocument,
|
|
void* aRefreshDriver)
|
|
{
|
|
nsIPresShell* shell = aDocument->GetShell();
|
|
if (!shell) {
|
|
return true;
|
|
}
|
|
|
|
nsPresContext* context = shell->GetPresContext();
|
|
if (!context || context->RefreshDriver() != aRefreshDriver) {
|
|
return true;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocument> kungFuDeathGrip(aDocument);
|
|
|
|
context->TransitionManager()->SortEvents();
|
|
context->AnimationManager()->SortEvents();
|
|
|
|
// Dispatch transition events first since transitions conceptually sit
|
|
// below animations in terms of compositing order.
|
|
context->TransitionManager()->DispatchEvents();
|
|
context->AnimationManager()->DispatchEvents();
|
|
|
|
aDocument->EnumerateSubDocuments(DispatchAnimationEventsOnSubDocuments,
|
|
aRefreshDriver);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::DispatchAnimationEvents()
|
|
{
|
|
if (!mPresContext) {
|
|
return;
|
|
}
|
|
|
|
DispatchAnimationEventsOnSubDocuments(mPresContext->Document(), this);
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
|
|
{
|
|
// Grab all of our frame request callbacks up front.
|
|
nsTArray<DocumentFrameCallbacks>
|
|
frameRequestCallbacks(mFrameRequestCallbackDocs.Length() +
|
|
mThrottledFrameRequestCallbackDocs.Length());
|
|
|
|
// First, grab throttled frame request callbacks.
|
|
{
|
|
nsTArray<nsIDocument*> docsToRemove;
|
|
|
|
// We always tick throttled frame requests if the entire refresh driver is
|
|
// throttled, because in that situation throttled frame requests tick at the
|
|
// same frequency as non-throttled frame requests.
|
|
bool tickThrottledFrameRequests = mThrottled;
|
|
|
|
if (!tickThrottledFrameRequests &&
|
|
aNowTime >= mNextThrottledFrameRequestTick) {
|
|
mNextThrottledFrameRequestTick = aNowTime + mThrottledFrameRequestInterval;
|
|
tickThrottledFrameRequests = true;
|
|
}
|
|
|
|
for (nsIDocument* doc : mThrottledFrameRequestCallbackDocs) {
|
|
if (tickThrottledFrameRequests) {
|
|
// We're ticking throttled documents, so grab this document's requests.
|
|
// We don't bother appending to docsToRemove because we're going to
|
|
// clear mThrottledFrameRequestCallbackDocs anyway.
|
|
TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
|
|
} else if (!doc->ShouldThrottleFrameRequests()) {
|
|
// This document is no longer throttled, so grab its requests even
|
|
// though we're not ticking throttled frame requests right now. If
|
|
// this is the first unthrottled document with frame requests, we'll
|
|
// enter high precision mode the next time the callback is scheduled.
|
|
TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
|
|
docsToRemove.AppendElement(doc);
|
|
}
|
|
}
|
|
|
|
// Remove all the documents we're ticking from
|
|
// mThrottledFrameRequestCallbackDocs so they can be readded as needed.
|
|
if (tickThrottledFrameRequests) {
|
|
mThrottledFrameRequestCallbackDocs.Clear();
|
|
} else {
|
|
// XXX(seth): We're using this approach to avoid concurrent modification
|
|
// of mThrottledFrameRequestCallbackDocs. docsToRemove usually has either
|
|
// zero elements or a very small number, so this should be OK in practice.
|
|
for (nsIDocument* doc : docsToRemove) {
|
|
mThrottledFrameRequestCallbackDocs.RemoveElement(doc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now grab unthrottled frame request callbacks.
|
|
for (nsIDocument* doc : mFrameRequestCallbackDocs) {
|
|
TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
|
|
}
|
|
|
|
// Reset mFrameRequestCallbackDocs so they can be readded as needed.
|
|
mFrameRequestCallbackDocs.Clear();
|
|
|
|
if (!frameRequestCallbacks.IsEmpty()) {
|
|
profiler_tracing("Paint", "Scripts", TRACING_INTERVAL_START);
|
|
for (const DocumentFrameCallbacks& docCallbacks : frameRequestCallbacks) {
|
|
// XXXbz Bug 863140: GetInnerWindow can return the outer
|
|
// window in some cases.
|
|
nsPIDOMWindow* innerWindow = docCallbacks.mDocument->GetInnerWindow();
|
|
DOMHighResTimeStamp timeStamp = 0;
|
|
if (innerWindow && innerWindow->IsInnerWindow()) {
|
|
nsPerformance* perf = innerWindow->GetPerformance();
|
|
if (perf) {
|
|
timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aNowTime);
|
|
}
|
|
// else window is partially torn down already
|
|
}
|
|
for (auto& callback : docCallbacks.mCallbacks) {
|
|
ErrorResult ignored;
|
|
callback->Call(timeStamp, ignored);
|
|
ignored.SuppressException();
|
|
}
|
|
}
|
|
profiler_tracing("Paint", "Scripts", TRACING_INTERVAL_END);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
|
{
|
|
NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
|
|
"Shouldn't have a JSContext on the stack");
|
|
|
|
if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
|
|
NS_ERROR("Refresh driver should not run during plugin call!");
|
|
// Try to survive this by just ignoring the refresh tick.
|
|
return;
|
|
}
|
|
|
|
PROFILER_LABEL("nsRefreshDriver", "Tick",
|
|
js::ProfileEntry::Category::GRAPHICS);
|
|
|
|
// We're either frozen or we were disconnected (likely in the middle
|
|
// of a tick iteration). Just do nothing here, since our
|
|
// prescontext went away.
|
|
if (IsFrozen() || !mPresContext) {
|
|
return;
|
|
}
|
|
|
|
// We can have a race condition where the vsync timestamp
|
|
// is before the most recent refresh due to a forced refresh.
|
|
// The underlying assumption is that the refresh driver tick can only
|
|
// go forward in time, not backwards. To prevent the refresh
|
|
// driver from going back in time, just skip this tick and
|
|
// wait until the next tick.
|
|
if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes) {
|
|
return;
|
|
}
|
|
|
|
TimeStamp previousRefresh = mMostRecentRefresh;
|
|
|
|
mMostRecentRefresh = aNowTime;
|
|
mMostRecentRefreshEpochTime = aNowEpoch;
|
|
|
|
if (IsWaitingForPaint(aNowTime)) {
|
|
// We're currently suspended waiting for earlier Tick's to
|
|
// be completed (on the Compositor). Mark that we missed the paint
|
|
// and keep waiting.
|
|
return;
|
|
}
|
|
mMostRecentTick = aNowTime;
|
|
if (mRootRefresh) {
|
|
mRootRefresh->RemoveRefreshObserver(this, Flush_Style);
|
|
mRootRefresh = nullptr;
|
|
}
|
|
mSkippedPaints = false;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
|
|
if (!presShell || (ObserverCount() == 0 && ImageRequestCount() == 0)) {
|
|
// Things are being destroyed, or we no longer have any observers.
|
|
// We don't want to stop the timer when observers are initially
|
|
// removed, because sometimes observers can be added and removed
|
|
// often depending on what other things are going on and in that
|
|
// situation we don't want to thrash our timer. So instead we
|
|
// wait until we get a Notify() call when we have no observers
|
|
// before stopping the timer.
|
|
StopTimer();
|
|
return;
|
|
}
|
|
|
|
AutoRestore<bool> restoreInRefresh(mInRefresh);
|
|
mInRefresh = true;
|
|
|
|
AutoRestore<TimeStamp> restoreTickStart(mTickStart);
|
|
mTickStart = TimeStamp::Now();
|
|
|
|
/*
|
|
* The timer holds a reference to |this| while calling |Notify|.
|
|
* However, implementations of |WillRefresh| are permitted to destroy
|
|
* the pres context, which will cause our |mPresContext| to become
|
|
* null. If this happens, we must stop notifying observers.
|
|
*/
|
|
for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
|
|
ObserverArray::EndLimitedIterator etor(mObservers[i]);
|
|
while (etor.HasMore()) {
|
|
RefPtr<nsARefreshObserver> obs = etor.GetNext();
|
|
obs->WillRefresh(aNowTime);
|
|
|
|
if (!mPresContext || !mPresContext->GetPresShell()) {
|
|
StopTimer();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (i == 0) {
|
|
// This is the Flush_Style case.
|
|
|
|
DispatchAnimationEvents();
|
|
RunFrameRequestCallbacks(aNowTime);
|
|
|
|
if (mPresContext && mPresContext->GetPresShell()) {
|
|
bool tracingStyleFlush = false;
|
|
nsAutoTArray<nsIPresShell*, 16> observers;
|
|
observers.AppendElements(mStyleFlushObservers);
|
|
for (uint32_t j = observers.Length();
|
|
j && mPresContext && mPresContext->GetPresShell(); --j) {
|
|
// Make sure to not process observers which might have been removed
|
|
// during previous iterations.
|
|
nsIPresShell* shell = observers[j - 1];
|
|
if (!mStyleFlushObservers.Contains(shell))
|
|
continue;
|
|
|
|
if (!tracingStyleFlush) {
|
|
tracingStyleFlush = true;
|
|
profiler_tracing("Paint", "Styles", mStyleCause, TRACING_INTERVAL_START);
|
|
mStyleCause = nullptr;
|
|
}
|
|
|
|
NS_ADDREF(shell);
|
|
mStyleFlushObservers.RemoveElement(shell);
|
|
shell->GetPresContext()->RestyleManager()->mObservingRefreshDriver = false;
|
|
shell->FlushPendingNotifications(ChangesToFlush(Flush_Style, false));
|
|
// Inform the FontFaceSet that we ticked, so that it can resolve its
|
|
// ready promise if it needs to (though it might still be waiting on
|
|
// a layout flush).
|
|
nsPresContext* presContext = shell->GetPresContext();
|
|
if (presContext) {
|
|
presContext->NotifyFontFaceSetOnRefresh();
|
|
}
|
|
NS_RELEASE(shell);
|
|
|
|
mNeedToRecomputeVisibility = true;
|
|
}
|
|
|
|
|
|
if (tracingStyleFlush) {
|
|
profiler_tracing("Paint", "Styles", TRACING_INTERVAL_END);
|
|
}
|
|
|
|
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
|
|
mPresContext->TickLastStyleUpdateForAllAnimations();
|
|
}
|
|
}
|
|
} else if (i == 1) {
|
|
// This is the Flush_Layout case.
|
|
bool tracingLayoutFlush = false;
|
|
nsAutoTArray<nsIPresShell*, 16> observers;
|
|
observers.AppendElements(mLayoutFlushObservers);
|
|
for (uint32_t j = observers.Length();
|
|
j && mPresContext && mPresContext->GetPresShell(); --j) {
|
|
// Make sure to not process observers which might have been removed
|
|
// during previous iterations.
|
|
nsIPresShell* shell = observers[j - 1];
|
|
if (!mLayoutFlushObservers.Contains(shell))
|
|
continue;
|
|
|
|
if (!tracingLayoutFlush) {
|
|
tracingLayoutFlush = true;
|
|
profiler_tracing("Paint", "Reflow", mReflowCause, TRACING_INTERVAL_START);
|
|
mReflowCause = nullptr;
|
|
}
|
|
|
|
NS_ADDREF(shell);
|
|
mLayoutFlushObservers.RemoveElement(shell);
|
|
shell->mReflowScheduled = false;
|
|
shell->mSuppressInterruptibleReflows = false;
|
|
mozFlushType flushType = HasPendingAnimations(shell)
|
|
? Flush_Layout
|
|
: Flush_InterruptibleLayout;
|
|
shell->FlushPendingNotifications(ChangesToFlush(flushType, false));
|
|
// Inform the FontFaceSet that we ticked, so that it can resolve its
|
|
// ready promise if it needs to.
|
|
nsPresContext* presContext = shell->GetPresContext();
|
|
if (presContext) {
|
|
presContext->NotifyFontFaceSetOnRefresh();
|
|
}
|
|
NS_RELEASE(shell);
|
|
|
|
mNeedToRecomputeVisibility = true;
|
|
}
|
|
|
|
if (tracingLayoutFlush) {
|
|
profiler_tracing("Paint", "Reflow", TRACING_INTERVAL_END);
|
|
}
|
|
}
|
|
|
|
// The pres context may be destroyed during we do the flushing.
|
|
if (!mPresContext || !mPresContext->GetPresShell()) {
|
|
StopTimer();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Recompute image visibility if it's necessary and enough time has passed
|
|
// since the last time we did it.
|
|
if (mNeedToRecomputeVisibility && !mThrottled &&
|
|
aNowTime >= mNextRecomputeVisibilityTick &&
|
|
!presShell->IsPaintingSuppressed()) {
|
|
mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
|
|
mNeedToRecomputeVisibility = false;
|
|
|
|
presShell->ScheduleImageVisibilityUpdate();
|
|
}
|
|
|
|
/*
|
|
* Perform notification to imgIRequests subscribed to listen
|
|
* for refresh events.
|
|
*/
|
|
|
|
for (auto iter = mStartTable.Iter(); !iter.Done(); iter.Next()) {
|
|
const uint32_t& delay = iter.Key();
|
|
ImageStartData* data = iter.UserData();
|
|
|
|
if (data->mStartTime) {
|
|
TimeStamp& start = *data->mStartTime;
|
|
TimeDuration prev = previousRefresh - start;
|
|
TimeDuration curr = aNowTime - start;
|
|
uint32_t prevMultiple = uint32_t(prev.ToMilliseconds()) / delay;
|
|
|
|
// We want to trigger images' refresh if we've just crossed over a
|
|
// multiple of the first image's start time. If so, set the animation
|
|
// start time to the nearest multiple of the delay and move all the
|
|
// images in this table to the main requests table.
|
|
if (prevMultiple != uint32_t(curr.ToMilliseconds()) / delay) {
|
|
mozilla::TimeStamp desired =
|
|
start + TimeDuration::FromMilliseconds(prevMultiple * delay);
|
|
BeginRefreshingImages(data->mEntries, desired);
|
|
}
|
|
} else {
|
|
// This is the very first time we've drawn images with this time delay.
|
|
// Set the animation start time to "now" and move all the images in this
|
|
// table to the main requests table.
|
|
mozilla::TimeStamp desired = aNowTime;
|
|
BeginRefreshingImages(data->mEntries, desired);
|
|
data->mStartTime.emplace(aNowTime);
|
|
}
|
|
}
|
|
|
|
if (mRequests.Count()) {
|
|
// RequestRefresh may run scripts, so it's not safe to directly call it
|
|
// while using a hashtable enumerator to enumerate mRequests in case
|
|
// script modifies the hashtable. Instead, we build a (local) array of
|
|
// images to refresh, and then we refresh each image in that array.
|
|
nsCOMArray<imgIContainer> imagesToRefresh(mRequests.Count());
|
|
|
|
for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
|
|
nsISupportsHashKey* entry = iter.Get();
|
|
auto req = static_cast<imgIRequest*>(entry->GetKey());
|
|
MOZ_ASSERT(req, "Unable to retrieve the image request");
|
|
nsCOMPtr<imgIContainer> image;
|
|
if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
|
|
imagesToRefresh.AppendElement(image);
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = 0; i < imagesToRefresh.Length(); i++) {
|
|
imagesToRefresh[i]->RequestRefresh(aNowTime);
|
|
}
|
|
}
|
|
|
|
for (nsIPresShell* shell : mPresShellsToInvalidateIfHidden) {
|
|
shell->InvalidatePresShellIfHidden();
|
|
}
|
|
mPresShellsToInvalidateIfHidden.Clear();
|
|
|
|
if (mViewManagerFlushIsPending) {
|
|
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
|
|
|
nsTArray<nsDocShell*> profilingDocShells;
|
|
GetProfileTimelineSubDocShells(GetDocShell(mPresContext), profilingDocShells);
|
|
for (nsDocShell* docShell : profilingDocShells) {
|
|
// For the sake of the profile timeline's simplicity, this is flagged as
|
|
// paint even if it includes creating display lists
|
|
MOZ_ASSERT(timelines);
|
|
MOZ_ASSERT(timelines->HasConsumer(docShell));
|
|
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::START);
|
|
}
|
|
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
|
|
printf_stderr("Starting ProcessPendingUpdates\n");
|
|
}
|
|
#endif
|
|
|
|
mViewManagerFlushIsPending = false;
|
|
RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
|
|
vm->ProcessPendingUpdates();
|
|
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
|
|
printf_stderr("Ending ProcessPendingUpdates\n");
|
|
}
|
|
#endif
|
|
|
|
for (nsDocShell* docShell : profilingDocShells) {
|
|
MOZ_ASSERT(timelines);
|
|
MOZ_ASSERT(timelines->HasConsumer(docShell));
|
|
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
|
|
}
|
|
|
|
if (nsContentUtils::XPConnect()) {
|
|
nsContentUtils::XPConnect()->NotifyDidPaint();
|
|
nsJSContext::NotifyDidPaint();
|
|
}
|
|
}
|
|
|
|
#ifndef ANDROID /* bug 1142079 */
|
|
mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::REFRESH_DRIVER_TICK, mTickStart);
|
|
#endif
|
|
|
|
nsTObserverArray<nsAPostRefreshObserver*>::ForwardIterator iter(mPostRefreshObservers);
|
|
while (iter.HasMore()) {
|
|
nsAPostRefreshObserver* observer = iter.GetNext();
|
|
observer->DidRefresh();
|
|
}
|
|
|
|
// Check if we should exit high precision timer mode.
|
|
ConfigureHighPrecision();
|
|
|
|
NS_ASSERTION(mInRefresh, "Still in refresh");
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
|
|
mozilla::TimeStamp aDesired)
|
|
{
|
|
for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
|
|
auto req = static_cast<imgIRequest*>(iter.Get()->GetKey());
|
|
MOZ_ASSERT(req, "Unable to retrieve the image request");
|
|
|
|
mRequests.PutEntry(req);
|
|
|
|
nsCOMPtr<imgIContainer> image;
|
|
if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
|
|
image->SetAnimationStartTime(aDesired);
|
|
}
|
|
}
|
|
aEntries.Clear();
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::Freeze()
|
|
{
|
|
StopTimer();
|
|
mFreezeCount++;
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::Thaw()
|
|
{
|
|
NS_ASSERTION(mFreezeCount > 0, "Thaw() called on an unfrozen refresh driver");
|
|
|
|
if (mFreezeCount > 0) {
|
|
mFreezeCount--;
|
|
}
|
|
|
|
if (mFreezeCount == 0) {
|
|
if (ObserverCount() || ImageRequestCount()) {
|
|
// FIXME: This isn't quite right, since our EnsureTimerStarted call
|
|
// updates our mMostRecentRefresh, but the DoRefresh call won't run
|
|
// and notify our observers until we get back to the event loop.
|
|
// Thus MostRecentRefresh() will lie between now and the DoRefresh.
|
|
NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsRefreshDriver::DoRefresh));
|
|
EnsureTimerStarted();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::FinishedWaitingForTransaction()
|
|
{
|
|
mWaitingForTransaction = false;
|
|
if (mSkippedPaints &&
|
|
!IsInRefresh() &&
|
|
(ObserverCount() || ImageRequestCount())) {
|
|
profiler_tracing("Paint", "RD", TRACING_INTERVAL_START);
|
|
DoRefresh();
|
|
profiler_tracing("Paint", "RD", TRACING_INTERVAL_END);
|
|
}
|
|
mSkippedPaints = false;
|
|
}
|
|
|
|
uint64_t
|
|
nsRefreshDriver::GetTransactionId()
|
|
{
|
|
++mPendingTransaction;
|
|
|
|
if (mPendingTransaction >= mCompletedTransaction + 2 &&
|
|
!mWaitingForTransaction &&
|
|
!mTestControllingRefreshes) {
|
|
mWaitingForTransaction = true;
|
|
mSkippedPaints = false;
|
|
}
|
|
|
|
return mPendingTransaction;
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::RevokeTransactionId(uint64_t aTransactionId)
|
|
{
|
|
MOZ_ASSERT(aTransactionId == mPendingTransaction);
|
|
if (mPendingTransaction == mCompletedTransaction + 2 &&
|
|
mWaitingForTransaction) {
|
|
MOZ_ASSERT(!mSkippedPaints, "How did we skip a paint when we're in the middle of one?");
|
|
FinishedWaitingForTransaction();
|
|
}
|
|
mPendingTransaction--;
|
|
}
|
|
|
|
mozilla::TimeStamp
|
|
nsRefreshDriver::GetTransactionStart()
|
|
{
|
|
return mTickStart;
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::NotifyTransactionCompleted(uint64_t aTransactionId)
|
|
{
|
|
if (aTransactionId > mCompletedTransaction) {
|
|
if (mPendingTransaction > mCompletedTransaction + 1 &&
|
|
mWaitingForTransaction) {
|
|
mCompletedTransaction = aTransactionId;
|
|
FinishedWaitingForTransaction();
|
|
} else {
|
|
mCompletedTransaction = aTransactionId;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime)
|
|
{
|
|
mRootRefresh->RemoveRefreshObserver(this, Flush_Style);
|
|
mRootRefresh = nullptr;
|
|
if (mSkippedPaints) {
|
|
DoRefresh();
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime)
|
|
{
|
|
if (mTestControllingRefreshes) {
|
|
return false;
|
|
}
|
|
// If we've skipped too many ticks then it's possible
|
|
// that something went wrong and we're waiting on
|
|
// a notification that will never arrive.
|
|
if (aTime > (mMostRecentTick + TimeDuration::FromMilliseconds(200))) {
|
|
mSkippedPaints = false;
|
|
mWaitingForTransaction = false;
|
|
if (mRootRefresh) {
|
|
mRootRefresh->RemoveRefreshObserver(this, Flush_Style);
|
|
}
|
|
return false;
|
|
}
|
|
if (mWaitingForTransaction) {
|
|
mSkippedPaints = true;
|
|
return true;
|
|
}
|
|
|
|
// Try find the 'root' refresh driver for the current window and check
|
|
// if that is waiting for a paint.
|
|
nsPresContext *rootContext = PresContext()->GetRootPresContext();
|
|
if (rootContext) {
|
|
nsRefreshDriver *rootRefresh = rootContext->RefreshDriver();
|
|
if (rootRefresh && rootRefresh != this) {
|
|
if (rootRefresh->IsWaitingForPaint(aTime)) {
|
|
if (mRootRefresh != rootRefresh) {
|
|
if (mRootRefresh) {
|
|
mRootRefresh->RemoveRefreshObserver(this, Flush_Style);
|
|
}
|
|
rootRefresh->AddRefreshObserver(this, Flush_Style);
|
|
mRootRefresh = rootRefresh;
|
|
}
|
|
mSkippedPaints = true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::SetThrottled(bool aThrottled)
|
|
{
|
|
if (aThrottled != mThrottled) {
|
|
mThrottled = aThrottled;
|
|
if (mActiveTimer) {
|
|
// We want to switch our timer type here, so just stop and
|
|
// restart the timer.
|
|
EnsureTimerStarted(eAdjustingTimer);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*static*/ void
|
|
nsRefreshDriver::PVsyncActorCreated(VsyncChild* aVsyncChild)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!XRE_IsParentProcess());
|
|
VsyncRefreshDriverTimer* vsyncRefreshDriverTimer =
|
|
new VsyncRefreshDriverTimer(aVsyncChild);
|
|
|
|
// If we are using software timer, swap current timer to
|
|
// VsyncRefreshDriverTimer.
|
|
if (sRegularRateTimer) {
|
|
sRegularRateTimer->SwapRefreshDrivers(vsyncRefreshDriverTimer);
|
|
delete sRegularRateTimer;
|
|
}
|
|
sRegularRateTimer = vsyncRefreshDriverTimer;
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::DoRefresh()
|
|
{
|
|
// Don't do a refresh unless we're in a state where we should be refreshing.
|
|
if (!IsFrozen() && mPresContext && mActiveTimer) {
|
|
DoTick();
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
bool
|
|
nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
|
|
mozFlushType aFlushType)
|
|
{
|
|
ObserverArray& array = ArrayFor(aFlushType);
|
|
return array.Contains(aObserver);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
nsRefreshDriver::ScheduleViewManagerFlush()
|
|
{
|
|
NS_ASSERTION(mPresContext->IsRoot(),
|
|
"Should only schedule view manager flush on root prescontexts");
|
|
mViewManagerFlushIsPending = true;
|
|
EnsureTimerStarted();
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument)
|
|
{
|
|
NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
|
|
mFrameRequestCallbackDocs.NoIndex &&
|
|
mThrottledFrameRequestCallbackDocs.IndexOf(aDocument) ==
|
|
mThrottledFrameRequestCallbackDocs.NoIndex,
|
|
"Don't schedule the same document multiple times");
|
|
if (aDocument->ShouldThrottleFrameRequests()) {
|
|
mThrottledFrameRequestCallbackDocs.AppendElement(aDocument);
|
|
} else {
|
|
mFrameRequestCallbackDocs.AppendElement(aDocument);
|
|
}
|
|
|
|
// make sure that the timer is running
|
|
ConfigureHighPrecision();
|
|
EnsureTimerStarted();
|
|
}
|
|
|
|
void
|
|
nsRefreshDriver::RevokeFrameRequestCallbacks(nsIDocument* aDocument)
|
|
{
|
|
mFrameRequestCallbackDocs.RemoveElement(aDocument);
|
|
mThrottledFrameRequestCallbackDocs.RemoveElement(aDocument);
|
|
ConfigureHighPrecision();
|
|
// No need to worry about restarting our timer in slack mode if it's already
|
|
// running; that will happen automatically when it fires.
|
|
}
|
|
|
|
#undef LOG
|