Socket
Socket
Sign inDemoInstall

xfc

Package Overview
Dependencies
7
Maintainers
3
Versions
25
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.11.0 to 1.12.0

6

CHANGELOG.md
Next Release
-------------
1.12.0
------
* Add support for different styles during iframe focus/blur events.
1.11.0
------
* Create a custom dispatch to override parent.postMessage
* Create a custom dispatch to override parent.postMessage

@@ -7,0 +11,0 @@ 1.10.1

@@ -82,3 +82,4 @@ "use strict";

_ref$customMethods = _ref.customMethods,
customMethods = _ref$customMethods === void 0 ? {} : _ref$customMethods;
customMethods = _ref$customMethods === void 0 ? {} : _ref$customMethods,
focusIndicator = _ref.focusIndicator;

@@ -93,2 +94,3 @@ this.source = source;

this.resizeConfig = resizeConfig;
this.focusIndicator = focusIndicator || null;
var self = this;

@@ -133,2 +135,17 @@ this.JSONRPC = new _jsonrpcDispatch.default(self.send, _objectSpread({

},
setFocus: function setFocus() {
if (self.focusIndicator && self.focusIndicator.classNameFocusStyle) {
self.iframe.classList.add(self.focusIndicator.classNameFocusStyle);
}
return Promise.resolve();
},
setBlur: function setBlur() {
// Removing the focus style className
self.iframe.classList.remove(self.focusIndicator.classNameFocusStyle);
return Promise.resolve();
},
isScrollingEnabled: function isScrollingEnabled() {
return Promise.resolve(self.iframe.getAttribute('scrolling') !== 'no');
},
event: function event(_event, detail) {

@@ -135,0 +152,0 @@ self.emit(_event, detail);

@@ -12,2 +12,4 @@ "use strict";

exports.getOffsetHeightToBody = getOffsetHeightToBody;
exports.isContentScrollable = isContentScrollable;
exports.hasInteractableElement = hasInteractableElement;

@@ -148,2 +150,27 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));

return !node ? 0 : getOffsetToBody(node) + node.scrollHeight;
}
/**
* This function returns boolean value representing whether scroll width or scroll height
* is larger than the client width or client height. It indicates that the content is larger
* than the viewable area and can be scrolled.
*
* @returns boolean true - when the content is scrollable, false - when the content is not scrollable
*/
function isContentScrollable() {
return document.documentElement.scrollHeight > document.documentElement.clientHeight || document.body.scrollHeight > document.body.clientHeight || document.documentElement.scrollWidth > document.documentElement.clientWidth || document.body.scrollWidth > document.body.clientWidth;
}
/**
* Determines if the document has interactable element in it.
*
* @returns boolean true - when there is interactable element in the document, false otherwise
*/
function hasInteractableElement() {
var interactableElementSelector = 'a[href]:not([tabindex=\'-1\']), area[href]:not([tabindex=\'-1\']), input:not([disabled]):not([tabindex=\'-1\']), ' + "select:not([disabled]):not([tabindex='-1']), textarea:not([disabled]):not([tabindex='-1']), button:not([disabled]):not([tabindex='-1']), " + "[contentEditable=true]:not([tabindex='-1'])";
return (0, _toConsumableArray2.default)(document.body.querySelectorAll("".concat(interactableElementSelector))).some(function (element) {
return !element.hasAttribute('disabled') && !element.getAttribute('aria-hidden') && !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length) && window.getComputedStyle(element).visibility !== 'hidden' && element.closest('[inert]') === null;
});
}

@@ -107,2 +107,11 @@ "use strict";

this.unload = this.unload.bind(this);
this.handleLoadEvent = this.handleLoadEvent.bind(this);
this.handleResizeEvent = this.handleResizeEvent.bind(this);
this.handleFocusEvent = this.handleFocusEvent.bind(this);
this.handleBlurEvent = this.handleBlurEvent.bind(this);
this.isIframeScrollable = this.isIframeScrollable.bind(this);
this.setTabIndexWhenRequired = this.setTabIndexWhenRequired.bind(this);
this.hasInteractableElement = true;
this.isScrollingEnabled = false;
this.originalTabIndexValue = null;

