From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../android/docs/geckoview/contributor/apilint.rst | 85 +++ .../geckoview/contributor/contributing-to-mc.rst | 188 +++++ .../geckoview/contributor/for-gecko-engineers.rst | 176 +++++ .../contributor/geckoview-architecture.rst | 826 +++++++++++++++++++++ .../contributor/geckoview-quick-start.rst | 342 +++++++++ .../android/docs/geckoview/contributor/index.rst | 29 + .../android/docs/geckoview/contributor/junit.rst | 373 ++++++++++ .../docs/geckoview/contributor/mc-quick-start.rst | 184 +++++ .../geckoview/contributor/native-debugging.rst | 262 +++++++ 9 files changed, 2465 insertions(+) create mode 100644 mobile/android/docs/geckoview/contributor/apilint.rst create mode 100644 mobile/android/docs/geckoview/contributor/contributing-to-mc.rst create mode 100644 mobile/android/docs/geckoview/contributor/for-gecko-engineers.rst create mode 100644 mobile/android/docs/geckoview/contributor/geckoview-architecture.rst create mode 100644 mobile/android/docs/geckoview/contributor/geckoview-quick-start.rst create mode 100644 mobile/android/docs/geckoview/contributor/index.rst create mode 100644 mobile/android/docs/geckoview/contributor/junit.rst create mode 100644 mobile/android/docs/geckoview/contributor/mc-quick-start.rst create mode 100644 mobile/android/docs/geckoview/contributor/native-debugging.rst (limited to 'mobile/android/docs/geckoview/contributor') diff --git a/mobile/android/docs/geckoview/contributor/apilint.rst b/mobile/android/docs/geckoview/contributor/apilint.rst new file mode 100644 index 0000000000..9d9e315896 --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/apilint.rst @@ -0,0 +1,85 @@ +apilint release process +~~~~~~~~~~~~~~~~~~~~~~~ + +To release a new version of `apilint `_, do the following: + +- Create a commit titled "Branch X.Y" and modify the files ``apilint/build.gradle`` and ``apilint/Config.java`` accordingly. See for example `Branch 0.5 `_. Note that it's not necessary to modify ``apilint/Config.java`` if there aren't any ``apidoc`` changes. + +- Create a git tag with the branch version + +.. code:: bash + + $ git tag X.Y + +- Run tests locally by running + +.. code:: bash + + $ ./gradlew build + + +- Publish new version to local repository + +.. code:: bash + + $ ./gradlew publishToMavenLocal + +- Modify ``mozilla-central`` locally to test ``apilint`` with the new version, add ``mavenLocal()`` to every ``repositories {}`` block inside the root ``build.gradle``, e.g. + + +.. code:: diff + + diff --git a/build.gradle b/build.gradle + index 813ba09aa3d4b..753fdb8d958a6 100644 + --- a/build.gradle + +++ b/build.gradle + @@ -60,6 +60,7 @@ allprojects { + } + + repositories { + + mavenLocal() + gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository -> + maven { + url repository + @@ -100,6 +101,7 @@ buildDir "${topobjdir}/gradle/build" + + buildscript { + repositories { + + mavenLocal() + gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository -> + maven { + url repository + @@ -113,7 +115,7 @@ buildscript { + ext.kotlin_version = '1.5.31' + + dependencies { + - classpath 'org.mozilla.apilint:apilint:0.5.2' + + classpath 'org.mozilla.apilint:apilint:0.X.Y' + classpath 'com.android.tools.build:gradle:7.0.3' + classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2' + classpath 'org.apache.commons:commons-exec:1.3' + +* Test integration running ``api-lint``, this should always pass with no ``api.txt`` modifications needed (there could be exceptions, but should be intentional). + +.. code:: bash + + $ ./mach lint -l android-api-lint + +- Push the tag to the remote repository (note, the branch commit is `not` pushed to the main branch). + +.. code:: bash + + $ git push -u origin X.Y + +- Wait until github automation finishes successfully. +- (optional, if there are any ``apidoc`` changes) ask the Releng team to publish a new `apidoc` version, the bundle will be present under the github artifacts, e.g. see ``maven.zip`` in `releases/tag/0.5 `_. See also `Bug 1727585 `_. + +- Add the ``plugins.gradle.org`` keys to your ``.gradle`` folder, see `publishing_gradle_plugins.html `_. + +- Publish plugin by running + +.. code:: bash + + $ ./gradlew apilint:publishPlugins + +- Finally, update ``mozilla-central`` to use the new version, e.g. see `this patch `_. diff --git a/mobile/android/docs/geckoview/contributor/contributing-to-mc.rst b/mobile/android/docs/geckoview/contributor/contributing-to-mc.rst new file mode 100644 index 0000000000..ee4f5be877 --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/contributing-to-mc.rst @@ -0,0 +1,188 @@ +.. -*- Mode: rst; fill-column: 80; -*- + +================================= +Mozilla Central Contributor Guide +================================= + +Table of contents +================= + +.. contents:: :local: + +Submitting a patch to Firefox using Git. +======================================== + +This guide will take you through submitting and updating a patch to +``mozilla-central`` as a git user. You need to already be `set up to use +git to contribute to mozilla-central `_. + +Performing a bug fix +-------------------- + +All of the open bugs for issues in Firefox can be found in +`Bugzilla `_. If you know the component +that you wish to contribute to you can use Bugzilla to search for issues +in that project. If you are unsure which component you are interested +in, you can search the `Good First +Bugs `_ +list to find something you want to work on. + +- Once you have your bug, assign it to yourself in Bugzilla. +- Update your local copy of the firefox codebase to match the current + version on the servers to ensure you are working with the most up to + date code. + +.. code:: bash + + git remote update + +- Create a new feature branch tracking either Central or Inbound. + +.. code:: bash + + git checkout -b bugxxxxxxx [inbound|central]/default + +- Work on your bug, checking into git according to your preferred + workflow. *Try to ensure that each individual commit compiles and + passes all of the tests for your component. This will make it easier + to land if you use ``moz-phab`` to submit (details later in this + post).* + +It may be helpful to have Mozilla commit access, at least level 1. There +are three levels of commit access that give increasing levels of access +to the repositories. + +Level 1: Try/User access. You will need this level of access commit to +the try server. + +Level 2: General access. This will give you full commit +access to any mercurial or SVN repository not requiring level 3 access. + +Level 3: Core access. You will need this level to commit directly to any +of the core repositories (Firefox/Thunderbird/Fennec). + +If you wish to apply for commit access, please follow the guide found in +the `Mozilla Commit Access +Policy `_. + +Submitting a patch that touches C/C++ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your patch makes changes to any C or C++ code and your editor does +not have ``clang-format`` support, you should run the clang-format +linter before submitting your patch to ensure that your code is properly +formatted. + +.. code:: bash + + mach clang-format -p path/to/file.cpp + +Note that ``./mach bootstrap`` will offer to set up a commit hook that +will automatically do this for you. + +Submitting to ``try`` with Level 1 commit access. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you only have Level 1 access, you will still need to submit your +patch through phabricator, but you can test it on the try server first. + +- Use ``./mach try fuzzy`` to select jobs to run and push to try. + +Submitting a patch via Phabricator. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To commit anything to the repository, you will need to set up moz-phab +and Phabricator. If you are using ``git-cinnabar`` then you will need to +use git enabled versions of these tools. + +Set up Phabricator +^^^^^^^^^^^^^^^^^^ + +- In a browser, visit Mozilla’s Phabricator instance at + https://phabricator.services.mozilla.com/. + +- Click “Log In” at the top of the page + + .. figure:: ../assets/LogInPhab.png + :alt: Log in to Phabricator + + alt text + +- Click the “Log In or Register” button on the next page. This will + take you to Bugzilla to log in or register a new account. + + .. figure:: ../assets/LogInOrRegister.png + :alt: Log in or register a Phabiricator account + + alt text + +- Sign in with your Bugzilla credentials, or create a new account. + + .. figure:: ../assets/LogInBugzilla.png + :alt: Log in with Bugzilla + + alt text + +- You will be redirected back to Phabricator, where you will have to + create a new Phabricator account. + + .. raw:: html + + + +- Fill in/amend any fields on the form and click “Register Account”. + + .. raw:: html + + + +- You now have a Phabricator account and can submit and review patches. + +Installing ``moz-phab`` +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: bash + + pip install MozPhab [--user] + +Submitting a patch using ``moz-phab``. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Ensure you are on the branch where you have commits that you want to + submit. + +.. code:: bash + + git checkout your-branch + +- Check the revision numbers for the commits you want to submit + +.. code:: bash + + git log + +- Run ``moz-phab``. Specifying a start commit will submit all commits + from that commit. Specifying an end commit will submit all commits up + to that commit. If no positional arguments are provided, the range is + determined to be starting with the first non-public, non-obsolete + changeset (for Mercurial) and ending with the currently checked-out + changeset. + +.. code:: bash + + moz-phab submit [start_rev] [end_rev] + +- You will receive a Phabricator link for each commit in the set. + +Updating a patch +~~~~~~~~~~~~~~~~ + +- Often you will need to make amendments to a patch after it has been + submitted to address review comments. To do this, add your commits to + the base branch of your fix as normal. + +For ``moz-phab`` run in the same way as the initial submission with the +same arguments, that is, specifying the full original range of commits. +Note that, while inserting and amending commits should work fine, +reordering commits is not yet supported, and deleting commits will leave +the associated revisions open, which should be abandoned manually diff --git a/mobile/android/docs/geckoview/contributor/for-gecko-engineers.rst b/mobile/android/docs/geckoview/contributor/for-gecko-engineers.rst new file mode 100644 index 0000000000..4517d950b4 --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/for-gecko-engineers.rst @@ -0,0 +1,176 @@ +.. -*- Mode: rst; fill-column: 80; -*- + +============================= +GeckoView For Gecko Engineers +============================= + +Table of contents +================= + +.. contents:: :local: + +Introduction +------------ + +Who this guide is for: As the title suggests, the target audience of +this guide is existing Gecko engineers who need to be able to build and +(locally) test GeckoView. If you aren’t already familiar with building +Firefox on a desktop platform, you’ll likely be better served by reading +`our general introduction `_. This guide may +also be helpful if you find you’ve written a patch that requires +changing GeckoView’s public API, see `Landing a Patch <#landing-a-patch>`_. + +Who this guide is not for: As mentioned above, if you are not already +familiar with building Firefox for desktop, you’d likely be better +served by our general bootstrapping guide. If you are looking to +contribute to front-end development of one of Mozilla’s Android +browsers, you’re likely better off starting with their codebase and +returning here only if actual GeckoView changes are needed. See, for +example, `Fenix’s GitHub `_. + +What to do if this guide contains bugs or leads you astray: The quickest +way to get a response is to ask generally on #gv on Mozilla Slack; +#mobile on Mozilla IRC may also work for the time being, albeit likely +with slower response times. If you believe the guide needs updating, it +would also be good to file a ticket to request that. + +Configuring the build system +---------------------------- + +First, a quick note: This guide was written on MacOS 10.14; it should +translate quite closely to other supported versions of MacOS and to +Linux. Building GeckoView on Windows is not officially supported at the +moment. To begin with, re-run ``./mach bootstrap``; it will present you +with options for the version of Firefox/GV that you want to build. +Currently, option ``3`` is +``GeckoView/Firefox for Android Artifact Mode`` and ``4`` is +``GeckoView/Firefox for Android``; if you’re here, you want one of +these. The brief and approximately correct breakdown of ``Artifact`` vs +regular builds for GeckoView is that ``Artifact`` builds will not allow +you to work on native code, only on JS or Java. Once you’ve selected +your build type, ``bootstrap`` should do its usual thing and grab +whatever dependencies are necessary. You may need to agree to some +licenses along the way. Once ``bootstrap`` has successfully completed, +it will spit out a recommended ``mozconfig``. + +Mozconfig and Building +---------------------- + +If you’ve followed from the previous section, ``./mach bootstrap`` +printed out a recommended ``mozconfig`` that looks something like this: + +:: + + # Build GeckoView/Firefox for Android: + ac_add_options --enable-project=mobile/android + + # Targeting the following architecture. + # For regular phones, no --target is needed. + # For x86 emulators (and x86 devices, which are uncommon): + # ac_add_options --target=i686 + # For newer phones. + # ac_add_options --target=aarch64 + # For x86_64 emulators (and x86_64 devices, which are even less common): + # ac_add_options --target=x86_64 + +As written, this defaults to building for a 32-bit ARM architecture, +which is probably not what you want. If you intend to work on an actual +device, you almost certainly want a 64-bit ARM build, as it is supported +by virtually all modern ARM phones/tablets and is the only ARM build we +ship on the Google Play Store. To go this route, uncomment the +``ac_add_options --target=aarch64`` line in the ``mozconfig``. On the +other hand, x86-64 emulated devices are widely used by the GeckoView +team and are used extensively on ``try``; if you intend to use an +emulator, uncomment the ``ac_add_options --target=x86_64`` line in the +``mozconfig``. Don’t worry about installing an emulator at the moment, +that will be covered shortly. It’s worth noting here that other +``mozconfig`` options will generally work as you’d expect. Additionally, +if you plan on debugging native code on Android, you should include the +``mozconfig`` changes mentioned `in our native debugging guide `_. Now, using +that ``mozconfig`` with any modifications you’ve made, simply +``./mach build``. If all goes well, you will have successfully built +GeckoView. + +Installing, Running, and Using in Fenix/AC +------------------------------------------ + +An (x86-64) emulator is the most common and developer-friendly way of +contributing to GeckoView in most cases. If you’re going to go this +route, simply run ``./mach android-emulator`` — by default, this will +install and launch an x86-64 Android emulator running the same Android +7.0 image that is used on ``try``. If you need a different emulator +image you can run ``./mach android-emulator --help`` for information on +what Android images are available via ``mach``. You can also install an +emulator image via Android Studio. In cases where an emulator may not +suffice (eg graphics or performance testing), or if you’d simply prefer +not to use an emulator, you can opt to use an actual phone instead. To +do so, you’ll need to enable ``USB Debugging`` on your phone if you +haven’t already. On most modern Android devices, you can do this by +opening ``Settings``, going to ``About phone``, and tapping +``Build number`` seven times. You should get a notification informing +you that you’ve unlocked developer options. Now return to ``Settings``, +go to ``Developer options``, and enable USB debugging. + +GeckoView Example App +~~~~~~~~~~~~~~~~~~~~~ + +Now that you’ve connected a phone or setup an emulator, the simplest way +to test GeckoView is to launch the GeckoView Example app by running +``./mach run`` (or install it with ``./mach install`` and run it +yourself). This is a simplistic GV-based browser that lives in the tree; +in many cases, it is sufficient to test and debug Gecko changes, and is +by far the simplest way of doing so. It supports remote debugging by +default — simply open Remote Debugging on your desktop browser and the +connected device/emulator should show up when the example app is open. +You can also use the example app for native debugging, follow the +`native debugging guide `_. + +GeckoView JUnit Tests +~~~~~~~~~~~~~~~~~~~~~ + +Once you’ve successfully built GV, you can run tests from the GeckoView +JUnit test suite with ``./mach geckoview-junit``. For further examples +(eg running individual tests, repeating tests, etc.), consult the `quick +start guide `_. + +Fenix and other GV-based Apps +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are working on something for which the GeckoView Example app is +not sufficient for some reason, you may need to `use your local build of +GeckoView in one of Mozilla’s GV-based apps like Fenix `_. + +Debugging +--------- + +Remote Debugging +~~~~~~~~~~~~~~~~ + +To recap a bit of the above, in the GeckoView Example app, remote +debugging is enabled by default, and your device should show up in your +desktop browser’s Remote Debugging window with no special effort. For +Fenix, you can enable remote debugging by opening the three-dot menu and +toggling ``Remote debugging via USB`` under ``Developer tools``; other +Mozilla GV-based browsers have similar options. + +Native Debugging +~~~~~~~~~~~~~~~~ + +To perform native debugging on any GV app will require you to install +Android Studio and follow instructions `here `_. + +Landing a Patch +--------------- + +In most cases, there shouldn’t be anything out of the ordinary to deal +with when landing a patch that affects GeckoView; make sure you include +Android in your ``try`` runs and you should be good. However, if you +need to alter the GeckoView public API in any way — essentially anything +that’s exposed as ``public`` in GeckoView Java files — then you’ll find +that you need to run the API linter and update the change log. To do +this, first run ``./mach lint --linter android-api-lint`` — if you have +indeed changed the public API, this will give you a ``gradle`` command +to run that will give further instructions. GeckoView API changes +require two reviews from GeckoView team members; you can open it up to +the team in general by adding ``#geckoview-reviewers`` as a reviewer on +Phabricator. diff --git a/mobile/android/docs/geckoview/contributor/geckoview-architecture.rst b/mobile/android/docs/geckoview/contributor/geckoview-architecture.rst new file mode 100644 index 0000000000..e4235dd4dd --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/geckoview-architecture.rst @@ -0,0 +1,826 @@ +.. -*- Mode: rst; fill-column: 80; -*- + +===================== +Architecture overview +===================== + +.. contents:: Table of Contents + :depth: 2 + :local: + +Introduction +============ + +*Gecko* is a Web engine developed by Mozilla and used to power Firefox on +various platforms. A Web engine is roughly comprised of a JavaScript engine, a +Rendering engine, HTML parser, a Network stack, various media encoders, a +Graphics engine, a Layout engine and more. + +Code that is part of a browser itself is usually referred to as "chrome" code +(from which the popular Chrome browser takes its name) as opposed to code part +of a Web site, which is usually referred to "content" code or content Web page. + +*GeckoView* is an Android library that can be used to embed Gecko into Android +apps. Android apps that embed Gecko this way are usually referred to by +"embedders" or simply "apps". + +GeckoView powers all currently active Mozilla browsers on Android, like Firefox +for Android and Firefox Focus. + +API +=== + +The following sections describe parts of the GeckoView API that are public and +exposed to embedders. + + |api-diagram| + +Overall tenets +-------------- + +GeckoView is an opinionated library that contains a minimal UI and makes no +assumption about the type of app that is being used by. Its main consumers +inside Mozilla are browsers, so a lot of features of GeckoView are geared +towards browsers, but there is no assumption that the embedder is actually a +browser (e.g. there is no concept of "tab" in GeckoView). + +The GeckoView API tries to retain as little data as possible, delegating most +data storage to apps. Notable exceptions to this rule are: permissions, +extensions and cookies. + +View, Runtime and Session +------------------------- + + |view-runtime-session| + +There are three main classes in the GeckoView API: + +- ``GeckoRuntime`` represents an instance of Gecko running in an app. Normally, + apps have only one instance of the runtime which lives for as long as the app + is alive. Any object in the API that is not specific to a *session* + (more to this later) is usually reachable from the runtime. +- ``GeckoSession`` represents a web site *instance*. You can think of it as a + *tab* in a browser or a Web view in an app. Any object related to the + specific session will be reachable from this object. Normally, embedders + would have many instances of ``GeckoSession`` representing each tab that is + currently open. Internally, a session is represented as a "window" with one + single tab in it. +- ``GeckoView`` is an Android ``View`` that embedders can use to paint a + ``GeckoSession`` in the app. Normally, only ``GeckoSession`` s associated to + a ``GeckoView`` are actually *alive*, i.e. can receive events, fire timers, + etc. + +Delegates +--------- + +Because GeckoView has no UI elements and doesn't store a lot of data, it needs +a way to *delegate* behavior when Web sites need functionality that requires +these features. + +To do that, GeckoView exposes Java interfaces to the embedders, called +Delegates. Delegates are normally associated to either the runtime, when they +don't refer to a specific session, or a session, when they are +session-specific. + +The most important delegates are: + +- ``Autocomplete.StorageDelegate`` Which is used by embedders to implement + autocomplete functionality for logins, addresses and credit cards. +- ``ContentDelegate`` Which receives events from the content Web page like + "open a new window", "on fullscreen request", "this tab crashed" etc. +- ``HistoryDelegate`` Which receives events about new or modified history + entries. GeckoView itself does not store history so the app is required to + listen to history events and store them permanently. +- ``NavigationDelegate`` Informs the embedder about navigation events and + requests. +- ``PermissionDelegate`` Used to prompt the user for permissions like + geolocation, notifications, etc. +- ``PromptDelegate`` Implements content-side prompts like alert(), confirm(), + basic HTTP auth, etc. +- ``MediaSession.Delegate`` Informs the embedder about media elements currently + active on the page and allows the embedder to pause, resume, receive playback + state etc. +- ``WebExtension.MessageDelegate`` Used by the embedder to exchange messages + with built-in extensions. See also `Interacting with Web Content <../consumer/web-extensions.html>`_. + + +.. _GeckoDisplay: + +GeckoDisplay +------------ + +GeckoView can paint to either a ``SurfaceView`` or a ``TextureView``. + +- ``SufaceView`` is what most apps will use and it's the default, it provides a + barebone wrapper around a GL surface where GeckoView can paint on. + SurfaceView is not part of normal Android compositing, which means that + Android is not able to paint (partially) on top of a SurfaceView or apply + transformations and animations to it. +- ``TextureView`` offers a surface which can be transformed and animated but + it's slower and requires more memory because it's `triple-buffered + `_ + (which is necessary to offer animations). + +Most apps will use the ``GeckoView`` class to paint the web page. The +``GeckoView`` class is an Android ``View`` which takes part in the Android view +hierarchy. + +Android recycles the ``GeckoView`` whenever the app is not visible, releasing +the associated ``SurfaceView`` or ``TextureView``. This triggers a few actions +on the Gecko side: + +- The GL Surface is released, and Gecko is notified in + `SyncPauseCompositor `_. +- The ```` associated to the ``GeckoSession`` is `set to inactive `_, + which essentially freezes the JavaScript engine. + +Apps that do not use ``GeckoView``, because e.g. they cannot use +``SurfaceView``, need to manage the active state manually and call +``GeckoSession.setActive`` whenever the session is not being painted on the +screen. + +Thread safety +------------- + +Apps will inevitably have to deal with the Android UI in a significant way. +Most of the Android UI toolkit operates on the UI thread, and requires +consumers to execute method calls on it. The Android UI thread runs an event +loop that can be used to schedule tasks on it from other threads. + +Gecko, on the other hand, has its own main thread where a lot of the front-end +interactions happen, and many methods inside Gecko expect to be called on the +main thread. + +To not overburden the App with unnecessary multi-threaded code, GeckoView will +always bridge the two "main threads" and redirect method calls as appropriate. +Most GeckoView delegate calls will thus happen on the Android UI thread and +most APIs are expected to be called on the UI thread as well. + +This can sometimes create unexpected performance considerations, as illustrated +in later sections. + +GeckoResult +----------- + +An ubiquitous tool in the GeckoView API is ``GeckoResult``. GeckoResult is a +promise-like class that can be used by apps and by Gecko to return values +asynchronously in a thread-safe way. Internally, ``GeckoResult`` will keep +track of what thread it was created on, and will execute callbacks on the same +thread using the thread's ``Handler``. + +When used in Gecko, ``GeckoResult`` can be converted to ``MozPromise`` using +``MozPromise::FromGeckoResult``. + +Page load +--------- + + |pageload-diagram| + +GeckoView offers several entry points that can be used to react to the various +stages of a page load. The interactions can be tricky and surprising so we will +go over them in details in this section. + +For each page load, the following delegate calls will be issued: +``onLoadRequest``, ``onPageStart``, ``onLocationChange``, +``onProgressChange``, ``onSecurityChange``, ``onSessionStateChange``, +``onCanGoBack``, ``onCanGoForward``, ``onLoadError``, ``onPageStop``. + +Most of the method calls are self-explanatory and offer the App a chance to +update the UI in response to a change in the page load state. The more +interesting delegate calls will be described below. + +onPageStart and onPageStop +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``onPageStart`` and ``onPageStop`` are guaranteed to appear in pairs and in +order, and denote the beginning and the end of a page load. In between a start +and stop event, multiple ``onLoadRequest`` and ``onLocationChange`` call can be +executed, denoting redirects. + +onLoadRequest +~~~~~~~~~~~~~ + +``onLoadRequest``, which is perhaps the most important, can be used by the App +to intercept page loads. The App can either *deny* the load, which will stop +the page from loading, and handle it internally, or *allow* the +load, which will load the page in Gecko. ``onLoadRequest`` is called for all +page loads, regardless of whether they were initiated by the app itself, by Web +content, or as a result of a redirect. + +When the page load originates in Web content, Gecko has to synchronously +wait for the Android UI thread to schedule the call to ``onLoadRequest`` and +for the App to respond. This normally takes a negligible amount of time, but +when the Android UI thread is busy, e.g. because the App is being painted for +the first time, the delay can be substantial. This is an area of GeckoView that +we are actively trying to improve. + +onLoadError +~~~~~~~~~~~ + +``onLoadError`` is called whenever the page does not load correctly, e.g. +because of a network error or a misconfigured HTTPS server. The App can return +a URL to a local HTML file that will be used as error page internally by Gecko. + +onLocationChange +~~~~~~~~~~~~~~~~ + +``onLocationChange`` is called whenever Gecko commits to a navigation and the +URL can safely displayed in the URL bar. + +onSessionStateChange +~~~~~~~~~~~~~~~~~~~~ + +``onSessionStateChange`` is called whenever any piece of the session state +changes, e.g. form content, scrolling position, zoom value, etc. Changes are +batched to avoid calling this API too frequently. + +Apps can use ``onSessionStateChange`` to store the serialized state to +disk to support restoring the session at a later time. + +Third-party root certificates +----------------------------- + +Gecko maintains its own Certificate Authority store and does not use the +platform's CA store. GeckoView follows the same policy and will not, by +default, read Android's CA store to determine root certificates. + +However, GeckoView provides a way to import all third-party CA roots added to +the Android CA store by setting the `enterpriseRootsEnabled +`_ +runtime setting to ``true``, this feature is implemented in `EnterpriseRoots +`_ + +There is not currently any API for an app to manually specify additional CA +roots, although this might change with `Bug 1522162 +`_. + +Lite and Omni builds +--------------------- + +A variation of the default GeckoView build, dubbed `Omni` in the codebase, +provides additional libraries that can be helpful when building a browser app. +Currently, the `Glean +`_ library is +included in the ``geckoview-omni`` package. The default build ``geckoview``, +which does not contain such libraries, is similarly dubbed `Lite` in the +codebase. + +The additional libraries in the Omni package are directly built into Gecko's +main ``.so`` file, ``libxul.so``. These libraries are then declared in the +``.module`` package inside the ``maven`` repository, e.g. see the ``.module`` +file for `geckoview-omni +`_: + +.. code-block:: json + + "capabilities": [ + { + "group": "org.mozilla.geckoview", + "name": "geckoview-omni", + "version": "102.0.20220623063721" + }, + { + "group": "org.mozilla.telemetry", + "name": "glean-native", + "version": "44.1.1" + } + ] + +Notice the ``org.mozilla.telemetry:glean-native`` capability is declared +alongside ``org.mozilla.geckoview``. + +The main Glean library then depends on ``glean-native`` which is either +provided in a standalone package (for apps that do not include GeckoView) or by +the GeckoView capability above. + +In Treeherder, the Lite build is denoted with ``Lite``, while the Omni builds +don't have extra denominations as they are the default build, so e.g. for +``x86_64`` the platorm names would be: + +- ``Android 7.0 x86-64`` for the Omni build +- ``Android 7.0 x86-64 Lite`` for the Lite build + +Extensions +---------- + +Extensions can be installed using ``WebExtensionController::install`` and +``WebExtensionController::installBuiltIn``, which asynchronously returns a +``WebExtension`` object that can be used to set delegates for +extension-specific behavior. + +The ``WebExtension`` object is immutable, and will be replaced every time a +property changes. For instance, to disable an extension, apps can use the +``disable`` method, which will return an updated version of the +``WebExtension`` object. + +Internally, all ``WebExtension`` objects representing one extension share the +same delegates, which are stored in ``WebExtensionController``. + +Given the extensive sprawling amount of data associated to extensions, +extension installation persists across restarts. Existing extensions can be +listed using ``WebExtensionController::list``. + +In addition to ordinary WebExtension APIs, GeckoView allows ``builtIn`` +extensions to communicate to the app via native messaging. Apps can register +themselves as native apps and extensions will be able to communicate to the app +using ``connectNative`` and ``sendNativeMessage``. Further information can be +found `here <../consumer/web-extensions.html>`__. + +Internals +========= + +The following sections describe how Gecko and GeckoView are implemented. These +parts of GeckoView are not normally exposed to embedders. + +Process Model +------------- + +Internally, Gecko uses a multi-process architecture, most of the chrome code +runs in the *main* process, while content code runs in *child* processes also +called *content* processes. There are additional types of specialized processes +like the *socket* process, which runs parts of the networking code, the *gpu* +process which executes GPU commands, the *extension* process which runs most +extension content code, etc. + +We intentionally do not expose our process model to embedders. + +To learn more about the multi-process architecture see `Fission for GeckoView +engineers `_. + +The majority of the GeckoView Java code runs on the main process, with a thin +glue layer on the child processes, mostly contained in ``GeckoThread``. + +Process priority on Android +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On Android, each process is assigned a given priority. When the device is +running low on memory, or when the system wants to conserve resources, e.g. +when the screen has been off for a long period of time, or the battery is low, +Android will sort all processes in reverse priority order and kill, using a +``SIGKILL`` event, enough processes until the given free memory and resource +threshold is reached. + +Processes that are necessary to the function of the device get the highest +priority, followed by apps that are currently visible and focused on the +screen, then apps that are visible (but not on focus), background processes and +so on. + +Processes that do not have a UI associated to it, e.g. background services, +will normally have the lowest priority, and thus will be killed most +frequently. + +To increase the priority of a service, an app can ``bind`` to it. There are +three possible ``bind`` priority values + +- ``BIND_IMPORTANT``: The process will be *as important* as the process binding + to it +- default priority: The process will have lower priority than the process + binding to it, but still higher priority than a background service +- ``BIND_WAIVE_PRIORITY``: The bind will be ignored for priority + considerations. + +It's important to note that the priority of each service is only relative to +the priority of the app binding to it. If the app is not visible, the app +itself and all services attached to it, regardless of binding, will get +background priority (i.e. the lowest possible priority). + +Process management +~~~~~~~~~~~~~~~~~~ + +Each Gecko process corresponds to an Android ``service`` instance, which has to +be declared in GeckoView's ``AndroidManifest.xml``. + +For example, this is the definition of the ``media`` process: + +.. code-block:: + + + +Process creation is controlled by Gecko which interfaces to Android using +``GeckoProcessManager``, which translates Gecko's priority to Android's +``bind`` values. + +Because all priorities are waived when the app is in the background, it's not +infrequent that Android kills some of GeckoView's services, while still leaving +the main process alive. + +It is therefore very important that Gecko is able to recover from process +disappearing at any moment at runtime. + +Priority Hint +~~~~~~~~~~~~~ + +Internally, GeckoView ties the lifetime of the ``Surface`` associated to a +``GeckoSession`` and the process priority of the process where the session +lives. + +The underlying assumption is that a session that is not visible doesn't have a +surface associated to it and it's not being used by the user so it shouldn't +receive high priority status. + +The way this is implemented is `by setting +`_ +the ``active`` property on the ``browser`` object to ``false``, which causes +Gecko to de-prioritize the process, assuming that no other windows in the same +process have ``active=true``. See also `GeckoDisplay`_. + +However, there are use cases where just looking at the surface is not enough. +For instance, when the user opens the settings menu, the currently selected tab +becomes invisible, but the user will still expect the browser to retain that +tab state with a higher priority than all the other tabs. Similarly, when the +browser is put in the background, the surface associated to the current tab +gets destroyed, but the current tab is still more important than the other +tabs, but because it doesn't have a surface associated to it, we have no way to +differentiate it from all the other tabs. + +To solve the above problem, we expose an API for consumers to *boost* a session +priority, `setPriorityHint +`_. +The priority hint is taken into consideration when calculating the +priority of a process. Any process that contains either an active session or a +session with the priority hint `is boosted +`_ +to the highest priority. + +Shutdown +-------- + +Android does not provide apps with a notification whenever the app is shutting +down. As explained in the section above, apps will simply be killed whenever +the system needs to reclaim resources. This means that Gecko on Android will +never shutdown cleanly, and that shutdown actions will never execute. + +.. _principals: + +Principals +---------- + +In Gecko, a *website* loaded in a session is represented by an abstraction +called `principal +`_. +Principals contain information that is used to determine what permissions have +been granted to the website instance, what APIs are available to it, which +container the page is loaded in, is the page in private browsing or not, etc. + +Principals are used throughout the Gecko codebase, GeckoView, however, does not +expose the concept to the API. This is intentional, as exposing it would +potentially expose the app to various security sensitive concepts, which would +violate the "secure" requirement for the GeckoView API. + +The absence of principals from the API is, e.g., why GeckoView does not offer a +way to set permissions given a URL string, as permissions are internally stored +by principal. See also `Setting Permissions`_. + +To learn more about principals see `this talk by Bobby Holley +`_. + +Window model +------------ + +Internally, Gecko has the concept of *window* and *tab*. Given that GeckoView +doesn't have the concept of tab (since it might be used to build something that +is *not* a browser) we hide Gecko tabs from the GeckoView API. + +Each ``GeckoSession`` corresponds to a Gecko ``window`` object with exactly one +``tab`` in it. Because of this you might see ``window`` and ``session`` used +interchangeably in the code. + +Internally, Gecko uses ``window`` s for other things other than +``GeckoSession``, so we have to sometime be careful about knowing which windows +belong to GeckoView and which don't. For example, the background extension page +is implemented as a ``window`` object that doesn't paint to a surface. + +EventDispatcher +--------------- + +The GeckoView codebase is written in C++, JavaScript and Java, it runs across +processes and often deals with asynchronous and garbage-collected code with +complex lifetime dependencies. To make all of this work together, GeckoView +uses a cross-language event-driven architecture. + +The main orchestrator of this event-driven architecture is ``EventDispatcher``. +Each language has an implementation of ``EventDispatcher`` that can be used to +fire events that are reachable from any language. + +Each window (i.e. each session) has its own ``EventDispatcher`` instance, which +is also present on the content process. There is also a global +``EventDispatcher`` that is used to send and receive events that are not +related to a specific session. + +Events can have data associated to it, which is represented as a +``GeckoBundle`` (essentially a ``String``-keyed variant map) on the Java and +C++ side, and a plain object on the JavaScript side. Data is automatically +converted back and forth by ``EventDispatcher``. + +In Java, events are fired in the same thread where the listener was registered, +which allows us to ensure that events are received in a consistent order and +data is kept consistent, so that we by and large don't have to worry about +multi-threaded issues. + +JNI +--- + +GeckoView code uses the Java Native Interface or JNI to communicate between +Java and C++ directly. Our JNI exports are generated from the Java source code +whenever the ``@WrapForJNI`` annotation is present. For non-GeckoView code, the +list of classes for which we generate imports is defined at +``widget/android/bindings``. + +The lifetime of JNI objects depends on their native implementation: + +- If the class implements ``mozilla::SupportsWeakPtr``, the Java object will + store a ``WeakPtr`` to the native object and will not own the lifetime of the + object. +- If the class implements ``AddRef`` and ``Release`` from ``nsISupports``, the + Java object will store a ``RefPtr`` to the native object and will hold a + strong reference until the Java object releases the object using + ``DisposeNative``. +- If neither cases apply, the Java object will store a C++ pointer to the + native object. + +Calling Runtime delegates from native code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Runtime delegates can be reached directly using the ``GeckoRuntime`` singleton. +A common pattern is to expose a ``@WrapForJNI`` method on ``GeckoRuntime`` that +will call the delegate, that than can be used on the native side. E.g. + +.. code:: java + + @WrapForJNI + private void featureCall() { + ThreadUtils.runOnUiThread(() -> { + if (mFeatureDelegate != null) { + mFeatureDelegate.feature(); + } + }); + } + +And then, on the native side: + +.. code:: cpp + + java::GeckoRuntime::LocalRef runtime = java::GeckoRuntime::GetInstance(); + if (runtime != nullptr) { + runtime->FeatureCall(); + } + +Session delegates +~~~~~~~~~~~~~~~~~ + +``GeckoSession`` delegates require a little more care, as there's a copy of a +delegate for each ``window``. Normally, a method on ``android::nsWindow`` is +added which allows Gecko code to call it. A reference to ``nsWindow`` can be +obtained from a ``nsIWidget`` using ``nsWindow::From``: + +.. code:: cpp + + RefPtr window = nsWindow::From(widget); + window->SessionDelegateFeature(); + +The ``nsWindow`` implementation can then forward the call to +``GeckoViewSupport``, which is the JNI native side of ``GeckoSession.Window``. + +.. code:: cpp + + void nsWindow::SessionDelegateFeature() { + auto acc(mGeckoViewSupport.Access()); + if (!acc) { + return; + } + acc->SessionDelegateFeature(aResponse); + } + +Which can in turn forward the call to the Java side using the JNI stubs. + +.. code:: cpp + + auto GeckoViewSupport::SessionDelegateFeature() { + GeckoSession::Window::LocalRef window(mGeckoViewWindow); + if (!window) { + return; + } + window->SessionDelegateFeature(); + } + +And finally, the Java implementation calls the session delegate. + +.. code:: java + + @WrapForJNI + private void sessionDelegateFeature() { + final GeckoSession session = mOwner.get(); + if (session == null) { + return; + } + ThreadUtils.postToUiThread(() -> { + final FeatureDelegate delegate = session.getFeatureDelegate(); + if (delegate == null) { + return; + } + delegate.feature(); + }); + } + +.. _permissions: + +Permissions +----------- + +There are two separate but related permission concepts in GeckoView: `Content` +permissions and `Android` permissions. See also the related `consumer doc +<../consumer/permissions.html>`_ on permissions. + +Content permissions +~~~~~~~~~~~~~~~~~~~ + +Content permissions are granted to individual web sites (more precisely, +`principals`_) and are managed internally using ``nsIPermissionManager``. +Content permissions are used by Gecko to keep track which website is allowed to +access a group of Web APIs or functionality. The Web has the concept of +permissions, but not all Gecko permissions map to Web-exposed permissions. + +For instance, the ``Notification`` permission, which allows websites to fire +notifications to the user, is exposed to the Web through +`Notification.requestPermission +`_, +while the `autoplay` permission, which allows websites to play video and audio +without user interaction, is not exposed to the Web and websites have no way to +set or request this permission. + +GeckoView retains content permission data, which is an explicit violation of +the design principle of not storing data. This is done because storing +permissions is very complex, making a mistake when dealing with permissions +often ends up being a security vulnerability, and because permissions depend on +concepts that are not exposed to the GeckoView API like `principals`_. + +Android permissions +~~~~~~~~~~~~~~~~~~~ + +Consumers of GeckoView are Android apps and therefore they have to receive +permission to use certain features on behalf of websites. + +For instance, when a website requests Geolocation permission for the first +time, the app needs to request the corresponding Geolocation Android permission +in order to receive position data. + +You can read more about Android permissions on `this doc +`_. + + +Implementation +~~~~~~~~~~~~~~ + +The main entry point from Gecko is ``nsIContentPermissionPrompt.prompt``, which +is handled in the `Permission module +`_ +in the same process where the request is originated. + +The permission module calls the child actor `GeckoViewPermission +`_ +which issues a `GeckoView:ContentPermission +`_ +request to the Java front-end as needed. + +Media permissions are requested using a global observer, and therefore are +handled in a `Process actor +`_, +media permissions requests have enough information to redirect the request to +the corresponding window child actor, with the exception of requests that are +not associated with a window, which are redirected to the `current active +window +`_. + +Setting permissions +~~~~~~~~~~~~~~~~~~~ + +Permissions are stored in a map between a `principal <#principals>`_ and a list +of permission (key, value) pairs. To prevent security vulnerabilities, GeckoView +does not provide a way to set permissions given an arbitrary URL and requires +consumers to get hold of the `ContentPermission +`_ +object. The ContentPermission object is returned in `onLocationChange +`_ +upon navigation, making it unlikely to have confusion bugs whereby the +permission is given to the wrong website. + +Internally, some permissions are only present when a certain override is set, +e.g. Tracking Protection override permissions are only present when the page +has been given a TP override. Because the only way to set the value of a +permission is to get hold of the ``ContentPermission`` object, `we manually insert +`_ +a `trackingprotection` permission on every page load. + +Autofill Support +---------------- + +GeckoView supports third-party autofill providers through Android's `autofill framework `_. Internally, this support is referred to as `autofill`. + +Document tree +~~~~~~~~~~~~~ + +The autofill Java front-end is located in the `Autofill class +`_. +GeckoView maintains a virtual tree structure of the current document for each +``GeckoSession``. + +The virtual tree structure is composed of `Node +`_ +objects which are immutable. Data associated to a node, including mutable data +like the current value, is stored in a separate `NodeData +`_ +class. Only HTML nodes that are relevant to autofilling are referenced in the +virtual structure and each node is associated to a root node, e.g. the root +``
`` element. All root nodes are children of the autofill `mRoot +`_ +node, hence making the overall structure a tree rather than a collection of +trees. Note that the root node is the only node in the virtual structure that +does not correspond to an actual element on the page. + +Internally, nodes are assigned a unique ``UUID`` string, which is used to match +nodes between the Java front-end and the data stored in GeckoView's chrome +Javascript. The autofill framework itself requires integer IDs for nodes, so we +store a mapping between UUIDs and integer IDs in the associated ``NodeData`` +object. The integer IDs are used only externally, while internally only the +UUIDs are used. The reason why we use a separate ID structure from the autofill +framework is that this allows us to `generate UUIDs +`_ +directly in the isolated content processes avoiding an IPC roundtrip to the +main process. + +Each ``Node`` object is associated to an ``EventCallback`` object which is +invoked whenever the node is autofilled by the autofill framework. + +Detecting autofillable nodes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GeckoView scans every web page for password ```` elements whenever the +``pageshow`` event `fires +`_. + +It also uses ``DOMFormHasPassword`` and ``DOMInputPasswordAdded`` to detect +whenever a password element is added to the DOM after the ``pageshow`` event. + +Prefs +----- + +`Preferences `_ (or prefs) are used throughout +Gecko to configure the browser, enable custom features, etc. + +GeckoView does not directly expose prefs to Apps. A limited set configuration +options is exposed through ``GeckoRuntimeSettings``. + +``GeckoRuntimeSettings`` can be easily mapped to a Gecko ``pref`` using +``Pref``, e.g. + +.. code:: java + + /* package */ final Pref mPrefExample = + new Pref("example.pref", false); + +The value of the pref can then be read internally using ``mPrefExample.get`` +and written to using ``mPrefExample.commit``. + +Front-end and back-end +---------------------- + + |code-layers| + +Gecko and GeckoView code can be divided in five layers: + +- **Java API** the outermost code layer that is publicly accessible to + GeckoView embedders. +- **Java Front-End** All the Java code that supports the API and talks directly + to the Android APIs and to the JavaScript and C++ front-ends. +- **JavaScript Front-End** The main interface to the Gecko back-end (or Gecko + proper) in GeckoView is JavaScript, we use this layer to call into Gecko and + other utilities provided by Gecko, code lives in ``mobile/android`` +- **C++ Front-End** A smaller part of GeckoView is written in C++ and interacts + with Gecko directly, most of this code is lives in ``widget/android``. +- **C++/Rust Back-End** This is often referred to as "platform", includes all + core parts of Gecko and is usually accessed to in GeckoView from the C++ + front-end or the JavaScript front-end. + +Modules and Actors +------------------ + +GeckoView's JavaScript Front-End is largely divided into units called modules +and actors. For each feature, each window will have an instance of a Module, a +parent-side Actor and (potentially many) content-side Actor instances. For a +detailed description of this see `here `__. + +Testing infrastructure +---------------------- + +For a detailed description of our testing infrastructure see `GeckoView junit +Test Framework `_. + +.. |api-diagram| image:: ../assets/api-diagram.png +.. |view-runtime-session| image:: ../assets/view-runtime-session.png +.. |pageload-diagram| image:: ../assets/pageload-diagram.png +.. |code-layers| image:: ../assets/code-layers.png diff --git a/mobile/android/docs/geckoview/contributor/geckoview-quick-start.rst b/mobile/android/docs/geckoview/contributor/geckoview-quick-start.rst new file mode 100644 index 0000000000..2954ea2b10 --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/geckoview-quick-start.rst @@ -0,0 +1,342 @@ +.. -*- Mode: rst; fill-column: 80; -*- + +================= +Contributor Guide +================= + +Table of contents +================= + +.. contents:: :local: + +GeckoView Contributor Quick Start Guide +======================================= + +This is a guide for developers who want to contribute to the GeckoView +project. If you want to get started using GeckoView in your app then you +should refer to the +`wiki `_. + +Get set up with Mozilla Central +------------------------------- + +The GeckoView codebase is part of the main Firefox tree and can be found +in ``mozilla-central``. You will need to get set up as a contributor to +Firefox in order to contribute to GeckoView. To get set up with +``mozilla-central``, follow the `Quick Start Guide for Git +Users `_, or the `Contributing to the Mozilla code +base `_ +guide and `Firefox Contributors’ Quick Reference +`_ +for Mercurial users. + +Once you have a copy of ``mozilla-central``, you will need to build +GeckoView. + +Bootstrap Gecko +--------------- + +Bootstrap configures everything for GeckoView and Fennec (Firefox for Android) development. + +- Ensure you have ``mozilla-central`` checked out. If this is the first + time you are doing this, it may take some time. + +.. code:: bash + + git checkout central/default + +If you are on Windows, you will need to install the +`Java 1.8 SDK `__. + +If you are on a mac, you will need to have the Xcode build tools +installed. You can do this by either `installing +Xcode `__ or installing only the +tools from the command line by running ``xcode-select --install`` and +following the on screen instructions. + +You will need to ``bootstrap`` for GeckoView/Firefox for Android. The easiest way is to run the following command: + +.. code:: bash + + ./mach --no-interactive bootstrap --application-choice="GeckoView/Firefox for Android" + +.. note:: + + - The ``--no-interactive`` argument will make ``bootstrap`` run start to finish without requiring any input from you. It will automatically accept any license agreements. + - The ``--application-choice="GeckoView/Firefox for Android"`` argument is needed when using ``--no-interactive`` so that "bootstrapping" is done for the correct application (instead of the default). + + If you want to make all the selections yourself and/or read through the license agreements, you can simply run: + + .. code:: bash + + ./mach bootstrap + + Select ``4. GeckoView/Firefox for Android`` when prompted and respond to any subsequent prompts as they appear. + +Once ``./mach bootstrap`` is complete, it will automatically write +the configuration into a new ``mozconfig`` file. If you already +have a ``mozconfig``, ``mach`` will instead output new configuration +that you should append to your existing file. + +Build from the command line +--------------------------- + +In order to pick up the configuration changes we just made we need to +build from the command line. This will update generated sources, compile +native code, and produce GeckoView AARs and example and test APKs. + +.. code:: bash + + ./mach build + +Build Using Android Studio +-------------------------- + +- Install `Android + Studio `_. +- Choose File->Open from the toolbar +- Navigate to the root of your ``mozilla-central`` source directory and + click “Open” +- Click yes if it asks if you want to use the gradle wrapper. + + - If the gradle sync does not automatically start, select File > + Sync Project with Gradle Files. + +- Wait for the project to index and gradle to sync. Once synced, the + workspace will reconfigure to display the different projects. + + - annotations contains custom Java annotations used inside GeckoView + - app contains geckoview build settings and omnijar. omnijar contains + the parts of Gecko and GeckoView that are not written in Java or Kotlin + - geckoview is the GeckoView project. Here is all the Java files + related to GeckoView + - geckoview_example is an example browser built using GeckoView. + + |alt text 1| + +Now you’re set up and ready to go. + +**Important: at this time, building from Android Studio or directly from +Gradle does not (re-)compile native code, including C++ and Rust.** This +means you will need to run ``mach build`` yourself to pick up changes to +native code. `Bug +1509539 `_ tracks +making Android Studio and Gradle do this automatically. + +If you want set up code formatting for Kotlin, please reference +`IntelliJ IDEA configuration +`_. + +Custom mozconfig with Android Studio +------------------------------------ + +Out of the box, Android Studio will use the default mozconfig file, normally +located at ``mozconfig`` in the root directory of your ``mozilla-central`` +checkout. + +To make Android Studio use a mozconfig in a custom location, you can add the +following to your ``local.properties``: + +:: + + mozilla-central.mozconfig=relative/path/to/mozconfig + +Note that, when running mach from the command line, this value will be ignored, +and the mozconfig from the mach environment will be used instead. + +To override the mozconfig used by mach, you can use the `MOZCONFIG` environment +variable, for example: + +:: + + MOZCONFIG=debug.mozconfig ./mach build + +Performing a bug fix +-------------------- + +One you have got GeckoView building and running, you will want to start +contributing. There is a general guide to `Performing a Bug Fix for Git +Developers `_ for you to follow. To contribute to +GeckoView specifically, you will need the following additional +information. + +Running tests and linter locally +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To ensure that your patch does not break existing functionality in +GeckoView, you can run the junit test suite with the following command + +:: + + ./mach geckoview-junit + +This command also allows you to run individual tests or test classes, +e.g. + +:: + + ./mach geckoview-junit org.mozilla.geckoview.test.NavigationDelegateTest + ./mach geckoview-junit org.mozilla.geckoview.test.NavigationDelegateTest#loadUnknownHost + +If your patch makes a GeckoView JavaScript module, you should run ESLint +as well: + +:: + + ./mach lint -l eslint mobile/android/modules/geckoview/ + +To see information on other options, simply run +``./mach geckoview-junit --help``; of particular note for dealing with +intermittent test failures are ``--repeat N`` and +``--run-until-failure``, both of which do exactly what you’d expect. + +Updating the changelog and API documentation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the patch that you want to submit changes the public API for +GeckoView, you must ensure that the API documentation is kept up to +date. To check whether your patch has altered the API, run the following +command. + +.. code:: bash + + ./mach lint --linter android-api-lint + +The output of this command will inform you if any changes you have made +break the existing API. Review the changes and follow the instructions +it provides. + +If the linter asks you to update the changelog, please ensure that you +follow the correct format for changelog entries. Under the heading for +the next release version, add a new entry for the changes that you are +making to the API, along with links to any relevant files, and bug +number e.g. + +:: + + - Added [`GeckoRuntimeSettings.Builder#aboutConfigEnabled`][71.12] to control whether or + not `about:config` should be available. + ([bug 1540065]({{bugzilla}}1540065)) + + [71.12]: {{javadoc_uri}}/GeckoRuntimeSettings.Builder.html#aboutConfigEnabled(boolean) + +Submitting to the ``try`` server +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is advisable to run your tests before submitting your patch. You can +do this using Mozilla’s ``try`` server. To submit a GeckoView patch to +``try`` before submitting it for review, type: + +.. code:: bash + + ./mach try auto + +This will automatically select tests to run from our suite. If your patch +passes on ``try`` you can be (fairly) confident that it will land successfully +after review. + +Tagging a reviewer +~~~~~~~~~~~~~~~~~~ + +When submitting a patch to Phabricator, if you know who you want to +review your patch, put their Phabricator handle against the +``reviewers`` field. + +If you don’t know who to tag for a review in the Phabricator submission +message, leave the field blank and, after submission, follow the link to +the patch in Phabricator and scroll to the bottom of the screen until +you see the comment box. + +- Select the ``Add Action`` drop down and pick the ``Change Reviewers`` option. +- In the presented box, add ``geckoview-reviewers``. Selecting this group as the reviewer will notify all the members of the GeckoView team there is a patch to review. +- Click ``Submit`` to submit the reviewer change request. + +Include GeckoView as a dependency +--------------------------------- + +If you want to include a development version of GeckoView as a +dependency inside another app, you must link to a local copy. There are +several ways to achieve this, but the preferred way is to use Gradle’s +*dependency substitution* mechanism, for which there is first-class +support in ``mozilla-central`` and a pattern throughout Mozilla’s +GeckoView-consuming ecosystem. + +The good news is that ``mach build`` produces everything you need, so +that after the configuration below, you should find that the following +commands rebuild your local GeckoView and then consume your local +version in the downstream project. + +.. code:: sh + + cd /path/to/mozilla-central && ./mach build + cd /path/to/project && ./gradlew assembleDebug + +**Be sure that your ``mozconfig`` specifies the correct ``--target`` +argument for your target device.** Many projects use “ABI splitting” to +include only the target device’s native code libraries in APKs deployed +to the device. On x86-64 and aarch64 devices, this can result in +GeckoView failing to find any libraries, because valid x86 and ARM +libraries were not included in a deployed APK. Avoid this by setting +``--target`` to the exact ABI that your device supports. + +Dependency substiting your local GeckoView into a Mozilla project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Most GeckoView-consuming projects produced by Mozilla support dependency +substitution via ``local.properties``. These projects include: + +- `Fenix `_ +- `reference-browser `_ +- `android-components `_ +- `Firefox Reality `_ + +Simply edit (or create) the file ``local.properties`` in the project +root and include a line like: + +.. code:: properties + + dependencySubstitutions.geckoviewTopsrcdir=/path/to/mozilla-central + +The default object directory – the one that a plain ``mach build`` +discovers – will be used. You can optionally specify a particular object +directory with an additional line like: + +.. code:: properties + + dependencySubstitutions.geckoviewTopobjdir=/path/to/object-directory + +With these lines, the GeckoView-consuming project should use the +GeckoView AAR produced by ``mach build`` in your local +``mozilla-central``. + +**Remember to remove the lines in ``local.properties`` when you want to +return to using the published GeckoView builds!** + +Dependency substituting your local GeckoView into a non-Mozilla project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In projects that don’t have first-class support for dependency +substitution already, you can do the substitution yourself. See the +documentation in +`substitue-local-geckoview.gradle `_, +but roughly: in each Gradle project that consumes GeckoView, i.e., in +each ``build.gradle`` with a +``dependencies { ... 'org.mozilla.geckoview:geckoview-...' }`` block, +include lines like: + +.. code:: groovy + + ext.topsrcdir = "/path/to/mozilla-central" + ext.topobjdir = "/path/to/object-directory" // Optional. + apply from: "${topsrcdir}/substitute-local-geckoview.gradle" + +**Remember to remove the lines from all ``build.gradle`` files when you +want to return to using the published GeckoView builds!** + +Next Steps +---------- + +- Get started with `Native Debugging `_ + +.. |alt text| image:: ../assets/DisableInstantRun.png +.. |alt text 1| image:: ../assets/GeckoViewStructure.png diff --git a/mobile/android/docs/geckoview/contributor/index.rst b/mobile/android/docs/geckoview/contributor/index.rst new file mode 100644 index 0000000000..f38b1c5677 --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/index.rst @@ -0,0 +1,29 @@ +.. -*- Mode: rst; fill-column: 80; -*- + +========================= +Contributing to GeckoView +========================= + +.. toctree:: + :maxdepth: 1 + :glob: + :hidden: + + * + +- `Contributor Quick Start Guide `_: + Get GeckoView set up for development. +- `GeckoView for Gecko Engineers `_: A + quick-start guide for those already familiar with contributing to + Firefox development. +- `Mozilla Central Quick Start Guide `_: Get Mozilla + Central set up for development. +- `Mozilla Central Contributor Guide `_: Get + started as a contributor to Mozilla Central. +- `Guide to Native Debugging in Android Studio `_: + Set up Android Studio for debugging native code. +- `Architecture overview `_: An overview of + GeckoView's architecture. +- `Junit Test Framework `_: An overview of GeckoView's custom + Junit code. +- `apilint `_: GeckoView's linter for the API. diff --git a/mobile/android/docs/geckoview/contributor/junit.rst b/mobile/android/docs/geckoview/contributor/junit.rst new file mode 100644 index 0000000000..bf8cfd4615 --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/junit.rst @@ -0,0 +1,373 @@ +.. -*- Mode: rst; fill-column: 80; -*- + +==================== +Junit Test Framework +==================== + +GeckoView has `a lot +`_ +of `custom +`_ +code that is used to run junit tests. This document is an overview of what this +code does and how it works. + +.. contents:: Table of Contents + :depth: 2 + :local: + +Introduction +============ + +`GeckoView `_ is an Android Library that can be used to +embed Gecko, the Web Engine behind Firefox, in applications. It is the +foundation for Firefox on Android, and it is intended to be used to build Web +Browsers, but can also be used to build other types of apps that need to +display Web content. + +GeckoView itself has no UI elements besides the Web View and uses Java +interfaces called "delegates" to let embedders (i.e. apps that use GeckoView) +implement UI behavior. + +For example, when a Web page's JavaScript code calls ``alert('Hello')`` the +embedder will receive a call to the `onAlertPrompt +`_ +method of the `PromptDelegate +`_ +interface with all the information needed to display the prompt. + +As most delegate methods deal with UI elements, GeckoView will execute them on +the UI thread for the embedder's convenience. + +GeckoResult +----------- + +One thing that is important to understand for what follows is `GeckoResult +`_. +``GeckoResult`` is a promise-like object that is used throughout the GeckoView +API, it allows embedders to asynchronously respond to delegate calls and +GeckoView to return results asynchronously. This is especially important for +GeckoView as it never provides synchronous access to Gecko as a design +principle. + +For example, when installing a WebExtension in GeckoView, the resulting +`WebExtension +`_ +object is returned in a ``GeckoResult``, which is completed when the extension +is fully installed: + +.. code:: java + + public GeckoResult install(...) + +To simplify memory safety, ``GeckoResult`` will always `execute callbacks +`_ +in the same thread where it was created, turning asynchronous code into +single-threaded javascript-style code. This is currently `implemented +`_ +using the Android Looper for the thread, which restricts ``GeckoResult`` to +threads that have a looper, like the Android UI thread. + +Testing overview +---------------- + +Given that GeckoView is effectively a translation layer between Gecko and the +embedder, it's mostly tested through integration tests. The vast majority of +the GeckoView tests are of the form: + +- Load simple test web page +- Interact with the web page through a privileged JavaScript test API +- Verify that the right delegates are called with the right inputs + +and most of the test framework is built around making sure that these +interactions are easy to write and verify. + +Tests in GeckoView can be run using the ``mach`` interface, which is used by +most Gecko tests. E.g. to run the `loadUnknownHost +`_ +test in ``NavigationDelegateTest`` you would type on your terminal: + +.. code:: shell + + ./mach geckoview-junit org.mozilla.geckoview.test.NavigationDelegateTest#loadUnknownHost + +Another way to run GeckoView tests is through the `Android Studio IDE +`_. By running tests this way, however, +some parts of the test framework are not initialized, and thus some tests +behave differently or fail, as will be explained later. + +Testing envelope +---------------- + +Being a library, GeckoView has a natural, stable, testing envelope, namely the +GeckoView API. The vast majority of GeckoView tests only use +publicly-accessible APIs to verify the behavior of the API. + +Whenever the API is not enough to properly test behavior, the testing framework +offers targeted "privileged" testing APIs. + +Using a restricted, stable testing envelope has proven over the years to be an +effective way of writing consistent tests that don't break upon refactoring. + +Testing Environment +------------------- + +When run through ``mach``, the GeckoView junit tests run in a similar +environment as mochitests (a type of Web regression tests used in Gecko). They +have access to the mochitest web server at `example.com`, and inherit most of +the testing prefs and profile. + +Note the environment will not be the same as mochitests when the test is run +through Android Studio, the prefs will be inherited from the default GeckoView +prefs (i.e. the same prefs that would be enabled in a consumer's build of +GeckoView) and the mochitest web server will not be available. + +Tests account for this using the `isAutomation +`_ +check, which essentially checks whether the test is running under ``mach`` or +via Android Studio. + +Unlike most other junit tests in the wild, GeckoView tests run in the UI +thread. This is done so that the GeckoResult objects are created on the right +thread. Without this, every test would most likely include a lot of blocks that +run code in the UI thread, adding significant boilerplate. + +Running tests on the UI thread is achieved by registering a custom ``TestRule`` +called `GeckoSessionTestRule +`_, +which, among other things, `overrides the evaluate +`_ +method and wraps everything into a ``instrumentation.runOnMainSync`` call. + +Verifying delegates +=================== + +As mentioned earlier, verifying that a delegate call happens is one of the most +common assertions that a GeckoView test makes. To facilitate that, +``GeckoSessionTestRule`` offers several ``delegate*`` utilities like: + +.. code:: java + + sessionRule.delegateUntilTestEnd(...) + sessionRule.delegateDuringNextWait(...) + sessionRule.waitUntilCalled(...) + sessionRule.forCallbacksDuringWait(...) + +These all take an arbitrary delegate object (which may include multiple +delegate implementations) and handle installing and cleaning up the delegate as +needed. + +Another set of facilities that ``GeckoSessionTestRule`` offers allow tests to +synchronously ``wait*`` for events, e.g. + +.. code:: java + + sessionRule.waitForJS(...) + sessionRule.waitForResult(...) + sessionRule.waitForPageStop(...) + +These facilities work together with the ``delegate*`` facilities by marking the +``NextWait`` or the ``DuringWait`` events. + +As an example, a test could load a page using ``session.loadUri``, wait until +the page has finished loading using ``waitForPageStop`` and then verify that +the expected delegate was called using ``forCallbacksDuringWait``. + +Note that the ``DuringWait`` here always refers to the last time a ``wait*`` +method was called and finished executing. + +The next sections will go into how this works and how it's implemented. + +Tracking delegate calls +----------------------- + +One thing you might have noticed in the above section is that +``forCallbacksDuringWait`` moves "backward" in time by replaying the delegates +called that happened while the wait was being executed. +``GeckoSessionTestRule`` achieves this by `injecting a proxy object +`_ +into every delegate, and `proxying every call +`_ +to the current delegate according to the ``delegate`` test calls. + +The proxy delegate `is built +`_ +using the Java reflection's ``Proxy.newProxyInstance`` method and receives `a +callback +`_ +every time a method on the delegate is being executed. + +``GeckoSessionTestRule`` maintains a list of `"default" delegates +`_ +used in GeckoView, and will `use reflection +`_ +to match the object passed into the ``delegate*`` calls to the proxy delegates. + +For example, when calling + +.. code:: java + + sessionRule.delegateUntilTestEnd(object : NavigationDelegate, ProgressDelegate {}) + +``GeckoSessionTestRule`` will know to redirect all ``NavigationDelegate`` and +``ProgressDelegate`` calls to the object passed in ``delegateUntilTestEnd``. + +Replaying delegate calls +------------------------ + +Some delegate methods require output data to be passed in by the embedder, and +this requires extra care when going "backward in time" by replaying the +delegate's call. + +For example, whenever a page loads, GeckoView will call +``GeckoResult onLoadRequest(...)`` to know if the load can +continue or not. When replaying delegates, however, we don't know what the +value of ``onLoadRequest`` will be (or if the test is going to install a +delegate for it, either!). + +What ``GeckoSessionTestRule`` does, instead, is to `return the default value +`_ +for the delegate method, and ignore the replayed delegate method return value. +This can be a little confusing for test writers, for example this code `will +not` stop the page from loading: + +.. code:: java + + session.loadUri("https://www.mozilla.org") + sessionRule.waitForPageStop() + sessionRule.forCallbacksDuringWait(object : NavigationDelegate { + override fun onLoadRequest(session: GeckoSession, request: LoadRequest) : + GeckoResult? { + // this value is ignored + return GeckoResult.deny() + } + }) + +as the page has already loaded by the time the ``forCallbacksDuringWait`` call is +executed. + +Tracking Waits +-------------- + +To track when a ``wait`` occurs and to know when to replay delegate calls, +``GeckoSessionTestRule`` `stores +`_ +the list of delegate calls in a ``List`` object, where +``CallRecord`` is a class that has enough information to replay a delegate +call. The test rule will track the `start and end index +`_ +of the last wait's delegate calls and `replay it +`_ +when ``forCallbacksDuringWait`` is called. + +To wait until a delegate call happens, the test rule will first `examine +`_ +the already executed delegate calls using the call record list described above. +If none of the calls match, then it will `wait for new calls +`_ +to happen, using ``UiThreadUtils.waitForCondition``. + +``waitForCondition`` is also used to implement other type of ``wait*`` methods +like ``waitForResult``, which waits until a ``GeckoResult`` is executed. + +``waitForCondition`` runs on the UI thread, and it synchronously waits for an +event to occur. The events it waits for normally execute on the UI thread as +well, so it `injects itself +`_ +in the Android event loop, checking for the condition after every event has +executed. If no more events remain in the queue, `it posts a delayed 100ms +`_ +task to avoid clogging the event loop. + +Executing Javascript +==================== + +As you might have noticed from an earlier section, the test rule allows tests +to run arbitrary JavaScript code using ``waitForJS``. The GeckoView API, +however, doesn't offer such an API. + +The way ``waitForJS`` and ``evaluateJS`` are implemented will be the focus of +this section. + +How embedders run javascript +---------------------------- + +The only supported way of accessing a web page for embedders is to `write a +built-in WebExtension +`_ +and install it. This was done intentionally to avoid having to rewrite a lot of +the Web-Content-related APIs that the WebExtension API offers. + +GeckoView extends the WebExtension API to allow embedders to communicate to the +extension by `overloading +`_ +the native messaging API (which is not normally implemented on mobile). +Embedders can register themselves as a `native app +`_ +and the built-in extension will be able to `exchange messages +`_ +and `open ports +`_ +with the embedder. + +This is still a controversial topic among smaller embedders, especially solo +developers, and we have discussed internally the possibility to expose a +simpler API to run one-off javascript snippets, similar to what Chromium's +WebView offers, but nothing has been developed so far. + +The test runner extension +------------------------- + +To run arbitrary javascript in GeckoView, the test runner installs a `support +extension +`_. + +The test framework then `establishes +`_ +a port for the background script, used to run code in the main process, and a +port for every window, to be able to run javascript on test web pages. + +When ``evaluateJS`` is called, the test framework will send `a message +`_ +to the extension which then `calls eval +`_ +on it and returns the `JSON`-stringified version of the result `back +`_ +to the test framework. + +The test framework also supports promises with `evaluatePromiseJS +`_. +It works similarly to ``evaluateJS`` but instead of returning the stringified +value, it `sets +`_ +the return value of the ``eval`` call into the ``this`` object, keyed by a +randomly-generated UUID. + +.. code:: java + + this[uuid] = eval(...) + +``evaluatePromiseJS`` then returns an ``ExtensionPromise`` Java object which +has a ``getValue`` method on it, which will essentially execute `await +this[uuid] +`_ +to get the value from the promise when needed. + +Beyond executing javascript +--------------------------- + +A natural way of breaking the boundaries of the GeckoView API is to run a +so-called "experiment extension". Experiment extensions have access to the full +Gecko front-end, which is written in JavaScript, and don't have limits on what +they can do. Experiment extensions are essentially what old add-ons used to be +in Firefox, very powerful and very dangerous. + +The test runner uses experiments to offer `privileged APIs +`_ +to tests like ``setPref`` or ``getLinkColor`` (which is not normally available +to websites for privacy concerns). + +Each privileged API is exposed as an `ordinary Java API +`_ +and the test framework doesn't offer a way to run arbitrary chrome code to +discourage developers from relying too much on implementation-dependent +privileged code. diff --git a/mobile/android/docs/geckoview/contributor/mc-quick-start.rst b/mobile/android/docs/geckoview/contributor/mc-quick-start.rst new file mode 100644 index 0000000000..bac9dc3ce5 --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/mc-quick-start.rst @@ -0,0 +1,184 @@ +.. -*- Mode: rst; fill-column: 80; -*- + +=========================== +Mozilla Central Quick Start +=========================== + +Table of contents +================= + +.. contents:: :local: + +Firefox Developer Git Quick Start Guide +======================================= + +Getting setup to as a first time Mozilla contributor is hard. There are +plenty of guides out there to help you get started as a contributor, but +many of the new contributor guides out of date often more current ones +are aimed at more experienced contributors. If you want to review these +guides, you can find several linked to from +:ref:`Working on Firefox `. + +This guide will take you through setting up as a contributor to +``mozilla-central``, the Firefox main repository, as a git user. + +Setup +----- + +The first thing you will need is to install Mercurial as this is the VCS +that ``mozilla-central`` uses. + +.. _mac-0: + +Mac +~~~ + +.. _homebrew-0: + +Homebrew +^^^^^^^^ + +.. code:: bash + + brew install mercurial + +macports +^^^^^^^^ + +.. code:: bash + + sudo port install mercurial + +Linux +~~~~~ + +apt +^^^ + +.. code:: bash + + sudo apt-get install mercurial + +Alternatively you can install `Mercurial directly `_. + +Check that you have successfully installed Mercurial by running: + +.. code:: bash + + hg --version + +If you are an experienced git user and are unfamiliar with Mercurial, +you may want to install ``git-cinnabar``. Cinnabar is a git remote +helper that allows you to interact with Mercurial repos using git +semantics. + +git-cinnabar +------------ + +There is a Homebrew install option for ``git-cinnabar``, but this did +not work for me, nor did the installer option. Using these tools, when I +tried to clone the Mercurial repo it hung and did not complete. I had to +do a manual install before I could use ``git-cinnabar`` successfully to +download a Mercurial repo. If you would like to try either of these +option, however, here they are: + +.. _mac-1: + +Mac +~~~~~ + +.. _homebrew-1: + +Homebrew +^^^^^^^^ + +.. code:: bash + + brew install git-cinnabar + +All Platforms +~~~~~~~~~~~~~ + +Installer +^^^^^^^^^ + +.. code:: bash + + git cinnabar download + +Manual installation +^^^^^^^^^^^^^^^^^^^ + +.. code:: bash + + git clone https://github.com/glandium/git-cinnabar.git && cd git-cinnabar + make + export PATH="$PATH:/somewhere/git-cinnabar" + echo 'export PATH="$PATH:/somewhere/git-cinnabar"' >> ~/.bash_profile + git cinnabar download + +``git-cinnabar``\ ’s creator, `glandium `_, has +written a number of posts about setting up for Firefox Development with +git. This `post `_ is the one +that has formed the basis for this walkthrough. + +In synopsis: + +- initialize an empty git repository + +.. code:: bash + + git init gecko && cd gecko + +- Configure git: + +.. code:: bash + + git config fetch.prune true + git config push.default upstream + +- Add remotes for your repositories. There are several to choose from, + ``central``, ``inbound``, ``beta``, ``release`` etc. but in reality, + if you plan on using Phabricator, which is Firefox’s preferred patch + submission system, you only need to set up ``central``. It might be + advisable to have access to ``inbound`` however, if you want to work + on a version of Firefox that is queued for release. This guide will + be focused on Phabricator. + +.. code:: bash + + git remote add central hg::https://hg.mozilla.org/mozilla-central -t branches/default/tip + git remote add inbound hg::https://hg.mozilla.org/integration/mozilla-inbound -t branches/default/tip + git remote set-url --push central hg::ssh://hg.mozilla.org/mozilla-central + git remote set-url --push inbound hg::ssh://hg.mozilla.org/integration/mozilla-inbound + +- Expose the branch tip to get quick access with some easy names. + +.. code:: bash + + git config remote.central.fetch +refs/heads/branches/default/tip:refs/remotes/central/default + git config remote.inbound.fetch +refs/heads/branches/default/tip:refs/remotes/inbound/default + +- Setup a remote for the try server. The try server is an easy way to + test a patch without actually checking the patch into the core + repository. Your code will go through the same tests as a + ``mozilla-central`` push, and you’ll be able to download builds if + you wish. + +.. code:: bash + + git remote add try hg::https://hg.mozilla.org/try + git config remote.try.skipDefaultUpdate true + git remote set-url --push try hg::ssh://hg.mozilla.org/try + git config remote.try.push +HEAD:refs/heads/branches/default/tip + +- Now update all the remotes. This performs a ``git fetch`` on all the + remotes. Mozilla Central is a *large* repository. Be prepared for + this to take a very long time. + +.. code:: bash + + git remote update + +All that’s left to do now is pick a bug to fix and `submit a +patch `__. diff --git a/mobile/android/docs/geckoview/contributor/native-debugging.rst b/mobile/android/docs/geckoview/contributor/native-debugging.rst new file mode 100644 index 0000000000..aab67e661c --- /dev/null +++ b/mobile/android/docs/geckoview/contributor/native-debugging.rst @@ -0,0 +1,262 @@ +.. -*- Mode: rst; fill-column: 80; -*- + +===================== +Debugging Native Code +===================== + +Table of contents +================= + +.. contents:: :local: + +Debugging Native Code in Android Studio. +======================================== + +If you want to work on the C++ code that powers GeckoView, you will need +to be able to perform native debugging inside Android Studio. This +article will guide you through how to do that. + +If you need to get set up with GeckoView for the first time, follow the +`Quick Start Guide `_. + +Perform a debug build of Gecko. +------------------------------- + +1. Edit your ``mozconfig`` file and add the following lines. These will + ensure that the build includes debug checks and symbols. + +.. code:: + + ac_add_options --enable-debug + +2. Ensure that the following lines are commented out in your + ``mozconfig`` if present. ``./mach configure`` will not allow + artifact builds to be enabled when generating a debug build. + +.. code:: + + # ac_add_options --enable-artifact-builds + +3. To be absolutely sure that Android Studio will pick up your debug + symbols, the first time you perform a debug build it is best to + clobber your ``MOZ_OBJDIR``. Subsequent builds should not need this + step. + +.. code:: bash + + ./mach clobber + +4. Build as usual. Because this is a debug build, and because you have + clobbered your ``MOZ_OBJDIR``, this will take a long time. Subsequent + builds will be incremental and take less time, so go make yourself a + cup of your favourite beverage. + +.. code:: bash + + ./mach build + +Set up lldb to find your symbols +-------------------------------- + +Edit your ``~/.lldbinit`` file (or create one if one does not already +exist) and add the following lines. + +The first line tells LLDB to enable inline breakpoints - Android Studio +will need this if you want to use visual breakpoints. + +The remaining lines tell LLDB where to go to find the symbols for +debugging. + +.. code:: bash + + settings set target.inline-breakpoint-strategy always + settings append target.exec-search-paths /objdir-android-opt/toolkit/library/build + settings append target.exec-search-paths /objdir-android-opt/mozglue/build + settings append target.exec-search-paths /objdir-android-opt/security + +Set up Android Studio to perform native debugging. +================================================== + +1. Edit the configuration that you want to debug by clicking + ``Run -> Edit Configurations...`` and selecting the correct + configuration from the options on the left hand side of the resulting + window. +2. Select the ``Debugger`` tab. +3. Select ``Dual`` from the ``Debug type`` select box. Dual will allow + debugging of both native and Java code in the same session. It is + possible to use ``Native``, but it will only allow for debugging + native code, and it’s frequently necessary to break in the Java code + that configures Gecko and child processes in order to attach + debuggers at the correct times. +4. Under ``Symbol Directories``, add a new path pointing to + ``/objdir-android-opt/toolkit/library/build``, the same path + that you entered into your ``.lldbinit`` file. +5. Select ``Apply`` and ``OK`` to close the window. + +Debug Native code in Android Studio +=================================== + +1. The first time you are running a debug session for your app, it’s + best to start from a completely clean build. Click + ``Build -> Rebuild Project`` to clean and rebuild. You can also + choose to remove any existing builds from your emulator to be + completely sure, but this may not be necessary. +2. If using Android Studio visual breakpoints, set your breakpoints in + your native code. +3. Run the app in debug mode as usual. +4. When debugging Fennec or geckoview_example, you will almost + immediately hit a breakpoint in ``ElfLoader.cpp``. This is expected. + If you are not using Android Studio visual breakpoints, you can set + your breakpoints here using the lldb console that is available now + this breakpoint has been hit. To set a breakpoint, select the app tab + (if running Dual, there will also be an `` java`` tab) from the + debug window, and then select the ``lldb`` console tab. Type the + following into the console: + +.. code:: + + b .cpp: + +5. Once your breakpoints have been set, click the continue execution + button to move beyond the ``ElfLoader`` breakpoint and your newly set + native breakpoints should be hit. Debug as usual. + +Attaching debuggers to content and other child processes +-------------------------------------------------------- + +Internally, GeckoView has a multi-process architecture. The main Gecko +process lives in the main Android process, but content rendering and +some other functions live in child processes. This balances load, +ensures certain critical security properties, and allows GeckoView to +recover if content processes become unresponsive or crash. However, it’s +generally delicate to debug child processes because they come and go. + +The general approach is to make the Java code in the child process that +you want to debug wait for a Java debugger at startup, and then to +connect such a Java debugger manually from the Android Studio UI. + +`Bug 1522318 `__ +added environment variables that makes GeckoView wait for Java debuggers +to attach, making this debug process more developer-friendly. See +`Configuring GeckoView for Automation <../consumer/automation.html>`__ +for instructions on how to set environment variables that configure +GeckoView’s runtime environment. + +Making processes wait for a Java debugger +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``set-debug-app`` command will make Android wait for a debugger before +running an app or service. e.g., to make GeckoViewExample wait, run the +following: + +.. code:: shell + + adb shell am set-debug-app -w --persistent org.mozilla.geckoview_example + +The above command works with child processes too, e.g. to make the GPU +process wait for a debugger, run: + +.. code:: shell + + adb shell am set-debug-app -w --persistent org.mozilla.geckoview_example:gpu + + +Attaching a Java debugger to a waiting child process +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is standard: follow the `Android Studio instructions `_. +You must attach a Java debugger, so you almost certainly want to attach +a ``Dual`` debugger and you definitely can’t attach only a ``Native`` +debugger. + +Determining the correct process to attach to is a little tricky because +the mapping from process ID (pid) to process name is not always clear. +Gecko content child processes are suffixed ``:tab`` at this time. + +If you attach ``Dual`` debuggers to both the main process and a content +child process, you will have four (4!) debug tabs to manage in Android +Studio, which is awkward. Android Studio doesn’t appear to configure +attached debuggers in the same way that it configures debuggers +connecting to launched Run Configurations, so you may need to manually +configure search paths – i.e., you may need to invoke the contents of +your ``lldbinit`` file in the appropriate ``lldb`` console by hand, +using an invocation like +``command source /absolute/path/to/topobjdir/lldbinit``. + +Android Studio also doesn’t appear to support targeting breakpoints from +the UI (say, from clicking in a gutter) to specific debug tabs, so you +may also need to set breakpoints in the appropriate ``lldb`` console by +hand. + +Managing more debug tabs may require different approaches. + +Debug Native Memory Allocations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Android Studio includes a `Native Memory Profiler +`_ +which works for physical devices running Android 10 and later. In order to +track allocations correctly Gecko must be built with ``jemalloc`` disabled. +Additionally, the native memory profiler appears to only work with ``aarch64`` +builds. The following must therefore be present in your ``mozconfig`` file: + +.. code:: + + ac_add_options --target=aarch64 + ac_add_options --disable-jemalloc + +The resulting profiles are symbolicated correctly in debug builds, however, you +may prefer to use a release build when profiling. Unfortunately a method to +symbolicate using local symbols from the development machine has not yet been +found, therefore in order for the profile to be symbolicated you must prevent +symbols being stripped during the build process. To do so, add the following to +your ``mozconfig``: + +.. code:: + + ac_add_options STRIP_FLAGS=--strip-debug + +And the following to ``mobile/android/geckoview/build.gradle``, and additionally +to ``mobile/android/geckoview_example/build.gradle`` if profiling GeckoView +Example, or ``app/build.gradle`` if profiling Fenix, for example. + +.. code:: groovy + + android { + packagingOptions { + doNotStrip "**/*.so" + } + } + +Using Android Studio on Windows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can now use :ref:`artifact builds ` +mode on `MozillaBuild environment `_ even if you are +not using WSL. If you want to debug GeckoView using Android Studio on +Windows, you have to set an additional environment variable via the +Control Panel to run the gradle script. The ``mach`` command sets these +variables automatically, but Android Studio cannot. + +If you install MozillaBuild tools to ``C:\mozilla-build`` (default +installation path), you have to set the ``MOZILLABUILD`` environment +variable to recognize MozillaBuild installation path. + +To set environment variable on Windows 10, open the ``Control Panel`` +from ``Windows System``, then select ``System and Security`` - +``System`` - ``Advanced system settings`` - +``Environment Variables ...``. + +To set the ``MOZILLABUILD`` variable, click ``New...`` in +``User variables for``, then ``Variable name:`` is ``MOZILLABUILD`` and +``Variable value:`` is ``C:\mozilla-build``. + +You also have to append some tool paths to the ``Path`` environment +variable. + +To append the variables to PATH, double click ``Path`` in +``User Variables for``, then click ``New``. And append the following +variables to ``Path``. + +- ``%MOZILLABUILD%\msys\bin`` +- ``%MOZILLABUILD%\bin`` -- cgit v1.2.3