首页
/ Electron Extensions API 详解:加载、管理与监听 Chrome 扩展的完整指南

Electron Extensions API 详解:加载、管理与监听 Chrome 扩展的完整指南

2026-09-04 21:18:48作者:董灵辛Dennis

本文围绕 Electron 的 Extensions 类展开,介绍如何通过 session.extensions 在 Electron 应用中加载未打包(unpacked)的 Chrome 扩展,管理其生命周期,并监听 extension-loadedextension-readyextension-unloaded 等实例事件;同时结合 Electron 源码中 electron_api_extensions.ccelectron_extension_system.cc 的实现,剖析路径校验、临时会话限制、allowFileAccess 选项和加载警告等底层机制,帮助你写出可靠的扩展集成代码。

获取 Extensions 实例:Sessionextensions 属性

Extensions 类**不会从 'electron' 模块直接导出**。它只能作为其他 API 方法的返回值使用,具体获取方式是访问 Session实例的extensions` 属性:

const { session } = require('electron')

const extensions = session.defaultSession.extensions

该属性在 Session 文档 中声明为只读(ses.extensions Readonly),每个 Session 对应一个独立的扩展实例。因此扩展是按 session 安装的:使用 session.fromPartition('persist:...') 创建的持久化 session 加载的扩展,只属于该 session,且不同 session 之间互不可见。

注意:旧版 API ses.loadExtension / ses.removeExtension / ses.getExtension / ses.getAllExtensions 已被标记为弃用(Deprecated),官方推荐使用新的 ses.extensions.loadExtension 等 API(见 session.md)。

实例事件

Extensions 实例是一个事件发射器,共提供三个实例事件。从源码 electron_api_extensions.h 可以看到,C++ 侧的 Extensions 类实现了 extensions::ExtensionRegistryObserver 接口,三个 JS 事件分别是 Chromium ExtensionRegistry 观察者回调 OnExtensionLoadedOnExtensionReadyOnExtensionUnloaded 的直通映射(见 electron_api_extensions.cc):

void Extensions::OnExtensionLoaded(content::BrowserContext* browser_context,
                                   const extensions::Extension* extension) {
  Emit("extension-loaded", extension);
}

void Extensions::OnExtensionUnloaded(content::BrowserContext* browser_context,
                                     const extensions::Extension* extension,
                                     extensions::UnloadedExtensionReason reason) {
  Emit("extension-unloaded", extension);
}

void Extensions::OnExtensionReady(content::BrowserContext* browser_context,
                                  const extensions::Extension* extension) {
  Emit("extension-ready", extension);
}

事件:extension-loaded

返回值:

扩展被加载后发出。每当一个扩展被加入该 session 的“enabled”扩展集合时就会触发,包括:

  • 通过 extensions.loadExtension 加载的扩展;
  • 扩展被重新加载的场景:
    • 从崩溃中恢复;
    • 扩展自身请求重载(调用 chrome.runtime.reload())。

事件:extension-unloaded

返回值:

扩展被卸载后发出。当调用 extensions.removeExtension 时触发。从源码 electron_extension_system.cc 可以看到,RemoveExtension 底层是调用 UnloadExtension(extension_id, UnloadedExtensionReason::UNINSTALL),即以“卸载”原因通知扩展系统移除该扩展,进而触发 OnExtensionUnloaded 回调。

事件:extension-ready

返回值:

扩展已加载、且所有必要的浏览器状态都已初始化以支持其 background page 启动后发出。也就是说,extension-loaded 表示扩展元数据注册完成,而 extension-ready 表示它已可完整运行(例如可以开始执行 background 逻辑)。在 spec/extensions-spec.ts 的测试中可以看到典型的“加载 + ready”监听写法:

const loadedPromise = once(customSession.extensions, 'extension-loaded')
// ... 并监听 'extension-ready'

事件与加载 Promise 配合使用,可以精确判断扩展可用的时机,再执行依赖扩展的行为。

实例方法

Extensions 实例提供四个实例方法,均在 C++ 侧通过 gin::ObjectTemplateBuilder 注册(见 electron_api_extensions.cc):

.SetMethod("loadExtension", &Extensions::LoadExtension)
.SetMethod("removeExtension", &Extensions::RemoveExtension)
.SetMethod("getExtension", &Extensions::GetExtension)
.SetMethod("getAllExtensions", &Extensions::GetAllExtensions)

extensions.loadExtension(path[, options])

  • path string - 包含未打包 Chrome 扩展的目录路径
  • options Object(可选)
    • allowFileAccess boolean - 是否允许扩展通过 file:// 协议读取本地文件,并将 content script 注入到 file:// 页面。例如在 file:// URL 上加载 DevTools 扩展时必须开启此选项。默认值为 false

返回 Promise<Extension> - 扩展加载完成后 resolve。

该方法在扩展无法加载时会抛出异常(Promise reject)。如果扩展安装时存在警告(例如扩展请求了 Electron 不支持的某个 API),警告会输出到控制台——从源码看,这些警告以 ExtensionLoadWarning 的类别名发出(electron_api_extensions.cc):

if (!error_msg.empty())
  util::EmitWarning(promise.isolate(), error_msg, "ExtensionLoadWarning");
promise.Resolve(extension)

这一点在测试中也有直接验证:加载带有格式错误的 host_permissions 的扩展时,扩展仍会加载成功,但会收到警告(见 spec/extensions-spec.ts):

await expectWarningMessages(
  async () => {
    const extPath = path.join(fixtures, 'extensions', 'host-permissions', 'malformed')
    await customSession.extensions.loadExtension(extPath)
  },
  { name: 'ExtensionLoadWarning', message: /URL pattern 'malformed_host' is malformed/ }
)

Electron 不支持完整的 Chrome 扩展 API 范围,仅支持一个子集(主要用于 DevTools 扩展和 Chromium 内部扩展),支持的 manifest 键与 chrome.* API 明细见 Chrome Extension Support

另外注意一个历史行为变更:在较早版本的 Electron 中,加载过的扩展会在后续应用启动时自动保留;现在不再如此——如果希望扩展被加载,必须在应用每次启动时都调用 loadExtension

典型用法(加载 React DevTools 扩展):

const { app, session } = require('electron')

const path = require('node:path')

app.whenReady().then(async () => {
  await session.defaultSession.extensions.loadExtension(
    path.join(__dirname, 'react-devtools'),
    // allowFileAccess is required to load the DevTools extension on file:// URLs.
    { allowFileAccess: true }
  )
  // Note that in order to use the React DevTools extension, you'll need to
  // download and unzip a copy of the extension.
})

该 API 不支持加载已打包(.crx)的扩展,只能加载 unpacked 目录。

约束条件与底层实现

loadExtension 有以下硬性约束,均可在 C++ 实现 LoadExtension 中找到对应代码:

  1. 必须在 appready 事件之后调用。

  2. 路径必须是绝对路径。 源码中显式校验并拒绝相对路径:

    if (!extension_path.IsAbsolute()) {
      promise.RejectWithErrorMessage(
          "The path to the extension in 'loadExtension' must be absolute");
      return handle;
    }
    

    这就是为什么示例中用 path.join(__dirname, 'react-devtools') 拼出绝对路径,而不是直接传 'react-devtools'

  3. 不能在内存(非持久化)session 中加载。 源码检查 IsOffTheRecord(),拒绝时抛出 Extensions cannot be loaded in a temporary sessionelectron_api_extensions.cc)。测试用例 spec/extensions-spec.ts 验证了这一行为:

    it('loading an extension in a temporary session throws an error', async () => {
      const customSession = session.fromPartition(require('uuid').v4())
      await expect(
        customSession.extensions.loadExtension(path.join(fixtures, 'extensions', 'content-script-test'))
      ).to.eventually.be.rejectedWith('Extensions cannot be loaded in a temporary session')
    })
    

    也就是说,只有 defaultSession 或带 persist: 前缀的 partition 才能加载扩展;session.fromPartition('uuid') 这类临时 session 会直接抛错。

  4. allowFileAccess 的底层含义。 源码中该选项会被映射为 Chromium 扩展加载标志(electron_api_extensions.cc):

    int load_flags = extensions::Extension::FOLLOW_SYMLINKS_ANYWHERE;
    gin_helper::Dictionary options;
    if (args->GetNext(&options)) {
      bool allowFileAccess = false;
      options.Get("allowFileAccess", &allowFileAccess);
      if (allowFileAccess)
        load_flags |= extensions::Extension::ALLOW_FILE_ACCESS;
    }
    

    即不开启时,扩展默认只能作用于 http://https:// 等网络协议页面file:// 页面无法被 content script 注入),而开启 ALLOW_FILE_ACCESS 后扩展才被允许访问 file:// 资源。这正是 DevTools 类扩展在本地 file:// 页面上工作时必须传 { allowFileAccess: true } 的原因。

extensions.removeExtension(extensionId)

  • extensionId string - 要移除的扩展 ID

卸载指定扩展。该 API 同样不能在 appready 事件之前调用。在 spec/extensions-spec.ts 中可以看到测试清理时的标准用法:遍历 getAllExtensions() 并逐一 removeExtension,确保测试之间互不污染:

afterEach(() => {
  for (const e of session.defaultSession.extensions.getAllExtensions()) {
    session.defaultSession.extensions.removeExtension(e.id)
  }
})

extensions.getExtension(extensionId)

  • extensionId string - 要查询的扩展 ID

返回 Extension | null - 给定 ID 的已加载扩展。源码实现直接查询该 browser context 的 ExtensionRegistry,未找到时返回 nullelectron_api_extensions.cc)。该 API 不能在 appready 事件之前调用。

extensions.getAllExtensions()

返回 Extension[] - 所有已加载扩展的列表。

一个值得注意的细节:实现中会过滤掉 Chromium 的 component 扩展(如内置的 PDF 查看器),只返回由用户通过 loadExtension 加载的扩展(electron_api_extensions.cc):

for (const auto& extension : extensions) {
  if (extension->location() != extensions::mojom::ManifestLocation::kComponent)
    extensions_vector.emplace_back(extension.get());
}

该 API 也不能在 appready 事件之前调用。

返回的 Extension 对象结构

loadExtension resolve 以及各事件回传的都是 Extension 对象,包含字段:

  • id string - 扩展 ID(chrome.runtime.id 对应的值,可用于后续 removeExtension / getExtension
  • manifest any - 扩展 manifest 数据的一份拷贝
  • name string
  • path string - 扩展的文件路径
  • version string
  • url string - 扩展的 chrome-extension:// URL

拿到 id 后即可与 getExtension / removeExtension 配合做完整的加载—查询—卸载生命周期管理。

支持的扩展 API 范围(速览)

由于 loadExtension 文档明确指向了支持范围说明,这里给出要点(完整清单见 docs/api/extensions.md):

  • 完整支持chrome.devtools.inspectedWindowchrome.devtools.networkchrome.devtools.panelschrome.scriptingchrome.webRequest(注意 Electron 自身的 webRequest 模块在冲突时优先于 chrome.webRequest)。
  • 部分支持chrome.runtime(支持 lastErrorid 属性及 getBackgroundPagegetManifestgetPlatformInfogetURLconnectsendMessagereload 方法与 onStartuponInstalledonSuspendonSuspendCanceledonConnectonMessage 事件);chrome.tabs(支持 sendMessagereloadexecuteScriptqueryupdate 为部分支持;且 -1 不代表“当前活动标签”);chrome.storage(仅 local,不支持 sync / managed);chrome.managementgetAllgetgetSelfgetPermissionWarningsByIdgetPermissionWarningsByManifestonEnabled / onDisabled);chrome.extension(仅 lastErrorgetURLgetBackgroundPage)。
  • 支持的 manifest 键nameversionauthorpermissionscontent_scriptsdefault_localedevtools_pageshort_namehost_permissions(Manifest V3)、manifest_versionbackground(Manifest V2)、minimum_chrome_version

列表之外的 API 即使当前碰巧可用,其支持也是临时的,随时可能移除。

小结

  • Extensions 实例只能通过 session.extensions 获取,扩展按 session 隔离,且每次应用启动都必须重新 loadExtension
  • loadExtension 要求绝对路径、仅支持 unpacked 扩展、仅限持久化 session;allowFileAccess: true 是扩展作用于 file:// 页面(如 DevTools 扩展)的必要开关。
  • 加载失败 reject、加载警告走 ExtensionLoadWarning、临时会话抛 Extensions cannot be loaded in a temporary session——这些行为均有 spec/extensions-spec.ts 中的测试用例佐证,可用于回归验证。
  • 通过 extension-loaded / extension-ready / extension-unloaded 三个事件,可以精确掌握扩展从注册、就绪到卸载的完整生命周期。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341