Skip to content

Commit c81b9ac

Browse files
committed
work without page reload
1 parent 3d2070b commit c81b9ac

2 files changed

Lines changed: 116 additions & 14 deletions

File tree

background.js

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,31 @@ function sendMessageToTab(tabId, message) {
276276
try {
277277
chrome.tabs.sendMessage(tabId, message, (response) => {
278278
if (chrome.runtime.lastError) {
279-
console.error(`Error sending message to tab ${tabId}:`, chrome.runtime.lastError.message || 'Unknown error');
280-
reject(new Error(chrome.runtime.lastError.message || 'Error communicating with tab'));
279+
const msg = chrome.runtime.lastError.message || 'Unknown error';
280+
// Attempt one-time dynamic injection if content script missing
281+
if (msg.includes('Could not establish connection. Receiving end does not exist')) {
282+
console.warn('Content script not found in tab ' + tabId + ', attempting dynamic injection...');
283+
// Try injecting JS and CSS then resend once
284+
Promise.all([
285+
chrome.scripting.insertCSS({ target: { tabId }, files: ['content.css'] }).catch(()=>{}),
286+
chrome.scripting.executeScript({ target: { tabId }, files: ['content.js'] })
287+
]).then(() => {
288+
chrome.tabs.sendMessage(tabId, message, (secondResp) => {
289+
if (chrome.runtime.lastError) {
290+
console.error('Retry after injection failed:', chrome.runtime.lastError.message);
291+
reject(new Error(chrome.runtime.lastError.message));
292+
} else {
293+
resolve(secondResp);
294+
}
295+
});
296+
}).catch(injectErr => {
297+
console.error('Dynamic injection failed:', injectErr);
298+
reject(new Error(msg));
299+
});
300+
} else {
301+
console.error(`Error sending message to tab ${tabId}:`, msg);
302+
reject(new Error(msg));
303+
}
281304
} else {
282305
resolve(response);
283306
}
@@ -289,6 +312,26 @@ function sendMessageToTab(tabId, message) {
289312
});
290313
}
291314

315+
// Ensure content script is present in tab before messaging heavy workflow
316+
async function ensureContentScript(tabId) {
317+
try {
318+
await sendMessageToTab(tabId, { action: 'ping' }); // cheap test
319+
return true; // already there
320+
} catch (e) {
321+
// Attempt injection
322+
try {
323+
await chrome.scripting.insertCSS({ target: { tabId }, files: ['content.css'] }).catch(()=>{});
324+
await chrome.scripting.executeScript({ target: { tabId }, files: ['content.js'] });
325+
// Small delay to allow script init
326+
await new Promise(r => setTimeout(r, 50));
327+
return true;
328+
} catch (injErr) {
329+
console.error('ensureContentScript failed injection:', injErr);
330+
return false;
331+
}
332+
}
333+
}
334+
292335
// Create a term frequency map for the text
293336
function createTextVector(text) {
294337
// Normalize text: lowercase, remove punctuation, split into words
@@ -534,9 +577,22 @@ async function processLicenseCheck(selectedText) {
534577
activeScans.add(originTabId);
535578

536579
try {
537-
// Show UI and clear previous results
538-
await sendMessageToTab(originTabId, { action: 'showUI' });
539-
await sendMessageToTab(originTabId, { action: 'clearResults' });
580+
// Make sure content script is available
581+
const ready = await ensureContentScript(originTabId);
582+
if (!ready) {
583+
console.error('Content script not available after injection attempts.');
584+
return;
585+
}
586+
// Show UI and clear previous results with retry
587+
for (const m of ['showUI','clearResults']) {
588+
try {
589+
await sendMessageToTab(originTabId, { action: m });
590+
} catch (msgErr) {
591+
console.warn('Retrying message', m, 'after brief delay due to', msgErr.message);
592+
await new Promise(r=>setTimeout(r,100));
593+
await sendMessageToTab(originTabId, { action: m });
594+
}
595+
}
540596

541597
// Progress callback for reporting scan progress
542598
const sendProgress = async (progress) => {
@@ -1013,20 +1069,56 @@ function sendProgressUpdate(progress, message, complete = false) {
10131069
// Update the action click handler to ensure connection before execution
10141070
chrome.action.onClicked.addListener(async (tab) => {
10151071
try {
1016-
// Make sure we're on a supported page
10171072
const url = tab.url || '';
1018-
if (url.startsWith('chrome://') || url.startsWith('chrome-extension://') ||
1073+
if (url.startsWith('chrome://') || url.startsWith('chrome-extension://') ||
10191074
url.startsWith('about:') || url.startsWith('edge://')) {
10201075
console.warn('Cannot run on this page type:', url);
10211076
return;
10221077
}
1023-
1078+
1079+
// Collect selection info from all accessible frames (longest selection wins)
10241080
chrome.scripting.executeScript({
1025-
target: { tabId: tab.id },
1026-
function: checkedLicenses
1081+
target: { tabId: tab.id, allFrames: true },
1082+
func: () => {
1083+
try {
1084+
const sel = window.getSelection();
1085+
if (sel && sel.toString().trim()) {
1086+
return {
1087+
text: sel.toString(),
1088+
inIframe: window.top !== window,
1089+
frameUrl: location.href
1090+
};
1091+
}
1092+
} catch (e) {
1093+
// ignore frame errors
1094+
}
1095+
return null;
1096+
}
1097+
}).then(results => {
1098+
if (!results || !Array.isArray(results)) {
1099+
showNotification(tab.id, 'No selection found.', 'warning');
1100+
return;
1101+
}
1102+
// Pick longest non-null selection
1103+
const candidates = results.map(r => r.result).filter(r => r && r.text && r.text.trim());
1104+
if (candidates.length === 0) {
1105+
showNotification(tab.id, 'Please select text before running license check.', 'warning');
1106+
return;
1107+
}
1108+
const best = candidates.reduce((a, b) => (b.text.length > a.text.length ? b : a));
1109+
1110+
// Send single message to existing handler (adds iframe metadata for future use)
1111+
// Directly initiate processing instead of round-trip messaging
1112+
originTabId = tab.id;
1113+
// Ensure content script/UI is present before processing (attempt a lightweight showUI message)
1114+
sendMessageToTab(tab.id, { action: 'showUI' })
1115+
.catch(() => Promise.resolve()) // ignore; injection fallback happens in sendMessageToTab
1116+
.finally(() => {
1117+
processLicenseCheck(best.text);
1118+
});
10271119
}).catch(err => {
1028-
console.error('Script execution error:', err);
1029-
showNotification(tab.id, "Error executing script: " + (err.message || "Unknown error"), "error");
1120+
console.error('Selection collection error:', err);
1121+
showNotification(tab.id, 'Error gathering selection: ' + (err.message || 'Unknown error'), 'error');
10301122
});
10311123
} catch (err) {
10321124
console.error('Error handling action click:', err);

content.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// Avoid duplicate UI when dynamically injected multiple times
2+
if (window.__LICENSE_DIFF_LOADED__) {
3+
console.log('LicenseDB Diff content script already initialized.');
4+
} else {
5+
window.__LICENSE_DIFF_LOADED__ = true;
16
console.log('Content script loaded.');
27

38
// Create a container for the UI
@@ -92,6 +97,10 @@ function showNotification(message, type = 'info', duration = 5000) {
9297

9398
// Listen for messages from the background script
9499
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
100+
if (message.action === 'ping') { // Health check from background
101+
sendResponse({ ok: true });
102+
return; // No async work
103+
}
95104
if (message.action === 'showUI') {
96105
uiContainer.style.display = 'flex';
97106
sendResponse({ success: true });
@@ -194,7 +203,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
194203
sendResponse({ success: true });
195204
}
196205

197-
return true; // Required to use sendResponse asynchronously
206+
// All current branches answer synchronously; do not return true to avoid warning if channel closes.
198207
});
199208

200209
// Function to setup copy buttons
@@ -248,4 +257,5 @@ try {
248257
chrome.runtime.sendMessage({ action: 'contentScriptReady' });
249258
} catch (err) {
250259
console.warn('Error notifying background script:', err);
251-
}
260+
}
261+
} // end guarded initialization

0 commit comments

Comments
 (0)