Skip to content

ajsb85/jstest-protocol-webext

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Thunderbird Custom Protocol WebExtension Example

This WebExtension demonstrates how to define, register, and use a new custom chat protocol within Thunderbird. It leverages Thunderbird's Experiment APIs to interact with core instant messaging functionalities and provides a template for developers looking to extend Thunderbird's chat capabilities.

1. Purpose

The primary goal of this extension is to showcase:

  • The creation of a new chat protocol using Thunderbird's jsProtoHelper.sys.mjs module, which provides base classes for protocol, account, and conversation implementations.
  • The registration of this custom protocol with Thunderbird's central instant messaging service (imCore.sys.mjs) so it becomes available in the account manager.
  • The use of Experiment APIs to achieve this, specifically:
    • A LegacyHelper API to register resource:// URLs, making custom JavaScript modules (.sys.mjs) loadable by Thunderbird. [cite: 22, 24]
    • A custom ProtocolRegistrar API to manage the lifecycle (registration, unregistration, and loading) of the new chat protocol.
  • A working example of a simple "Echo" protocol that users can add as a chat account and interact with.

This extension serves as a foundational example for developers aiming to integrate unsupported chat services or create novel messaging experiences within Thunderbird.

2. How It Works

At a high level, the extension operates as follows:

  1. Module Loading Setup: The background.js script first utilizes the LegacyHelper API. This API registers a special resource:// URL that points to the extension's modules/ directory. [cite: 14, 15, 25] This step is crucial for Thunderbird to be able to locate and load the custom protocol implementation file (jsCustomProto.sys.mjs).
  2. Protocol Registration: Next, background.js calls the ProtocolRegistrar.register() function. This function, part of our custom Experiment API, performs several key actions:
    • It dynamically loads your jsCustomProto.sys.mjs module using the resource:// URL.
    • It calls imCore.sys.mjs's internal registerProtocol(protocolId, dummyCid) function. This makes Thunderbird's core messaging system aware of the new protocol's existence, allowing it to be listed in the account manager. A "dummy" Contract ID (CID) is used here because the actual protocol object isn't a standard XPCOM component.
    • It "patches" (wraps) the getProtocolById method on the imCoreService instance. This patched method intercepts requests for the custom protocol. When imCoreService tries to get an instance of the custom protocol, the patched function instantiates your jsCustomProto.sys.mjs:Protocol class, initializes it, and returns it.
  3. Protocol Implementation: The jsCustomProto.sys.mjs file defines the behavior of the new "Echo" protocol. It uses classes from jsProtoHelper.sys.mjs (GenericProtocolPrototype, GenericAccountPrototype, GenericConvIMPrototype) to structure the protocol, account, and conversation logic.
  4. User Interaction: Once registered, the user can add a new chat account in Thunderbird, selecting "My Custom Echo Protocol" from the list. Sending a message in this chat will result in the message being echoed back.
  5. Cleanup: When the extension is disabled or uninstalled, the onShutdown methods within the LegacyHelper and ProtocolRegistrar API implementations are triggered. These methods unregister the resource:// URLs, unpatch the getProtocolById method, and unregister the custom protocol from imCore.sys.mjs, ensuring Thunderbird returns to its original state. [cite: 41, 42, 43]

3. Key Files and Components

  • manifest.json:
    • Defines the extension's metadata (name, version, permissions). [cite: 3]
    • Declares the LegacyHelper and ProtocolRegistrar Experiment APIs, linking them to their schema and implementation files. [cite: 3, 4, 5]
  • background.js:
    • The main entry point of the extension. [cite: 12]
    • Orchestrates the registration of the resource:// URL via LegacyHelper. [cite: 15]
    • Calls ProtocolRegistrar.register() to activate the custom protocol.
  • modules/jsCustomProto.sys.mjs:
    • Contains the actual JavaScript implementation of your custom chat protocol (e.g., "My Custom Echo Protocol").
    • Exports a Protocol class that extends GenericProtocolPrototype.
    • Defines CustomAccount and CustomConversation classes extending their respective generic prototypes from jsProtoHelper.sys.mjs.
  • api/LegacyHelper/:
    • implementation.js: Script that allows registration of resource:// and chrome:// URLs. [cite: 22, 29, 32, 35]
    • schema.json: Defines the interface for the LegacyHelper API (e.g., registerGlobalUrls function). [cite: 45, 46]
  • api/ProtocolRegistrar/:
    • implementation.js: The core logic for managing the custom protocol. It imports symbols from imCore.sys.mjs, patches getProtocolById, and maintains a list of custom registered protocols.
    • schema.json: Defines the interface for the ProtocolRegistrar API (e.g., register and unregister functions).

