Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion src/entrypoints/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default defineBackground({
type: "module",
main() {
var tabInfo = new Map();
const BIB_EXPORT_TIMEOUT_MS = 5000;

/*
Show/hide import button for all tabs (when add-on is loaded).
Expand Down Expand Up @@ -145,6 +146,9 @@ export default defineBackground({

async function sendBibEntryHttp(bibtex) {
const baseUrl = await getBaseUrl();
await browser.runtime.sendMessage({
popupLog: `Trying JabRef HTTP endpoint at ${baseUrl}`,
});

const health = await fetch(baseUrl, { method: "GET", cache: "no-store" });
if (!(health.ok || health.status === 404)) {
Expand All @@ -161,13 +165,23 @@ export default defineBackground({
const body = await resp.text().catch(() => "");
throw new Error(`HTTP ${resp.status}${body ? `: ${body}` : ""}`);
}

await browser.runtime.sendMessage({
popupLog: "JabRef accepted data over HTTP",
});
}

async function sendBibEntryNative(bibtex) {
await browser.runtime.sendMessage({
popupLog: "Trying native messaging to reach JabRef",
});
const response = await browser.runtime.sendNativeMessage("org.jabref.jabref", {
text: bibtex,
});
if (response?.message === "ok") {
await browser.runtime.sendMessage({
popupLog: "JabRef accepted data over native messaging",
});
return;
}

Expand All @@ -190,13 +204,22 @@ export default defineBackground({

try {
await sendBibEntryHttp(bibtex);
await browser.runtime.sendMessage({
popupLog: "Send to JabRef finished",
});
await browser.runtime.sendMessage({ popupClose: "close" });
return;
} catch (httpError) {
console.warn("JabRef: HTTP send failed, falling back to native messaging", httpError);
await browser.runtime.sendMessage({
popupLog: `HTTP send failed, falling back to native messaging: ${httpError}`,
});
}

await sendBibEntryNative(bibtex);
await browser.runtime.sendMessage({
popupLog: "Send to JabRef finished",
});
await browser.runtime.sendMessage({ popupClose: "close" });
}

Expand Down Expand Up @@ -314,6 +337,18 @@ export default defineBackground({
return cfg.exportMode || "bibtex";
}

async function raceWithTimeout(promise, timeoutMs, label) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(
() => reject(new Error(`${label} timed out after ${timeoutMs} ms`)),
timeoutMs,
);
}),
]);
}

async function prepareForExport(items) {
const { takeSnapshots } = await browser.storage.sync.get({ takeSnapshots: false });

Expand Down Expand Up @@ -395,11 +430,24 @@ export default defineBackground({
return;
}
const { url, items } = message;
await browser.runtime.sendMessage({
popupLog: `Translator returned ${items.length} item(s) for ${url}`,
});
const conversionMode = await getConversionMode();
await prepareForExport(items);
await browser.runtime.sendMessage({ onConvertToBibtex: "convertStarted" });
const bib = await exportItems(items, conversionMode);
await browser.runtime.sendMessage({
popupLog: `Starting BibTeX export in background for ${items.length} item(s)`,
});
const bib = await raceWithTimeout(
exportItems(items, conversionMode),
BIB_EXPORT_TIMEOUT_MS,
"BibTeX export",
);
console.debug("JabRef: Exported BibTeX: %o", bib);
await browser.runtime.sendMessage({
popupLog: `BibTeX export finished using mode ${conversionMode}`,
});
await sendBibTexToJabRef(bib);
} else if (message.eval) {
console.debug(
Expand All @@ -419,6 +467,11 @@ export default defineBackground({
}
} catch (e) {
console.error("JabRef: Error handling message in background.js", e);
try {
await browser.runtime.sendMessage({
popupLog: `Background error: ${e instanceof Error ? e.message : String(e)}`,
});
} catch {}
throw e;
}
});
Expand Down
17 changes: 16 additions & 1 deletion src/entrypoints/content/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export default defineContentScript({
matches: [],

async main() {
if (globalThis.__JABREF_CONTENT_SCRIPT_INITIALIZED__) {
console.debug("[contentScript] already initialized");
return;
}
globalThis.__JABREF_CONTENT_SCRIPT_INITIALIZED__ = true;
console.debug("[contentScript] started");

browser.runtime.onMessage.addListener(async (msg, _sender, _sendResponse) => {
Expand Down Expand Up @@ -76,7 +81,17 @@ export default defineContentScript({
);
const result = await translateEngine.translate(document, translators);
console.debug("Content script obtained translation result %o", result);
await browser.runtime.sendMessage({ type: "offscreenResult", url, items: result.items });
console.debug(
"Content script sending offscreenResult with %o item(s) for %o",
result.items?.length ?? 0,
url,
);
const response = await browser.runtime.sendMessage({
type: "offscreenResult",
url,
items: result.items,
});
console.debug("Content script received background ack for offscreenResult %o", response);
});
},
});
12 changes: 12 additions & 0 deletions src/entrypoints/options/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ <h3>Connection Status</h3>
<span id="connectionStatusHttp">Testing connection...</span>
</div>
</div>
<div class="grid-container">
<div class="grid-header">
<h3>Diagnostics</h3>
</div>
<div class="grid-item">
<label for="testNativeMessage">Native messaging probe</label>
</div>
<div class="grid-item diagnostics-cell">
<button id="testNativeMessage" type="button">Run test</button>
<pre id="nativeMessageResult">No test run yet.</pre>
</div>
</div>
<script type="module" src="./main.js"></script>
</body>
</html>
83 changes: 68 additions & 15 deletions src/entrypoints/options/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,50 @@ var ExportMode = Object.freeze({
});

const DEFAULT_PORT = 23119;
const NATIVE_MESSAGE_TIMEOUT_MS = 5000;
const NATIVE_MESSAGE_TEST_LOG_TAG = "JBE_NATIVE_TEST";

function raceWithTimeout(promise, timeoutMs, label) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs} ms`)), timeoutMs);
}),
]);
}

function formatError(error) {
if (error instanceof Error) {
return error.message;
}
return String(error);
}

async function sendNativeValidation() {
const requestId = `${NATIVE_MESSAGE_TEST_LOG_TAG}-${Date.now()}`;
console.log(`${NATIVE_MESSAGE_TEST_LOG_TAG} sending requestId=${requestId}`);
return raceWithTimeout(
browser.runtime.sendNativeMessage("org.jabref.jabref", {
status: "validate",
requestId,
}),
NATIVE_MESSAGE_TIMEOUT_MS,
"Native messaging",
);
}

function renderNativeStatus(statusElement, response) {
if (response.message === "jarNotFound") {
statusElement.setAttribute("class", "alert-error");
statusElement.textContent = `Unable to locate JabRef at: ${response.path}`;
} else if (response.message === "jarFound") {
statusElement.setAttribute("class", "alert-positive");
statusElement.textContent = "Communication to JabRef successful!";
} else {
statusElement.setAttribute("class", "alert-error");
statusElement.textContent = `Unexpected response: ${response.message}`;
}
}

async function connectToJabRef(port) {
const base = `http://localhost:${port}/`;
Expand All @@ -24,25 +68,14 @@ async function connectToJabRef(port) {

function checkConnections({ httpPort }) {
let status = document.getElementById("connectionStatusNative");
browser.runtime
.sendNativeMessage("org.jabref.jabref", {
status: "validate",
})
status.textContent = "Testing connection...";
sendNativeValidation()
.then((response) => {
if (response.message === "jarNotFound") {
status.setAttribute("class", "alert-error");
status.textContent = "Unable to locate JabRef at:<br>" + response.path;
} else if (response.message === "jarFound") {
status.setAttribute("class", "alert-positive");
status.textContent = "Communication to JabRef successful!";
} else {
status.setAttribute("class", "alert-error");
status.innerHTML = "Unexpected response:<br>" + response.message;
}
renderNativeStatus(status, response);
})
.catch((error) => {
status.setAttribute("class", "alert-error");
status.textContent = error.message;
status.textContent = formatError(error);
});

let httpStatus = document.getElementById("connectionStatusHttp");
Expand Down Expand Up @@ -71,6 +104,25 @@ function checkConnections({ httpPort }) {
});
}

function initializeNativeMessagingDiagnosticButton() {
const diagnosticButton = document.getElementById("testNativeMessage");
const diagnosticResult = document.getElementById("nativeMessageResult");

diagnosticButton.addEventListener("click", async () => {
diagnosticButton.disabled = true;
diagnosticResult.textContent = "Running native messaging diagnostic...";

try {
const response = await sendNativeValidation();
diagnosticResult.textContent = JSON.stringify(response, null, 2);
} catch (error) {
diagnosticResult.textContent = formatError(error);
} finally {
diagnosticButton.disabled = false;
}
});
}

async function restoreOptions() {
const options = await browser.storage.sync.get({
exportMode: ExportMode.BibTeX,
Expand Down Expand Up @@ -126,6 +178,7 @@ function saveOptions() {
async function init() {
const options = await restoreOptions();
checkConnections(options);
initializeNativeMessagingDiagnosticButton();

document.getElementById("exportBiblatex").addEventListener("change", () => saveOptions());
document.getElementById("exportBibtex").addEventListener("change", () => saveOptions());
Expand Down
29 changes: 29 additions & 0 deletions src/entrypoints/options/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ body {
min-width: 500px;
}

button {
font: inherit;
padding: 4px 10px;
}

label {
cursor: default;
margin-top: 1px;
Expand Down Expand Up @@ -38,6 +43,30 @@ label {
text-align: center;
}

.diagnostics-cell {
display: flex;
flex-direction: column;
gap: 8px;
align-items: flex-start;
}

#nativeMessageResult {
margin: 0;
padding: 8px;
width: min(100%, 42rem);
min-height: 6rem;
white-space: pre-wrap;
overflow-wrap: anywhere;
background: #f5f5f7;
border: 1px solid #d7d7db;
text-shadow: none;
font:
12px/1.4 ui-monospace,
SFMono-Regular,
Menlo,
monospace;
}

input[type="radio"] {
vertical-align: middle;
-moz-appearance: none;
Expand Down
5 changes: 5 additions & 0 deletions src/entrypoints/popup/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import "./style.css";

browser.runtime.onMessage.addListener(function (message, _sender, _sendResponse) {
console.debug("JabRef: Received message in popup:", message);
if (message.popupLog) {
appendLog(message.popupLog, "info");
}
if (message.popupClose) {
// The popup should be closed
setTimeout(function () {
Expand All @@ -10,8 +13,10 @@ browser.runtime.onMessage.addListener(function (message, _sender, _sendResponse)
console.log("JabRef: Popup closed");
} else if (message.onConvertToBibtex) {
document.getElementById("status").innerHTML = "Converting to BibTeX...";
appendLog("Translation done, converting item data to BibTeX", "info");
} else if (message.onSendToJabRef) {
document.getElementById("status").innerHTML = "Sending to JabRef...";
appendLog("BibTeX ready, sending data to JabRef", "info");
}
});

Expand Down
Loading