Skip to content

Commit ffb10bc

Browse files
authored
Merge pull request #729 from sohailrajdev97/runtime-events-proposal
Add proposal for runtime.onEnabled and runtime.onExtensionLoaded events
2 parents 945c24d + c7f3da6 commit ffb10bc

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# Proposal: New runtime.onEnabled and runtime.onExtensionLoaded events
2+
3+
**Summary**
4+
5+
Creates two new extension state notification events that will help developers
6+
take action on the full “lifecycle” of their extensions.
7+
8+
**Document Metadata**
9+
10+
**Author:** [email protected]
11+
12+
**Sponsoring Browser:** Google Chrome and Microsoft Edge
13+
14+
**Contributors:** [email protected]
15+
16+
**Created:** 2024-12-03
17+
18+
**Related Issues:** https://github.com/w3c/webextensions/issues/353
19+
20+
## Motivation
21+
22+
### Objective
23+
24+
Enable two new extension lifecycle events for the developer to add listeners for:
25+
26+
#### chrome.runtime.onEnabled
27+
Enables a developer to listen to when an extension is re-enabled from a disabled
28+
state. There is currently no way for a developer to listen for the extension
29+
being disabled and then enabled.
30+
31+
### chrome.runtime.onExtensionLoaded
32+
Enables a developer to listen to when an extension is added to the set of active
33+
extensions. This can occur from being installed, enabled, or when the browser /
34+
profile first starts. The listener will be given the cause so they can decide to
35+
take different actions based on the reason it was added to the set. There is
36+
currently no generic event that encapsulates all instances of extension install
37+
(and update), startup, and being enabled from a disabled state.
38+
39+
#### Use Cases `chrome.runtime.onEnabled`
40+
A developer wants to run code once in an extension’s "lifecycle", without storing
41+
a flag to keep track of this.
42+
43+
Before this API the developer would need to manually keep track of this state
44+
via a storage flag in persistent storage.
45+
46+
With this new API a single listener can be registered and reduce the extension’s
47+
complexity.
48+
49+
More use cases can be found [here](https://github.com/w3c/webextensions/issues/353#issuecomment-1490078217).
50+
51+
#### Use Cases `chrome.runtime.onExtensionLoaded`
52+
An extension automatically does initial bootstrapping, such as creating an
53+
initial state used throughout the browser session, creating a WebSocket
54+
connection, checking for things you've missed while the browser was closed etc.
55+
56+
Before this API the developer would have to create multiple listeners for
57+
`chrome.runtime.onInstalled()`, `onStartup()`, and (above) `onEnabled()`.
58+
59+
With this new API method the developer could write one method that performs the
60+
same action for all three, potentially with small differences based on the
61+
loaded cause.
62+
63+
Some browsers (e.g. Chromium) support running a separate instance of the background
64+
script in incognito mode, by setting `incognito: "split"` in the extension manifest.
65+
This event will be fired for such instances separately. The event may fire
66+
repeatedly during the same browser session if the incognito session ends and
67+
(re)starts, for example by closing the last incognito window and opening a new
68+
incognito window.
69+
70+
More use cases can be found [here](https://github.com/w3c/webextensions/issues/353#issuecomment-1582536300).
71+
72+
### Known Consumers
73+
74+
This is a broadly applicable API method that will be helpful to any developer
75+
wanting to execute code as the extension changes running states. But there
76+
appears to be strong interest per the [WECG discussion](https://github.com/w3c/webextensions/issues/353).
77+
78+
## Specification
79+
80+
### Schema
81+
#### `chrome.runtime.onEnabled`
82+
```typescript
83+
namespace runtime {
84+
// Fired when an extension goes from being in a disabled state to an enabled
85+
// state.
86+
export interface onEnabled {
87+
addListener(callback: () => void): void;
88+
removeListener(callback: () => void): void;
89+
}
90+
}
91+
```
92+
93+
#### `chrome.runtime.onExtensionLoaded`
94+
```typescript
95+
namespace runtime {
96+
// The reason for which the event is being dispatched.
97+
//
98+
// 'enabled': The extension was re-enabled from a disabled state.
99+
//
100+
// 'installed': The extension was newly installed.
101+
//
102+
// 'updated': The extension was reloaded after an update.
103+
//
104+
// 'startup': The extension is being loaded during browser startup.
105+
//
106+
// 'reload': The extension was reloaded (e.g. via `chrome.runtime.reload() or`
107+
// the user manually reloaded the extension).
108+
export type OnLoadedReason = 'enabled' |
109+
'installed' |
110+
'updated' |
111+
'startup' |
112+
'reload';
113+
114+
export interface ExtensionLoadDetails {
115+
// The reason that this event is being dispatched.
116+
reason: OnLoadedReason;
117+
118+
// Indicates the previous version of the extension, which has just been
119+
// updated. This is present only if 'reason' is 'updated' or ‘enabled’.
120+
previousVersion?: string;
121+
}
122+
123+
// Fired when the extension is added to the set of active extensions. This can
124+
// occur in multiple scenarios: when an extension is first installed, updated
125+
// to a new version, and enabled after being disabled.
126+
export interface onExtensionLoaded {
127+
addListener(callback: (details: ExtensionLoadDetails) => void): void;
128+
hasListener(callback: (details: ExtensionLoadDetails) => void): boolean;
129+
removeListener(callback: (details: ExtensionLoadDetails) => void): void;
130+
}
131+
}
132+
```
133+
134+
### Behavior
135+
136+
Described as code comments in schema description.
137+
138+
### New Permissions
139+
140+
No new permissions are needed as extensions can access the `browser.runtime`
141+
API without any permissions.
142+
143+
### Manifest File Changes
144+
145+
No new manifest changes are added.
146+
147+
## Security and Privacy
148+
149+
### Exposed Sensitive Data
150+
151+
The new events expose some information that wasn’t present before. It does
152+
expose that an extension was (explicitly) disabled, which is something we don't
153+
expose today. Today, an extension cannot necessarily distinguish between being
154+
disabled and the user simply not opening the browser for <n> amount of
155+
time.
156+
157+
### Abuse Mitigations
158+
159+
These APIs would be difficult to use in an abusive way since they do not give
160+
information or access to other extensions, and don’t give control into the
161+
extensions lifecycle – only knowledge as it transitions to different states.
162+
163+
### Additional Security Considerations
164+
165+
N/A.
166+
167+
## Alternatives
168+
169+
### Existing Workarounds
170+
171+
There is currently `chrome.runtime.onInstalled`, and `chrome.runtime.onStartup`.
172+
These API have gaps in listening that are addressed by these two events. In
173+
short, `chrome.runtime.onEnabled`, covers the remaining status that developers
174+
care to know, and `chrome.runtime.onExtensionLoaded` encapsulates them all
175+
together with a single listener.
176+
177+
### Open Web API
178+
179+
These API methods are specific to extension lifetime / lifecycle and would not
180+
be appropriate to be in the open web.
181+
182+
## Implementation Notes
183+
184+
N/A.
185+
186+
## Future Work
187+
188+
N/A.

0 commit comments

Comments
 (0)