diff --git a/injected/entry-points/android.js b/injected/entry-points/android.js index f3f5e69f0d..7a4a8e3592 100644 --- a/injected/entry-points/android.js +++ b/injected/entry-points/android.js @@ -4,7 +4,7 @@ import { load, init } from '../src/content-scope-features.js' import { processConfig, isGloballyDisabled } from './../src/utils' import { isTrackerOrigin } from '../src/trackers' -import { AndroidMessagingConfig } from '../../messaging/index.js' +import { AndroidMessagingConfig } from '../../messaging/lib/android.js' function initCode () { // @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f diff --git a/injected/entry-points/apple.js b/injected/entry-points/apple.js index e95a23c127..8a317aabf8 100644 --- a/injected/entry-points/apple.js +++ b/injected/entry-points/apple.js @@ -4,7 +4,8 @@ import { load, init } from '../src/content-scope-features.js' import { processConfig, isGloballyDisabled } from './../src/utils' import { isTrackerOrigin } from '../src/trackers' -import { WebkitMessagingConfig, TestTransportConfig } from '../../messaging/index.js' +import { TestTransportConfig } from '../../messaging/index.js' +import { WebkitMessagingConfig } from '../../messaging/lib/webkit.js' function initCode () { // @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f diff --git a/injected/entry-points/windows.js b/injected/entry-points/windows.js index 5337cfde1b..6bab82c5eb 100644 --- a/injected/entry-points/windows.js +++ b/injected/entry-points/windows.js @@ -4,7 +4,7 @@ import { load, init } from '../src/content-scope-features.js' import { processConfig, isGloballyDisabled, windowsSpecificFeatures } from './../src/utils' import { isTrackerOrigin } from '../src/trackers' -import { WindowsMessagingConfig } from '../../messaging/index.js' +import { WindowsMessagingConfig } from '../../messaging/lib/windows.js' function initCode () { // @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f diff --git a/injected/src/content-feature.js b/injected/src/content-feature.js index 3460ff7c8a..3d570ea66b 100644 --- a/injected/src/content-feature.js +++ b/injected/src/content-feature.js @@ -3,7 +3,7 @@ import { immutableJSONPatch } from 'immutable-json-patch' import { PerformanceMonitor } from './performance.js' import { defineProperty, shimInterface, shimProperty, wrapMethod, wrapProperty, wrapToString } from './wrapper-utils.js' import { Proxy, Reflect } from './captured-globals.js' -import { Messaging, MessagingContext } from '../../messaging/index.js' +import { MessagingContext } from '../../messaging/index.js' import { extensionConstructMessagingConfig } from './sendmessage-transport.js' /** @@ -125,7 +125,7 @@ export default class ContentFeature { if (this.platform?.name !== 'extension') throw new Error('Only extension messaging supported, all others should be passed in') messagingConfig = extensionConstructMessagingConfig() } - this._messaging = new Messaging(messagingContext, messagingConfig) + this._messaging = messagingConfig.intoMessaging(messagingContext) return this._messaging } diff --git a/injected/src/features/click-to-load.js b/injected/src/features/click-to-load.js index 6562bc6235..b5c6c3e7e0 100644 --- a/injected/src/features/click-to-load.js +++ b/injected/src/features/click-to-load.js @@ -1,4 +1,3 @@ -import { Messaging, TestTransportConfig, WebkitMessagingConfig } from '../../../messaging/index.js' import { createCustomEvent, originalWindowDispatchEvent } from '../utils.js' import { logoImg, loadingImages, closeIcon, facebookLogo } from './click-to-load/ctl-assets.js' import { getStyles, getConfig } from './click-to-load/ctl-config.js' @@ -7,6 +6,7 @@ import ContentFeature from '../content-feature.js' import { DDGCtlPlaceholderBlockedElement } from './click-to-load/components/ctl-placeholder-blocked.js' import { DDGCtlLoginButton } from './click-to-load/components/ctl-login-button.js' import { registerCustomElements } from './click-to-load/components' +import { TestTransportConfig } from '@duckduckgo/messaging' /** * @typedef {'darkMode' | 'lightMode' | 'loginMode' | 'cancelMode'} displayMode @@ -2019,16 +2019,10 @@ export default class ClickToLoad extends ContentFeature { if (this.platform.name === 'android' || this.platform.name === 'extension') { this._clickToLoadMessagingTransport = new SendMessageMessagingTransport() const config = new TestTransportConfig(this._clickToLoadMessagingTransport) - this._messaging = new Messaging(this.messagingContext, config) + this._messaging = config.intoMessaging(this.messagingContext) return this._messaging } else if (this.platform.name === 'ios' || this.platform.name === 'macos') { - const config = new WebkitMessagingConfig({ - secret: '', - hasModernWebkitAPI: true, - webkitMessageHandlerNames: ['contentScopeScriptsIsolated'] - }) - this._messaging = new Messaging(this.messagingContext, config) - return this._messaging + return this.messaging } else { throw new Error('Messaging not supported yet on platform: ' + this.name) } diff --git a/injected/unit-test/messaging.js b/injected/unit-test/messaging.js index 1ca9b266a0..a20cdb3adc 100644 --- a/injected/unit-test/messaging.js +++ b/injected/unit-test/messaging.js @@ -1,5 +1,4 @@ import { - Messaging, MessagingContext, TestTransportConfig, RequestMessage, NotificationMessage, Subscription, MessageResponse, SubscriptionEvent @@ -82,7 +81,7 @@ describe('Android', () => { featureName, env: 'development' }) - const messaging = new Messaging(messageContextA, config) + const messaging = config.intoMessaging(messageContextA) return { messaging } } it('sends notification to 1 feature', () => { @@ -232,7 +231,7 @@ function createMessaging () { env: 'development' }) - const messaging = new Messaging(messagingContext, testTransportConfig) + const messaging = testTransportConfig.intoMessaging(messagingContext) return { transport, messaging } } diff --git a/messaging/index.js b/messaging/index.js index 356085f03c..e085f9bc25 100644 --- a/messaging/index.js +++ b/messaging/index.js @@ -19,11 +19,16 @@ * - Implementation Guide: {@link "Messaging Implementation Guide"} * * @module Messaging + * + * @import { WindowsMessagingConfig, } from './lib/windows.js' + * @import { WebkitMessagingConfig, WebkitMessagingTransport } from './lib/webkit.js'; + * @import { AndroidMessagingConfig } from './lib/android.js'; + * @typedef {WebkitMessagingConfig | WindowsMessagingConfig | AndroidMessagingConfig | TestTransportConfig} MessagingConfig + * @typedef {{intoMessaging: (ctx: MessagingContext) => Messaging}} IntoMessaging + * */ -import { WindowsMessagingConfig, WindowsMessagingTransport, WindowsInteropMethods, WindowsNotification, WindowsRequestMessage } from './lib/windows.js' -import { WebkitMessagingConfig, WebkitMessagingTransport } from './lib/webkit.js' + import { NotificationMessage, RequestMessage, Subscription, MessageResponse, MessageError, SubscriptionEvent } from './schema.js' -import { AndroidMessagingConfig, AndroidMessagingTransport } from './lib/android.js' import { createTypedMessages } from './lib/typed-messages.js' /** @@ -45,20 +50,22 @@ export class MessagingContext { } /** - * @typedef {WebkitMessagingConfig | WindowsMessagingConfig | AndroidMessagingConfig | TestTransportConfig} MessagingConfig - */ - -/** - * + * The consumer interface */ export class Messaging { /** * @param {MessagingContext} messagingContext - * @param {MessagingConfig} config + * @param {MessagingTransport} transport */ - constructor (messagingContext, config) { + constructor (messagingContext, transport) { + /** + * @internal + */ this.messagingContext = messagingContext - this.transport = getTransport(config, this.messagingContext) + /** + * @internal + */ + this.transport = transport } /** @@ -164,6 +171,7 @@ export class MessagingTransport { * It's useful for debugging, and for enabling scripts to run in * other environments - for example, testing in a browser without the need * for a full integration + * @implements IntoMessaging */ export class TestTransportConfig { /** @@ -172,6 +180,13 @@ export class TestTransportConfig { constructor (impl) { this.impl = impl } + + /** + * @param {MessagingContext} context + */ + intoMessaging (context) { + return new Messaging(context, new TestTransport(this, context)) + } } /** @@ -200,27 +215,6 @@ export class TestTransport { } } -/** - * @param {WebkitMessagingConfig | WindowsMessagingConfig | AndroidMessagingConfig | TestTransportConfig} config - * @param {MessagingContext} messagingContext - * @returns {MessagingTransport} - */ -function getTransport (config, messagingContext) { - if (config instanceof WebkitMessagingConfig) { - return new WebkitMessagingTransport(config, messagingContext) - } - if (config instanceof WindowsMessagingConfig) { - return new WindowsMessagingTransport(config, messagingContext) - } - if (config instanceof AndroidMessagingConfig) { - return new AndroidMessagingTransport(config, messagingContext) - } - if (config instanceof TestTransportConfig) { - return new TestTransport(config, messagingContext) - } - throw new Error('unreachable') -} - /** * Thrown when a handler cannot be found */ @@ -239,20 +233,11 @@ export class MissingHandler extends Error { * Some re-exports for convenience */ export { - WebkitMessagingConfig, - WebkitMessagingTransport, - WindowsMessagingConfig, - WindowsMessagingTransport, - WindowsInteropMethods, NotificationMessage, RequestMessage, Subscription, MessageResponse, MessageError, SubscriptionEvent, - WindowsNotification, - WindowsRequestMessage, - AndroidMessagingConfig, - AndroidMessagingTransport, createTypedMessages } diff --git a/messaging/lib/android.js b/messaging/lib/android.js index f63ff29c52..10b8648bc3 100644 --- a/messaging/lib/android.js +++ b/messaging/lib/android.js @@ -6,13 +6,16 @@ * */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { MessagingTransport, MessageResponse, SubscriptionEvent } from '../index.js' +import { Messaging, MessagingTransport } from '../index.js' import { isResponseFor, isSubscriptionEventFor } from '../schema.js' /** * @typedef {import('../index.js').Subscription} Subscription * @typedef {import('../index.js').MessagingContext} MessagingContext * @typedef {import('../index.js').RequestMessage} RequestMessage + * @typedef {import('../index.js').SubscriptionEvent} SubscriptionEvent + * @typedef {import('../index.js').MessageResponse} MessageResponse + * @typedef {import('../index.js').IntoMessaging} IntoMessaging * @typedef {import('../index.js').NotificationMessage} NotificationMessage */ @@ -171,6 +174,7 @@ export class AndroidMessagingTransport { * } * } * ``` + * @implements IntoMessaging */ export class AndroidMessagingConfig { /** @type {(json: string, secret: string) => void} */ @@ -210,6 +214,14 @@ export class AndroidMessagingConfig { this._assignHandlerMethod() } + /** + * @param {MessagingContext} context + * @return {Messaging} + */ + intoMessaging (context) { + return new Messaging(context, new AndroidMessagingTransport(this, context)) + } + /** * The transport can call this to transmit a JSON payload along with a secret * to the native Android handler. diff --git a/messaging/lib/examples/android.example.js b/messaging/lib/examples/android.example.js index 73a47c80ed..50f7a9983c 100644 --- a/messaging/lib/examples/android.example.js +++ b/messaging/lib/examples/android.example.js @@ -1,4 +1,4 @@ -import { Messaging, MessagingContext } from '../../index.js' +import { MessagingContext } from '../../index.js' import { AndroidMessagingConfig } from '../android.js' /** @@ -30,7 +30,7 @@ const messagingContext = new MessagingContext({ /** * And then send notifications! */ -const messaging = new Messaging(messagingContext, config) +const messaging = config.intoMessaging(messagingContext) messaging.notify('helloWorld') /** @@ -63,8 +63,8 @@ const messagingContext2 = { ...messagingContext1, featureName: 'duckPlayer' } /** * Now, each feature has its own isolated messaging... */ -const messaging1 = new Messaging(messagingContext, config) +const messaging1 = config.intoMessaging(messagingContext1) messaging1.notify('helloWorld') -const messaging2 = new Messaging(messagingContext2, config) +const messaging2 = config.intoMessaging(messagingContext2) messaging2.notify('getUserValues') diff --git a/messaging/lib/examples/test.example.js b/messaging/lib/examples/test.example.js index 7f2f74f982..f1e938d738 100644 --- a/messaging/lib/examples/test.example.js +++ b/messaging/lib/examples/test.example.js @@ -1,4 +1,4 @@ -import { Messaging, MessagingContext, TestTransportConfig } from '../../index.js' +import { MessagingContext, TestTransportConfig } from '../../index.js' /** * Creates an ad-hoc messaging transport on the fly - useful for testing @@ -36,7 +36,7 @@ const messagingContext = new MessagingContext({ /** * And then send notifications! */ -const messaging = new Messaging(messagingContext, config) +const messaging = config.intoMessaging(messagingContext) messaging.notify('helloWorld') /** diff --git a/messaging/lib/examples/webkit.example.js b/messaging/lib/examples/webkit.example.js index f78e3119c6..7f1269bed3 100644 --- a/messaging/lib/examples/webkit.example.js +++ b/messaging/lib/examples/webkit.example.js @@ -1,4 +1,5 @@ -import { Messaging, MessagingContext, WebkitMessagingConfig } from '../../index.js' +import { MessagingContext } from '../../index.js' +import { WebkitMessagingConfig } from '../webkit.js' /** * Configuration for WebkitMessaging @@ -21,7 +22,7 @@ const messagingContext = new MessagingContext({ /** * With config + context, now create an instance: */ -const messaging = new Messaging(messagingContext, config) +const messaging = config.intoMessaging(messagingContext) /** * send notifications (fire and forget) diff --git a/messaging/lib/examples/windows.example.js b/messaging/lib/examples/windows.example.js index b3c22e9f76..4a07e10f96 100644 --- a/messaging/lib/examples/windows.example.js +++ b/messaging/lib/examples/windows.example.js @@ -1,5 +1,5 @@ import { WindowsMessagingConfig } from '../windows.js' -import { Messaging, MessagingContext } from '../../index.js' +import { MessagingContext } from '../../index.js' /** * These 3 required methods that get assigned by the Native side. @@ -32,7 +32,7 @@ const messagingContext = new MessagingContext({ /** * And then send notifications! */ -const messaging = new Messaging(messagingContext, config) +const messaging = config.intoMessaging(messagingContext) messaging.notify('helloWorld') /** diff --git a/messaging/lib/webkit.js b/messaging/lib/webkit.js index 73573b0dda..66d7a4a113 100644 --- a/messaging/lib/webkit.js +++ b/messaging/lib/webkit.js @@ -7,7 +7,7 @@ * part of the message handling, see {@link WebkitMessagingTransport} for details. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { MessagingTransport, MissingHandler } from '../index.js' +import { MessagingTransport, MissingHandler, Messaging } from '../index.js' import { isResponseFor, isSubscriptionEventFor } from '../schema.js' /** @@ -317,6 +317,9 @@ export class WebkitMessagingTransport { * Please see {@link WebkitMessagingTransport} for details on how messages are sent/received * * [Example](./examples/webkit.example.js) + * + * @import { IntoMessaging, MessagingContext } from "../index.js" + * @implements IntoMessaging */ export class WebkitMessagingConfig { /** @@ -354,6 +357,14 @@ export class WebkitMessagingConfig { */ this.secret = params.secret } + + /** + * @param {MessagingContext} context + * @return {Messaging} + */ + intoMessaging (context) { + return new Messaging(context, new WebkitMessagingTransport(this, context)) + } } /** diff --git a/messaging/lib/windows.js b/messaging/lib/windows.js index 0af6fb5252..bea49ffc80 100644 --- a/messaging/lib/windows.js +++ b/messaging/lib/windows.js @@ -7,7 +7,7 @@ * */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { MessagingTransport, NotificationMessage, RequestMessage } from '../index.js' +import { MessagingTransport, NotificationMessage, RequestMessage, Messaging } from '../index.js' /** * An implementation of {@link MessagingTransport} for Windows @@ -204,6 +204,8 @@ export class WindowsMessagingTransport { * * [Example](./examples/windows.example.js) * + * @import { IntoMessaging, MessagingContext } from "../index.js" + * @implements IntoMessaging */ export class WindowsMessagingConfig { /** @@ -221,6 +223,14 @@ export class WindowsMessagingConfig { */ this.platform = 'windows' } + + /** + * @param {MessagingContext} context + * @return {Messaging} + */ + intoMessaging (context) { + return new Messaging(context, new WindowsMessagingTransport(this, context)) + } } /** diff --git a/special-pages/shared/create-special-page-messaging.js b/special-pages/shared/create-special-page-messaging.js index 7ad91a6957..8a9dceb6cb 100644 --- a/special-pages/shared/create-special-page-messaging.js +++ b/special-pages/shared/create-special-page-messaging.js @@ -1,12 +1,14 @@ import { - AndroidMessagingConfig, Messaging, MessagingContext, - TestTransportConfig, - WebkitMessagingConfig, - WindowsMessagingConfig + TestTransport, + TestTransportConfig } from '@duckduckgo/messaging' +import { WindowsMessagingConfig } from '@duckduckgo/messaging/lib/windows.js' +import { WebkitMessagingConfig } from '@duckduckgo/messaging/lib/webkit.js' +import { AndroidMessagingConfig } from '@duckduckgo/messaging/lib/android.js' + /** * @param {object} opts * @param {ImportMeta['env']} opts.env @@ -33,14 +35,14 @@ export function createSpecialPageMessaging (opts) { removeEventListener: window.chrome.webview.removeEventListener } }) - return new Messaging(messageContext, opts) + return opts.intoMessaging(messageContext) } else if (opts.injectName === 'apple') { const opts = new WebkitMessagingConfig({ hasModernWebkitAPI: true, secret: '', webkitMessageHandlerNames: ['specialPages'] }) - return new Messaging(messageContext, opts) + return opts.intoMessaging(messageContext) } else if (opts.injectName === 'android') { const opts = new AndroidMessagingConfig({ messageSecret: 'duckduckgo-android-messaging-secret', @@ -49,7 +51,7 @@ export function createSpecialPageMessaging (opts) { target: globalThis, debug: true }) - return new Messaging(messageContext, opts) + return opts.intoMessaging(messageContext) } } catch (e) { console.error('could not access handlers for %s, falling back to mock interface', opts.injectName) @@ -88,5 +90,5 @@ export function createSpecialPageMessaging (opts) { } }) - return new Messaging(messageContext, fallback) + return new Messaging(messageContext, new TestTransport(fallback, messageContext)) }