Skip to content

Implement Custom URL Protocol Handling#15378

Open
FynnianB wants to merge 4 commits intoJabRef:mainfrom
FynnianB:feature/protocol-handler
Open

Implement Custom URL Protocol Handling#15378
FynnianB wants to merge 4 commits intoJabRef:mainfrom
FynnianB:feature/protocol-handler

Conversation

@FynnianB
Copy link
Copy Markdown
Contributor

@FynnianB FynnianB commented Mar 19, 2026

Related issues and pull requests

Closes no direct related Issue in JabRef repository

Related JabRef/JabRef-Browser-Extension-fresh#17
Related to JabRef/JabRef-Browser-Extension-fresh#18
Related to #14884

Accidently closed old pr: #15294

PR Description

Implements Custom URL Protocol Handling so that the browser extension can open/focus the JabRef Desktop-App if its not running. More specifically, we register the custom URL protocol in the installation processes of the various operating systems and ensure that the application can handle the call by the OS.

This PR is part my bachelorthesis regarding the communication between the browser extension and the JabRef Desktop-App. The corresponding PR in the JabRef-Browser-Extension-fresh repository will follow shortly.

_Hint: This PR will follow another one which adds the MADR for it.

Steps to test

  1. Install JabRef via installer
  2. Open jabref:// or jabref://open in browser
  3. Ensure JabRef is opening (or focussing if one instance is already open)

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • I manually tested my changes in running JabRef (always required)
    • I manually tested on Windows; Could not test it on Linux because I personally dont run an applicable Linux distro; Could not test it on Mac because of privacy settings (Crowdstrike) of my mac. Can you test these other OS?
  • I added JUnit tests for changes (if applicable)
  • [/] I added screenshots in the PR description (if change is visible to the user)
  • [/] I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Implement custom URL protocol handler for browser extension integration

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Implement custom jabref:// URL protocol handler registration across platforms
• Filter protocol handler URLs from CLI arguments to prevent parsing errors
• Add Focus UI command when protocol handler is invoked
• Register platform-specific protocol handlers (macOS via Desktop API, Windows/Linux via installers)
• Add comprehensive unit tests for protocol handler argument filtering
Diagram
flowchart LR
  A["OS triggers jabref:// URL"] --> B["ArgumentProcessor filters protocol args"]
  B --> C["protocolHandlerInvoked flag set"]
  C --> D["Focus UI command added"]
  A --> E["macOS Desktop API handler"]
  E --> D
  F["Windows Registry setup"] --> G["Protocol handler registered"]
  H["Linux desktop file setup"] --> G
  G --> A
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/cli/ArgumentProcessor.java ✨ Enhancement +21/-1

Filter protocol URLs and add focus command

jabgui/src/main/java/org/jabref/cli/ArgumentProcessor.java


2. jabgui/src/main/java/org/jabref/gui/JabRefGUI.java ✨ Enhancement +6/-0

Register protocol handler callback in startup

jabgui/src/main/java/org/jabref/gui/JabRefGUI.java


3. jabgui/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java ✨ Enhancement +10/-0

Add protocol handler registration interface

jabgui/src/main/java/org/jabref/gui/desktop/os/NativeDesktop.java


View more (9)
4. jabgui/src/main/java/org/jabref/gui/desktop/os/OSX.java ✨ Enhancement +24/-1

Implement macOS protocol handler via Desktop API

jabgui/src/main/java/org/jabref/gui/desktop/os/OSX.java


5. jabgui/src/test/java/org/jabref/cli/ArgumentProcessorTest.java 🧪 Tests +83/-0

Add comprehensive protocol handler argument tests

jabgui/src/test/java/org/jabref/cli/ArgumentProcessorTest.java


6. CHANGELOG.md 📝 Documentation +1/-0

Document protocol handler feature addition

CHANGELOG.md


7. docs/decisions/0055-browser-extension-communication-architecture.md 📝 Documentation +97/-0

Add architecture decision record for hybrid communication

docs/decisions/0055-browser-extension-communication-architecture.md


8. flatpak/org.jabref.jabref.desktop ⚙️ Configuration changes +1/-1

Register jabref protocol scheme in Flatpak

flatpak/org.jabref.jabref.desktop


9. jabgui/buildres/linux/JabRef.desktop ⚙️ Configuration changes +1/-1

Register jabref protocol scheme in Linux desktop file

jabgui/buildres/linux/JabRef.desktop


10. jabgui/buildres/macos/Info.plist ⚙️ Configuration changes +11/-0

Register jabref protocol scheme in macOS bundle

jabgui/buildres/macos/Info.plist


11. jabgui/buildres/windows/main.wxs ⚙️ Configuration changes +16/-0

Register jabref protocol handler in Windows installer

jabgui/buildres/windows/main.wxs


12. snap/gui/jabref.desktop ⚙️ Configuration changes +1/-1

Register jabref protocol scheme in Snap desktop file

snap/gui/jabref.desktop


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects bot commented Mar 19, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 📐 Spec deviations (0)

Grey Divider


Action required

1. Case-sensitive protocol filtering🐞 Bug ✓ Correctness
Description
ArgumentProcessor.filterProtocolHandlerArgs only removes args starting with lowercase "jabref:", so
a case-variant invocation like "JABREF://open" will be passed to picocli as a positional <FILE>
(List<Path>) and can crash or misbehave before the Focus command is added. This can break
protocol-handler launches/focus on Windows/Linux when the scheme casing differs.
Code

jabgui/src/main/java/org/jabref/cli/ArgumentProcessor.java[R42-56]

+        String[] filteredArgs = filterProtocolHandlerArgs(args);
+        this.protocolHandlerInvoked = filteredArgs.length < args.length;
+
      cli = new CommandLine(this.guiCli);
-        cli.parseArgs(args);
+        cli.parseArgs(filteredArgs);
+    }
+
+    /// Removes `jabref://` protocol handler URLs from the argument list.
+    /// These are passed by the OS when the `jabref://` URL scheme is triggered
+    /// and must not reach picocli, which would try to parse them as file paths.
+    private static String[] filterProtocolHandlerArgs(String[] args) {
+        return Arrays.stream(args)
+                     .filter(arg -> !arg.startsWith(JABREF_PROTOCOL_SCHEME))
+                     .toArray(String[]::new);
  }
Evidence
The new filter uses a case-sensitive prefix check, and unfiltered arguments are treated as
positional file paths (Path conversion). JabRef already documents that ':' is not allowed in Windows
file names, and jabref:// URLs necessarily contain ':', so failing to filter a case-variant scheme
can lead to parsing errors/crashes on Windows.

jabgui/src/main/java/org/jabref/cli/ArgumentProcessor.java[20-56]
jabgui/src/main/java/org/jabref/cli/GuiCommandLine.java[11-15]
jabgui/src/main/java/org/jabref/Launcher.java[152-154]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`filterProtocolHandlerArgs` filters protocol-handler URLs using a case-sensitive `startsWith(&amp;quot;jabref:&amp;quot;)`. URI schemes are case-insensitive in practice, so `JABREF://...` can slip through and be parsed by picocli as a positional `&amp;lt;FILE&amp;gt;` (`List&amp;lt;Path&amp;gt;`), which is especially problematic on Windows due to `:` in the value.
## Issue Context
The goal of the filter is to prevent protocol URLs from reaching picocli path parsing.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/cli/ArgumentProcessor.java[42-56]
- jabgui/src/test/java/org/jabref/cli/ArgumentProcessorTest.java[21-33]
## Implementation notes
- Make the filter case-insensitive, e.g. `arg.regionMatches(true, 0, JABREF_PROTOCOL_SCHEME, 0, JABREF_PROTOCOL_SCHEME.length())` or `arg.toLowerCase(Locale.ROOT).startsWith(JABREF_PROTOCOL_SCHEME)`.
- Extend the parameterized test to include an uppercase variant like `&amp;quot;JABREF://open&amp;quot;` and assert Focus is produced.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Boolean assertions in ArgumentProcessorTest📘 Rule violation ⚙ Maintainability
Description
New tests assert boolean predicates via assertTrue/assertFalse instead of asserting concrete
contents/structure of the commands list. This reduces test clarity and makes failures less
informative than content-based assertions.
Code

jabgui/src/test/java/org/jabref/cli/ArgumentProcessorTest.java[R31-45]

+        assertTrue(commands.stream().anyMatch(UiCommand.Focus.class::isInstance));
+    }
+
+    @Test
+    void normalArgumentsAreNotAffectedByProtocolFilter() {
+        ArgumentProcessor processor = new ArgumentProcessor(
+                new String[] {"--blank"},
+                ArgumentProcessor.Mode.REMOTE_START,
+                preferences);
+
+        List<UiCommand> commands = processor.processArguments();
+
+        assertTrue(commands.stream().anyMatch(UiCommand.BlankWorkspace.class::isInstance));
+        assertFalse(commands.stream().anyMatch(UiCommand.Focus.class::isInstance));
+    }
Evidence
The checklist requires tests to prefer assertions that validate object contents over indirect
boolean checks. The added test code repeatedly uses assertTrue(...anyMatch(...)) /
assertFalse(...anyMatch(...)) to validate the produced commands instead of asserting the list
contents more directly.

AGENTS.md
jabgui/src/test/java/org/jabref/cli/ArgumentProcessorTest.java[31-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new unit tests use `assertTrue`/`assertFalse` over stream predicates to verify that certain command types are present/absent. Per project test style, prefer assertions that validate list contents/structure more directly so failures are clearer.
## Issue Context
Current assertions are of the form `assertTrue(commands.stream().anyMatch(...))`, which is an indirect boolean check.
## Fix Focus Areas
- jabgui/src/test/java/org/jabref/cli/ArgumentProcessorTest.java[31-45]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@testlens-app

This comment has been minimized.

@testlens-app

This comment has been minimized.

@koppor koppor added the status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers label Mar 20, 2026
@LyzardKing
Copy link
Copy Markdown
Collaborator

On Linux the .desktop file is:

[Desktop Entry]
Name=JabRef
Comment=JabRef
Exec=/opt/jabref/bin/JabRef
STARTUP_DIRECTORY
Icon=/opt/jabref/lib/JabRef.png
Terminal=false
Type=Application
Categories=Office
MimeType=text/x-bibtex;x-scheme-handler/jabref;

GenericName=BibTeX Editor
Keywords=bibtex;biblatex;latex;bibliography
StartupWMClass=org.jabref.gui.JabRefGUI

There is STARTUP DIRECTORY, which should not be there (unless it should be replaced, but I guess it's not needed)
Also, to use the jabref:// scheme we need to add %u at the end of the Exec line

%u	A single URL. Local files may either be passed as file: URLs or as file path. 

With these two changes it seems to work as intended

@github-actions github-actions bot added status: changes-required Pull requests that are not yet complete and removed status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers status: no-bot-comments labels Mar 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Your pull request conflicts with the target branch.

Please merge with your code. For a step-by-step guide to resolve merge conflicts, see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line.

@testlens-app
Copy link
Copy Markdown

testlens-app bot commented Mar 28, 2026

✅ All tests passed ✅

🏷️ Commit: a8d6298
▶️ Tests: 10215 executed
⚪️ Checks: 52/52 completed


Learn more about TestLens at testlens.app.

@github-actions github-actions bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants