CINXE.COM
Asynchronous Panning and Zooming — Firefox Source Docs documentation
<!doctype html> <html class="writer-html5" lang="en" data-content_root="../"> <head> <base href="https://firefox-source-docs.mozilla.org/gfx/AsyncPanZoom.html"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Asynchronous Panning and Zooming — Firefox Source Docs documentation</title> <link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=fa44fd50"> <link rel="stylesheet" type="text/css" href="../_static/css/theme.css?v=19f00094"> <link rel="stylesheet" type="text/css" href="../_static/graphviz.css?v=fd3f3429"> <link rel="stylesheet" type="text/css" href="../_static/copybutton.css?v=76b2166b"> <link rel="stylesheet" type="text/css" href="../_static/custom_theme.css?v=a7d3e023"> <link rel="stylesheet" type="text/css" href="../_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css?v=0a3b3ea7"> <link rel="shortcut icon" href="../_static/firefox.ico"><!--[if lt IE 9]> <script src="../_static/js/html5shiv.min.js"></script> <![endif]--> <script src="../_static/jquery.js?v=5d32c60e"></script> <script src="../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script> <script src="../_static/documentation_options.js?v=5929fcd5"></script> <script src="../_static/doctools.js?v=9a2dae69"></script> <script src="../_static/sphinx_highlight.js?v=dc90522c"></script> <script src="../_static/clipboard.min.js?v=a7894cd8"></script> <script src="../_static/copybutton.js?v=30646c52"></script> <script src="../_static/design-tabs.js?v=36754332"></script> <script src="../_static/js/theme.js"></script> <link rel="index" title="Index" href="../genindex.html"> <link rel="search" title="Search" href="../search.html"> <link rel="next" title="Silk Overview" href="Silk.html"> <link rel="prev" title="Layers History" href="LayersHistory.html"> <meta http-equiv="X-Translated-By" content="Google"> <meta http-equiv="X-Translated-To" content="iw"> <script type="text/javascript" src="https://www.gstatic.com/_/translate_http/_/js/k=translate_http.tr.en_GB.1hbgkFx4Qn8.O/am=DgY/d=1/rs=AN8SPfqlmAPxwfG457BPbRXwNq39oSMGHg/m=corsproxy" data-sourceurl="https://firefox-source-docs.mozilla.org/gfx/AsyncPanZoom.html"></script> <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet"> <script type="text/javascript" src="https://www.gstatic.com/_/translate_http/_/js/k=translate_http.tr.en_GB.1hbgkFx4Qn8.O/am=DgY/d=1/exm=corsproxy/ed=1/rs=AN8SPfqlmAPxwfG457BPbRXwNq39oSMGHg/m=phishing_protection" data-phishing-protection-enabled="false" data-forms-warning-enabled="true" data-source-url="https://firefox-source-docs.mozilla.org/gfx/AsyncPanZoom.html"></script> <meta name="robots" content="none"> </head> <body class="wy-body-for-nav"> <script type="text/javascript" src="https://www.gstatic.com/_/translate_http/_/js/k=translate_http.tr.en_GB.1hbgkFx4Qn8.O/am=DgY/d=1/exm=corsproxy,phishing_protection/ed=1/rs=AN8SPfqlmAPxwfG457BPbRXwNq39oSMGHg/m=navigationui" data-environment="prod" data-proxy-url="https://firefox--source--docs-mozilla-org.translate.goog" data-proxy-full-url="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB" data-source-url="https://firefox-source-docs.mozilla.org/gfx/AsyncPanZoom.html" data-source-language="pl" data-target-language="iw" data-display-language="en-GB" data-detected-source-language="" data-is-source-untranslated="false" data-source-untranslated-url="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://firefox-source-docs.mozilla.org/gfx/AsyncPanZoom.html&anno=2" data-client="tr"></script> <div class="wy-grid-for-nav"> <nav data-toggle="wy-nav-shift" class="wy-nav-side"> <div class="wy-side-scroll"> <div class="wy-side-nav-search"><a href="https://firefox--source--docs-mozilla-org.translate.goog/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB" class="icon icon-home"> Firefox Source Docs <img src="../_static/firefox-wordmark.svg" class="logo" alt="Logo"> </a><!-- -- This code is governed by the BSD license ----> <div> <h3>Quick search</h3> <script> (function () { var cx = "dd12886298f75dbef"; var gcse = document.createElement("script"); gcse.async = true; gcse.src = "https://cse.google.com/cse.js?cx=" + cx; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(gcse, s); })(); </script><gcse:search></gcse:search> </div> </div> <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu"> <p class="caption" role="heading"><span class="caption-text">Overview</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/glossary/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">A Glossary of Common Terms</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/overview/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">A Quick Guide to Mozilla Applications</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Getting Started</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/setup/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Getting Set Up To Work On The Firefox Codebase</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Working On Firefox</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/contributing/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Working on Firefox</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/bug-mgmt/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Bug Handling</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Firefox User Guide</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/devtools-user/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Firefox DevTools User Docs</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Source Code Documentation</span></p> <ul class="current"> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/mots/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Governance</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/browser/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Firefox Front-end</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/dom/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">DOM</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/editor/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Editor</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/layout/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Style system (CSS) & Layout</a></li> <li class="toctree-l1 current"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Graphics</a> <ul class="current"> <li class="toctree-l2"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/RenderingOverview.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Rendering Overview</a></li> <li class="toctree-l2"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/LayersHistory.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Layers History</a></li> <li class="toctree-l2 current"><a class="current reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#">Asynchronous Panning and Zooming</a> <ul> <li class="toctree-l3"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#goals">Goals</a></li> <li class="toctree-l3"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#context-and-surrounding-architecture">Context and surrounding architecture</a></li> <li class="toctree-l3"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#technical-overview">Technical overview</a> <ul> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#checkerboarding">Checkerboarding</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#multiple-scrollable-elements">Multiple scrollable elements</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#hit-detection">Hit detection</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#scroll-handoff">Scroll Handoff</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#input-event-untransformation">Input event untransformation</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#content-independently-adjusting-scrolling">Content independently adjusting scrolling</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#content-preventing-default-behaviour-of-input-events">Content preventing default behaviour of input events</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#other-event-types">Other event types</a></li> </ul></li> <li class="toctree-l3"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#technical-details">Technical details</a> <ul> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#overall-flow-of-input-events">Overall flow of input events</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#webrender-integration">WebRender Integration</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#fission-integration">Fission Integration</a></li> </ul></li> <li class="toctree-l3"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#testing">Testing</a> <ul> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#mochitest">Mochitest</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#gtest">GTest</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#reftests">Reftests</a></li> </ul></li> <li class="toctree-l3"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#threading-locking-overview">Threading / Locking Overview</a> <ul> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#threads">Threads</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#locks">Locks</a></li> <li class="toctree-l4"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#thread-lock-ordering">Thread / Lock Ordering</a></li> </ul></li> </ul></li> <li class="toctree-l2"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/Silk.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Silk Overview</a></li> <li class="toctree-l2"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/Moz2D.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Moz2D</a></li> </ul></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/ipc/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Processes, Threads and IPC</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/devtools/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Firefox DevTools Contributor Docs</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/toolkit/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Toolkit</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/js/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">SpiderMonkey</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/mobile/android/geckoview/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">GeckoView</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/mobile/android/fenix/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Fenix</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/mobile/android/focus-android/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Focus for Android</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/dom/bindings/webidl/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">WebIDL</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/modules/libpref/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">libpref</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/networking/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Networking</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/remote/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Remote Protocols</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/services/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Services</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/uriloader/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">File Handling</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/widget/cocoa/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Firefox on macOS</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/widget/windows/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Firefox on Windows</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/toolkit/components/ml/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Firefox AI Platform</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/accessible/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Accessibility</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/code-quality/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Code quality</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/writing-rust-code/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Writing Rust Code</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/rust-components/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Rust Components</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/tools/profiler/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Gecko Profiler</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/performance/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Performance</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/storage/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Database bindings (SQLite, KV, …)</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/xpcom/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">XPCOM</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/nspr/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">NSPR</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/security/nss/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Network Security Services (NSS)</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/content-security/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Web Security Checks in Gecko</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">The Firefox Build System</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/mach/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Mach</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/tools/try/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Pushing to Try</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/build/buildsystem/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Build System</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/taskcluster/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Firefox CI and Taskgraph</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/tools/moztreedocs/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Managing Documentation</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/mozbuild/vendor/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Vendoring Third Party Components</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Testing & Test Infrastructure</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/automated-testing/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Automated Testing</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/treeherder-try/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Understanding Treeherder Results</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/sheriffed-intermittents/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Sheriffed intermittent failures</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/tests-for-new-config/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Turning on Firefox tests for a new configuration</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/intermittent/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Avoiding intermittent tests</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/testing-policy/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Testing Policy</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/ci-configs/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Configuration Changes</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/browser-chrome/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Browser chrome mochitests</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/chrome-tests/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Chrome Tests</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/marionette/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Marionette</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/geckodriver/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">geckodriver</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/test-verification/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Test Verification</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/webrender/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">WebRender Tests</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/mochitest-plain/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Mochitest</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/xpcshell/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">XPCShell tests</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/tps/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">TPS</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/web-platform/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">web-platform-tests</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gtest/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">GTest</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/tools/fuzzing/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Fuzzing</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/tools/sanitizer/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Sanitizer</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/perfdocs/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Performance Testing</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/tools/code-coverage/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Code coverage</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/testing-rust-code/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Testing & Debugging Rust Code</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Releases & Updates</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/update-infrastructure/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Mozilla Update Infrastructure</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/update-infrastructure/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#watershed-updates">Watershed Updates</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/update-infrastructure/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#desupport-updates">Desupport Updates</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Localization & Internationalization</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/intl/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Internationalization</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/l10n/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Localization</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Firefox and Python</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/mozbase/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">mozbase</a></li> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/python/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Using third-party Python packages</a></li> </ul> <p class="caption" role="heading"><span class="caption-text">Metrics Collected in Firefox</span></p> <ul> <li class="toctree-l1"><a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/metrics/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Metrics</a></li> </ul> </div> </div> </nav> <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"> <nav class="wy-nav-top" aria-label="Mobile navigation menu"><i data-toggle="wy-nav-top" class="fa fa-bars"></i> <a href="https://firefox--source--docs-mozilla-org.translate.goog/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Firefox Source Docs</a> </nav> <div class="wy-nav-content"> <div class="rst-content"><!-- 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/. --> <div role="navigation" aria-label="Page navigation"> <ul class="wy-breadcrumbs"> <li><a href="https://firefox--source--docs-mozilla-org.translate.goog/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB" class="icon icon-home" aria-label="Home"></a></li> <li class="breadcrumb-item"><a href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Graphics</a></li> <li class="breadcrumb-item active">Asynchronous Panning and Zooming</li> <li class="wy-breadcrumbs-aside"><a href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://bugzilla.mozilla.org/enter_bug.cgi?product%3DDeveloper%2BInfrastructure%26component%3DFirefox%2BSource%2BDocs%253A%2BContent%26short_desc%3DDocumentation%2Bissue%2Bon%2Bgfx/AsyncPanZoom%26comment%3DURL%2B%3D%2Bhttps://firefox-source-docs.mozilla.org/gfx/AsyncPanZoom.html%26bug_file_loc%3Dhttps://firefox-source-docs.mozilla.org/gfx/AsyncPanZoom.html" rel="nofollow">Report an issue</a> / <a href="https://firefox--source--docs-mozilla-org.translate.goog/_sources/gfx/AsyncPanZoom.rst.txt?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB" rel="nofollow"> View page source</a></li> </ul> <hr> </div> <div role="main" class="document" itemscope itemtype="http://schema.org/Article"> <div itemprop="articleBody"> <section id="asynchronous-panning-and-zooming"><span id="apz"></span> <h1>Asynchronous Panning and Zooming<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#asynchronous-panning-and-zooming" title="Link to this heading">¶</a></h1> <p><strong>This document is a work in progress. Some information may be missing or incomplete.</strong></p><img alt="../_images/AsyncPanZoomArchitecture.png" src="../_images/AsyncPanZoomArchitecture.png"> <section id="goals"> <h2>Goals<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#goals" title="Link to this heading">¶</a></h2> <p>We need to be able to provide a visual response to user input with minimal latency. In particular, on devices with touch input, content must track the finger exactly while panning, or the user experience is very poor. According to the UX team, 120ms is an acceptable latency between user input and response.</p> </section> <section id="context-and-surrounding-architecture"> <h2>Context and surrounding architecture<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#context-and-surrounding-architecture" title="Link to this heading">¶</a></h2> <p>The fundamental problem we are trying to solve with the Asynchronous Panning and Zooming (APZ) code is that of responsiveness. By default, web browsers operate in a “game loop” that looks like this:</p> <div class="highlight-default notranslate"> <div class="highlight"> <pre><span></span><span class="k">while</span> <span class="n">true</span><span class="p">:</span> <span class="n">process</span> <span class="nb">input</span> <span class="n">do</span> <span class="n">computations</span> <span class="n">repaint</span> <span class="n">content</span> <span class="n">display</span> <span class="n">repainted</span> <span class="n">content</span> </pre> </div> </div> <p>In browsers the “do computation” step can be arbitrarily expensive because it can involve running event handlers in web content. Therefore, there can be an arbitrary delay between the input being received and the on-screen display getting updated.</p> <p>Responsiveness is always good, and with touch-based interaction it is even more important than with mouse or keyboard input. In order to ensure responsiveness, we split the “game loop” model of the browser into a multithreaded variant which looks something like this:</p> <div class="highlight-default notranslate"> <div class="highlight"> <pre><span></span><span class="n">Thread</span> <span class="mi">1</span> <span class="p">(</span><span class="n">compositor</span> <span class="n">thread</span><span class="p">)</span> <span class="k">while</span> <span class="n">true</span><span class="p">:</span> <span class="n">receive</span> <span class="nb">input</span> <span class="n">send</span> <span class="n">a</span> <span class="n">copy</span> <span class="n">of</span> <span class="nb">input</span> <span class="n">to</span> <span class="n">thread</span> <span class="mi">2</span> <span class="n">adjust</span> <span class="n">rendered</span> <span class="n">content</span> <span class="n">based</span> <span class="n">on</span> <span class="nb">input</span> <span class="n">display</span> <span class="n">adjusted</span> <span class="n">rendered</span> <span class="n">content</span> <span class="n">Thread</span> <span class="mi">2</span> <span class="p">(</span><span class="n">main</span> <span class="n">thread</span><span class="p">)</span> <span class="k">while</span> <span class="n">true</span><span class="p">:</span> <span class="n">receive</span> <span class="nb">input</span> <span class="kn">from</span> <span class="nn">thread</span> <span class="mi">1</span> <span class="n">do</span> <span class="n">computations</span> <span class="n">rerender</span> <span class="n">content</span> <span class="n">update</span> <span class="n">the</span> <span class="n">copy</span> <span class="n">of</span> <span class="n">rendered</span> <span class="n">content</span> <span class="ow">in</span> <span class="n">thread</span> <span class="mi">1</span> </pre> </div> </div> <p>This multithreaded model is called off-main-thread compositing (OMTC), because the compositing (where the content is displayed on-screen) happens on a separate thread from the main thread. Note that this is a very very simplified model, but in this model the “adjust rendered content based on input” is the primary function of the APZ code.</p> <p>A couple of notes on APZ’s relationship to other browser architecture improvements:</p> <ol class="arabic simple"> <li><p>Due to Electrolysis (e10s), Site Isolation (Fission), and GPU Process isolation, the above two threads often actually run in different processes. APZ is largely agnostic to this, as all communication between the two threads for APZ purposes happens using an IPC layer that abstracts over communication between threads vs. processes.</p></li> <li><p>With the WebRender graphics backend, part of the rendering pipeline is also offloaded from the main thread. In this architecture, the information sent from the main thread consists of a display list, and scrolling-related metadata referencing content in that display list. The metadata is kept in a queue until the display list undergoes an additional rendering step in the compositor (scene building). At this point, we are ready to tell APZ about the new content and have it start applying adjustments to it, as further rendering steps beyond scene building are done synchronously on each composite.</p></li> </ol> <p>The compositor in theory can continuously composite previously rendered content (adjusted on each composite by APZ) to the screen while the main thread is busy doing other things and rendering new content.</p> <p>The APZ code takes the input events that are coming in from the hardware and uses them to figure out what the user is trying to do (e.g. pan the page, zoom in). It then expresses this user intention in the form of translation and/or scale transformation matrices. These transformation matrices are applied to the rendered content at composite time, so that what the user sees on-screen reflects what they are trying to do as closely as possible.</p> </section> <section id="technical-overview"> <h2>Technical overview<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#technical-overview" title="Link to this heading">¶</a></h2> <p>As per the heavily simplified model described above, the fundamental purpose of the APZ code is to take input events and produce transformation matrices. This section attempts to break that down and identify the different problems that make this task non-trivial.</p> <section id="checkerboarding"> <h3>Checkerboarding<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#checkerboarding" title="Link to this heading">¶</a></h3> <p>The area of page content for which a display list is built and sent to the compositor is called the “displayport”. The APZ code is responsible for determining how large the displayport should be. On the one hand, we want the displayport to be as large as possible. At the very least it needs to be larger than what is visible on-screen, because otherwise, as soon as the user pans, there will be some unpainted area of the page exposed. However, we cannot always set the displayport to be the entire page, because the page can be arbitrarily long and this would require an unbounded amount of memory to store. Therefore, a good displayport size is one that is larger than the visible area but not so large that it is a huge drain on memory. Because the displayport is usually smaller than the whole page, it is always possible for the user to scroll so fast that they end up in an area of the page outside the displayport. When this happens, they see unpainted content; this is referred to as “checkerboarding”, and we try to avoid it where possible.</p> <p>There are many possible ways to determine what the displayport should be in order to balance the tradeoffs involved (i.e. having one that is too big is bad for memory usage, and having one that is too small results in excessive checkerboarding). Ideally, the displayport should cover exactly the area that we know the user will make visible. Although we cannot know this for sure, we can use heuristics based on current panning velocity and direction to ensure a reasonably-chosen displayport area. This calculation is done in the APZ code, and a new desired displayport is frequently sent to the main thread as the user is panning around.</p> </section> <section id="multiple-scrollable-elements"> <h3>Multiple scrollable elements<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#multiple-scrollable-elements" title="Link to this heading">¶</a></h3> <p>Consider, for example, a scrollable page that contains an iframe which itself is scrollable. The iframe can be scrolled independently of the top-level page, and we would like both the page and the iframe to scroll responsively. This means that we want independent asynchronous panning for both the top-level page and the iframe. In addition to iframes, elements that have the overflow:scroll CSS property set are also scrollable. In the display list, scrollable elements are arranged in a tree structure, and in the APZ code we have a matching tree of AsyncPanZoomController (APZC) objects, one for each scrollable element. To manage this tree of APZC instances, we have a single APZCTreeManager object. Each APZC is relatively independent and handles the scrolling for its associated scrollable element, but there are some cases in which they need to interact; these cases are described in the sections below.</p> </section> <section id="hit-detection"> <h3>Hit detection<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#hit-detection" title="Link to this heading">¶</a></h3> <p>Consider again the case where we have a scrollable page that contains an iframe which itself is scrollable. As described above, we will have two APZC instances - one for the page and one for the iframe. When the user puts their finger down on the screen and moves it, we need to do some sort of hit detection in order to determine whether their finger is on the iframe or on the top-level page. Based on where their finger lands, the appropriate APZC instance needs to handle the input.</p> <p>This hit detection is done by APZCTreeManager in collaboration with WebRender, which has more detailed information about the structure of the page content than is stored in APZ directly. See <a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#wr-hit-test-details"><span class="std std-ref">this section</span></a> for more details.</p> <p>Also note that for some types of input (e.g. when the user puts two fingers down to do a pinch) we do not want the input to be “split” across two different APZC instances. In the case of a pinch, for example, we find a “common ancestor” APZC instance - one that is zoomable and contains all of the touch input points, and direct the input to that APZC instance.</p> </section> <section id="scroll-handoff"> <h3>Scroll Handoff<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#scroll-handoff" title="Link to this heading">¶</a></h3> <p>Consider yet again the case where we have a scrollable page that contains an iframe which itself is scrollable. Say the user scrolls the iframe so that it reaches the bottom. If the user continues panning on the iframe, the expectation is that the top-level page will start scrolling. However, as discussed in the section on hit detection, the APZC instance for the iframe is separate from the APZC instance for the top-level page. Thus, we need the two APZC instances to communicate in some way such that input events on the iframe result in scrolling on the top-level page. This behaviour is referred to as “scroll handoff” (or “fling handoff” in the case where analogous behaviour results from the scrolling momentum of the page after the user has lifted their finger).</p> </section> <section id="input-event-untransformation"><span id="id1"></span> <h3>Input event untransformation<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#input-event-untransformation" title="Link to this heading">¶</a></h3> <p>The APZC architecture by definition results in two copies of a “scroll position” for each scrollable element. There is the original copy on the main thread that is accessible to web content and the layout and painting code. And there is a second copy on the compositor side, which is updated asynchronously based on user input, and corresponds to what the user visually sees on the screen. Although these two copies may diverge temporarily, they are reconciled periodically. In particular, they diverge while the APZ code is performing an async pan or zoom action on behalf of the user, and are reconciled when the APZ code requests a repaint from the main thread.</p> <p>Because of the way input events are represented, this has some unfortunate consequences. Input event coordinates are represented relative to the device screen - so if the user touches at the same physical spot on the device, the same input events will be delivered regardless of the content scroll position. When the main thread receives a touch event, it combines that with the content scroll position in order to figure out what DOM element the user touched. However, because we now have two different scroll positions, this process may not work perfectly. A concrete example follows:</p> <p>Consider a device with screen size 600 pixels tall. On this device, a user is viewing a document that is 1000 pixels tall, and that is scrolled down by 200 pixels. That is, the vertical section of the document from 200px to 800px is visible. Now, if the user touches a point 100px from the top of the physical display, the hardware will generate a touch event with y=100. This will get sent to the main thread, which will add the scroll position (200) and get a document-relative touch event with y=300. This new y-value will be used in hit detection to figure out what the user touched. If the document had a absolute-positioned div at y=300, then that would receive the touch event.</p> <p>Now let us add some async scrolling to this example. Say that the user additionally scrolls the document by another 10 pixels asynchronously (i.e. only on the compositor thread), and then does the same touch event. The same input event is generated by the hardware, and as before, the document will deliver the touch event to the div at y=300. However, visually, the document is scrolled by an additional 10 pixels so this outcome is wrong. What needs to happen is that the APZ code needs to intercept the touch event and account for the 10 pixels of asynchronous scroll. Therefore, the input event with y=100 gets converted to y=110 in the APZ code before being passed on to the main thread. The main thread then adds the scroll position it knows about and determines that the user touched at a document-relative position of y=310.</p> <p>Analogous input event transformations need to be done for horizontal scrolling and zooming.</p> </section> <section id="content-independently-adjusting-scrolling"> <h3>Content independently adjusting scrolling<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#content-independently-adjusting-scrolling" title="Link to this heading">¶</a></h3> <p>As described above, there are two copies of the scroll position in the APZ architecture - one on the main thread and one on the compositor thread. Usually for architectures like this, there is a single “source of truth” value and the other value is simply a copy. However, in this case that is not easily possible to do. The reason is that both of these values can be legitimately modified. On the compositor side, the input events the user is triggering modify the scroll position, which is then propagated to the main thread. However, on the main thread, web content might be running Javascript code that programmatically sets the scroll position (via window.scrollTo, for example). Scroll changes driven from the main thread are just as legitimate and need to be propagated to the compositor thread, so that the visual display updates in response.</p> <p>Because the cross-thread messaging is asynchronous, reconciling the two types of scroll changes is a tricky problem. Our design solves this using various flags and generation counters. The general heuristic we have is that content-driven scroll position changes (e.g. scrollTo from JS) are never lost. For instance, if the user is doing an async scroll with their finger and content does a scrollTo in the middle, then some of the async scroll would occur before the “jump” and the rest after the “jump”.</p> </section> <section id="content-preventing-default-behaviour-of-input-events"> <h3>Content preventing default behaviour of input events<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#content-preventing-default-behaviour-of-input-events" title="Link to this heading">¶</a></h3> <p>Another problem that we need to deal with is that web content is allowed to intercept touch events and prevent the “default behaviour” of scrolling. This ability is defined in web standards and is non-negotiable. Touch event listeners in web content are allowed call preventDefault() on the touchstart or first touchmove event for a touch point; doing this is supposed to “consume” the event and prevent touch-based panning. As we saw in a previous section, the input event needs to be untransformed by the APZ code before it can be delivered to content. But, because of the preventDefault problem, we cannot fully process the touch event in the APZ code until content has had a chance to handle it.</p> <p>To balance the needs of correctness (which calls for allowing web content to successfully prevent default handling of events if it wishes to) and responsiveness (which calls for avoiding blocking on web content Javascript for a potentially-unbounded amount of time before reacting to an event), APZ gives web content a “deadline” to process the event and tell APZ whether preventDefault() was called on the event. The deadline is 400ms from the time APZ receives the event on desktop, and 600ms on mobile. If web content is able to process the event before this deadline, the decision to preventDefault() the event or not will be respected. If web content fails to process the event before the deadline, APZ assumes preventDefault() will not be called and goes ahead and processes the event.</p> <p>To implement this, upon receiving a touch event, APZ immediately returns an untransformed version that can be dispatched to content. It also schedules the 400ms or 600ms timeout. There is an API that allows the main-thread event dispatching code to notify APZ as to whether or not the default action should be prevented. If the APZ content response timeout expires, or if the main-thread event dispatching code notifies the APZ of the preventDefault status, then the APZ continues with the processing of the events (which may involve discarding the events).</p> <p>To limit the responsiveness impact of this round-trip to content, APZ tries to identify cases where it can rule out preventDefault() as a possible outcome. To this end, the hit-testing information sent to the compositor includes information about which regions of the page are occupied by elements that have a touch event listener. If an event targets an area outside of these regions, preventDefault() can be ruled out, and the round-trip skipped.</p> <p>Additionally, recent enhancements to web standards have given page authors new tools that can further limit the responsiveness impact of preventDefault():</p> <ol class="arabic simple"> <li><p>Event listeners can be registered as “passive”, which means they are not allowed to call preventDefault(). Authors can use this flag when writing listeners that only need to observe the events, not alter their behaviour via preventDefault(). The presence of passive event listeners does not cause APZ to perform the content round-trip.</p></li> <li><p>If page authors wish to disable certain types of touch interactions completely, they can use the <code class="docutils literal notranslate"><span class="pre">touch-action</span></code> CSS property from the pointer-events spec to do so declaratively, instead of registering event listeners that call preventDefault(). Touch-action flags are also included in the hit-test information sent to the compositor, and APZ uses this information to respect <code class="docutils literal notranslate"><span class="pre">touch-action</span></code>. (Note that the touch-action information sent to the compositor is not always 100% accurate, and sometimes APZ needs to fall back on asking the main thread for touch-action information, which again involves a round-trip.)</p></li> </ol> </section> <section id="other-event-types"> <h3>Other event types<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#other-event-types" title="Link to this heading">¶</a></h3> <p>The above sections talk mostly about touch events, but over time APZ has been extended to handle a variety of other event types, such as trackpad and mousewheel scrolling, scrollbar thumb dragging, and keyboard scrolling in some cases. Much of the above applies to these other event types too (for example, wheel events can be prevent-defaulted as well).</p> <p>Importantly, the “untransformation” described above needs to happen even for event types which are not handled in APZ, such as mouse click events, since async scrolling can still affect the correct targeting of such events.</p> </section> </section> <section id="technical-details"> <h2>Technical details<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#technical-details" title="Link to this heading">¶</a></h2> <p>This section describes various pieces of the APZ code, and goes into more specific detail on APIs and code than the previous sections. The primary purpose of this section is to help people who plan on making changes to the code, while also not going into so much detail that it needs to be updated with every patch.</p> <section id="overall-flow-of-input-events"> <h3>Overall flow of input events<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#overall-flow-of-input-events" title="Link to this heading">¶</a></h3> <p>This section describes how input events flow through the APZ code.</p> <p>Disclaimer: some details in this section are out of date (for example, it assumes the case where the main thread and compositor thread are in the same process, which is rarely the case these days, so in practice e.g. steps 6 and 8 involve IPC, not just “stack unwinding”).</p> <ol class="arabic simple"> <li><p>Input events arrive from the hardware/widget code into the APZ via APZCTreeManager::ReceiveInputEvent. The thread that invokes this is called the “controller thread”, and may or may not be the same as the Gecko main thread.</p></li> <li><p>Conceptually the first thing that the APZCTreeManager does is to associate these events with “input blocks”. An input block is a set of events that share certain properties, and generally are intended to represent a single gesture. For example with touch events, all events following a touchstart up to but not including the next touchstart are in the same block. All of the events in a given block will go to the same APZC instance and will either all be processed or all be dropped.</p></li> <li><p>Using the first event in the input block, the APZCTreeManager does a hit-test to see which APZC it hits. If no APZC is hit, the events are discarded and we jump to step 6. Otherwise, the input block is tagged with the hit APZC as a tentative target and put into a global APZ input queue. In addition the target APZC, the result of the hit test also includes whether the input event landed on a “dispatch-to-content” region. These are regions of the page where there is something going on that requires dispatching the event to content and waiting for a response _before_ processing the event in APZ; an example of this is a region containing an element with a non-passive event listener, as described above. (TODO: Add a section that talks about the other uses of the dispatch-to-content mechanism.)</p></li> <li> <ol class="lowerroman simple"> <li><p>If the input events landed outside a dispatch-to-content region, any available events in the input block are processed. These may trigger behaviours like scrolling or tap gestures.</p></li> <li><p>If the input events landed inside a dispatch-to-content region, the events are left in the queue and a timeout is initiated. If the timeout expires before step 9 is completed, the APZ assumes the input block was not cancelled and the tentative target is correct, and processes them as part of step 10.</p></li> </ol></li> <li><p>The call stack unwinds back to APZCTreeManager::ReceiveInputEvent, which does an in-place modification of the input event so that any async transforms are removed.</p></li> <li><p>The call stack unwinds back to the widget code that called ReceiveInputEvent. This code now has the event in the coordinate space Gecko is expecting, and so can dispatch it to the Gecko main thread.</p></li> <li><p>Gecko performs its own usual hit-testing and event dispatching for the event. As part of this, it records whether any touch listeners cancelled the input block by calling preventDefault(). It also activates inactive scrollframes that were hit by the input events.</p></li> <li><p>The call stack unwinds back to the widget code, which sends two notifications to the APZ code on the controller thread. The first notification is via APZCTreeManager::ContentReceivedInputBlock, and informs the APZ whether the input block was cancelled. The second notification is via APZCTreeManager::SetTargetAPZC, and informs the APZ of the results of the Gecko hit-test during event dispatch. Note that Gecko may report that the input event did not hit any scrollable frame at all. The SetTargetAPZC notification happens only once per input block, while the ContentReceivedInputBlock notification may happen once per block, or multiple times per block, depending on the input type.</p></li> <li> <ol class="lowerroman simple"> <li><p>If the events were processed as part of step 4(i), the notifications from step 8 are ignored and step 10 is skipped.</p></li> <li><p>If events were queued as part of step 4(ii), and steps 5-8 complete before the timeout, the arrival of both notifications from step 8 will mark the input block ready for processing.</p></li> <li><p>If events were queued as part of step 4(ii), but steps 5-8 take longer than the timeout, the notifications from step 8 will be ignored and step 10 will already have happened.</p></li> </ol></li> <li><p>If events were queued as part of step 4(ii) they are now either processed (if the input block was not cancelled and Gecko detected a scrollframe under the input event, or if the timeout expired) or dropped (all other cases). Note that the APZC that processes the events may be different at this step than the tentative target from step 3, depending on the SetTargetAPZC notification. Processing the events may trigger behaviours like scrolling or tap gestures.</p></li> </ol> <p>If the CSS touch-action property is enabled, the above steps are modified as follows:</p> <ul class="simple"> <li><p>In step 4, the APZC also requires the allowed touch-action behaviours for the input event. This might have been determined as part of the hit-test in APZCTreeManager; if not, the events are queued.</p></li> <li><p>In step 6, the widget code determines the content element at the point under the input element, and notifies the APZ code of the allowed touch-action behaviours. This notification is sent via a call to APZCTreeManager::SetAllowedTouchBehavior on the input thread.</p></li> <li><p>In step 9(ii), the input block will only be marked ready for processing once all three notifications arrive.</p></li> </ul> <section id="threading-considerations"> <h4>Threading considerations<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#threading-considerations" title="Link to this heading">¶</a></h4> <p>The bulk of the input processing in the APZ code happens on what we call “the controller thread”. In practice the controller thread could be the Gecko main thread, the compositor thread, or some other thread. There are obvious downsides to using the Gecko main thread - that is,“asynchronous” panning and zooming is not really asynchronous as input events can only be processed while Gecko is idle. In an e10s environment, using the Gecko main thread of the chrome process is acceptable, because the code running in that process is more controllable and well-behaved than arbitrary web content. Using the compositor thread as the controller thread could work on some platforms, but may be inefficient on others. For example, on Android (Fennec) we receive input events from the system on a dedicated UI thread. We would have to redispatch the input events to the compositor thread if we wanted to the input thread to be the same as the compositor thread. This introduces a potential for higher latency, particularly if the compositor does any blocking operations - blocking SwapBuffers operations, for example. As a result, the APZ code itself does not assume that the controller thread will be the same as the Gecko main thread or the compositor thread.</p> </section> <section id="active-vs-inactive-scrollframes"> <h4>Active vs. inactive scrollframes<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#active-vs-inactive-scrollframes" title="Link to this heading">¶</a></h4> <p>The number of scrollframes on a page is potentially unbounded. However, we do not want to create a separate displayport for each scrollframe right away, as this would require large amounts of memory. Therefore, scrollframes as designated as either “active” or “inactive”. Active scrollframes get a displayport, and an APZC on the compositor side. Inactive scrollframes do not get a displayport (a display list is only built for their viewport, i.e. what is currently visible) and do not get an APZC.</p> <p>Consider a page with a scrollframe that is initially inactive. This scroll frame does not get an APZC, and therefore events targeting it will target the APZC for the nearest active scrollable ancestor (let’s call it P; note, the rootmost scroll frame in a given process is always active). However, the presence of the inactive scroll frame is reflected by a dispatch-to-content region that prevents events over the frame from erroneously scrolling P.</p> <p>When the user starts interacting with that content, the hit-test in the APZ code hits the dispatch-to-content region of P. The input block therefore has a tentative target of P when it goes into step 4(ii) in the flow above. When gecko processes the input event, it must detect the inactive scrollframe and activate it, as part of step 7. Finally, the widget code sends the SetTargetAPZC notification in step 8 to notify the APZ that the input block should really apply to this new APZC. An issue here is that the transaction containing metadata for the newly active scroll frame must reach the compositor and APZ before the SetTargetAPZC notification. If this does not occur within the 400ms timeout, the APZ code will be unable to update the tentative target, and will continue to use P for that input block. Input blocks that start after the transaction will get correctly routed to the new scroll frame as there will now be an APZC instance for the active scrollframe.</p> <p>This model implies that when the user initially attempts to scroll an inactive scrollframe, it may end up scrolling an ancestor scrollframe. Only after the round-trip to the gecko thread is complete is there an APZC for async scrolling to actually occur on the scrollframe itself. At that point the scrollframe will start receiving new input blocks and will scroll normally.</p> <p>Note: with Fission (where inactive scroll frames would make it impossible to target the correct process in all situations; see <a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#fission-hit-testing"><span class="std std-ref">this section</span></a> for more details) and WebRender (which makes displayports more lightweight as the actual rendering is offloaded to the compositor and can be done on demand), inactive scroll frames are being phased out, and we are moving towards a model where all scroll frames with nonempty scroll ranges are active and get a displayport and an APZC. To conserve memory, displayports for scroll frames which have not been recently scrolled are kept to a “minimal” size equal to the viewport size.</p> </section> </section> <section id="webrender-integration"> <h3>WebRender Integration<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#webrender-integration" title="Link to this heading">¶</a></h3> <p>This section describes how APZ interacts with the WebRender graphics backend.</p> <p>Note that APZ predates WebRender, having initially been written to work with the earlier Layers graphics backend. The design of Layers has influenced APZ significantly, and this still shows in some places in the code. Now that the Layers backend has been removed, there may be opportunities to streamline the interaction between APZ and WebRender.</p> <section id="hittestingtree"> <h4>HitTestingTree<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#hittestingtree" title="Link to this heading">¶</a></h4> <p>The APZCTreeManager keeps as part of its internal state a tree of HitTestingTreeNode instances. This is referred to as the HitTestingTree.</p> <p>The main purpose of the HitTestingTree is to model the spatial relationships between content that’s affected by async scrolling. Tree nodes fall roughly into the following categories:</p> <ul class="simple"> <li><p>Nodes representing scrollable content in an active scroll frame. These nodes are associated with the scroll frame’s APZC.</p></li> <li><p>Nodes representing other content that may move in special ways in response to async scrolling, such as fixed content, sticky content, and scrollbars.</p></li> <li><p>(Non-leaf) nodes which do not represent any content, just metadata (e.g. a transform) that applies to its descendant nodes.</p></li> </ul> <p>An APZC may be associated with multiple nodes, if e.g. a scroll frame scrolls two pieces of content that are interleaved with non-scrolling content.</p> <p>Arranging these nodes in a tree allows modelling relationships such as what content is scrolled by a given scroll frame, what the scroll handoff relationships are between APZCs, and what content is subject to what transforms.</p> <p>An additional use of the HitTestingTree is to allow APZ to keep content processes up to date about enclosing transforms that they are subject to. See <a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#sending-transforms-to-content-processes"><span class="std std-ref">this section</span></a> for more details.</p> <p>(In the past, with the Layers backend, the HitTestingTree was also used for compositor hit testing, hence the name. This is no longer the case, and there may be opportunities to simplify the tree as a result.)</p> <p>The HitTestingTree is created from another tree data structure called WebRenderScrollData. The relevant types here are:</p> <ul class="simple"> <li><p>WebRenderScrollData which stores the entire tree.</p></li> <li><p>WebRenderLayerScrollData, which represents a single “layer” of content, i.e. a group of display items that move together when scrolling (or metadata applying to a subtree of such layers). In the Layers backend, such content would be rendered into a single texture which could then be moved asynchronously at composite time. Since a layer of content can be scrolled by multiple (nested) scroll frames, a WebRenderLayerScrollData may contain scroll metadata for more than one scroll frame.</p></li> <li><p>WebRenderScrollDataWrapper, which wraps WebRenderLayerScrollData but “expanded” in a way that each node only stores metadata for a single scroll frame. WebRenderScrollDataWrapper nodes have a 1:1 correspondence with HitTestingTreeNodes.</p></li> </ul> <p>It’s not clear whether the distinction between WebRenderLayerScrollData and WebRenderScrollDataWrapper is still useful in a WebRender-only world. The code could potentially be revised such that we directly build and store nodes of a single type with the behaviour of WebRenderScrollDataWrapper.</p> <p>The WebRenderScrollData structure is built on the main thread, and then shipped over IPC to the compositor where it’s used to construct the HitTestingTree.</p> <p>WebRenderScrollData is built in WebRenderCommandBuilder, during the same traversal of the Gecko display list that is used to build the WebRender display list. As of this writing, the architecture for this is that, as we walk the Gecko display list, we query it to see if it contains any information that APZ might need to know (e.g. CSS transforms) via a call to <code class="docutils literal notranslate"><span class="pre">nsDisplayItem::UpdateScrollData(nullptr,</span> <span class="pre">nullptr)</span></code>. If this call returns true, we create a WebRenderLayerScrollData instance for the item, and populate it with the necessary information in <code class="docutils literal notranslate"><span class="pre">WebRenderLayerScrollData::Initialize</span></code>. We also create WebRenderLayerScrollData instances if we detect (via ASR changes) that we are now processing a Gecko display item that is in a different scrollframe than the previous item.</p> <p>The main sources of complexity in this code come from:</p> <ol class="arabic simple"> <li><p>Ensuring the ScrollMetadata instances end on the proper WebRenderLayerScrollData instances (such that every path from a leaf WebRenderLayerScrollData node to the root has a consistent ordering of scrollframes without duplications).</p></li> <li><p>The deferred-transform optimization that is described in more detail at the declaration of <code class="docutils literal notranslate"><span class="pre">StackingContextHelper::mDeferredTransformItem</span></code>.</p></li> </ol> </section> <section id="hit-testing"><span id="wr-hit-test-details"></span> <h4>Hit-testing<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#hit-testing" title="Link to this heading">¶</a></h4> <p>Since the HitTestingTree is not used for actual hit-testing purposes with the WebRender backend (see previous section), this section describes how hit-testing actually works with WebRender.</p> <p>The Gecko display list contains display items (<code class="docutils literal notranslate"><span class="pre">nsDisplayCompositorHitTestInfo</span></code>) that store hit-testing state. These items implement the <code class="docutils literal notranslate"><span class="pre">CreateWebRenderCommands</span></code> method and generate a “hit-test item” into the WebRender display list. This is basically just a rectangle item in the WebRender display list that is no-op for painting purposes, but contains information that should be returned by the hit-test (specifically the hit info flags and the scrollId of the enclosing scrollframe). The hit-test item gets clipped and transformed in the same way that all the other items in the WebRender display list do, via clip chains and enclosing reference frame/stacking context items.</p> <p>When WebRender needs to do a hit-test, it goes through its display list, taking into account the current clips and transforms, adjusted for the most recent async scroll/zoom, and determines which hit-test item(s) are under the target point, and returns those items. APZ can then take the frontmost item from that list (or skip over it if it happens to be inside a OOP subdocument that’s <code class="docutils literal notranslate"><span class="pre">pointer-events:none</span></code>) and use that as the hit target. Note that the hit-test uses the last transform provided by the <code class="docutils literal notranslate"><span class="pre">SampleForWebRender</span></code> API (see next section) which generally reflects the last composite, and doesn’t take into account further changes to the transforms that have occurred since then. In practice, we should be compositing frequently enough that this doesn’t matter much.</p> <p>When debugging hit-test issues, it is often useful to apply the patches on <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://bugzilla.mozilla.org/show_bug.cgi?id%3D1656260">bug 1656260</a>, which introduce a guid on Gecko display items and propagate it all the way through to where APZ gets the hit-test result. This allows answering the question “which nsDisplayCompositorHitTestInfo was responsible for this hit-test result?” which is often a very good first step in solving the bug. From there, one can determine if there was some other display item in front that should have generated a nsDisplayCompositorHitTestInfo but didn’t, or if display item itself had incorrect information. The second patch on that bug further allows exposing hand-written debug info to the APZ code, so that the WR hit-testing mechanism itself can be more effectively debugged, in case there is a problem with the WR display items getting improperly transformed or clipped.</p> <p>The information returned by WebRender to APZ in response to the hit test is enough for APZ to identify a HitTestingTreeNode as the target of the event. APZ can then take actions such as scrolling the target node’s associated APZC, or other appropriate actions (e.g. initiating a scrollbar drag if a scrollbar thumb node was targeted by a mouse-down event).</p> </section> <section id="sampling"> <h4>Sampling<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#sampling" title="Link to this heading">¶</a></h4> <p>The compositing step needs to read the latest async transforms from APZ in order to ensure scrollframes are rendered at the right position. The API for this is exposed via the <code class="docutils literal notranslate"><span class="pre">APZSampler</span></code> class. When WebRender is ready to do a composite, it invokes <code class="docutils literal notranslate"><span class="pre">APZSampler::SampleForWebRender</span></code>. In here, APZ gathers all async transforms that WebRender needs to know about, including transforms to apply to scrolled content, fixed and sticky content, and scrollbar thumbs.</p> <p>Along with sampling the APZ transforms, the compositor also triggers APZ animations to advance to the next timestep (usually the next vsync). This happens just before reading the APZ transforms.</p> </section> </section> <section id="fission-integration"> <h3>Fission Integration<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#fission-integration" title="Link to this heading">¶</a></h3> <p>This section describes how APZ interacts with the Fission (Site Isolation) project.</p> <section id="introduction"> <h4>Introduction<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#introduction" title="Link to this heading">¶</a></h4> <p>Fission is an architectural change motivated by security considerations, where web content from each origin is isolated in its own process. Since a page can contain a mixture of content from different origins (for example, the top level page can be content from origin A, and it can contain an iframe with content from origin B), that means that rendering and interacting with a page can now involve coordination between APZ and multiple content processes.</p> </section> <section id="content-process-selection-for-input-events"><span id="fission-hit-testing"></span> <h4>Content Process Selection for Input Events<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#content-process-selection-for-input-events" title="Link to this heading">¶</a></h4> <p>Input events are initially received in the browser’s parent process. With Fission, the browser needs to decide which of possibly several content processes an event is targeting.</p> <p>Since process boundaries correspond to iframe (subdocument) boundaries, and every (html) document has a root scroll frame, process boundaries are therefore also scroll frame boundaries. Since APZ already needs a hit test mechanism to be able to determine which scroll frame an event targets, this hit test mechanism was a good fit to also use to determine which content process an event targets.</p> <p>APZ’s hit test was therefore expanded to serve this purpose as well. This mostly required only minor modifications, such as making sure that APZ knows about the root scroll frames of iframes even if they’re not scrollable. Since APZ already needs to process all input events to potentially apply <a class="reference internal" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#input-event-untransformation"><span class="std std-ref">untransformations</span></a> related to async scrolling, as part of this process it now also labels input events with information identifying which content process they target.</p> </section> <section id="hit-testing-accuracy"> <h4>Hit Testing Accuracy<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#hit-testing-accuracy" title="Link to this heading">¶</a></h4> <p>Prior to Fission, APZ’s hit test could afford to be somewhat inaccurate, as it could fall back on the dispatch-to-content mechanism to wait for a more accurate answer from the main thread if necessary, suffering a performance cost only (not a correctness cost).</p> <p>With Fission, an inaccurate compositor hit test now implies a correctness cost, as there is no cross-process main-thread fallback mechanism. (Such a mechanism was considered, but judged to require too much complexity and IPC traffic to be worth it.)</p> <p>Luckily, with WebRender the compositor has much more detailed information available to use for hit testing than it did with Layers. For example, the compositor can perform accurate hit testing even in the presence of irregular shapes such as rounded corners.</p> <p>APZ leverages WebRender’s more accurate hit testing ability to aim to accurately select the target process (and target scroll frame) for an event in general.</p> <p>One consequence of this is that the dispatch-to-content mechanism is now used less often than before (its primary remaining use is handling <cite>preventDefault()</cite>).</p> </section> <section id="sending-transforms-to-content-processes"><span id="id2"></span> <h4>Sending Transforms To Content Processes<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#sending-transforms-to-content-processes" title="Link to this heading">¶</a></h4> <p>Content processes sometimes need to be able to convert between screen coordinates and their local coordinates. To do this, they need to know about any transforms that their containing iframe and its ancestors are subject to, including async transforms (particularly in cases where the async transforms persist for more than just a few frames).</p> <p>APZ has information about these transforms in its HitTestingTree. With Fission, APZ periodically sends content processes information about these transforms so that they are kept relatively up to date.</p> </section> </section> </section> <section id="testing"> <h2>Testing<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#testing" title="Link to this heading">¶</a></h2> <p>APZ makes use of several test frameworks to verify the expected behavior is seen.</p> <section id="mochitest"> <h3>Mochitest<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#mochitest" title="Link to this heading">¶</a></h3> <p>The APZ specific mochitests are useful when specific gestures or events need to be tested with specific content. The APZ mochitests are located in <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://searchfox.org/mozilla-central/source/gfx/layers/apz/test/mochitest">gfx/layers/apz/test/mochitest</a>. To run all of the APZ mochitests, run something like the following:</p> <div class="highlight-default notranslate"> <div class="highlight"> <pre><span></span><span class="o">./</span><span class="n">mach</span> <span class="n">mochitest</span> <span class="o">./</span><span class="n">gfx</span><span class="o">/</span><span class="n">layers</span><span class="o">/</span><span class="n">apz</span><span class="o">/</span><span class="n">test</span><span class="o">/</span><span class="n">mochitest</span> </pre> </div> </div> <p>The APZ mochitests are often organized as subtests that run in a group. For example, the <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://searchfox.org/mozilla-central/source/gfx/layers/apz/test/mochitest/test_group_hittest-2.html">test_group_hittest-2.html</a> contains >20 subtests like <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://searchfox.org/mozilla-central/source/gfx/layers/apz/test/mochitest/helper_hittest_overscroll.html">helper_hittest_overscroll.html</a>. When working on a specific subtest, it is often helpful to use the <cite>apz.subtest</cite> preference to filter the subtests run to just the tests you are working on. For example, the following would only run the <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://searchfox.org/mozilla-central/source/gfx/layers/apz/test/mochitest/helper_hittest_overscroll.html">helper_hittest_overscroll.html</a> subtest of the <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://searchfox.org/mozilla-central/source/gfx/layers/apz/test/mochitest/test_group_hittest-2.html">test_group_hittest-2.html</a> group.</p> <div class="highlight-default notranslate"> <div class="highlight"> <pre><span></span><span class="o">./</span><span class="n">mach</span> <span class="n">mochitest</span> <span class="o">--</span><span class="n">setpref</span> <span class="n">apz</span><span class="o">.</span><span class="n">subtest</span><span class="o">=</span><span class="n">helper_hittest_overscroll</span><span class="o">.</span><span class="n">html</span> \ <span class="o">./</span><span class="n">gfx</span><span class="o">/</span><span class="n">layers</span><span class="o">/</span><span class="n">apz</span><span class="o">/</span><span class="n">test</span><span class="o">/</span><span class="n">mochitest</span><span class="o">/</span><span class="n">test_group_hittest</span><span class="o">-</span><span class="mf">2.</span><span class="n">html</span> </pre> </div> </div> <p>For more information on mochitest, see the <a class="reference external" href="https://firefox--source--docs-mozilla-org.translate.goog/testing/mochitest-plain/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Mochitest Documentation</a>.</p> </section> <section id="gtest"> <h3>GTest<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#gtest" title="Link to this heading">¶</a></h3> <p>The APZ specific GTests can be found in <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://searchfox.org/mozilla-central/source/gfx/layers/apz/test/gtest/">gfx/layers/apz/test/gtest/</a>. To run these tests, run something like the following:</p> <div class="highlight-default notranslate"> <div class="highlight"> <pre><span></span><span class="o">./</span><span class="n">mach</span> <span class="n">gtest</span> <span class="s2">"APZ*"</span> </pre> </div> </div> <p>For more information, see the <a class="reference external" href="https://firefox--source--docs-mozilla-org.translate.goog/gtest/index.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">GTest Documentation</a>.</p> </section> <section id="reftests"> <h3>Reftests<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#reftests" title="Link to this heading">¶</a></h3> <p>The APZ reftests can be found in <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://searchfox.org/mozilla-central/source/layout/reftests/async-scrolling/">layout/reftests/async-scrolling/</a> and <a class="reference external" href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://searchfox.org/mozilla-central/source/gfx/layers/apz/test/reftest/">gfx/layers/apz/test/reftest</a>. To run the relevant reftests for APZ, run a large portion of the APZ reftests, run something like the following:</p> <div class="highlight-default notranslate"> <div class="highlight"> <pre><span></span><span class="o">./</span><span class="n">mach</span> <span class="n">reftest</span> <span class="o">./</span><span class="n">layout</span><span class="o">/</span><span class="n">reftests</span><span class="o">/</span><span class="k">async</span><span class="o">-</span><span class="n">scrolling</span><span class="o">/</span> </pre> </div> </div> <p>Useful information about the reftests can be found in the <a class="reference external" href="https://firefox--source--docs-mozilla-org.translate.goog/layout/Reftest.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB">Reftest Documentation</a>.</p> <p>There is no defined process for choosing which directory the APZ reftests should be placed in, but in general reftests should exist where other similar tests do.</p> </section> </section> <section id="threading-locking-overview"> <h2>Threading / Locking Overview<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#threading-locking-overview" title="Link to this heading">¶</a></h2> <section id="threads"> <h3>Threads<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#threads" title="Link to this heading">¶</a></h3> <p>There are three threads relevant to APZ: the <strong>controller thread</strong>, the <strong>updater thread</strong>, and the <strong>sampler thread</strong>. This table lists which threads play these roles on each platform / configuration:</p> <table class="docutils align-default"> <thead> <tr class="row-odd"> <th class="head"><p>APZ Thread Name</p></th> <th class="head"><p>Desktop</p></th> <th class="head"><p>Desktop+GPU</p></th> <th class="head"><p>Android</p></th> </tr> </thead> <tbody> <tr class="row-even"> <td><p><strong>controller thread</strong></p></td> <td><p>UI main</p></td> <td><p>GPU main</p></td> <td><p>Java UI</p></td> </tr> <tr class="row-odd"> <td><p><strong>updater thread</strong></p></td> <td><p>SceneBuilder</p></td> <td><p>SceneBuilder</p></td> <td><p>SceneBuilder</p></td> </tr> <tr class="row-even"> <td><p><strong>sampler thread</strong></p></td> <td><p>RenderBackend</p></td> <td><p>RenderBackend</p></td> <td><p>RenderBackend</p></td> </tr> </tbody> </table> </section> <section id="locks"> <h3>Locks<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#locks" title="Link to this heading">¶</a></h3> <p>There are also a number of locks used in APZ code:</p> <table class="docutils align-default"> <thead> <tr class="row-odd"> <th class="head"><p>Lock type</p></th> <th class="head"><p>How many instances</p></th> </tr> </thead> <tbody> <tr class="row-even"> <td><p>APZ tree lock</p></td> <td><p>one per APZCTreeManager</p></td> </tr> <tr class="row-odd"> <td><p>APZC map lock</p></td> <td><p>one per APZCTreeManager</p></td> </tr> <tr class="row-even"> <td><p>APZC instance lock</p></td> <td><p>one per AsyncPanZoomController</p></td> </tr> <tr class="row-odd"> <td><p>APZ test lock</p></td> <td><p>one per APZCTreeManager</p></td> </tr> <tr class="row-even"> <td><p>Checkerboard event lock</p></td> <td><p>one per AsyncPanZoomController</p></td> </tr> </tbody> </table> </section> <section id="thread-lock-ordering"> <h3>Thread / Lock Ordering<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#thread-lock-ordering" title="Link to this heading">¶</a></h3> <p>To avoid deadlocks, the threads and locks have a global <strong>ordering</strong> which must be respected.</p> <p>Respecting the ordering means the following:</p> <ul class="simple"> <li><p>Let “A < B” denote that A occurs earlier than B in the ordering</p></li> <li><p>Thread T may only acquire lock L, if T < L</p></li> <li><p>A thread may only acquire lock L2 while holding lock L1, if L1 < L2</p></li> <li><p>A thread may only block on a response from another thread T while holding a lock L, if L < T</p></li> </ul> <p><strong>The lock ordering is as follows</strong>:</p> <ol class="arabic simple"> <li><p>UI main</p></li> <li><p>GPU main (only if GPU process enabled)</p></li> <li><p>Compositor thread</p></li> <li><p>SceneBuilder thread</p></li> <li><p><strong>APZ tree lock</strong></p></li> <li><p>RenderBackend thread</p></li> <li><p><strong>APZC map lock</strong></p></li> <li><p><strong>APZC instance lock</strong></p></li> <li><p><strong>APZ test lock</strong></p></li> <li><p><strong>Checkerboard event lock</strong></p></li> </ol> <section id="example-workflows"> <h4>Example workflows<a class="headerlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#example-workflows" title="Link to this heading">¶</a></h4> <p>Here are some example APZ workflows. Observe how they all obey the global thread/lock ordering. Feel free to add others:</p> <ul class="simple"> <li><p><strong>Input handling</strong> (with GPU process): UI main -> GPU main -> APZ tree lock -> RenderBackend thread</p></li> <li><p><strong>Sync messages</strong> in <code class="docutils literal notranslate"><span class="pre">PCompositorBridge.ipdl</span></code>: UI main thread -> Compositor thread</p></li> <li><p><strong>GetAPZTestData</strong>: Compositor thread -> SceneBuilder thread -> test lock</p></li> <li><p><strong>Scene swap</strong>: SceneBuilder thread -> APZ tree lock -> RenderBackend thread</p></li> <li><p><strong>Updating hit-testing tree</strong>: SceneBuilder thread -> APZ tree lock -> APZC instance lock</p></li> <li><p><strong>Updating APZC map</strong>: SceneBuilder thread -> APZ tree lock -> APZC map lock</p></li> <li><p><strong>Sampling and animation deferred tasks</strong> <a class="footnote-reference brackets" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#id4" id="id3" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a>: RenderBackend thread -> APZC map lock -> APZC instance lock</p></li> <li><p><strong>Advancing animations</strong>: RenderBackend thread -> APZC instance lock</p></li> </ul> <aside class="footnote-list brackets"> <aside class="footnote brackets" id="id4" role="doc-footnote"><span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/AsyncPanZoom.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB#id3">1</a><span class="fn-bracket">]</span></span> <p>It looks like there are two deferred tasks that actually need the tree lock, <code class="docutils literal notranslate"><span class="pre">AsyncPanZoomController::HandleSmoothScrollOverscroll</span></code> and <code class="docutils literal notranslate"><span class="pre">AsyncPanZoomController::HandleFlingOverscroll</span></code>. We should be able to rewrite these to use the map lock instead of the tree lock. This will allow us to continue running the deferred tasks on the sampler thread rather than having to bounce them to another thread.</p> </aside> </aside> </section> </section> </section> </section> </div> </div> <footer> <div class="rst-footer-buttons" role="navigation" aria-label="Footer"><a href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/LayersHistory.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB" class="btn btn-neutral float-left" title="Layers History" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a> <a href="https://firefox--source--docs-mozilla-org.translate.goog/gfx/Silk.html?_x_tr_sl=pl&_x_tr_tl=iw&_x_tr_hl=en-GB" class="btn btn-neutral float-right" title="Silk Overview" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a> </div> <hr> <div role="contentinfo"> <p></p> </div> Built with <a href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://www.sphinx-doc.org/">Sphinx</a> using a <a href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://github.com/readthedocs/sphinx_rtd_theme">theme</a> provided by <a href="https://translate.google.com/website?sl=pl&tl=iw&hl=en-GB&u=https://readthedocs.org">Read the Docs</a>. </footer> </div> </div> </section> </div> <script> jQuery(function () { SphinxRtdTheme.Navigation.enable(true); }); </script> <script>function gtElInit() {var lib = new google.translate.TranslateService();lib.translatePage('pl', 'iw', function () {});}</script> <script src="https://translate.google.com/translate_a/element.js?cb=gtElInit&hl=en-GB&client=wt" type="text/javascript"></script> </body> </html>