Skip to content

Commit

Permalink
- require a target window only when posting a message (not when recei…
Browse files Browse the repository at this point in the history
…ving), because that will not cause issues on the Cadenza end and can be handled by the custom app (by unsubscribing all subscribers)

- add CadenzaClient#destroy for conveniently unsubscribing all subscribers

(cherry picked from commit 9233dd5)
  • Loading branch information
jkissel committed Jun 11, 2024
1 parent 0e8e585 commit 3c16209
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 9 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
### Added
- `CadenzaClient#destroy`

### Fixed
- To avoid errors with active subscriptions when the iframe is not visible, a target window (for postMessage communication) is now required only for _sending_ a message (and not for receiving messages).

## 2.6.0 - 2024-04-12
### Added
Expand Down
26 changes: 18 additions & 8 deletions src/cadenza.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ export class CadenzaClient {
return this.#iframeElement;
}

get #targetWindow() {
return /** @type {WindowProxy | null} */ this.iframe?.contentWindow;
}

get #requiredIframe() {
const iframe = /** @type {HTMLIFrameElement} */ (this.iframe);
assert(
Expand Down Expand Up @@ -683,10 +687,7 @@ export class CadenzaClient {
#onMessage = (
/** @type MessageEvent<CadenzaEvent<never, never>> */ event,
) => {
if (
event.origin !== this.#origin ||
event.source !== this.#requiredIframe.contentWindow
) {
if (event.origin !== this.#origin || event.source !== this.#targetWindow) {
return;
}

Expand All @@ -699,6 +700,16 @@ export class CadenzaClient {
});
};

/**
* Remove all subscriptions.
*
* @see {@link CadenzaClient#on}
*/
destroy() {
this.#subscriptions = [];
window.removeEventListener('message', this.#onMessage);
}

/**
* Posts an event to Cadenza and returns a `Promise` for the response.
*
Expand Down Expand Up @@ -735,10 +746,9 @@ export class CadenzaClient {
#postEvent(type, detail, transfer) {
const cadenzaEvent = { type, detail };
this.#log('postMessage', cadenzaEvent);
const contentWindow = /** @type {WindowProxy} */ (
this.#requiredIframe.contentWindow
);
contentWindow.postMessage(cadenzaEvent, {
const targetWindow = this.#targetWindow;
assert(targetWindow != null, 'Cannot find target window');
(/** @type {WindowProxy} */ (targetWindow)).postMessage(cadenzaEvent, {
targetOrigin: this.#origin,
transfer,
});
Expand Down
12 changes: 11 additions & 1 deletion src/cadenza.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('Given a Cadenza JS client instance', () => {

it('Throws when attempting to show an embedding target without an iframe', () =>
expect(() => cadenza(BASE_URL).show(EMBEDDING_TARGET_ID)).toThrow(
'present',
'Required iframe',
));

it('Throws when attempting to show an embedding target in an invisible iframe', () => {
Expand Down Expand Up @@ -219,6 +219,16 @@ describe('Given a Cadenza JS client instance', () => {
expect(subscriber).not.toHaveBeenCalled());
});

describe('When the event is sent again after the instance has been destroyed', () => {
beforeEach(() => {
cad.destroy();
sendEvent(EVENT.type, EVENT.detail);
});

it('Subscribers are not called anymore', () =>
expect(subscriber).not.toHaveBeenCalled());
});

describe('When the event is sent from another origin', () => {
beforeEach(() => sendEvent(EVENT.type, EVENT.detail, 'http://disy.net'));

Expand Down

0 comments on commit 3c16209

Please sign in to comment.