4. Setup and Installation

  1. Enable Debugging: It's helpful to set devtools.chrome.enabled and devtools.debugger.remote-enabled to true in Thunderbird's Config Editor (about:config) for easier debugging.
  2. Load the Extension:
    • Open Thunderbird.
    • Go to Tools > Add-ons and Themes.
    • Click the Cogwheel icon (⚙️) next to "Manage Your Extensions".
    • Select "Debug Add-ons".
    • Click "Load Temporary Add-on...".
    • Navigate to the directory containing this extension's manifest.json file and select it.
  3. Check Console:
    • Open the Browser Console: Tools > Developer Tools > Browser Console (or Ctrl+Shift+J / Cmd+Shift+J).
    • Look for log messages from "CustomProtocolExt:", "LegacyHelper:", and "ProtocolRegistrar:". You should see confirmations of resource URL registration, patching, and protocol registration.
    • Any errors during setup will also appear here.

5. Using and Testing the Custom Protocol

  1. Add a Chat Account:
    • In Thunderbird, go to File > New > Chat Account... (or Edit > Account Settings, then Account Actions > Add Chat Account...).
    • In the "Protocol" dropdown menu, you should now see "My Custom Echo Protocol" (or the name defined in jsCustomProto.sys.mjs).
    • Select it.
    • The account options defined in jsCustomProto.sys.mjs (e.g., "Username," "Server (optional)") will appear. Fill them in as desired (they are mostly for demonstration in the Echo protocol).
    • Click "Continue" and then "Done" (or "Sign In" depending on Thunderbird version and protocol options).
  2. Connect and Chat:
    • The account should attempt to connect. In the Chat tab (View > Today Pane > Chat or via the dedicated Chat tab if enabled), you should see your new account.
    • If it connects successfully, you'll usually see a system message like "Connected to Custom Protocol...".
    • Open a "New Chat" with any contact name for this account (since it's an echo protocol, the recipient doesn't matter).
    • Type a message and press Enter. The message should be displayed as outgoing, and then shortly after, the same message should appear as an incoming "Echo:" message.

6. Important Development Notes

  • Experiment APIs are Powerful but Risky: Experiment APIs provide deep access to Thunderbird's internals. This allows for powerful extensions but also means that changes in Thunderbird's internal code can break your extension. They are not considered stable in the same way as standard WebExtension APIs.
  • Thunderbird Version Compatibility: This example is targeted for recent versions of Thunderbird (e.g., 115+). Compatibility with much older or future versions is not guaranteed without testing and potential updates. The strict_min_version in manifest.json should be set appropriately. [cite: 3]
  • Caching: Thunderbird can aggressively cache JavaScript modules (.sys.mjs). When making changes to jsCustomProto.sys.mjs or API implementation files:
    • The ?v=${Date.now()} query string appended to module URLs in ProtocolRegistrar/implementation.js helps to bust the cache for the protocol module.
    • Restarting Thunderbird entirely is often the most reliable way to ensure all changes are loaded.
    • Using "Load Temporary Add-on" reloads the extension, but module caching might still persist for already-loaded modules in some cases.
  • Cleanup is Crucial: The onShutdown methods in the Experiment API implementations are vital. [cite: 41] They must clean up any modifications made to Thunderbird (like unpatching methods and unregistering protocols) when the extension is disabled, uninstalled, or when Thunderbird itself closes (unless isAppShutdown is true, in which case Thunderbird handles some cleanup). Failure to do so can leave Thunderbird in an unstable state.
  • Debugging: Use the Browser Console extensively for logging and error checking. The Add-on Debugger (about:debugging#/runtime/this-firefox then inspect your extension) can also be invaluable for stepping through background.js and, to some extent, API scripts.
  • Error Handling: The provided code includes basic error handling. For a production-quality extension, more robust error management and user feedback mechanisms would be necessary.

7. Customizing jsCustomProto.sys.mjs

The jsCustomProto.sys.mjs file is where you'll implement the unique logic for your desired chat protocol.

  • Protocol Properties: Modify get name(), get normalizedName(), and get options{} in the Protocol.prototype to define your protocol's display name and the account settings fields.
  • Account Logic (CustomAccount.prototype):
    • Implement connect(): Handle the actual connection to your chat service (e.g., WebSocket, HTTP requests). Use this.reportConnecting(), this.reportConnected(), or this.reportError() to update Thunderbird's UI.
    • Implement disconnect(): Handle disconnection from your service.
    • Manage account state (e.g., online status, user information).
  • Conversation Logic (CustomConversation.prototype):
    • Implement dispatchMessage(aMsg): Send outgoing messages to your service.
    • When your service sends incoming messages, use this.writeMessage(sender, message, { incoming: true, ... }) to display them in the chat window.
    • Handle other conversation events like typing notifications if your protocol supports them.
  • Interfaces: Refer to the prplI* interfaces (like prplIProtocol, prplIAccount, prplIConvIM) in the Thunderbird source code for more details on available methods and properties you can implement or override.

This example provides a solid starting point. Happy developing!

About

Thunderbird Custom Protocol WebExtension Example

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published