| # Web platform compatibility in Android WebView | |
| The Android WebView sometimes needs special consideration and care when making | |
| changes to web platform APIs and behaviours. Chromium developers who make these | |
| changes (especially deliberate interventions) may want to consult this | |
| documentation and/or reach out to the WebView team if they're unsure how a | |
| change might affect WebView. | |
| [TOC] | |
| ## Why is WebView special when it comes to compatibility? | |
| ### Android has a different approach to compatibility than the web: targetSdkVersion | |
| WebView is part of the Android platform and very widely used in the Android app | |
| ecosystem. Android tries hard to avoid making backward-incompatible changes to | |
| the platform; existing apps (including those the user may have paid for) are | |
| expected to continue to work after an OS update. | |
| One key way this is achieved is that Android apps declare a | |
| [target SDK version](https://developer.android.com/guide/topics/manifest/uses-sdk-element) | |
| in their manifest; this specifies the latest version of the Android SDK that the | |
| developer considered and tested against. Behaviour changes and new restrictions | |
| in the platform are often only applied to apps which target at least the version | |
| in which those changes were introduced; this allows already-published apps to | |
| remain unaffected by the changes. Android's | |
| [release notes](https://developer.android.com/about/versions/10/behavior-changes-10) | |
| for developers call out which changes developers can expect to see when they | |
| update their app's target SDK to the latest version, to make it easier to test | |
| and update apps. | |
| To avoid apps continuing to target old SDK versions forever (e.g. to permanently | |
| avoid newly introduced restrictions), the Play Store | |
| [enforces a minimum](https://developer.android.com/distribute/best-practices/develop/target-sdk) | |
| when developers upload app binaries to the store: new and updated apps must | |
| target a sufficiently recent version. However, there is no enforcement applied | |
| to existing app binaries: apps that are updated very infrequently or which are | |
| no longer being actively developed at all remain on the store, and may still | |
| target much older SDK versions. This means that the number of apps requiring any | |
| particular piece of backward-compatibility code drops over time, but never to | |
| zero, and in most cases Android retains the backward-compatible code path | |
| indefinitely. | |
| This means that even though WebView has been receiving frequent updates since | |
| Android 5.0, app developers often still expect that incompatible changes to its | |
| behaviour only happen in new Android releases. | |
| ### Android's Compatibility Test Suite tests some WebView behaviours | |
| Android relies on the | |
| [**Compatibility Test Suite** (**CTS**)](https://source.android.com/compatibility/cts) | |
| to ensure that different devices' implementations of Android are compatible with | |
| each other. CTS is versioned alongside the Android OS; any Android 10 device is | |
| expected to pass CTS version 10, and so on. CTS does have minor version updates | |
| to address bugs in the tests themselves, such as flakiness or assumptions that | |
| are discovered not to be valid on all devices; see | |
| [WebView's CTS docs](../tools/cts_config/README.md#changing-cts-tests-retroactively) | |
| for more information on when this may be appropriate. | |
| CTS contains a number of tests which test the WebView's APIs and behaviours, and | |
| it's expected that these tests will pass even if the device has been updated to | |
| the latest version of WebView. This presents a significant problem for web | |
| platform changes if they cause an existing CTS test to fail; this means WebView | |
| is no longer compatible with that Android version's expectations, and the entire | |
| point of those expectations is that apps may also rely on them. CTS test | |
| failures introduced by Chromium changes are generally considered P1 and | |
| `ReleaseBlock-Dev`. | |
| ### WebView is often used to show "first-party" content | |
| While many apps do use the WebView to display general web content that's also | |
| intended to be used in browsers, it's also very common for apps to display what | |
| the WebView team calls **first-party** (**1P**) content: HTML/CSS/JS which is | |
| developed specifically for a particular app's WebView, rather than for the open | |
| web. | |
| The app developer generally considers their 1P content to be part of the app | |
| itself, whether it's fetched over the network via HTTP(S) at runtime, or loaded | |
| in an app-specific way. This content can present additional challenges for | |
| compatibility for several reasons: | |
| * Developers may expect Android's general compatibility rules to apply to all | |
| the web platform APIs that their content interacts with, even though this is | |
| not the web's usual model. | |
| * Developers may expect their content to be considered just as trustworthy as | |
| the rest of their app, so may object to user agent interventions being | |
| applied to it; for example, they might expect it to be able to function as | |
| normal even if the WebView is in the background or offscreen. | |
| * 1P content is often packaged inside the app binary, dynamically generated, | |
| or downloaded from servers that aren't linked to on the open web. | |
| * This often makes it "invisible" to search engines, including the HTTP | |
| Archive data used by Google as one way to evaluate the compatibility | |
| impact of web platform changes, and to Chrome's UMA metrics. Using | |
| WebView's own UMA metrics can mitigate this. | |
| * This also often makes it slower and more difficult for the developer to | |
| fix compatibility issues, since a new version of the app binary may have | |
| to be published to the Play Store, and users will not see the changes | |
| until they install the app update. | |
| This means that WebView's API surface effectively includes not just the Java | |
| APIs that apps call to control it, but also the entire web platform, and | |
| therefore changes to Chromium need not touch any code in `//android_webview` to | |
| be a risk. | |
| ### WebView's metrics and experiments are less mature and comprehensive than Chrome's | |
| WebView does have crash reporting, UMA, and Finch, but in many cases the data is | |
| noisier, less actionable, or less comprehensive for a number of reasons. This | |
| can make it harder to rely on data to assess potential compatibility impact. | |
| WebView's beta/dev/canary populations are fairly small and as in Chrome these | |
| users are a self-selected sample who aren't always representative of the stable | |
| population. A particular concern for WebView here is that even a fairly popular | |
| Android app's user base might include significantly fewer pre-stable WebView | |
| users than the overall population size would suggest; an issue that only affects | |
| a few specific apps may never be encountered in the field until the stable | |
| release. | |
| UMA metrics implemented in `//content` (and in those `//components` which are | |
| shared between WebView and Chrome) are generally already being recorded in | |
| WebView. UMA metrics implemented in `//chrome` code will not be recorded in | |
| WebView, and should be moved to lower layers of the code if they're relevant to | |
| WebView so that they can be shared and compared. | |
| ## What are WebView's compatibility goals? | |
| WebView has to strike a balance between allowing the web platform to move | |
| forward, and ensuring that existing Android apps continue to work correctly. | |
| Since it's WebView's explicit goal (and on most OS versions, a strict technical | |
| requirement) to ship new versions alongside Chrome for Android, it's neither | |
| realistic nor desirable to hold back the vast majority of web platform changes, | |
| even if they present _some_ compatibility risk. WebView focuses on: | |
| * **Issues that affect a library, framework, or SDK that is used by more than | |
| one app developer.** For example: mobile ad SDKs, cross-platform app | |
| deployment environments like Cordova, or the AndroidX libraries. | |
| * It can take a long time for apps to update to a new library version with | |
| the fix, because libraries are almost always compiled into the | |
| individual app binaries; Android apps generally can't share library code | |
| on-device. | |
| * **Issues where no straightforward fix or workaround can be applied by the | |
| app developer.** For example: feature removals, new restrictions, or user | |
| agent interventions. If the developer can't realistically update their app | |
| to a working state in a short period of time, there's a stronger need for | |
| WebView to revert the change or work around the issue on their behalf. | |
| * **Issues that affect common patterns of WebView API usage.** The design | |
| choices of WebView's Java APIs often mean that many different apps have | |
| similar patterns of usage, either learned from examples on the internet or | |
| through natural convergence of implementation; some of these patterns result | |
| in `//content` APIs being invoked in ways that Chrome and other browsers do | |
| not, and many of them rely on _de facto_ existing behaviour of WebView | |
| rather than documented API behaviours. Some are well-known by the WebView | |
| team, but others are only discovered after a change is made. | |
| * **Issues that affect very popular apps.** The number of installs of an app | |
| roughly has a long-tail distribution, so a very large number of users can be | |
| impacted even if just one app is affected by a change. | |
| ## What kind of changes are likely to have an effect on WebView compatibility? | |
| ### Unusual WebView behaviours | |
| WebView has a number of behaviours and APIs that aren't shared with Chrome or | |
| other browsers; these can cause unexpected compatibility issues with Chromium | |
| changes, even if the change is not intended to change web-exposed behaviour. | |
| Some of the most important examples: | |
| #### URL and origin handling | |
| * WebView allows apps to use any string as a URL scheme, without requiring any | |
| definition of which custom schemes are valid. | |
| * Requests to URLs with custom schemes trigger normal requests which the | |
| app can intercept to provide a response. | |
| * The origin of a URL with a custom scheme is just the scheme prefix (e.g. | |
| `foo://`), so any URLs with the same custom scheme are considered | |
| same-origin. | |
| * CORS is expected to work with custom schemes in the same way as with | |
| HTTP(S). | |
| * WebView allows apps to load arbitrary content as if it came from an | |
| arbitrary URL via | |
| [`loadDataWithBaseURL`](https://developer.android.com/reference/android/webkit/WebView#loadDataWithBaseURL(java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String)). | |
| * The navigation is treated as if it were loading the "base URL", but | |
| instead of making a request to that URL, the custom content is loaded as | |
| if it were a `data:` URI. | |
| * The origin of the loaded page is the origin of the base URL. | |
| * WebView apps frequently use `file://` URLs to load local content; special | |
| settings are supported. | |
| * The WebView | |
| [`setAllowFileAccessFromFileURLs`](https://developer.android.com/reference/android/webkit/WebSettings#setAllowFileAccessFromFileURLs(boolean)) | |
| API causes all `file://` URLs to be considered same-origin with each | |
| other, instead of the default Chrome behaviour of `file://` URLs having | |
| an opaque origin. | |
| * The WebView | |
| [`setAllowUniversalAccessFromFileURLs`](https://developer.android.com/reference/android/webkit/WebSettings#setAllowUniversalAccessFromFileURLs(boolean)) | |
| API allows all `file://` URLs to make requests to **any** origin without | |
| CORS or any other restrictions. | |
| * WebView also supports `content://` URLs to access Android content | |
| providers, and these are treated like `file://` URLs in most respects. | |
| #### Process model | |
| * [WebView runs as a library inside the embedding application.](./architecture.md#processes) | |
| WebView's browser process is the app's process, and thus any process-global | |
| state is potentially shared with the embedding app. | |
| * Depending on the OS version and device configuration, WebView may run in | |
| [either "single-process" or "multi-process" mode.](../renderer/README.md) | |
| Only one renderer per app is currently used in multi-process mode, but this | |
| limit may increase in the future. | |
| * WebView cannot use non-sandboxed child processes regardless of whether it's | |
| running in single- or multi- process mode; only sandboxed child processes | |
| are supported. | |
| * WebView does not use a separate GPU process even when running in | |
| multi-process mode. | |
| #### UI and browser chrome | |
| * WebView doesn't render any UI elements outside of the content viewport. The | |
| embedding app is responsible for any UI that may be needed to implement | |
| WebView callbacks. | |
| * Callback APIs exist to trigger permission prompts (though apps may not | |
| implement them), but there's no current API to render infobars, control how | |
| the URL or security status is shown (if at all), or similar features. | |
| * Cooperation from the embedding app is needed to implement features such as | |
| popup windows and fullscreen, and apps may not implement these, or implement | |
| them incorrectly. | |
| #### Graphics | |
| * WebView's graphics rendering architecture is somewhat different to Chrome's | |
| to support specific behaviours apps rely on; see | |
| [the architecture doc](https://docs.google.com/document/d/1MLPEmMugdVvfeMeQQN_NMolqs4zZekfKjZeNAQJJnMo) | |
| for an overview. | |
| ### Issues with existing WebView APIs | |
| #### Permissions | |
| WebView's APIs for allowing the app to control web permissions have significant | |
| limitations at present. The Web Permissions API is not implemented in WebView, | |
| and in general any code in Chromium that relies on being able to silently check | |
| the status of permissions will not work - only actual permission requests are | |
| supported. This can cause problems when implementing web platform features that | |
| are gated behind permissions, or when changing the way that existing features | |
| make permissions checks. | |
| ## How do I determine the effect of a change on WebView compatibility? | |
| ### CTS | |
| Ensuring that WebView's CTS tests all pass with your change enabled is a very | |
| important first step; if it causes CTS failures then this means WebView | |
| compatibility is _definitely_ affected, and means you should definitely | |
| [reach out to the WebView team](https://groups.google.com/a/chromium.org/forum/#!forum/android-webview-dev) | |
| to discuss it. | |
| CTS is run as part of the [required CQ trybots](https://chromium.googlesource.com/chromium/src/+/HEAD/infra/config/generated/cq-builders.md#required-builders-chromium): | |
| - [`android-arm64-rel`](https://ci.chromium.org/ui/p/chromium/builders/try/android-arm64-rel) | |
| (for device-side CTS) | |
| - [`android-x64-rel`](https://ci.chromium.org/ui/p/chromium/builders/try/android-x64-rel) (for host-driven CTS) | |
| and on the main waterfall. If your change causes CTS failures, | |
| you may need to [run CTS locally](./test-instructions.md#cts) to investigate. | |
| ### UMA, Finch, and other data collection from the field | |
| WebView has UMA, Finch, and crash reporting, but does not currently have any | |
| equivalent of UKM. Googlers can read the WebView-specific | |
| [UMA](http://go/clank-webview-uma) and [Finch](http://go/clank-webview/finch) | |
| docs for more detailed information, but some key points: | |
| * WebView is a separate "platform" for UMA/Finch purposes; experiments | |
| targeting "android" only affect Chrome for Android, _not_ WebView. | |
| * WebView's dev and canary populations are fairly small, although the beta | |
| population is decently sized. | |
| * WebView currently uses _per-app_ anonymous IDs for privacy reasons - this | |
| means that each individual app on a particular Android device will be | |
| considered a "user" or "client" for data collection purposes, with no way | |
| to aggregate data at the level of actual users or devices. | |
| * Individual WebView apps can disable metrics collection, even if the user | |
| opted in on their device. | |
| * WebView does not always record the package name of the app for privacy | |
| reasons. | |
| ## My change is likely to (or already did) affect WebView compatibility; what should I do? | |
| There is no single solution here, but there are several common approaches; the | |
| person(s) making the change should discuss it with the WebView team to decide | |
| what's appropriate in a particular case. These are not mutually exclusive; it | |
| may be useful to combine approaches or to move to another approach at a later | |
| time. | |
| ### Apply the change to all apps, but use a Finch experiment for rollout | |
| This is generally the best option if there is some reason to believe that the | |
| change might be a compatibility issue, but there aren't any specific apps which | |
| are known to be impacted, and there's no practical way to collect metrics on the | |
| impact from the field. | |
| You should implement your change behind a flag (if it isn't already), and make | |
| sure that the flag is **disabled** by default for WebView. You should also | |
| [expose that flag in WebView's developer UI](./developer-ui.md#Adding-your-flags-and-features-to-the-UI) | |
| so that testers, app developers, and users can check if their issue is caused by | |
| your change. On production devices, only the flags in the developer UI can be | |
| used; it's not possible to pass arbitrary command line flags. | |
| *** aside | |
| We don't recommend enabling flags by default and relying on "kill switches" in | |
| Finch for potentially incompatible WebView changes, because WebView apps can't | |
| apply experiment settings from the server the first time they are launched. This | |
| means that if an app is broken by your change, it will disproportionately affect | |
| new users of the app, which is an understandably major concern for app | |
| developers! | |
| *** | |
| Before enabling the Finch experiment, reach out to the WebView team to discuss | |
| testing; WebView's QA testers can test popular applications with your flag | |
| enabled to give you early feedback on potential issues. If there are no issues | |
| found at this stage, you can proceed with the Finch experiment. Googlers can | |
| also read | |
| [the WebView-specific Finch documentation](http://go/clank-webview/finch). | |
| *** note | |
| If the change causes a WebView CTS test to fail, this is usually not going to be | |
| an option: existing versions of CTS are expected to pass even with an updated | |
| version of WebView. Instead, you should work with the WebView team to update the | |
| _next_ version of CTS, and only apply your change to apps which target the next | |
| version of Android or later. An exception _may_ be made if the change fixes an | |
| existing security issue; please discuss this with the WebView team if you think | |
| it may apply. | |
| *** | |
| ### Apply the change to all apps unconditionally | |
| Sometimes it may not be necessary (or reasonable) to use a Finch experiment to | |
| roll out a change. Some examples of when this may be appropriate: | |
| * if the effect of the change will be immediately obvious to app developers | |
| who encounter it, and the appropriate fix is straightforward; e.g. if APIs | |
| will now return errors or throw exceptions in cases where they did not | |
| before, and the errors provide enough information to address the issue | |
| * if metrics show that the number of affected apps is very small and the issue | |
| is rarely encountered | |
| * if the change addresses a security issue | |
| * if the change cannot reasonably be implemented behind a flag for technical | |
| reasons | |
| Even in this case, it can still be useful to have a temporary flag to revert to | |
| the old behaviour, so that testers, app developers, and users can check if their | |
| issue is caused by the change. | |
| ### Reach out to affected apps to ask them to fix the incompatibility | |
| If you are able to determine that only a small number of apps are affected by | |
| the change, it may be possible to reach out to the developers of those apps and | |
| suggest changes that will avoid the compatibility issue; you can discuss this | |
| with the WebView team and we can help. This is more likely to succeed if the | |
| apps in question are popular and are actively being updated, such that a | |
| relatively small amount of effort can resolve the issue for a large number of | |
| users. | |
| You should implement your change behind a flag and | |
| [expose that flag in WebView's developer UI](./developer-ui.md#Adding-your-flags-and-features-to-the-UI) | |
| to enable the app developer to test their app with the new behaviour. | |
| You should also ensure that metrics are implemented which will clearly show | |
| whether apps are still relying on the old behaviour. The metrics you already | |
| have for the feature may be sufficient, but it may also be useful to add new | |
| WebView-specific metrics (e.g. to see how relevant WebView Java APIs are being | |
| used). | |
| Once the flag and metrics are implemented and released (ideally to at least the | |
| beta channel), the WebView team can help you reach out to the developers in | |
| question. If the metrics show that the compatibility risk has decreased | |
| significantly, you can consider rolling the change out via a Finch experiment as | |
| described above. | |
| ### Apply the change to apps that declare targetSdkVersion >= X | |
| This is often applicable when the change is adding a restriction, handling input | |
| more strictly, or changing the interpretation of currently-accepted but dubious | |
| or invalid inputs. If apps are using an API incorrectly or ill-advisedly, this | |
| will allow their existing binaries to continue working, but force them to | |
| correct their code at some point in the future when they target the new SDK | |
| version. It also gives us a standard way to communicate the change to | |
| developers: adding it to the release notes of the next Android version. | |
| The downside is that the old behaviour must still be maintained, either by | |
| keeping the old code path or by adding explicit compatibility code; both | |
| versions need to be appropriately tested to avoid future problems, and this can | |
| eventually become technical debt. You should expect to **maintain both versions | |
| indefinitely**; we do not currently have a process to sunset target SDK | |
| compatibility behaviours in WebView. | |
| When making a change conditional on targetSdkVersion, it should only apply to | |
| Android versions that have yet to be released to developers; once the final | |
| Android SDK has been released (which happens while the OS update itself is still | |
| in beta, before the general public release), it's too late to add new | |
| targetSdkVersion-based requirements. This will often mean that you don't get any | |
| feedback about how the new behaviour affects WebView for _at least 6-12 months_, | |
| as apps will still be using the old behaviour for some time. | |
| ### Expose a new Java API or setting for apps to control it | |
| If it's reasonable for an app to want to keep the old behaviour in some cases, | |
| but the new behaviour is also useful/applicable in WebView, then it may be | |
| desirable to give the app control. This usually either means adding a new | |
| setting that can be configured through WebView's Java API, or adding a new Java | |
| callback which apps can implement to make case-by-case decisions. | |
| Where possible, settings should be configurable on a per-WebView basis | |
| (equivalent to per-tab in Chrome) - apps often use different WebViews for very | |
| different purposes, and may contain third party libraries that use WebView in a | |
| manner outside of the app's direct control, so global settings can be difficult | |
| to use successfully. | |
| WebView's Java API can be extended via the AndroidX compatibility library, which | |
| is released more frequently than the Android OS, and allows developers to use | |
| new WebView APIs even on older versions of Android (as long as a new enough | |
| version of WebView itself is installed on the device). | |
| It's usually necessary for the default to be the _old_ behaviour, and for apps | |
| to explicitly opt in to the new behaviour. If the new behaviour will usually be | |
| preferable and the old behaviour is only desired in exceptional cases, one | |
| option is to make the default conditional on targetSdkVersion (as described in | |
| the previous section) - default to the old behaviour (with an explicit opt-in to | |
| the new behaviour) on apps which target <X, and default to the new behaviour | |
| (with an explicit opt-out to revert to the old behaviour) on apps which target | |
| >=X. | |
| ### Last resort: don't apply the change to WebView at all | |
| This is usually only applicable when it makes sense for the change to be a | |
| matter of user agent policy, e.g.: | |
| * if it's controlled by a normal user preference in Chrome (**not** by a flag | |
| or command line switch) | |
| * if it's controlled by an enterprise policy in Chrome that's not intended to | |
| expire in a future release | |
| * if it's explicitly stated to be user agent defined in web specifications | |
| * if other browsers behave differently and aren't going to adopt the same | |
| behaviour | |
| This is generally a last resort even if these apply; every place WebView | |
| entirely diverges from Chrome may become a source of technical debt and other | |
| issues later. Please talk to the WebView team before deciding to omit WebView | |
| from a web platform change! | |
| ## How do I implement behaviours/options differently in WebView than Chrome? | |
| All code in `//android_webview` is exclusively used in WebView, and no code in | |
| `//chrome` is ever used by WebView, so if you can make the change at that level, | |
| it's guaranteed not to affect the other. | |
| In common code (such as `//content` and `//components`), you can use one of | |
| the mechanisms below. Since Chrome and WebView share the same GN `target_os`, | |
| each of these requires a different strategy to make sure both behaviors are | |
| unit tested. | |
| *** note | |
| **Note:** Out of these, only (1) can be controlled server-side to mitigate | |
| incidents, so be careful. | |
| *** | |
| 1. If you're launching a feature in Chrome first and will later launch in | |
| WebView (or vice-versa), use a `base::Feature` with overrides in | |
| `AwFieldTrials::RegisterFeatureOverrides()`. You can configure which code | |
| path is tested with `base::test::ScopedFeatureList`. | |
| 2. Otherwise, if you want a configurable setting (by the user, enterprise | |
| policy, or embedding app) that has different defaults in Chrome and WebView, | |
| declare the setting in common code and the defaults in each embedder. | |
| Examples of these are: | |
| * `blink::web_pref::WebPreferences`: these are usually per-WebView | |
| settings accessed via `android.webkit.WebSettings`. WebView values are | |
| populated in `AwSettings::PopulateWebPreferencesLocked()`. Tests can set | |
| different values via `content::WebContents::SetWebPreferences()` or | |
| `blink::WebView::ApplyWebPreferences()`. | |
| * Prefs: these are per-`AwBrowserContext` or local-state settings. For an | |
| example, see `PrefService::GetBoolean()` and | |
| `PrefRegistrySimple::RegisterBooleanPref()`. Tests can set different | |
| values via `TestingPrefServiceSimple`. | |
| 3. Otherwise, if either the Chrome or WebView implementation needs to rely | |
| on functionality that lives in `//chrome` or `//android_webview`, declare a | |
| method in a new/existing interface and implement it in each embedder. Tests | |
| can use a fake or mock the method. | |
| 4. Otherwise, check `build::IsAndroidWebView()`. Perform the check in a code | |
| path that allows testing, such as a) the implementation of a method in some | |
| interface (uses the same test strategy as above), or b) when deciding what | |
| argument to pass to a function; different tests can pass different values. | |
| In general this is less convenient than (1) but good testing can and should | |
| still be achieved. |