-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #729 from sohailrajdev97/runtime-events-proposal
Add proposal for runtime.onEnabled and runtime.onExtensionLoaded events
- Loading branch information
Showing
1 changed file
with
188 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
# Proposal: New runtime.onEnabled and runtime.onExtensionLoaded events | ||
|
||
**Summary** | ||
|
||
Creates two new extension state notification events that will help developers | ||
take action on the full “lifecycle” of their extensions. | ||
|
||
**Document Metadata** | ||
|
||
**Author:** [email protected] | ||
|
||
**Sponsoring Browser:** Google Chrome and Microsoft Edge | ||
|
||
**Contributors:** [email protected] | ||
|
||
**Created:** 2024-12-03 | ||
|
||
**Related Issues:** https://github.com/w3c/webextensions/issues/353 | ||
|
||
## Motivation | ||
|
||
### Objective | ||
|
||
Enable two new extension lifecycle events for the developer to add listeners for: | ||
|
||
#### chrome.runtime.onEnabled | ||
Enables a developer to listen to when an extension is re-enabled from a disabled | ||
state. There is currently no way for a developer to listen for the extension | ||
being disabled and then enabled. | ||
|
||
### chrome.runtime.onExtensionLoaded | ||
Enables a developer to listen to when an extension is added to the set of active | ||
extensions. This can occur from being installed, enabled, or when the browser / | ||
profile first starts. The listener will be given the cause so they can decide to | ||
take different actions based on the reason it was added to the set. There is | ||
currently no generic event that encapsulates all instances of extension install | ||
(and update), startup, and being enabled from a disabled state. | ||
|
||
#### Use Cases `chrome.runtime.onEnabled` | ||
A developer wants to run code once in an extension’s "lifecycle", without storing | ||
a flag to keep track of this. | ||
|
||
Before this API the developer would need to manually keep track of this state | ||
via a storage flag in persistent storage. | ||
|
||
With this new API a single listener can be registered and reduce the extension’s | ||
complexity. | ||
|
||
More use cases can be found [here](https://github.com/w3c/webextensions/issues/353#issuecomment-1490078217). | ||
|
||
#### Use Cases `chrome.runtime.onExtensionLoaded` | ||
An extension automatically does initial bootstrapping, such as creating an | ||
initial state used throughout the browser session, creating a WebSocket | ||
connection, checking for things you've missed while the browser was closed etc. | ||
|
||
Before this API the developer would have to create multiple listeners for | ||
`chrome.runtime.onInstalled()`, `onStartup()`, and (above) `onEnabled()`. | ||
|
||
With this new API method the developer could write one method that performs the | ||
same action for all three, potentially with small differences based on the | ||
loaded cause. | ||
|
||
Some browsers (e.g. Chromium) support running a separate instance of the background | ||
script in incognito mode, by setting `incognito: "split"` in the extension manifest. | ||
This event will be fired for such instances separately. The event may fire | ||
repeatedly during the same browser session if the incognito session ends and | ||
(re)starts, for example by closing the last incognito window and opening a new | ||
incognito window. | ||
|
||
More use cases can be found [here](https://github.com/w3c/webextensions/issues/353#issuecomment-1582536300). | ||
|
||
### Known Consumers | ||
|
||
This is a broadly applicable API method that will be helpful to any developer | ||
wanting to execute code as the extension changes running states. But there | ||
appears to be strong interest per the [WECG discussion](https://github.com/w3c/webextensions/issues/353). | ||
|
||
## Specification | ||
|
||
### Schema | ||
#### `chrome.runtime.onEnabled` | ||
```typescript | ||
namespace runtime { | ||
// Fired when an extension goes from being in a disabled state to an enabled | ||
// state. | ||
export interface onEnabled { | ||
addListener(callback: () => void): void; | ||
removeListener(callback: () => void): void; | ||
} | ||
} | ||
``` | ||
|
||
#### `chrome.runtime.onExtensionLoaded` | ||
```typescript | ||
namespace runtime { | ||
// The reason for which the event is being dispatched. | ||
// | ||
// 'enabled': The extension was re-enabled from a disabled state. | ||
// | ||
// 'installed': The extension was newly installed. | ||
// | ||
// 'updated': The extension was reloaded after an update. | ||
// | ||
// 'startup': The extension is being loaded during browser startup. | ||
// | ||
// 'reload': The extension was reloaded (e.g. via `chrome.runtime.reload() or` | ||
// the user manually reloaded the extension). | ||
export type OnLoadedReason = 'enabled' | | ||
'installed' | | ||
'updated' | | ||
'startup' | | ||
'reload'; | ||
|
||
export interface ExtensionLoadDetails { | ||
// The reason that this event is being dispatched. | ||
reason: OnLoadedReason; | ||
|
||
// Indicates the previous version of the extension, which has just been | ||
// updated. This is present only if 'reason' is 'updated' or ‘enabled’. | ||
previousVersion?: string; | ||
} | ||
|
||
// Fired when the extension is added to the set of active extensions. This can | ||
// occur in multiple scenarios: when an extension is first installed, updated | ||
// to a new version, and enabled after being disabled. | ||
export interface onExtensionLoaded { | ||
addListener(callback: (details: ExtensionLoadDetails) => void): void; | ||
hasListener(callback: (details: ExtensionLoadDetails) => void): boolean; | ||
removeListener(callback: (details: ExtensionLoadDetails) => void): void; | ||
} | ||
} | ||
``` | ||
|
||
### Behavior | ||
|
||
Described as code comments in schema description. | ||
|
||
### New Permissions | ||
|
||
No new permissions are needed as extensions can access the `browser.runtime` | ||
API without any permissions. | ||
|
||
### Manifest File Changes | ||
|
||
No new manifest changes are added. | ||
|
||
## Security and Privacy | ||
|
||
### Exposed Sensitive Data | ||
|
||
The new events expose some information that wasn’t present before. It does | ||
expose that an extension was (explicitly) disabled, which is something we don't | ||
expose today. Today, an extension cannot necessarily distinguish between being | ||
disabled and the user simply not opening the browser for <n> amount of | ||
time. | ||
|
||
### Abuse Mitigations | ||
|
||
These APIs would be difficult to use in an abusive way since they do not give | ||
information or access to other extensions, and don’t give control into the | ||
extensions lifecycle – only knowledge as it transitions to different states. | ||
|
||
### Additional Security Considerations | ||
|
||
N/A. | ||
|
||
## Alternatives | ||
|
||
### Existing Workarounds | ||
|
||
There is currently `chrome.runtime.onInstalled`, and `chrome.runtime.onStartup`. | ||
These API have gaps in listening that are addressed by these two events. In | ||
short, `chrome.runtime.onEnabled`, covers the remaining status that developers | ||
care to know, and `chrome.runtime.onExtensionLoaded` encapsulates them all | ||
together with a single listener. | ||
|
||
### Open Web API | ||
|
||
These API methods are specific to extension lifetime / lifecycle and would not | ||
be appropriate to be in the open web. | ||
|
||
## Implementation Notes | ||
|
||
N/A. | ||
|
||
## Future Work | ||
|
||
N/A. |