Platform Developer Guide

If you are a Platform owner who wants to include OpenFin's web capabilities in your platform, there are a few steps required. This section will guide you through the process of setting up an environment. Please note, none of these steps are necessary for content developers (See the content developer guide).

Ensure that @openfin/core-web is installed by running the following command:

npm i @openfin/core-web -S

An @openfin/core-web/shared-worker entry point is included in this package's distribution. This is a non-customizable, standalone piece of javascript that must be hosted on your server for @openfin/core-web to function.

This file has already been bundled, which means you just need to host it on a web server on a known URL.

An HTML page, loaded as a hidden iframe by clients, must be hosted in the same origin as the @openfin/shared-worker. This iframe acts as a gatekeeper to the shared-worker and therefore must be hosted on the same domain.

In order to build a Web Broker, the following requirements must be met:

  1. You must host the @openfin/core-web/shared-worker bundle on a domain (for example https://www.example.com/mysharedworker.js).

  2. You must host a web broker page on that same domain (for example, https://www.example.com/web-broker).

  3. That page must call init from @openfin/core-web/iframe-broker with the URL of the shared worker hosted in step 1.

    // Runs on https://www.example.com/web-broker
    import { init } from '@openfin/core-web/iframe-broker';

    const sharedWorkerUrl = 'https://www.example.com/mysharedworker.js';

    await init({ sharedWorkerUrl });

Here is a basic example of hosting a Web Broker:

First, host @openfin/core-web/shared-worker at /openfin-shared-worker.js

File: iframe-broker.html

<html>
<body>
<script src="iframe-broker.js"></script>
</body>
</html>

File: iframe-broker.js

import { init } from '@openfin/core-web/iframe-broker';

init({
sharedWorkerUrl: `${location.origin}/openfin-shared-worker.js`
});

As an owner of an Iframe Broker, you should first rely on existing web security tools such as the frame-ancestors CSP rule to prevent content from connecting to you which you don't expect.

@openfin/core-web exposes a rejectConnections utility if you wish to implement custom rejection logic. If neither init or rejectConnection is invoked, an embedding client may hang indefinitely.

import { init, rejectConnections } from '@openfin/core-web/iframe-broker';

// If the origins do not match.
if (new URL(document.referrer).origin !== location.origin) {
rejectConnections({
reason: 'Connections from this domain are not supported' // Reason allows an error to be returned to the connecting client.
});
} else {
init({
sharedWorkerUrl: `${location.origin}/openfin-shared-worker.js`
});
}

By default, @openfin/core-web disables the sharing of connections across browser Tabs. However, this feature may be enabled by specifying the following flag in an IFrame Broker:

init({
sharedWorkerUrl: `${location.origin}/openfin-shared-worker.js`,
experimental: {
crossTab: 'same-site'
}
});

@openfin/core-web features configurable logging using the logLevel connection option. It accepts the following values: 'debug', 'info', 'warn', 'error', 'none'. By default it is set to 'error'. Change the level to see more information in the console:

init({
sharedWorkerUrl: `${location.origin}/openfin-shared-worker.js`,
logLevel: 'debug'
});

const fin = await connect({
connectionInheritance: 'enabled',
options: { brokerUrl },
platform: { layoutSnapshot },
logLevel: 'debug'
});

@openfin/core-web enables capturing logs via an optional logger connection option. It accepts an implementation of CustomLogger, which allows you to forward logs to your own logging infrastructure or external services.

The CustomLogger interface requires four methods: debug, info, warn, and error, each accepting a string message.

import { connect, type CustomLogger } from '@openfin/core-web';

const myLogger: CustomLogger = {
debug: (message: string) => {...},
info: (message: string) => {...},
warn: (message: string) => {...},
error: (message: string) => {...}
};

const fin = await connect({
...,
logger: myLogger,
logLevel: 'debug'
});

Note: The logger option respects the logLevel setting. Only messages at or above the specified level will get propagated.

Similar functionality exists for setting up a web broker:

import { init } from '@openfin/core-web/iframe-broker';
import { type CustomLogger } from '@openfin/core-web';

init({
...,
logger: myLogger,
logLevel: 'debug'
});

Layouts in the web are very similar to the desktop OpenFin environment. They consist of a layout configuration that describes the rows/columns/view components. The major difference is that the layout key is not supported. Instead, we expose the layoutSnapshot key to plug into the multiple-layout architecture. See the Multi-Layout guide for more information on how to set it up.

Note that you can achieve a single layout by having only 1 key in your layoutSnapshot:

const layoutSnapshot = {
layouts: {
layout: { ... }
}
}

The JSON structure of the underlying layout options is interchangeable, though not identical, between Desktop and Web. Web Layout Snapshots have an optional web property within the view componentState options which stores web specific properties. This web property is ignored on desktop.

Layouts in the web support the following LayoutOptions.settings:

  • reorderEnabled (Default value true): If true, users can re-arrange the layout by dragging items by their tabs to the desired location. Note, unlike in an OpenFin environment the dragging of items is limited to the current window.
  • hasHeaders (Default value true): Turns tab headers on or off. If false, the layout will be displayed with splitters only.
  • preventSplitterResize (Default value false): When true the splitters will not be draggable and the layout will not resize.
  • showMaximiseIcon (Default value false): Enables maximise button for stacks. The button maximises the current tab to fill the entire layout container.

To enable web API usage in iframes view componentState includes a permissions field which maps to allow attributes in view iframes to enable web APIs. Currently the only supported values are 'clipboard-read' and 'clipboard-sanitized-write'.

const layoutSnapshot: WebLayoutSnapshot = {
layouts: {
tab1: {
content: [
{
type: 'row',
content: [
{
type: 'stack',
content: [
{
title: 'Example',
type: 'component',
componentName: 'view',
componentState: {
url: 'http://example.com/',
permissions: {
webAPIs: ['clipboard-read', 'clipboard-sanitized-write']
}
}
}
]
}
]
}
]
}
}
};

From within your OpenFin v34+ Desktop environment, retrieve the current layoutSnapshot:

const layoutManager = fin.Platform.Layout.getCurrentLayoutManagerSync();
// returns an object like: { layouts: { ... } } where each key in layouts.<key> is it's own separate layout configuration
const layoutSnapshot = await layoutManager.getLayoutSnapshot();

Note that the fin.Platform.Layout API is exactly the same in web, so you can retrieve the Web Layout Snapshot using the same code.

In order to use layouts, specify the platform option to the connect call and pass in the layoutSnapshot.

import { connect, type WebLayoutSnapshot } from '@openfin/core-web';

const brokerUrl = 'http://example.com/web-broker';

// Presumably retrieved from fin.Platform.Layout.getCurrentLayoutManagerSync().getLayoutSnapshot()
// in a v34+ Desktop environment, but also can be manually constructed like this:
const layoutSnapshot: WebLayoutSnapshot = {
layouts: {
tab1: {
content: [
{
type: 'row',
content: [
{
type: 'stack',
content: [
{
title: 'Example',
type: 'component',
componentName: 'view',
componentState: {
url: 'http://example.com/'
}
}
]
},
{
type: 'stack',
content: [
{
title: 'Example',
type: 'component',
componentName: 'view',
componentState: {
url: 'http://example.com/'
}
}
]
}
]
}
]
},
tab2: {
content: [
{
type: 'stack',
content: [
{
title: 'Example',
type: 'component',
componentName: 'view',
componentState: {
url: 'http://example.com/'
}
}
]
}
]
}
}
};

(async () => {
// Connect to the OpenFin Web Broker. Pass in the `platform` key with a layoutSnapshot.
const fin = await connect({
connectionInheritance: 'enabled',
options: { brokerUrl },
platform: { layoutSnapshot }
});

const container = document.getElementById('layout-container');

// You may now use the `fin` object. In this case, we want to initialize and create layouts.
await fin.Platform.Layout.init({ container });

// For multi-layout information see https://developers.openfin.co/of-docs/docs/multi-layouts
// In the multi-layout case we would also call `fin.Platform.Layout.create()`.
})();

Note that cross-tab support is experimental, browser-dependent and respects each browser's privacy sandbox.

(Please note, these links work in your IDE and not on npmjs.com, we will update this soon.)