feat(extension): add Firefox WebExtension support
Testing / remote-protocol-compat (0.9.5) (push) Successful in 48s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 47s
Build & Publish Package / publish (push) Successful in 46s
Package Extension / package-extension (push) Successful in 59s
Testing / test (push) Failing after 50s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 48s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 47s
Build & Publish Package / publish (push) Successful in 46s
Package Extension / package-extension (push) Successful in 59s
Testing / test (push) Failing after 50s
- Add a neutral WebExtension API adapter that uses Firefox browser.* or Chromium chrome.* without mutating globals. - Switch extension runtime code to the adapter and add Firefox-specific typings for tabs, windows, tab groups, storage, scripting, and native messaging ports. - Fix Firefox temporary add-on instructions to load the packaged manifest with background.scripts instead of the Chromium service worker manifest. - Detect Firefox in clients.list via runtime.getBrowserInfo and keep Chromium user-agent fallback support. - Make navigate.open wait briefly for Firefox to replace initial about:blank with the requested URL. - Add JS coverage for API selection, clients.list browser detection, and Firefox navigate.open URL polling. - Bump package and extension version to 0.15.2.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { webExtApi as api } from '../browser-api';
|
||||
import { getLargeOperationThrottle, getProfileAlias, getSessionTabs, getSessions, groupTabs, normalizeGroupColor, runLargeOperation, throwIfJobCancelled, updateJobProgress, updateTabGroup, yieldForLargeOperation } from '../core';
|
||||
import { CommandGroup } from '../classes/CommandGroup';
|
||||
import { AutoSaveManager } from './autosave';
|
||||
@@ -32,18 +33,18 @@ export class SessionCommands extends CommandGroup {
|
||||
const { session, tabCount } = await captureCurrentSession();
|
||||
const sessions = await getSessions();
|
||||
sessions[name] = session;
|
||||
await chrome.storage.local.set({ sessions });
|
||||
await api.storage.local.set({ sessions });
|
||||
return { name, tabs: tabCount };
|
||||
}
|
||||
|
||||
// Public: invoked from index.ts on chrome.tabs.onActivated.
|
||||
// Public: invoked from index.ts on api.tabs.onActivated.
|
||||
async activateLazyTab(tabId: number | string) {
|
||||
const { lazySessionTabs } = await chrome.storage.local.get<{ lazySessionTabs?: LazySessionMap }>("lazySessionTabs");
|
||||
const { lazySessionTabs } = await api.storage.local.get<{ lazySessionTabs?: LazySessionMap }>("lazySessionTabs");
|
||||
const entry = lazySessionTabs?.[tabId];
|
||||
if (!entry?.url) return false;
|
||||
delete lazySessionTabs[tabId];
|
||||
await chrome.storage.local.set({ lazySessionTabs });
|
||||
await chrome.tabs.update(Number(tabId), { url: entry.url });
|
||||
await api.storage.local.set({ lazySessionTabs });
|
||||
await api.tabs.update(Number(tabId), { url: entry.url });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -58,24 +59,24 @@ export class SessionCommands extends CommandGroup {
|
||||
const throttle = await getLargeOperationThrottle(sessionTabs.length, gentleMode);
|
||||
const createBatchSize = Math.max(1, Math.min(10, throttle.batchSize));
|
||||
const eagerLimit = lazy ? Math.max(0, Number(eagerTabs) || 0) : sessionTabs.length;
|
||||
const { lazySessionTabs } = await chrome.storage.local.get<{ lazySessionTabs?: LazySessionMap }>("lazySessionTabs");
|
||||
const { lazySessionTabs } = await api.storage.local.get<{ lazySessionTabs?: LazySessionMap }>("lazySessionTabs");
|
||||
const lazyMap: LazySessionMap = lazySessionTabs || {};
|
||||
updateJobProgress(__job, { phase: "opening", current: 0, total: sessionTabs.length });
|
||||
|
||||
for (const [idx, entry] of sessionTabs.entries()) {
|
||||
throwIfJobCancelled(__job);
|
||||
const shouldLazy = lazy && idx >= eagerLimit;
|
||||
const tab = await chrome.tabs.create({ url: shouldLazy ? lazyPlaceholderUrl(entry.url) : entry.url, active: false, pinned: Boolean(entry.pinned) });
|
||||
const tab = await api.tabs.create({ url: shouldLazy ? lazyPlaceholderUrl(entry.url) : entry.url, active: false, pinned: Boolean(entry.pinned) });
|
||||
createdTabs.push({ tabId: tab.id, entry });
|
||||
if (shouldLazy) {
|
||||
lazyMap[String(tab.id)] = { url: entry.url, createdAt: Date.now() };
|
||||
} else if (discardBackgroundTabs && !entry.pinned && chrome.tabs.discard) {
|
||||
try { await chrome.tabs.discard(tab.id); } catch (_) {}
|
||||
} else if (discardBackgroundTabs && !entry.pinned && api.tabs.discard) {
|
||||
try { await api.tabs.discard(tab.id); } catch (_) {}
|
||||
}
|
||||
updateJobProgress(__job, { phase: shouldLazy ? "creating lazy placeholders" : "opening", current: createdTabs.length, total: sessionTabs.length });
|
||||
await yieldForLargeOperation(createdTabs.length, createBatchSize, Math.max(50, throttle.pauseMs));
|
||||
}
|
||||
if (lazy) await chrome.storage.local.set({ lazySessionTabs: lazyMap });
|
||||
if (lazy) await api.storage.local.set({ lazySessionTabs: lazyMap });
|
||||
|
||||
const groups = new Map();
|
||||
for (const { tabId, entry } of createdTabs) {
|
||||
@@ -119,7 +120,7 @@ export class SessionCommands extends CommandGroup {
|
||||
const sessions = await getSessions();
|
||||
if (!(name in sessions)) throw new Error(`Session '${name}' not found`);
|
||||
delete sessions[name];
|
||||
await chrome.storage.local.set({ sessions });
|
||||
await api.storage.local.set({ sessions });
|
||||
return { name };
|
||||
}
|
||||
|
||||
@@ -154,16 +155,18 @@ export class SessionCommands extends CommandGroup {
|
||||
if (!overwrite && sessions[name]) throw new Error(`Session '${name}' already exists`);
|
||||
const stored = session as object as StoredSession;
|
||||
sessions[name] = { ...stored, savedAt: Number(stored.savedAt) || Date.now() };
|
||||
await chrome.storage.local.set({ sessions });
|
||||
await api.storage.local.set({ sessions });
|
||||
return { name, tabs: getSessionTabs(sessions[name]).length };
|
||||
}
|
||||
|
||||
private async clientsList() {
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
const manifest = api.runtime.getManifest();
|
||||
const alias = await getProfileAlias();
|
||||
const browserInfo = api.runtime.getBrowserInfo ? await api.runtime.getBrowserInfo() : null;
|
||||
const userAgent = navigator.userAgent;
|
||||
return [{
|
||||
name: "Chrome",
|
||||
version: navigator.userAgent.match(/Chrome\/([\d.]+)/)?.[1] || "unknown",
|
||||
name: browserInfo?.name || (userAgent.includes("Firefox/") ? "Firefox" : "Chrome"),
|
||||
version: browserInfo?.version || userAgent.match(/(?:Chrome|Firefox)\/([\d.]+)/)?.[1] || "unknown",
|
||||
platform: navigator.platform,
|
||||
extensionVersion: manifest.version,
|
||||
profile: alias,
|
||||
@@ -171,7 +174,7 @@ export class SessionCommands extends CommandGroup {
|
||||
}
|
||||
|
||||
private async clientsRenameProfile({ alias }: ClientsRenameProfileArgs) {
|
||||
await chrome.storage.local.set({ profileAlias: alias });
|
||||
await api.storage.local.set({ profileAlias: alias });
|
||||
return { alias };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user