diff --git a/background/service-worker.js b/background/service-worker.js index a92114f..a991cf2 100644 --- a/background/service-worker.js +++ b/background/service-worker.js @@ -2,8 +2,7 @@ import { notifyMessage } from '../modules/messaging.js'; import { t, initializeLanguage } from '../modules/i18n.js'; import { migrateEnabledProvidersOnUpdate } from '../modules/provider-defaults.js'; import { - filterProvidersWithGrantedAccess, - syncOptionalProviderAccess + reconcileOptionalProviderAccess } from '../modules/optional-provider-access.js'; // Install event - setup context menus @@ -114,24 +113,6 @@ async function migrateProviderSettingsForUpdate(details) { } } -async function reconcileOptionalProviderAccess() { - try { - const settings = await chrome.storage.sync.get({ enabledProviders: [] }); - const enabledProviders = Array.isArray(settings.enabledProviders) - ? settings.enabledProviders - : []; - const providersWithAccess = await filterProvidersWithGrantedAccess(enabledProviders); - - if (providersWithAccess.length !== enabledProviders.length) { - await chrome.storage.sync.set({ enabledProviders: providersWithAccess }); - } - - await syncOptionalProviderAccess(providersWithAccess); - } catch (error) { - console.error('[Background] Failed to synchronize optional provider access:', error); - } -} - let optionalProviderReconcileQueue = Promise.resolve(); function scheduleOptionalProviderReconcile() { diff --git a/modules/optional-provider-access.js b/modules/optional-provider-access.js index d847c37..3c58e0a 100644 --- a/modules/optional-provider-access.js +++ b/modules/optional-provider-access.js @@ -107,6 +107,23 @@ export async function syncOptionalProviderAccess(enabledProviderIds) { await syncFrameRules(configs.map(([, config]) => config), desiredConfigs); } +/** + * Reconcile device-local scripts and frame rules with the synced provider preference. + * Missing device permissions must never overwrite the user's synced preference. + */ +export async function reconcileOptionalProviderAccess() { + try { + const settings = await chrome.storage.sync.get({ enabledProviders: [] }); + const enabledProviderIds = Array.isArray(settings.enabledProviders) + ? settings.enabledProviders + : []; + + await syncOptionalProviderAccess(enabledProviderIds); + } catch (error) { + console.error('[Background] Failed to synchronize optional provider access:', error); + } +} + async function syncContentScripts(allConfigs, desiredConfigs) { if (!chrome.scripting?.getRegisteredContentScripts) { return; diff --git a/modules/providers.js b/modules/providers.js index ed5e4fd..8a1b624 100644 --- a/modules/providers.js +++ b/modules/providers.js @@ -1,5 +1,8 @@ import { DEFAULT_PROVIDER_IDS } from './provider-defaults.js'; -import { OPTIONAL_PROVIDER_CONFIGS } from './optional-provider-access.js'; +import { + OPTIONAL_PROVIDER_CONFIGS, + filterProvidersWithGrantedAccess +} from './optional-provider-access.js'; export const PROVIDERS = [ { @@ -137,8 +140,10 @@ export async function getEnabledProviders() { console.warn('Failed to load provider settings, using defaults'); } - // Filter enabled providers - let enabledProviders = PROVIDERS.filter(p => settings.enabledProviders.includes(p.id)); + // Synced preferences may include optional providers that are not authorized on + // this device. Keep the preference intact, but do not load inaccessible panels. + const availableProviderIds = await filterProvidersWithGrantedAccess(settings.enabledProviders); + let enabledProviders = PROVIDERS.filter(p => availableProviderIds.includes(p.id)); // Sort by custom order if available if (settings.providerOrder && Array.isArray(settings.providerOrder)) { diff --git a/multi-panel/multi-panel.js b/multi-panel/multi-panel.js index 08b6855..af96406 100644 --- a/multi-panel/multi-panel.js +++ b/multi-panel/multi-panel.js @@ -1139,16 +1139,20 @@ async function initializePanels() { enabledProviders: DEFAULT_PROVIDERS, multiPanelProviders: DEFAULT_PROVIDERS }); + const availableProviderIds = new Set( + (await getEnabledProviders()).map(({ id }) => id) + ); // Use providerOrder if available (from settings page), fallback to multiPanelProviders let providerIds; if (settings.providerOrder && Array.isArray(settings.providerOrder) && settings.providerOrder.length > 0) { // Use providerOrder directly since it now reflects enabled providers in correct order - // Filter to ensure all providers in providerOrder are actually enabled - providerIds = settings.providerOrder.filter(id => settings.enabledProviders.includes(id)); + // Filter to providers that are enabled and authorized on this device. + providerIds = settings.providerOrder.filter(id => availableProviderIds.has(id)); } else { // Fallback: use enabledProviders in their stored order, or multiPanelProviders - providerIds = settings.enabledProviders || settings.multiPanelProviders; + const configuredProviderIds = settings.enabledProviders || settings.multiPanelProviders; + providerIds = configuredProviderIds.filter(id => availableProviderIds.has(id)); } const panelCount = LAYOUT_PANEL_COUNTS[currentLayout] || 4; diff --git a/options/options.js b/options/options.js index 5a1eb62..c1fdc5d 100644 --- a/options/options.js +++ b/options/options.js @@ -2,7 +2,10 @@ import { PROVIDERS, getProviderIcon } from '../modules/providers.js'; import { DEFAULT_PROVIDER_IDS } from '../modules/provider-defaults.js'; import { appendProviderToOrder, normalizeProviderOrder } from '../modules/provider-order.js'; -import { requestOptionalProviderPermission } from '../modules/optional-provider-access.js'; +import { + hasOptionalProviderPermission, + requestOptionalProviderPermission +} from '../modules/optional-provider-access.js'; import { getSettings, getSetting, saveSettings, saveSetting, resetSettings, exportSettings, importSettings } from '../modules/settings.js'; import { DEFAULT_GOOGLE_PROVIDER_MODE, @@ -248,6 +251,7 @@ async function init() { await renderProviderList(); setupEventListeners(); setupStorageChangeListener(); + setupPermissionChangeListeners(); setupShortcutHelpers(); refreshAutoSizedSelects(); } @@ -314,6 +318,11 @@ async function renderProviderList() { const googleProviderMode = getGoogleProviderModeOrDefault(settings); const displayOrder = getProviderDisplayOrder(settings); const listContainer = document.getElementById('provider-list'); + const providerAccessEntries = await Promise.all(PROVIDERS.map(async (provider) => [ + provider.id, + await hasOptionalProviderPermission(provider.id) + ])); + const providerAccess = new Map(providerAccessEntries); const orderIndex = new Map(displayOrder.map((id, index) => [id, index])); const sortedProviders = [...PROVIDERS].sort((a, b) => { @@ -321,13 +330,19 @@ async function renderProviderList() { }); listContainer.innerHTML = sortedProviders.map(provider => { - const isEnabled = enabledProviders.includes(provider.id); + const isEnabledPreference = enabledProviders.includes(provider.id); + const hasAccess = providerAccess.get(provider.id) !== false; + const isEnabled = isEnabledPreference && hasAccess; const googleModeControl = provider.id === 'google' ? renderGoogleModeSelectMarkup(googleProviderMode, isEnabled) : ''; return ` -