Merge pull request #67 from outis1one/claude/review-silent-send-gHgCS
Fix race condition: vault:unlocked message lost when service worker r…
This commit is contained in:
+40
-31
@@ -58,20 +58,14 @@
|
|||||||
// Load mappings and settings, then inject into page
|
// Load mappings and settings, then inject into page
|
||||||
async function init() {
|
async function init() {
|
||||||
const result = await api.storage.local.get(['ss_mappings', 'ss_identity', 'ss_settings']);
|
const result = await api.storage.local.get(['ss_mappings', 'ss_identity', 'ss_settings']);
|
||||||
const settings = result.ss_settings || { enabled: true };
|
|
||||||
|
|
||||||
// Check if data is encrypted — pass empty config and request decryption
|
// Check if data is encrypted — pass empty config; decrypted data arrives via vault:unlocked
|
||||||
// The background will send decrypted data via vault:unlocked when ready
|
|
||||||
const isLocked = result.ss_mappings?._ssLocalEncrypted ||
|
const isLocked = result.ss_mappings?._ssLocalEncrypted ||
|
||||||
result.ss_identity?._ssLocalEncrypted;
|
result.ss_identity?._ssLocalEncrypted ||
|
||||||
|
result.ss_settings?._ssLocalEncrypted;
|
||||||
const mappings = isLocked ? [] : (result.ss_mappings || []);
|
const mappings = isLocked ? [] : (result.ss_mappings || []);
|
||||||
const identityData = isLocked ? {} : (result.ss_identity || {});
|
const identityData = isLocked ? {} : (result.ss_identity || {});
|
||||||
|
const settings = isLocked ? { enabled: true } : (result.ss_settings || { enabled: true });
|
||||||
// If data is encrypted but the vault key may already be cached
|
|
||||||
// (e.g. after a sync import), ask the background to push decrypted data
|
|
||||||
if (isLocked) {
|
|
||||||
api.runtime.sendMessage({ type: 'vault:request-unlock' }).catch(() => {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge active profiles into a flat identity object for the content script
|
// Merge active profiles into a flat identity object for the content script
|
||||||
const identity = mergeProfiles(identityData);
|
const identity = mergeProfiles(identityData);
|
||||||
@@ -84,12 +78,47 @@
|
|||||||
await new Promise(resolve => { docScannerScript.onload = resolve; docScannerScript.onerror = resolve; });
|
await new Promise(resolve => { docScannerScript.onload = resolve; docScannerScript.onerror = resolve; });
|
||||||
docScannerScript.remove();
|
docScannerScript.remove();
|
||||||
|
|
||||||
|
// Register the runtime message listener BEFORE injecting content.js and BEFORE
|
||||||
|
// sending vault:request-unlock. This prevents two races:
|
||||||
|
// 1. The background could respond to vault:request-unlock before the listener
|
||||||
|
// is registered (if the service worker is already warm), dropping the message.
|
||||||
|
// 2. window.postMessage from the listener must reach content.js's message handler,
|
||||||
|
// which is only registered after content.js finishes executing.
|
||||||
|
// Solution: register the listener here, but send vault:request-unlock only inside
|
||||||
|
// script.onload (after content.js has fully executed).
|
||||||
|
api.runtime.onMessage.addListener(async (message) => {
|
||||||
|
if (message.type === 'settings:updated') {
|
||||||
|
window.postMessage({
|
||||||
|
type: 'ss:config-updated',
|
||||||
|
settings: message.settings,
|
||||||
|
}, '*');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vault unlocked — background sends pre-decrypted data
|
||||||
|
if (message.type === 'vault:unlocked') {
|
||||||
|
window.postMessage({
|
||||||
|
type: 'ss:config-updated',
|
||||||
|
mappings: message.mappings || [],
|
||||||
|
identity: message.identity || {},
|
||||||
|
settings: message.settings || {},
|
||||||
|
}, '*');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Inject the main interception script into the page's world
|
// Inject the main interception script into the page's world
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.setAttribute('data-ss-config', JSON.stringify({ mappings, identity, settings }));
|
script.setAttribute('data-ss-config', JSON.stringify({ mappings, identity, settings }));
|
||||||
script.src = api.runtime.getURL('src/content/content.js');
|
script.src = api.runtime.getURL('src/content/content.js');
|
||||||
(document.head || document.documentElement).appendChild(script);
|
(document.head || document.documentElement).appendChild(script);
|
||||||
script.onload = () => script.remove();
|
script.onload = () => {
|
||||||
|
script.remove();
|
||||||
|
// content.js has fully executed — its window.message listener is now live.
|
||||||
|
// Safe to request decrypted data; the vault:unlocked response will be
|
||||||
|
// delivered to content.js without a race.
|
||||||
|
if (isLocked) {
|
||||||
|
api.runtime.sendMessage({ type: 'vault:request-unlock' }).catch(() => {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Listen for substitution events from the page script
|
// Listen for substitution events from the page script
|
||||||
window.addEventListener('message', async (event) => {
|
window.addEventListener('message', async (event) => {
|
||||||
@@ -148,26 +177,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for settings updates and vault unlock from background
|
|
||||||
api.runtime.onMessage.addListener(async (message) => {
|
|
||||||
if (message.type === 'settings:updated') {
|
|
||||||
window.postMessage({
|
|
||||||
type: 'ss:config-updated',
|
|
||||||
settings: message.settings,
|
|
||||||
}, '*');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vault unlocked — background sends pre-decrypted data
|
|
||||||
if (message.type === 'vault:unlocked') {
|
|
||||||
window.postMessage({
|
|
||||||
type: 'ss:config-updated',
|
|
||||||
mappings: message.mappings || [],
|
|
||||||
identity: message.identity || {},
|
|
||||||
settings: message.settings || {},
|
|
||||||
}, '*');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Storage bridge — lets page world script read/write storage
|
// Storage bridge — lets page world script read/write storage
|
||||||
window.addEventListener('message', async (event) => {
|
window.addEventListener('message', async (event) => {
|
||||||
if (event.source !== window) return;
|
if (event.source !== window) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user