firenvim icon indicating copy to clipboard operation
firenvim copied to clipboard

chrome: migrate Chrome extension to Manifest V3 with service worker support

Open hamidi-dev opened this issue 1 month ago • 2 comments

Changes

Manifest V3 Migration (src/manifest.json)

  • Updated manifest_version from 2 to 3
  • Changed browser_action to action
  • Migrated background.scripts to background.service_worker
  • Added host_permissions array for required permissions
  • Updated web_accessible_resources to MV3 object format

Service Worker Compatibility (src/background.ts)

  • Replaced window.crypto with globalThis.crypto for service worker compatibility
  • Changed all window references to globalThis to work in service worker context
  • Updated browser.browserAction.setIcon() to browser.action.setIcon() for MV3 API

Browser Detection Fix (src/utils/utils.ts)

  • Fixed "window is not defined" error in service workers by making browser detection lazy
  • Added service worker fallback using browser.runtime.getBrowserInfo instead of window.location
  • Previously top-level code accessed window.location at module load time, breaking service workers

Icon Generation Dual-Path (src/utils/utils.ts)

  • Service workers use OffscreenCanvas with PNG since createImageBitmap() doesn't support SVG
  • DOM contexts continue using regular canvas with SVG (unchanged)
  • Runtime detection via typeof window === 'undefined' || typeof document === 'undefined'

Canvas Performance Optimization (src/utils/utils.ts, src/renderer.ts)

  • Added { willReadFrequently: true } to all canvas context creation to fix Chrome performance warnings

Webpack Configuration (webpack.config.js)

  • Chrome builds use service_worker for background
  • Firefox builds explicitly override to use scripts: ["background.js"] (Firefox MV3 still supports background scripts)
  • Updated Firefox minimum version to 109.0
  • Fixed CSP format for MV3 testing builds

Why This Approach

Firefox Compatibility: Firefox MV3 background scripts retain window/document access, so webpack explicitly configures Firefox to use scripts mode. The runtime detection ensures the existing SVG code path is taken.

Chrome Service Workers: Chrome MV3 requires true service workers without DOM access, so we use OffscreenCanvas with a pre-generated PNG for icon generation.

closes #1674

hamidi-dev avatar Nov 18 '25 17:11 hamidi-dev

Hi, thanks for taking the time to open an MR. Someone already did this (also with AI!) here: https://github.com/glacambre/firenvim/pull/1692 , I just haven't taken the time to properly review it. Your MR seems to do a lot less than theirs, do you know why?

glacambre avatar Nov 19 '25 07:11 glacambre

Hi!

Yes, I can explain the scope difference:

The key architectural difference:

  • My approach (#1700): Keeps the existing preloaded Neovim instance and funcName-based RPC system. I focused on making minimal changes to get Chrome working with MV3 service workers by using globalThis instead of window, adding lazy browser detection, and creating a dual-path for icon generation (OffscreenCanvas for service workers, DOM for regular contexts).

  • #1692 approach: Removes the preloaded instance entirely (creating Neovim on-demand), refactors the entire message passing system with a typed MessageType enum, migrates all state to browser.storage.session API, and brings both Chrome AND Firefox to MV3. It's a much more comprehensive migration.

Why I took the simpler approach:

When I read your earlier comment about how "Manifest v3 disallows long-lived processes, so a lot of refactoring may have to happen," I wanted to first see if we could get Chrome working without that major refactor. In my testing, the preloaded instance actually works fine in Chrome's service worker environment.

Potential path forward:

My thinking was this could serve as an intermediate solution to unblock Chrome users quickly while you have time to properly review and test the more comprehensive refactor. Chrome support has been broken for a while, so getting something working sooner might be valuable to users—even if it's not the final architecture.

The other approach is definitely more thorough and addresses the architectural concerns more completely. It's really a question of whether you'd prefer:

  1. Ship mine quickly as a stopgap → users have working Chrome extension → later review/merge the comprehensive refactor at your pace
  2. Take time to properly review the comprehensive approach → merge that directly → Chrome users wait longer but get the better architecture

Either way is fine with me! I'm just trying to help unblock the situation.

In the meantime I will continue using my fork until the situation is resolved :)

hamidi-dev avatar Nov 19 '25 16:11 hamidi-dev