Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POC - adding messaging proxy #869

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft

POC - adding messaging proxy #869

wants to merge 3 commits into from

Conversation

shakyShane
Copy link
Contributor

@shakyShane shakyShane commented Dec 13, 2023

Main Related Task: https://app.asana.com/0/1203581873609357/1205887441046040/f
Tech Design: https://app.asana.com/0/0/1206158388554363/f

Description

Build upon our low-level messaging library by adding a proxy that can live inside isolated contexts and be used to marshal messages from page scripts to native platforms.

NOTE: This is just a POC to aid the conversation in the Tech Design - I used it to successfully implement Duck Player with the macOS handlers removed from the page world.

New Things

  • Added a new transport config that page-level scripts can import, ProxyMessagingConfig
  • Added a new CSS feature that acts as the proxy within isolated contexts

Examples

Duck Player, before + after
if (opts.injectName === 'windows') {
    const opts = new WindowsMessagingConfig({
        methods: {
            // @ts-expect-error - not in @types/chrome
            postMessage: window.chrome.webview.postMessage,
            // @ts-expect-error - not in @types/chrome
            addEventListener: window.chrome.webview.addEventListener,
            // @ts-expect-error - not in @types/chrome
            removeEventListener: window.chrome.webview.removeEventListener
        }
    })
    const messaging = new Messaging(messageContext, opts)
    return new DuckPlayerPageMessages(messaging)
} else if (opts.injectName === 'apple') {
    const opts = new WebkitMessagingConfig({
        hasModernWebkitAPI: true,
        secret: '',
        webkitMessageHandlerNames: ['specialPages']
    })
    const messaging = new Messaging(messageContext, opts)
    return new DuckPlayerPageMessages(messaging)
}

After:

const context = new MessagingContext({
    context: 'pageWorldMessageProxy',
    featureName: 'duckPlayerPage',
    env: opts.env
})
const config = new ProxyMessagingConfig()
const messaging = new Messaging(context, config)
return new DuckPlayerPageMessages(messaging)

message proxy (4)

message proxy (5)

@shakyShane
Copy link
Contributor Author

Current dependencies on/for this PR:

This stack of pull requests is managed by Graphite.

@shakyShane shakyShane changed the title Connect permissions API to native + update structure (#861) POC - adding messaging proxy Dec 13, 2023
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what web applications, or special pages would import

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is virtually identical to the Android implementation. I didn't want to cause confusion in the POC with any re-factors though. If we come to agreement on the direction we should probably transition the Android implementation to use this

Comment on lines +33 to +50
setupIncomingListener: (dispatch) => {
globalThis.addEventListener('message', (evt) => {
if (evt.origin !== globalThis.location.origin) {
console.warn('ignoring non-matching origin')
return
}
if (!evt.data || !('messageProxyResponse' in evt.data)) {
return
}
dispatch(evt.data.messageProxyResponse)
})
},
send: (msg) => {
globalThis.postMessage({ messageProxy: msg }, globalThis.location.origin)
},
setupSubscription: (msg) => {
globalThis.postMessage({ subscriptionProxy: msg }, globalThis.location.origin)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are the 3 side-effecting things that would occur in the same context as the application's JS (ie: none-isolated)

Comment on lines -114 to -160
if (opts.injectName === 'windows') {
const opts = new WindowsMessagingConfig({
methods: {
// @ts-expect-error - not in @types/chrome
postMessage: window.chrome.webview.postMessage,
// @ts-expect-error - not in @types/chrome
addEventListener: window.chrome.webview.addEventListener,
// @ts-expect-error - not in @types/chrome
removeEventListener: window.chrome.webview.removeEventListener
}
})
const messaging = new Messaging(messageContext, opts)
return new DuckPlayerPageMessages(messaging)
} else if (opts.injectName === 'apple') {
const opts = new WebkitMessagingConfig({
hasModernWebkitAPI: true,
secret: '',
webkitMessageHandlerNames: ['specialPages']
})
const messaging = new Messaging(messageContext, opts)
return new DuckPlayerPageMessages(messaging)
} else if (opts.injectName === 'integration') {
const config = new TestTransportConfig({
notify (msg) {
console.log(msg)
},
request: (msg) => {
console.log(msg)
if (msg.method === 'getUserValues') {
return Promise.resolve(new UserValues({
overlayInteracted: false,
privatePlayerMode: { alwaysAsk: {} }
}))
}
return Promise.resolve(null)
},
subscribe (msg) {
console.log(msg)
return () => {
console.log('teardown')
}
}
})
const messaging = new Messaging(messageContext, config)
return new DuckPlayerPageMessages(messaging)
}
throw new Error('unreachable - platform not supported')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥳 this is the kind of wins we get - since the proxy is sat inside C-S-S the platform-specific parts are already in place.

Comment on lines +12 to +23
init (args) {
const thisContext = this._createMessagingContext()

for (const { origin, feature } of args.messageProxies) {
if (!matchHostname(globalThis.location.hostname, origin)) {
continue
}
const nextMessagingContext = { ...thisContext, featureName: feature }
const messaging = new Messaging(nextMessagingContext, args.messagingConfig)
this.initFeatureProxy(feature, messaging, origin)
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this POC, I'm delivering hostnames + feature names via UserPreferences - the reason is that during development it would make it significantly easier than having to make changes to remote config. Very much open to suggestions here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant