mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Clean up shim initialization
This commit is contained in:
@@ -2,13 +2,13 @@
|
||||
|
||||
A bridge application instance (`statusBridge`) is created to keep track of receivers’ statuses. This is expected to exist throughout the lifetime of the extension and will automatically reconnect if unexpectedly disconnected.
|
||||
|
||||
The `shim/contentSetup.ts` content script is registered for all pages. It creates an empty `window.chrome` object in the page context since some sites may expect it to exist. It also intercepts any `src` attribute changes on `<script>` elements where the cast API may be loaded directly from a `chrome-extension://` URL, then sets them to the regular cast API script URL.
|
||||
The `shim/content.ts` content script is registered for all pages. It creates an empty `window.chrome` object in the page context since some sites may expect it to exist. It also intercepts any `src` attribute changes on `<script>` elements where the cast API may be loaded directly from a `chrome-extension://` URL, then sets them to the regular cast API script URL.
|
||||
|
||||
## Shim Initialization
|
||||
|
||||
The background script registers a `webRequest.onBeforeRequest` listener that intercepts requests to Google’s Cast API library.
|
||||
|
||||
When a request is intercepted, the `shim/content.ts` script is executed in the content script context. This facilitates any message passing across content/page script isolation (the shim itself is executed in the page context, both for convenience — since it interacts substantially with page scripts — and security reasons).
|
||||
When a request is intercepted, the `shim/contentBridge.ts` script is executed in the content script context. This facilitates any message passing across content/page script isolation (the shim itself is executed in the page context, both for convenience — since it interacts substantially with page scripts — and security reasons).
|
||||
|
||||
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.ts` module.
|
||||
|
||||
@@ -23,6 +23,12 @@ 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 a `main:/shimInitialized` message to the background script. The bridge sends `shim:/serviceUp` messages for any discovered devices with device info (address, port, label, etc…).
|
||||
|
||||
### Extension Sender Apps
|
||||
|
||||
The initialization for built-in sender apps (media/mirroring) works slightly differently. They run in the content script context and import the shim, rather than running in the page script context.
|
||||
|
||||
The exported shim comes from `shim/export.ts`. Instead of setting the `window.__onGCastApiAvailable` callback, the module provides an init function which returns a promise. The `shim/contentBridge.ts` script is executed from that module and the initialization process is identical until the `shim:/initialized` message is received, at which point the promise is resolved.
|
||||
|
||||
## User Interaction
|
||||
|
||||
A user will trigger casting through the web app interface and the app calls `chrome.cast.requestSession`. The shim sends a `main:/selectReceiverBegin` message to the background script to open the receiver selector.
|
||||
|
||||
@@ -258,14 +258,7 @@ browser.menus.onShown.addListener(info => {
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
async details => {
|
||||
await browser.tabs.executeScript(details.tabId, {
|
||||
code: `window._isFramework = ${
|
||||
details.url === CAST_FRAMEWORK_LOADER_SCRIPT_URL}`
|
||||
, frameId: details.frameId
|
||||
, runAt: "document_start"
|
||||
});
|
||||
|
||||
await browser.tabs.executeScript(details.tabId, {
|
||||
file: "shim/content.js"
|
||||
file: "shim/contentBridge.js"
|
||||
, frameId: details.frameId
|
||||
, runAt: "document_start"
|
||||
});
|
||||
@@ -402,7 +395,7 @@ browser.runtime.onMessage.addListener(async message => {
|
||||
// Defines window.chrome for site compatibility
|
||||
browser.contentScripts.register({
|
||||
allFrames: true
|
||||
, js: [{ file: "shim/contentSetup.js" }]
|
||||
, js: [{ file: "shim/content.js" }]
|
||||
, matches: [ "<all_urls>" ]
|
||||
, runAt: "document_start"
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ErrorCode
|
||||
|
||||
import { ListenerObject
|
||||
, onMessage
|
||||
, sendMessageResponse } from "../../messageBridge";
|
||||
, sendMessageResponse } from "../../eventMessageChannel";
|
||||
|
||||
import { Callbacks
|
||||
, CallbacksMap
|
||||
|
||||
@@ -28,7 +28,7 @@ import * as media from "./media";
|
||||
|
||||
import { ReceiverSelectorMediaType }
|
||||
from "../../receiver_selectors/ReceiverSelector";
|
||||
import { onMessage, sendMessageResponse } from "../messageBridge";
|
||||
import { onMessage, sendMessageResponse } from "../eventMessageChannel";
|
||||
|
||||
|
||||
type ReceiverActionListener = (
|
||||
|
||||
@@ -23,7 +23,7 @@ import { PlayerState
|
||||
import _Error from "../../classes/Error";
|
||||
import { ErrorCode } from "../../enums";
|
||||
|
||||
import { onMessage, sendMessageResponse } from "../../../messageBridge";
|
||||
import { onMessage, sendMessageResponse } from "../../../eventMessageChannel";
|
||||
|
||||
import { Callbacks
|
||||
, CallbacksMap
|
||||
|
||||
@@ -1,42 +1,38 @@
|
||||
"use strict";
|
||||
|
||||
import { Message } from "../types";
|
||||
import { onMessageResponse, sendMessage } from "./messageBridge";
|
||||
|
||||
import { loadScript } from "../lib/utils";
|
||||
import { CAST_LOADER_SCRIPT_URL
|
||||
, CAST_SCRIPT_URLS } from "../endpoints";
|
||||
|
||||
|
||||
if ((window as any)._isFramework) {
|
||||
loadScript(browser.runtime.getURL("vendor/webcomponents-lite.js"));
|
||||
}
|
||||
const _window = (window.wrappedJSObject as any);
|
||||
|
||||
_window.chrome = cloneInto({}, window);
|
||||
_window.navigator.presentation = cloneInto({}, window);
|
||||
|
||||
|
||||
// Message ports
|
||||
const backgroundPort = browser.runtime.connect({ name: "shim" });
|
||||
let popupPort: browser.runtime.Port;
|
||||
/**
|
||||
* Replace the src property setter on <script> elements to
|
||||
* intercept the new value.
|
||||
*
|
||||
* If it matches one of Chrome's cast extension sender script
|
||||
* URLs, replace it with the standard API URL, the request for
|
||||
* which is handled in the main script.
|
||||
*/
|
||||
const { get, set } = Reflect.getOwnPropertyDescriptor(
|
||||
HTMLScriptElement.prototype.wrappedJSObject, "src");
|
||||
|
||||
Reflect.defineProperty(
|
||||
HTMLScriptElement.prototype.wrappedJSObject, "src", {
|
||||
|
||||
// Set popupPort once it connects
|
||||
browser.runtime.onConnect.addListener(port => {
|
||||
if (port.name === "popup") {
|
||||
popupPort = port;
|
||||
}
|
||||
configurable: true
|
||||
, enumerable: true
|
||||
, get
|
||||
|
||||
port.onMessage.addListener(sendMessage);
|
||||
});
|
||||
|
||||
// Forward background messages to shim
|
||||
backgroundPort.onMessage.addListener(sendMessage);
|
||||
|
||||
// Forward shim messages to popup and background script
|
||||
onMessageResponse((message: Message) => {
|
||||
const [ destination ] = message.subject.split(":/");
|
||||
|
||||
if (destination === "popup") {
|
||||
if (popupPort) {
|
||||
popupPort.postMessage(message);
|
||||
, set: exportFunction(function (value) {
|
||||
if (CAST_SCRIPT_URLS.includes(value)) {
|
||||
return set.call(this, CAST_LOADER_SCRIPT_URL);
|
||||
}
|
||||
} else {
|
||||
backgroundPort.postMessage(message);
|
||||
}
|
||||
|
||||
return set.call(this, value);
|
||||
}, window)
|
||||
});
|
||||
|
||||
15
ext/src/shim/contentBridge.ts
Normal file
15
ext/src/shim/contentBridge.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
import { onMessageResponse, sendMessage } from "./eventMessageChannel";
|
||||
|
||||
|
||||
// Message port to background script
|
||||
const backgroundPort = browser.runtime.connect({ name: "shim" });
|
||||
|
||||
// Forward background messages to shim
|
||||
backgroundPort.onMessage.addListener(sendMessage);
|
||||
|
||||
// Forward shim messages to background
|
||||
onMessageResponse(message => {
|
||||
backgroundPort.postMessage(message);
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
import { CAST_LOADER_SCRIPT_URL
|
||||
, CAST_SCRIPT_URLS } from "../endpoints";
|
||||
|
||||
|
||||
const _window = (window.wrappedJSObject as any);
|
||||
|
||||
_window.chrome = cloneInto({}, window);
|
||||
_window.navigator.presentation = cloneInto({}, window);
|
||||
|
||||
|
||||
/**
|
||||
* Replace the src property setter on <script> elements to
|
||||
* intercept the new value.
|
||||
*
|
||||
* If it matches one of Chrome's cast extension sender script
|
||||
* URLs, replace it with the standard API URL, the request for
|
||||
* which is handled in the main script.
|
||||
*/
|
||||
const { get, set } = Reflect.getOwnPropertyDescriptor(
|
||||
HTMLScriptElement.prototype.wrappedJSObject, "src");
|
||||
|
||||
Reflect.defineProperty(
|
||||
HTMLScriptElement.prototype.wrappedJSObject, "src", {
|
||||
|
||||
configurable: true
|
||||
, enumerable: true
|
||||
, get
|
||||
|
||||
, set: exportFunction(function (value) {
|
||||
if (CAST_SCRIPT_URLS.includes(value)) {
|
||||
return set.call(this, CAST_LOADER_SCRIPT_URL);
|
||||
}
|
||||
|
||||
return set.call(this, value);
|
||||
}, window)
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import * as cast from "./cast";
|
||||
|
||||
import { BridgeInfo } from "../lib/getBridgeInfo";
|
||||
import { Message } from "../types";
|
||||
import { onMessage } from "./messageBridge";
|
||||
import { onMessage } from "./eventMessageChannel";
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,7 +18,7 @@ export function init (): Promise<BridgeInfo> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
|
||||
// Trigger message port setup side-effects
|
||||
import("./content");
|
||||
import("./contentBridge");
|
||||
|
||||
onMessage(message => {
|
||||
switch (message.subject) {
|
||||
|
||||
@@ -28,7 +28,7 @@ import { ActiveInputState
|
||||
|
||||
import GoogleCastLauncher from "./GoogleCastLauncher";
|
||||
|
||||
import { onMessage } from "../messageBridge";
|
||||
import { onMessage } from "../eventMessageChannel";
|
||||
|
||||
|
||||
export default {
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as cast from "./cast";
|
||||
|
||||
import { CAST_FRAMEWORK_SCRIPT_URL } from "../endpoints";
|
||||
import { loadScript } from "../lib/utils";
|
||||
import { onMessage } from "./messageBridge";
|
||||
import { onMessage } from "./eventMessageChannel";
|
||||
|
||||
|
||||
const _window = (window as any);
|
||||
@@ -47,6 +47,12 @@ if (document.currentScript) {
|
||||
|
||||
isFramework = true;
|
||||
|
||||
/**
|
||||
* Framework API library requires webcomponents for the cast
|
||||
* button custom element (<google-cast-launcher>).
|
||||
*/
|
||||
loadScript(browser.runtime.getURL("vendor/webcomponents-lite.js"));
|
||||
|
||||
const script = loadScript(CAST_FRAMEWORK_SCRIPT_URL);
|
||||
script.addEventListener("load", ev => {
|
||||
callPageReadyFunction();
|
||||
|
||||
@@ -26,7 +26,7 @@ module.exports = (env) => ({
|
||||
// Shim entries
|
||||
, "shim/bundle": `${env.includePath}/shim/index.ts`
|
||||
, "shim/content": `${env.includePath}/shim/content.ts`
|
||||
, "shim/contentSetup": `${env.includePath}/shim/contentSetup.ts`
|
||||
, "shim/contentBridge": `${env.includePath}/shim/contentBridge.ts`
|
||||
}
|
||||
, output: {
|
||||
filename: "[name].js"
|
||||
|
||||
Reference in New Issue
Block a user