@@ -157,2 +166,6 @@ this.dispatchFunction = dispatchFunction || function (message, targetOrigin) {

window.addEventListener('beforeunload', this.unload);
window.addEventListener('load', this.handleLoadEvent, true);
window.addEventListener('resize', this.handleResizeEvent, true);
window.addEventListener('focus', this.handleFocusEvent, true);
window.addEventListener('blur', this.handleBlurEvent, true);
}

@@ -422,2 +435,88 @@ /**

}
/**
* When page load is completed, get the
* iframe's scrolling config, and set the tabIndex
* on the document.body of the embedded page if
* necessary.
*/
}, {
key: "handleLoadEvent",
value: function handleLoadEvent() {
this.JSONRPC.request('isScrollingEnabled', []).then(this.setTabIndexWhenRequired);
}
/**
* Handle the resize event and check if tabIndex is needed to be set or removed.
* Depending on the content is scrollable or not, we need to update the tabIndex
* accordingly so focus isn't getting in the document when not needed.
*/
}, {
key: "handleResizeEvent",
value: function handleResizeEvent() {
if (this.isIframeScrollable() && (0, _dimension.isContentScrollable)() && !this.hasInteractableElement) {
// Set tabIndex="0" so focus can go into the document
document.body.tabIndex = 0;
} else if (this.originalTabIndexValue === null) {
document.body.removeAttribute('tabIndex');
} else {
document.body.tabIndex = this.originalTabIndexValue;
}
}
/**
* Handle the focus event by sending a message to the frame.
*/
}, {
key: "handleFocusEvent",
value: function handleFocusEvent() {
// Send message to the consumer/frame.js to handle `setFocus` event
if (this.hasInteractableElement) {
return;
}
this.JSONRPC.notification('setFocus');
}
/**
* Handle the blur event by sending a message to the frame.
*/
}, {
key: "handleBlurEvent",
value: function handleBlurEvent() {
// Send message to the consumer/frame.js to handle `setBlur` event
this.JSONRPC.notification('setBlur');
}
/**
* Return the value of iframe's scroll config stored in `isScrollingEnabled`.
*
* @returns boolean true - when the iframe is scrollable, false - when the iframe is not scrollable
*/
}, {
key: "isIframeScrollable",
value: function isIframeScrollable() {
return this.isScrollingEnabled;
}
/**
* Sets the `tabIndex=0` on the `document.body` if required
* so focus can get into the embedded document.
*
* @param {*} iframeScrollingEnabled - boolean true when iframe's scrolling is true,
* false when iframe's scrolling is false
*/
}, {
key: "setTabIndexWhenRequired",
value: function setTabIndexWhenRequired(iframeScrollingEnabled) {
this.isScrollingEnabled = iframeScrollingEnabled;
this.hasInteractableElement = (0, _dimension.hasInteractableElement)();
this.originalTabIndexValue = document.body.getAttribute('tabIndex');
if (iframeScrollingEnabled && (0, _dimension.isContentScrollable)() && !this.hasInteractableElement) {
// Set tabIndex="0" so focus can go into the document when
// using tab key when scrolling is enabled
document.body.tabIndex = 0;
}
}
}]);

@@ -424,0 +523,0 @@ return Application;

2

