133 lines
5.5 KiB
JavaScript
Executable File
133 lines
5.5 KiB
JavaScript
Executable File
// Let Chrome open the panel when the toolbar icon is clicked
|
|
// Guard: some Chromium variants may not expose sidePanel
|
|
const hasSidePanel = !!chrome.sidePanel?.setOptions;
|
|
const GCM_PROJECT_NUMBER = '1088502361802';
|
|
const EXT_TOKEN_KEY = 'dewemojiExtToken';
|
|
const GCM_LOCK_KEY = 'dewemojiExtTokenLockTs';
|
|
const GCM_LAST_ERR_KEY = 'dewemojiExtTokenLastError';
|
|
const GCM_LAST_ERR_TS_KEY = 'dewemojiExtTokenLastErrorTs';
|
|
const GCM_LOCK_WINDOW_MS = 60000;
|
|
let gcmRegisterPromise = null;
|
|
let gcmInMemoryLockTs = 0;
|
|
|
|
async function storeExtensionToken(token) {
|
|
try { await chrome.storage.local.set({ [EXT_TOKEN_KEY]: token }); } catch {}
|
|
}
|
|
|
|
async function getStoredToken() {
|
|
try {
|
|
const got = await chrome.storage.local.get([EXT_TOKEN_KEY]);
|
|
return got[EXT_TOKEN_KEY] || null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function registerGcmToken() {
|
|
if (gcmRegisterPromise) return gcmRegisterPromise;
|
|
if (!chrome?.gcm?.register) return Promise.resolve(null);
|
|
gcmRegisterPromise = new Promise((resolve) => {
|
|
(async () => {
|
|
if (gcmInMemoryLockTs && Date.now() - gcmInMemoryLockTs < GCM_LOCK_WINDOW_MS) {
|
|
return resolve(null);
|
|
}
|
|
// Avoid repeated register attempts within a short window
|
|
const lock = await chrome.storage.local.get([GCM_LOCK_KEY]);
|
|
const lastTs = Number(lock[GCM_LOCK_KEY] || 0);
|
|
if (Date.now() - lastTs < GCM_LOCK_WINDOW_MS) {
|
|
return resolve(null);
|
|
}
|
|
const now = Date.now();
|
|
gcmInMemoryLockTs = now;
|
|
await chrome.storage.local.set({ [GCM_LOCK_KEY]: now });
|
|
})().then(() => {
|
|
chrome.gcm.register([GCM_PROJECT_NUMBER], async (registrationId) => {
|
|
if (chrome.runtime.lastError || !registrationId) {
|
|
const msg = chrome.runtime.lastError?.message || 'no registrationId';
|
|
try { await chrome.storage.local.set({ [GCM_LAST_ERR_KEY]: msg, [GCM_LAST_ERR_TS_KEY]: Date.now() }); } catch {}
|
|
if (!/asynchronous operation is pending/i.test(msg)) {
|
|
console.warn('GCM register failed', msg);
|
|
}
|
|
// back off on pending errors
|
|
if (/asynchronous operation is pending/i.test(msg)) {
|
|
const now = Date.now();
|
|
gcmInMemoryLockTs = now;
|
|
try { await chrome.storage.local.set({ [GCM_LOCK_KEY]: now }); } catch {}
|
|
}
|
|
return resolve(null);
|
|
}
|
|
try { await chrome.storage.local.remove([GCM_LAST_ERR_KEY, GCM_LAST_ERR_TS_KEY]); } catch {}
|
|
await storeExtensionToken(registrationId);
|
|
console.log('GCM token stored', registrationId.slice(0, 8) + '…');
|
|
resolve(registrationId);
|
|
});
|
|
}).catch(() => resolve(null));
|
|
}).finally(() => {
|
|
gcmRegisterPromise = null;
|
|
});
|
|
return gcmRegisterPromise;
|
|
}
|
|
|
|
chrome.runtime.onInstalled.addListener(() => {
|
|
if (hasSidePanel) chrome.sidePanel.setPanelBehavior?.({ openPanelOnActionClick: true });
|
|
});
|
|
|
|
// Intentionally do NOT auto-register on startup to avoid concurrent GCM calls.
|
|
|
|
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
|
if (msg?.type !== 'dewemoji_get_ext_token') return;
|
|
(async () => {
|
|
const force = !!msg.force;
|
|
let token = await getStoredToken();
|
|
if (!token || force) {
|
|
await registerGcmToken();
|
|
// best-effort wait for storage to be populated
|
|
await new Promise(r => setTimeout(r, 300));
|
|
token = await getStoredToken();
|
|
}
|
|
sendResponse({ ok: !!token, token });
|
|
})();
|
|
return true;
|
|
});
|
|
|
|
// If you still want to ensure the correct path on click, setOptions ONLY:
|
|
chrome.action.onClicked.addListener(async () => {
|
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
if (!tab?.id) return;
|
|
|
|
// inject content.js before the panel opens (best effort; ignore errors)
|
|
try {
|
|
const pong = await chrome.tabs.sendMessage(tab.id, { type: 'dewemoji_ping' });
|
|
if (!pong?.ok) {
|
|
await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content.js'] });
|
|
}
|
|
} catch {
|
|
// No content script: inject it
|
|
try { await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content.js'] }); } catch {}
|
|
}
|
|
|
|
await chrome.sidePanel.setOptions({ tabId: tab.id, path: "panel.html" });
|
|
try { await chrome.sidePanel.open({ tabId: tab.id }); } catch {}
|
|
});
|
|
|
|
// Keyboard shortcut: we do open() here (this is a user gesture)
|
|
chrome.commands.onCommand.addListener(async (command) => {
|
|
if (command !== "toggle-panel") return;
|
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
if (!tab?.id) return;
|
|
|
|
// inject content.js before we open the panel with keyboard
|
|
try {
|
|
const pong = await chrome.tabs.sendMessage(tab.id, { type: 'dewemoji_ping' });
|
|
if (!pong?.ok) {
|
|
await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content.js'] });
|
|
}
|
|
} catch {
|
|
// No content script: inject it
|
|
try { await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content.js'] }); } catch {}
|
|
}
|
|
|
|
await chrome.sidePanel.setOptions({ tabId: tab.id, path: "panel.html" });
|
|
await chrome.sidePanel.open({ tabId: tab.id });
|
|
});
|