Skip to content

Experimental cross window messaging library based on JavaScript Proxies.

License

Notifications You must be signed in to change notification settings

Katona/really-simple-xdm

Repository files navigation

really-simple-xdm

CircleCI

Experimental JavaScript Cross Domain Messaging library based on JavaScript proxies.

The goal of this library is to simplify cross domain messaging in browsers, that is, making the communication with a Javascript object in a iframe (almost) as simple as it were a local one.

Quick Start

Let's assume that we would like to call Math.abs(-2), if Math were a local object, then the call would look like this:

console.log(Math.abs(-2)); // Prints '2'

If Math is in an iframe, calling it with really-simple-xdm is demonstrated below.

Initialization in the iframe

The first step is to expose the service object (Math in our case) by creating a server:

import { createServer } from 'really-simple-xdm';

const server = createServer({ serviceObject: Math, targetOrigin: "*" });
server.serve();

createServer accepts a config object which contains the service object to be exposed (serviceObject) and the origin of the target frame (targetOrigin). Messages will be sent to and accepted only from the target origin (see the documentation of postMessage for more information).

Initialization in the host page

In the host page a client has to be made which connects to the server created above.

import { createClient } from 'really-simple-xdm';

const iframeElement = document.getElementById('testFrame'); // the id of the frame containing the `Math` object to be called
const mathProxyPromise = createClient({ targetWindow: iframeElement.contentWindow, targetOrigin: "*" }); // 'mathProxyPromise' is a promise which resolves with the proxy of 'Math'

createClient expects a config object which contains the window containing the server object (targetWindow) and the origin of the target (targetOrigin).

Using the object

Now, with everything is setup, the actual call would be the following:

mathProxyPromise.then(mathProxy => {
    mathProxy.abs(-2).then(result => console.log(result)); // Prints '2'
});

If we were to use async functions, then the client code would be as simple as:

const mathProxy = await createClient({ targetWindow: iframeElement.contentWindow, targetOrigin: "*" });
const result = await mathProxy.abs(-2);
console.log(result);

The createClient function returns a promise which is resolved with a proxy object when the connection is estabilished with the server object in the embedded frame. All the methods of the service object (Math in the example) can be called on the proxy almost the same as if it was the service object itself. The only difference is the calls return a Promise in every case. If the call is successful, then the promise is resolved with the return value if any, if the call fails then the promise is rejected.

Callback support

Functions can also be passed as arguments and when called in the frame, the call will be dispatched to the host page.

Event listeners

Event listeners here are treated as special callbacks: they can be registered and deregistered. For this to work, the server needs some auxiliary information to know the function pairs used to register and deregister the event listener.

import { createServer } from 'really-simple-xdm';

const config = {
    serviceObject: Math,
    targetOrigin: "*",
    events: [ { register: 'on', deregister: 'off' } ]
}
const server = createServer(config);
server.serve();

The events configuration property is an array which describe events provided by the proxied object. It's elements, the EventMetadatas, specify the functions used to register and deregister event listeners for the particular event. This information will be used for book keeping the event listener registrations.

After the server is configured properly, event listeners can be registered on the client:

import { createClient } from 'really-simple-xdm';

const config = {
    targetWindow: document.getElementById('testFrame').contentWindow,
    targetOrigin: "*"
}
const client = await createClient(config);
const clickListener = e => {
    console.log(e);
};
await client.on('click', clickListener); // registering the listener
await client.off('click', clickListener); // deregistration

Note that the register/deregister methods (on and off respectively) return a promise which resolves when the registration/deregistration is completed.

The only difference between normal callbacks and event listeners is callback registrations are not tracked so they can not be deregistered.

Exposing multiple service objects from one iframe

It is possible to expose multiple service objects from one iframe by giving them a name using the name configuration property:

import { createServer } from 'really-simple-xdm';

const mathServer = createServer({ serviceObject: Math, targetOrigin: "*", name: "Math" });
mathServer.serve();

const numberServer = createServer({ serviceObject: Number, targetOrigin: "*", name: "Number" });
numberServer.serve();

The names specified during server creation has to be passed to the clients via the serverName configuration property:

import { createClient } from 'really-simple-xdm';

const targetWindow = document.getElementById('testFrame').contentWindow;
const targetOrigin = "*";

const mathProxy = await createClient({ targetWindow, targetOrigin, serverName: "Math" });
const abs = await mathProxy.abs(-1);
console.log(abs);

const integerProxy = await createClient({ targetWindow, targetOrigin, serverName: "Number" });
const isInteger = await integerProxy.isInteger(1);
console.log(isInteger);

API documentation

See TypeScript type definitions for the API documentation.

About

Experimental cross window messaging library based on JavaScript Proxies.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published