package.json
{
"name": "xfc",
"version": "1.11.0",
"version": "1.12.0",
"description": "A Cross Frame Container that handles securely embedding web content into a 3rd party domain",

@@ -5,0 +5,0 @@ "author": "Cerner Corporation",

@@ -92,3 +92,31 @@ # XFC (Cross-Frame-Container)

### Applying Visual Focus Indicator Style on Iframes
Certain configuration within this library such as `fixedWidth` or `fixedHeight` may prevent the content to be fully displayed within the viewport, and thereby needing `scrolling` to be enabled. For keyboard only users, it is important to display a visual focus indicator to indicate where the focus currently is at to help guide them with page navigation.
Example CSS and JS Code:
```css
.iframe-focus-style {
outline: 2px dashed #000;
outline-offset: 1px;
}
```
```js
XFC.Consumer.mount(document.body,
'http://localprovider.com:8080/example/provider.html',
{
iframeAttrs: {
id: "frame-id",
style: "margin-top: 4px; margin-bottom: 4px;",
},
focusIndicator: {
classNameFocusStyle: "iframe-focus-style",
}
}
);
```
Please note that the style associated with `classNameFocusStyle` will be applied to the iframe by setting the `classNameFocusStyle` to `class` attribute on the iframe when there is a focus event. During a blur event, the `classNameFocusStyle` name will be removed.
### Monitoring Embedded App Lifecycles

@@ -95,0 +123,0 @@

@@ -28,3 +28,3 @@ import { EventEmitter } from 'events';

init(container, source, {
secret = null, resizeConfig = {}, iframeAttrs = {}, customMethods = {},
secret = null, resizeConfig = {}, iframeAttrs = {}, customMethods = {}, focusIndicator,
} = {}) {

@@ -39,2 +39,3 @@ this.source = source;

this.resizeConfig = resizeConfig;
this.focusIndicator = focusIndicator || null;

@@ -80,2 +81,19 @@ const self = this;

setFocus() {
if (self.focusIndicator && self.focusIndicator.classNameFocusStyle) {
self.iframe.classList.add(self.focusIndicator.classNameFocusStyle);
}
return Promise.resolve();
},
setBlur() {
// Removing the focus style className
self.iframe.classList.remove(self.focusIndicator.classNameFocusStyle);
return Promise.resolve();
},
isScrollingEnabled() {
return Promise.resolve(self.iframe.getAttribute('scrolling') !== 'no');
},
event(event, detail) {

@@ -82,0 +100,0 @@ self.emit(event, detail);

@@ -112,1 +112,34 @@ import logger from './logger';

}
/**
* This function returns boolean value representing whether scroll width or scroll height
* is larger than the client width or client height. It indicates that the content is larger
* than the viewable area and can be scrolled.
*
* @returns boolean true - when the content is scrollable, false - when the content is not scrollable
*/
export function isContentScrollable() {
return (document.documentElement.scrollHeight > document.documentElement.clientHeight
|| document.body.scrollHeight > document.body.clientHeight
|| document.documentElement.scrollWidth > document.documentElement.clientWidth
|| document.body.scrollWidth > document.body.clientWidth);
}
/**
* Determines if the document has interactable element in it.
*
* @returns boolean true - when there is interactable element in the document, false otherwise
*/
export function hasInteractableElement() {
const interactableElementSelector = 'a[href]:not([tabindex=\'-1\']), area[href]:not([tabindex=\'-1\']), input:not([disabled]):not([tabindex=\'-1\']), '
+ "select:not([disabled]):not([tabindex='-1']), textarea:not([disabled]):not([tabindex='-1']), button:not([disabled]):not([tabindex='-1']), "
+ "[contentEditable=true]:not([tabindex='-1'])";
return [...document.body.querySelectorAll(`${interactableElementSelector}`)].some(
(element) => !element.hasAttribute('disabled')
&& !element.getAttribute('aria-hidden')
&& !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length)
&& window.getComputedStyle(element).visibility !== 'hidden'
&& element.closest('[inert]') === null,
);
}

@@ -9,3 +9,5 @@ /* eslint-disable no-mixed-operators, no-restricted-globals */

import logger from '../lib/logger';
import { getOffsetHeightToBody, calculateHeight, calculateWidth } from '../lib/dimension';
import {
getOffsetHeightToBody, calculateHeight, calculateWidth, isContentScrollable, hasInteractableElement,
} from '../lib/dimension';

@@ -54,2 +56,11 @@ /** Application class which represents an embedded application. */

this.unload = this.unload.bind(this);
this.handleLoadEvent = this.handleLoadEvent.bind(this);
this.handleResizeEvent = this.handleResizeEvent.bind(this);
this.handleFocusEvent = this.handleFocusEvent.bind(this);
this.handleBlurEvent = this.handleBlurEvent.bind(this);
this.isIframeScrollable = this.isIframeScrollable.bind(this);
this.setTabIndexWhenRequired = this.setTabIndexWhenRequired.bind(this);
this.hasInteractableElement = true;
this.isScrollingEnabled = false;
this.originalTabIndexValue = null;

@@ -86,2 +97,3 @@ this.dispatchFunction = dispatchFunction || ((message, targetOrigin) => {

);
observer.observe(

@@ -111,2 +123,6 @@ document.body,

window.addEventListener('beforeunload', this.unload);
window.addEventListener('load', this.handleLoadEvent, true);
window.addEventListener('resize', this.handleResizeEvent, true);
window.addEventListener('focus', this.handleFocusEvent, true);
window.addEventListener('blur', this.handleBlurEvent, true);
}

@@ -127,2 +143,3 @@

if (!this.resizeConfig) return;
if (this.resizeConfig.customCal) {

@@ -342,4 +359,79 @@ this.JSONRPC.notification('resize');

}
/**
* When page load is completed, get the
* iframe's scrolling config, and set the tabIndex
* on the document.body of the embedded page if
* necessary.
*/
handleLoadEvent() {
this.JSONRPC.request('isScrollingEnabled', [])
.then(this.setTabIndexWhenRequired);
}
/**
* Handle the resize event and check if tabIndex is needed to be set or removed.
* Depending on the content is scrollable or not, we need to update the tabIndex
* accordingly so focus isn't getting in the document when not needed.
*/
handleResizeEvent() {
if (this.isIframeScrollable() && isContentScrollable() && !this.hasInteractableElement) {
// Set tabIndex="0" so focus can go into the document
document.body.tabIndex = 0;
} else if (this.originalTabIndexValue === null) {
document.body.removeAttribute('tabIndex');
} else {
document.body.tabIndex = this.originalTabIndexValue;
}
}
/**
* Handle the focus event by sending a message to the frame.
*/
handleFocusEvent() {
// Send message to the consumer/frame.js to handle `setFocus` event
if (this.hasInteractableElement) {
return;
}
this.JSONRPC.notification('setFocus');
}
/**
* Handle the blur event by sending a message to the frame.
*/
handleBlurEvent() {
// Send message to the consumer/frame.js to handle `setBlur` event
this.JSONRPC.notification('setBlur');
}
/**
* Return the value of iframe's scroll config stored in `isScrollingEnabled`.
*
* @returns boolean true - when the iframe is scrollable, false - when the iframe is not scrollable
*/
isIframeScrollable() {
return this.isScrollingEnabled;
}
/**
* Sets the `tabIndex=0` on the `document.body` if required
* so focus can get into the embedded document.
*
* @param {*} iframeScrollingEnabled - boolean true when iframe's scrolling is true,
* false when iframe's scrolling is false
*/
setTabIndexWhenRequired(iframeScrollingEnabled) {
this.isScrollingEnabled = iframeScrollingEnabled;
this.hasInteractableElement = hasInteractableElement();
this.originalTabIndexValue = document.body.getAttribute('tabIndex');
if (iframeScrollingEnabled && isContentScrollable() && !this.hasInteractableElement) {
// Set tabIndex="0" so focus can go into the document when
// using tab key when scrolling is enabled
document.body.tabIndex = 0;
}
}
}
export default Application;
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc