mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
fix: prevent ServiceWorker registration errors in VS Code webview
- Add ServiceWorker prevention utility to block registration attempts in webview context - Initialize prevention before any third-party libraries load - Update PostHog configuration to disable features that might use ServiceWorkers - Add comprehensive tests for ServiceWorker prevention functionality Fixes #9903
This commit is contained in:
parent
946fd0390f
commit
a4496a6d85
4 changed files with 241 additions and 0 deletions
|
|
@ -6,6 +6,11 @@ import App from "./App"
|
|||
import "../node_modules/@vscode/codicons/dist/codicon.css"
|
||||
|
||||
import { getHighlighter } from "./utils/highlighter"
|
||||
import { initServiceWorkerPrevention } from "./utils/serviceWorkerPrevention"
|
||||
|
||||
// Prevent ServiceWorker registration in VS Code webview environment
|
||||
// This must be done before any third-party libraries are initialized
|
||||
initServiceWorkerPrevention()
|
||||
|
||||
// Initialize Shiki early to hide initialization latency (async)
|
||||
getHighlighter().catch((error: Error) => console.error("Failed to initialize Shiki highlighter:", error))
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ class TelemetryClient {
|
|||
capture_pageview: false,
|
||||
capture_pageleave: false,
|
||||
autocapture: false,
|
||||
// Disable features that might use ServiceWorkers
|
||||
disable_persistence: false, // Keep localStorage persistence but avoid advanced features
|
||||
bootstrap: {}, // Avoid bootstrapping that might trigger ServiceWorker
|
||||
// Disable session recording which might use ServiceWorkers
|
||||
disable_session_recording: true,
|
||||
})
|
||||
} else {
|
||||
TelemetryClient.telemetryEnabled = false
|
||||
|
|
|
|||
167
webview-ui/src/utils/__tests__/serviceWorkerPrevention.spec.ts
Normal file
167
webview-ui/src/utils/__tests__/serviceWorkerPrevention.spec.ts
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"
|
||||
import {
|
||||
isVSCodeWebview,
|
||||
disableServiceWorkerRegistration,
|
||||
initServiceWorkerPrevention,
|
||||
} from "../serviceWorkerPrevention"
|
||||
|
||||
describe("serviceWorkerPrevention", () => {
|
||||
let originalAcquireVsCodeApi: any
|
||||
|
||||
beforeEach(() => {
|
||||
// Store original values
|
||||
originalAcquireVsCodeApi = (global as any).acquireVsCodeApi
|
||||
|
||||
// Mock navigator.serviceWorker for each test
|
||||
const mockServiceWorker = {
|
||||
register: vi.fn(),
|
||||
getRegistration: vi.fn(),
|
||||
getRegistrations: vi.fn(),
|
||||
ready: Promise.resolve(),
|
||||
controller: null,
|
||||
oncontrollerchange: null,
|
||||
onmessage: null,
|
||||
onmessageerror: null,
|
||||
}
|
||||
|
||||
// Use vi.stubGlobal for navigator
|
||||
vi.stubGlobal("navigator", {
|
||||
serviceWorker: mockServiceWorker,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original values
|
||||
vi.unstubAllGlobals()
|
||||
|
||||
if (originalAcquireVsCodeApi !== undefined) {
|
||||
;(global as any).acquireVsCodeApi = originalAcquireVsCodeApi
|
||||
} else {
|
||||
delete (global as any).acquireVsCodeApi
|
||||
}
|
||||
})
|
||||
|
||||
describe("isVSCodeWebview", () => {
|
||||
it("should return true when acquireVsCodeApi is defined", () => {
|
||||
// Mock VS Code webview environment
|
||||
;(global as any).acquireVsCodeApi = vi.fn()
|
||||
|
||||
expect(isVSCodeWebview()).toBe(true)
|
||||
})
|
||||
|
||||
it("should return false when acquireVsCodeApi is undefined", () => {
|
||||
// Ensure it's undefined
|
||||
delete (global as any).acquireVsCodeApi
|
||||
|
||||
expect(isVSCodeWebview()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("disableServiceWorkerRegistration", () => {
|
||||
it("should override navigator.serviceWorker.register in VS Code webview", async () => {
|
||||
// Mock VS Code webview environment
|
||||
;(global as any).acquireVsCodeApi = vi.fn()
|
||||
|
||||
disableServiceWorkerRegistration()
|
||||
|
||||
// Try to register a service worker
|
||||
await expect(navigator.serviceWorker.register("/sw.js")).rejects.toThrow(
|
||||
"ServiceWorker registration is not allowed in VS Code webview",
|
||||
)
|
||||
})
|
||||
|
||||
it("should not override navigator.serviceWorker when not in VS Code webview", () => {
|
||||
// Ensure we're not in VS Code webview
|
||||
delete (global as any).acquireVsCodeApi
|
||||
|
||||
const originalRegister = navigator.serviceWorker.register
|
||||
|
||||
disableServiceWorkerRegistration()
|
||||
|
||||
// Should still be the original function
|
||||
expect(navigator.serviceWorker.register).toBe(originalRegister)
|
||||
})
|
||||
|
||||
it("should provide mock getRegistration that returns undefined", async () => {
|
||||
// Mock VS Code webview environment
|
||||
;(global as any).acquireVsCodeApi = vi.fn()
|
||||
|
||||
disableServiceWorkerRegistration()
|
||||
|
||||
const registration = await navigator.serviceWorker.getRegistration()
|
||||
expect(registration).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should provide mock getRegistrations that returns empty array", async () => {
|
||||
// Mock VS Code webview environment
|
||||
;(global as any).acquireVsCodeApi = vi.fn()
|
||||
|
||||
disableServiceWorkerRegistration()
|
||||
|
||||
const registrations = await navigator.serviceWorker.getRegistrations()
|
||||
expect(registrations).toEqual([])
|
||||
})
|
||||
|
||||
it("should handle missing navigator.serviceWorker gracefully", () => {
|
||||
// Mock VS Code webview environment
|
||||
;(global as any).acquireVsCodeApi = vi.fn()
|
||||
|
||||
// Remove serviceWorker from navigator
|
||||
vi.stubGlobal("navigator", {})
|
||||
|
||||
// Should not throw
|
||||
expect(() => disableServiceWorkerRegistration()).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("initServiceWorkerPrevention", () => {
|
||||
it("should initialize without throwing errors", () => {
|
||||
// Mock VS Code webview environment
|
||||
;(global as any).acquireVsCodeApi = vi.fn()
|
||||
|
||||
expect(() => initServiceWorkerPrevention()).not.toThrow()
|
||||
})
|
||||
|
||||
it("should not throw even if prevention fails", () => {
|
||||
// Mock VS Code webview environment
|
||||
;(global as any).acquireVsCodeApi = vi.fn()
|
||||
|
||||
// Mock console.error
|
||||
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {})
|
||||
|
||||
// Create a scenario where defineProperty might fail
|
||||
// by stubbing a read-only navigator
|
||||
const readOnlyNavigator = {
|
||||
get serviceWorker() {
|
||||
throw new Error("Cannot access serviceWorker")
|
||||
},
|
||||
}
|
||||
vi.stubGlobal("navigator", readOnlyNavigator)
|
||||
|
||||
// Should not throw even if there's an error
|
||||
expect(() => initServiceWorkerPrevention()).not.toThrow()
|
||||
|
||||
consoleErrorSpy.mockRestore()
|
||||
})
|
||||
|
||||
it("should successfully prevent ServiceWorker registration when called", async () => {
|
||||
// Mock VS Code webview environment
|
||||
;(global as any).acquireVsCodeApi = vi.fn()
|
||||
|
||||
// Mock console.warn to check for warning message
|
||||
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
|
||||
|
||||
initServiceWorkerPrevention()
|
||||
|
||||
// Try to register a service worker
|
||||
await expect(navigator.serviceWorker.register("/sw.js")).rejects.toThrow()
|
||||
|
||||
// Should have logged a warning
|
||||
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||
"ServiceWorker registration blocked in VS Code webview environment",
|
||||
)
|
||||
|
||||
consoleWarnSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
})
|
||||
64
webview-ui/src/utils/serviceWorkerPrevention.ts
Normal file
64
webview-ui/src/utils/serviceWorkerPrevention.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* Prevents ServiceWorker registration in VS Code webview environment
|
||||
* This is necessary because VS Code webviews have security restrictions
|
||||
* that don't allow ServiceWorker registration, which can cause errors
|
||||
* with third-party libraries like PostHog that attempt to use them.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if we're running in a VS Code webview environment
|
||||
*/
|
||||
export function isVSCodeWebview(): boolean {
|
||||
// Check for VS Code webview-specific global
|
||||
return typeof acquireVsCodeApi !== "undefined"
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables ServiceWorker registration by overriding the navigator.serviceWorker API
|
||||
* This prevents third-party libraries from attempting to register ServiceWorkers
|
||||
* which would fail in the VS Code webview environment.
|
||||
*/
|
||||
export function disableServiceWorkerRegistration(): void {
|
||||
if (!isVSCodeWebview()) {
|
||||
// Only disable in VS Code webview environment
|
||||
return
|
||||
}
|
||||
|
||||
// Override navigator.serviceWorker to prevent registration attempts
|
||||
if (typeof navigator !== "undefined" && "serviceWorker" in navigator) {
|
||||
// Create a mock serviceWorker object that prevents registration
|
||||
const mockServiceWorker = {
|
||||
register: () => {
|
||||
console.warn("ServiceWorker registration blocked in VS Code webview environment")
|
||||
return Promise.reject(new Error("ServiceWorker registration is not allowed in VS Code webview"))
|
||||
},
|
||||
getRegistration: () => Promise.resolve(undefined),
|
||||
getRegistrations: () => Promise.resolve([]),
|
||||
ready: new Promise(() => {
|
||||
// Never resolves, as no ServiceWorker will be ready
|
||||
}),
|
||||
controller: null,
|
||||
oncontrollerchange: null,
|
||||
onmessage: null,
|
||||
onmessageerror: null,
|
||||
}
|
||||
|
||||
// Override the serviceWorker property
|
||||
Object.defineProperty(navigator, "serviceWorker", {
|
||||
get: () => mockServiceWorker,
|
||||
configurable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize ServiceWorker prevention
|
||||
* This should be called as early as possible in the application lifecycle
|
||||
*/
|
||||
export function initServiceWorkerPrevention(): void {
|
||||
try {
|
||||
disableServiceWorkerRegistration()
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize ServiceWorker prevention:", error)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue