forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a small doc for extension event registration and dispatching.
extensions/ event code has some non-intuitive bits, add an intro doc to give an overview of the events system. Bug: None Test: Used tool/md_browser/md_browser.py to sanity check doc locally. Change-Id: I2d377cb039764d03f1cdd6ccb62721e4dc73767b Reviewed-on: https://chromium-review.googlesource.com/671201 Commit-Queue: Istiaque Ahmed <lazyboy@chromium.org> Reviewed-by: Devlin <rdevlin.cronin@chromium.org> Cr-Commit-Position: refs/heads/master@{#508769}
- Loading branch information
Istiaque Ahmed
authored and
Commit Bot
committed
Oct 13, 2017
1 parent
eaeea6e
commit 0396303
Showing
2 changed files
with
126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
# Extension events | ||
|
||
The Chrome extensions system has its own implementation of events (typically | ||
exposed as `chrome.<api>.onFoo`, e.g. `chrome.tabs.onUpdated`). This doc | ||
provides some notes about its implementation, specifically how event listeners | ||
are registered and how they are dispatched. | ||
|
||
## High level overview of extension event dispatching | ||
|
||
An event listener registered in the renderer process is sent to the browser | ||
process (via IPC). The browser process stores the listener information in | ||
`EventListenerMap`. Events are dispatched from the browser process to the | ||
renderer process via IPC. If browser process requires to persist any listener, | ||
it does so by storing the listener information in the prefs. | ||
|
||
## Relevant concepts | ||
|
||
* __Listener contexts__: | ||
Typically denotes the page/script where the listener is registered from, e.g. | ||
an extension's background page or an extension's service worker script. | ||
|
||
* __Lazy contexts__: | ||
Contexts that are not persistent and typically shut down when inactive, e.g. an | ||
event page's background script or an extension service worker script. Non-lazy | ||
contexts are often called "persistent" contexts. | ||
|
||
* __Persistent listeners / Non-lazy listeners__: | ||
Listeners from contexts that are not lazy. | ||
|
||
* __Lazy listeners__: | ||
Listeners from lazy context. | ||
See the scenario description (_Case 1_ and _Case 2_) below for quick explanation | ||
of how registration of a listener from a lazy context can result in two (a lazy | ||
and a non-lazy) listeners. An event can be dispatched to these listeners while | ||
the corresponding lazy context is not running. | ||
|
||
* __Filtered events__: | ||
A listener can specify additional matching criterea that we call event filters. | ||
Some events support filters. IPCs (along with most but not all of the browser/ | ||
or renderer/ code) use `DictionaryValue` to represent an event filter. | ||
|
||
|
||
## Event listener registration | ||
|
||
Event listeners are registered in JavaScript in the renderer process. The | ||
event bindings code handles this registration and the browser process is made | ||
aware of it via IPC. | ||
|
||
In particular, a message filter (`ExtensionMessageFilter`) receives event | ||
registration IPCs and it passes them to `EventRouter` to be stored in | ||
`EventListenerMap`. If the listener is required to be persisted (for lazy | ||
events), they are also recorded in `ExtensionPrefs`. | ||
|
||
Note that when the renderer context is shut down, it removes the listener. The | ||
exception is lazy event listener, which is not removed. | ||
|
||
|
||
### Additional notes about lazy listeners | ||
|
||
When a lazy listener is added for an event, a regular (non-lazy) listener | ||
(call it `L1`) is added for it and in addition to that, a lazy variant of the | ||
listener (call it `L2`) is also added. `L2` helps browser process remember that | ||
the listener should be persisted and it should have lazy behavior. | ||
|
||
## Event dispatching | ||
|
||
`EventRouter` is responsible for dispatching events from the browser process. | ||
When an event is required to be dispatched, `EventRouter` fetches EventListeners | ||
from `EventListenerMap` and dispatches them to appropriate contexts (renderer | ||
or service worker scripts) | ||
|
||
### Additional notes about lazy event dispatching | ||
|
||
Recall that a lazy listener is like a regular listeners, except that it is | ||
registered from a lazy context. A lazy context can be shut down. If an | ||
interesting event ocurrs while a lazy context (with a listener to that event) | ||
is no longer running, then the lazy context is woken up to dispatch the event. | ||
|
||
The following (simplified) steps describe how dispatch is performed. | ||
|
||
#### Case 1: Event dispatched while context (lazy or non-lazy) is running | ||
|
||
* Because `EventListenerMap` will contain an entry for the listener (`L1`), it | ||
will dispatch the event in normal fashion: by sending an IPC to the renderer | ||
through `ExtensionMessageFilter`. | ||
|
||
#### Case 2: Event dispatched while (lazy) context is not running | ||
|
||
* If the context is not running, then `EventListenerMap` will not have any entry | ||
for `L1` (because context shutdown will remove `L1`), but it will have an entry | ||
for the lazy version of it, `L2`. Note that `L2` will exist even if the browser | ||
process is restarted, `EventRouter::OnExtensionLoaded` will have loaded these | ||
lazy events through `EventListenerMap::Load(Un)FilteredLazyListeners`. | ||
|
||
* Realize that `L2` is lazy, so wake up its lazy context. Waking up an event | ||
page context entails spinning up its background page, while waking up a service | ||
worker context means starting the service worker. | ||
|
||
* The lazy context will register `L1` and `L2` again, because the same code that | ||
added the initial listeners will run again. This is an important step that | ||
isn't intuitive. Note that `L2`, since it already exists in the browser process, | ||
is not re-added. | ||
|
||
* Dispatch `L1` (same as _Case 1_ above). | ||
|
||
## Notes about extension service worker (ESW) events | ||
|
||
* ESW events behave similar to event page events, i.e. lazy events. | ||
|
||
* ESW events are registered from worker threads, instead of main renderer | ||
threads. | ||
|
||
* Similarly, event dispatch target is worker thread instead of main renderer | ||
thread. Therefore, at dispatch time, browser process knows about the worker | ||
thread | ||
id in a RenderProcessHost. This is why worker event listener IPCs have | ||
`worker_thread_id` param in them. | ||
|
||
## TODOs | ||
|
||
* Explain filters a bit more, where filter matching is performed and how much of | ||
it lives in the renderer/ process. | ||
|
||
* Describe what "manual" removal of event listeners means. |