This directory holds the core of Chromium's web app system. For a quick code starting point see WebAppProvider::Start(), this is the entry point where everything web app related begins.
Simply put web apps are sites that the user installs onto their machine mimicking a native app install on their respective operating system.
Sites that meet our install promotion requirements will have an install prompt appear in the omnibox on the right.
Menu > More tools > Create shortcut...
.Users can see all of their web apps on chrome://apps (viewable on non-ChromeOS).
Sites customise how their installed site integrates at the OS level using a web app manifest. See developer guides for in depth overviews:
This refers to the the document described by the appmanifest spec, with some extra features described by manifest-incubations. This document describes metadata and developer configuration of an installable webapp.
A manifest link is something that looks like this in a html document:
<link rel="manifest" href="manifest.webmanifest">
This link ties the manifest to the document, and subsequently used in the spec algorithms defined in appmanifest or manifest-incubations to describe the webapp and determine if it is installable.
If a document or page is considered “installable”, then the user agent can create some form of installed web app for that page. To be installable, web_app::CanCreateWebApp must return true, where:
http
, https
, or chrome-extension
This is different than promotable below, which determines if Chrome will promote installation of the page.
A document is considered “promotable” if it fulfils a set of criteria. This criteria may change to further encourage a better user experience for installable web apps. There are also a few optional checks that depend on the promotability checker. This general criteria as of 2021/04/20:
name
start_url
icons
with at least one icon with a valid response that is a parsable image.display
field that is not "browser"
start_url
is ‘controlled’ (can be served by) a serviceworker. Optionally turned offNotes:
start_url
origin must match.start_url
origin does not have to match the manifest_url
originstart_url
could be different than the document_url
.Scope refers to the prefix that a WebApp controls. All paths at or nested inside of a WebApp's scope is thought of as “controlled” or “in-scope” of that WebApp. This is a simple string prefix match. For example, if scope
is /my-app
, then the following will be “in-scope”:
/my-app/index.html
/my-app/sub/dir/hello.html
/my-app-still-prefixed/index.html
(Note: if the scope was /my-app/
, then this would not be out-of-scope)And the following will “out-of-scope”:
/my-other-app/index.html
/index.html
The display
of a webapp determines how the developer would like the app to look like to the user. See the spec for how the display
member is processed in the manifest and what the display modes mean.
In addition to the developer-specified display
, the user can specify how they want a WebApp to be displayed, with the only option being whether to “open in a window” or not. Internally, this is expressed in the same display mode enumeration type as display
, but only the kStandalone
and kBrowser
values are used to specify “open in a window” and “do not open in a window”, respectively.
The psuedocode to determine the ACTUAL display mode a WebApp is displayed is:
if (user_display_mode == kStandalone) return developer_specified_display_mode; else return kBrowser; // Open in a tab.
This refers to the user specifying that a WebApp should open in the developer specified display mode.
This refers to the user specifying that a WebApp should NOT open in a window, and thus the WebApp, if launched, will just be opened in a browser tab.
There are some webapps which are managed by external sources - for example, the enterprise policy force-install apps, or the system web apps for ChromeOS. These are generally not installed by user interaction, and the WebAppProvider needs to install something for each of these apps.
Sometimes, the installation of these apps can fail because the install url is not reachable (usually a cert or login needs to occur, and the url is redirected). When this happens, the system can install a “placeholder” app, which is a fake application that, when launched navigates to the install url of the application, given by the external app manager.
When any web contents, either in-(placeholder)-app or in the browser, successfully navigates to a install url that the placeholder app is installed for, the web app installation is restarted for the true app, and after that installation succeeds the placeholder app is uninstalled.
When signing into a non-ChromeOS device, all web apps are installed but not locally installed. This means that OS integration is not triggered (so there are no platform shortcuts created), install icons will still show up for the app websites, and the app icon will appear greyed out on chrome://apps.
For an app to become locally installed, the user must do one of the following:
chrome://apps
, find the greyed-out icon of the app, right click on it, and select “Install”.This was done because on non-ChromeOS devices it was considered a bad user experience to fully install all of the profile's web apps (creating platform shortcuts, etc), as this might not be expected by the user.
The task of turning web sites into “apps” in the user's OS environment has surprisingly many parts to it. Here are some (but not all) of the key ones.
WebAppProvider
This is a per-profile object housing all the various web app subsystems. This is the “main()” of the web app implementation where everything starts.
WebApp
This is the representation of an installed web app in RAM. Its member fields largely reflect all the ways a site can configure their web app manifest plus miscellaneous internal bookkeeping and user settings.
WebAppRegistrar
This is where all the WebApp
s live in memory and what many other subsystems query to look up any given web app's fields. Mutations to the registry have to go via WebAppSyncBridge
.
Why is it full of GetAppXYZ()
getters for every field instead of just returning a WebApp
reference? Because web apps used to be backed by Extension
s and in that mode there were no WebApp
s; instead everything was stored on an Extension
. See WebAppRegistrar
's sibling BookmarkAppRegistrar
for that implementation. Since the backing object was decided based on the feature flag kDesktopPWAsWithoutExtensions
the rest of the logic had to go through a generic interface AppRegistrar
with a separate getter for each field. Note that the Extensions based implementation is obsolete and needs cleaning up.
WebAppInstallManager
This is where web apps are created, updated and removed. The install manager spawns WebAppInstallTask
s for each “job”.
Installation comes in many different forms from a simple “here's all the info necessary please install it” to “please install the site currently loaded in this web contents and fetch all the manifest data it specifies” with a few inbetweens.
ExternallyManagedAppManager
This is for all installs that are not initiated by the user. This includes preinstalled apps, policy installed apps and system web apps.
These all specify a set of install URLs which the ExternallyManagedAppManager
synchronises the set of currently installed web apps with.
WebAppInstallFinalizer
This is the tail end of the installation process where we write all our web app metadata to disk and deploy OS integrations (like desktop shortcuts and file handlers) using the OsIntegrationManager
.
This also manages the uninstallation process.
WebAppUiManager
Sometimes we need to query window state from chrome/browser/ui land even though our BUILD.gn targets disallow this as it would be a circular dependency. This abstract class + impl injects the dependency at link time (see WebAppUiManager::Create()
's declaration and definition locations).
TODO: How to write a unit test. TODO: How to write a browser test.
Use chrome://internals/web-app to inspect internal web app state.