feat(extensions): expose ExtensionRegistryObserver events in Session (#25385)

* feat(extensions): expose ExtensionRegistryObserver events in Session

Extensions can be loaded and unloaded for various reasons. In some cases this can
occur by no means of the Electron programmer, such as in the case of chrome.runtime.reload().

In order to be able to manage state about extensions outside of Electron's APIs, events
reloaded to loading and unloaded are needed.

* docs(extensions): elaborate on extension-loaded/unloaded details

* fix: remove scoped extension registry observer

* docs: update extension-unloaded
This commit is contained in:
Samuel Maddock 2020-09-23 15:29:08 -04:00 committed by GitHub
parent 881ac995da
commit 9d0d9a1664
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 92 additions and 0 deletions

View file

@ -91,6 +91,40 @@ session.defaultSession.on('will-download', (event, item, webContents) => {
})
```
#### Event: 'extension-loaded'
Returns:
* `event` Event
* `extension` [Extension](structures/extension.md)
Emitted after an extension is loaded. This occurs whenever an extension is
added to the "enabled" set of extensions. This includes:
- Extensions being loaded from `Session.loadExtension`.
- Extensions being reloaded:
* from a crash.
* if the extension requested it ([`chrome.runtime.reload()`](https://developer.chrome.com/extensions/runtime#method-reload)).
#### Event: 'extension-unloaded'
Returns:
* `event` Event
* `extension` [Extension](structures/extension.md)
Emitted after an extension is unloaded. This occurs when
`Session.removeExtension` is called.
#### Event: 'extension-ready'
Returns:
* `event` Event
* `extension` [Extension](structures/extension.md)
Emitted after an extension is loaded and all necessary browser state is
initialized to support the start of the extension's background page.
#### Event: 'preconnect'
Returns:

View file

@ -281,6 +281,10 @@ Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
service->SetHunspellObserver(this);
}
#endif
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
extensions::ExtensionRegistry::Get(browser_context)->AddObserver(this);
#endif
}
Session::~Session() {
@ -294,6 +298,10 @@ Session::~Session() {
service->SetHunspellObserver(nullptr);
}
#endif
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
extensions::ExtensionRegistry::Get(browser_context())->RemoveObserver(this);
#endif
}
void Session::OnDownloadCreated(content::DownloadManager* manager,
@ -749,6 +757,22 @@ v8::Local<v8::Value> Session::GetAllExtensions() {
}
return gin::ConvertToV8(v8::Isolate::GetCurrent(), extensions_vector);
}
void Session::OnExtensionLoaded(content::BrowserContext* browser_context,
const extensions::Extension* extension) {
Emit("extension-loaded", extension);
}
void Session::OnExtensionUnloaded(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) {
Emit("extension-unloaded", extension);
}
void Session::OnExtensionReady(content::BrowserContext* browser_context,
const extensions::Extension* extension) {
Emit("extension-ready", extension);
}
#endif
v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {

View file

@ -25,6 +25,11 @@
#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h" // nogncheck
#endif
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#endif
class GURL;
namespace base {
@ -55,6 +60,9 @@ class Session : public gin::Wrappable<Session>,
public gin_helper::CleanedUpAtExit,
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
public SpellcheckHunspellDictionary::Observer,
#endif
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
public extensions::ExtensionRegistryObserver,
#endif
public content::DownloadManager::Observer {
public:
@ -126,6 +134,15 @@ class Session : public gin::Wrappable<Session>,
void RemoveExtension(const std::string& extension_id);
v8::Local<v8::Value> GetExtension(const std::string& extension_id);
v8::Local<v8::Value> GetAllExtensions();
// extensions::ExtensionRegistryObserver:
void OnExtensionLoaded(content::BrowserContext* browser_context,
const extensions::Extension* extension) override;
void OnExtensionReady(content::BrowserContext* browser_context,
const extensions::Extension* extension) override;
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) override;
#endif
protected:

View file

@ -123,6 +123,23 @@ describe('chrome extensions', () => {
}
});
it('emits extension lifecycle events', async () => {
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
const loadedPromise = emittedOnce(customSession, 'extension-loaded');
const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));
const [, loadedExtension] = await loadedPromise;
const [, readyExtension] = await emittedOnce(customSession, 'extension-ready');
expect(loadedExtension.id).to.equal(extension.id);
expect(readyExtension.id).to.equal(extension.id);
const unloadedPromise = emittedOnce(customSession, 'extension-unloaded');
await customSession.removeExtension(extension.id);
const [, unloadedExtension] = await unloadedPromise;
expect(unloadedExtension.id).to.equal(extension.id);
});
it('lists loaded extensions in getAllExtensions', async () => {
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));