diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1c4c74d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +# Contributing + +Contributions welcome. + +Implementation notes are at [IMPLEMENTATION.md](implementation.md). +If you're unsure about anything, feel free to ask. + +Just submit a PR for small changes (bugfixes, typos, etc...). Comment first on existing +issues if you're going to work on something to avoid duplication of effort. + +Submit an issue for new features before submitting a PR. diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md new file mode 100644 index 0000000..e97d655 --- /dev/null +++ b/IMPLEMENTATION.md @@ -0,0 +1,39 @@ +# Extension Lifetime + +## Shim Initialization + +The background script registers a `webRequest.onBeforeRequest` handler that intercepts requests to Google's [Cast SDK library](https://developers.google.com/cast/docs/developers#chrome_sender_api_library). + +When a request is intercepted, the `shim/content.js` script is executed in the content script context. This facilitates message passing across [content/page script isolation](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Sharing_objects_with_page_scripts) (the shim itself is executed in the page context since it interacts substantially with page scripts). + +Messages passed to the shim are custom events of type `__castMessage`. Messages passed back from the shim are custom events of type `__castMessageResponse`. Event listening and creation is handled by the `shim/messageBridge.js` script. + +The request is then redirected to the shim bundle (`shim/index.js`) which creates the `window.chrome.cast` API interface. + +The `shim/content.js` script creates a message port connection (named `shim`) to the background script through which messages from the shim (except `popup:/` messages) are forwarded to the background script. The connection triggers the background script to spawn a bridge application instance. The tab/frame ID is stored and used to associate shim instances with popup windows. + +Messages are forwarded to the bridge and other parts of the extension via the background script (except for popups which make a direct connection to the shim). + +The `shim:/initialized` message is sent to the shim and the `window.__onGCastApiAvailable` API callback is called with the availability state (bridge availability/compatibility is passed as the message data). + +The cast API is now available to the web app. + +The web app calls `chrome.cast.initialize` with an `ApiConfig` object containing the Chromecast receiver app ID to use. The shim sends the `bridge:/discover` message to the bridge and triggers network discovery. The bridge sends `shim:/serviceUp` messages for any discovered devices with device info (address, port, label, etc...) which are stored in the `state.receiverList` array. + + +## User Interaction + +A user will trigger casting through the web app interface and the app calls `chrome.cast.requestSession`. The shim sends a `main:/openPopup` message to the background script to open the receiver selection popup. + +The popup window is created and the ID of the shim is stored as the popup opener (`popupShimId`). The popup script creates a message port connection (named `popup`) to the background script and receives a `popup:/assignShim` message containing the tab/frame ID of the opener shim. + +The popup creates a message port connection to the shim, sends a `shim:/popupReady` message and receives a `popup:/populateReceiverList` message in response containing the `state.receiverList` and type of session (sender app, media, or screen mirroring). + +Once the user selects a receiver device to cast to, the popup sends a `shim:/selectReceiver` message to the shim which then establishes the session. + + +## Shim Implementation + +Cast SDK API calls are translated into Chromecast protocol messages and sent via `node-castv2`. Based on [@GPMDP/electron-chromecast](https://github.com/GPMDP/electron-chromecast), so there are many similarities. The shim and the bridge exchange messages to implement API methods which require communication with the receiver device. + +`Session` and `Media` objects have a counterpart object within the bridge. Some messages are routed directly to these objects. For `Session`, these are in the format `bridge:/session/impl_`. For `Media`, it's `bridge:/media/impl_`. diff --git a/README.md b/README.md index 31d0a0f..366a03d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,11 @@ # fx_cast -No full public release yet! Pre-release beta version is incomplete and likely buggy. +Firefox extension that implements the [Chrome sender API](https://developers.google.com/cast/docs/reference/chrome/) and exposes it to web apps to enable cast support. + +Communication with receiver devices is handled by a native application (bridge). Check the [implementation notes](IMPLEMENTATION.md) for more info. + +**No full public release yet! Pre-release beta version is incomplete and likely buggy.** ## Installing