diff --git a/lib/public/components/common/selection/infoLoggerButtonGroup/CopyToClipboardComponent.js b/lib/public/components/common/selection/infoLoggerButtonGroup/CopyToClipboardComponent.js index 207756602f..9d5da31277 100644 --- a/lib/public/components/common/selection/infoLoggerButtonGroup/CopyToClipboardComponent.js +++ b/lib/public/components/common/selection/infoLoggerButtonGroup/CopyToClipboardComponent.js @@ -41,6 +41,55 @@ export class CopyToClipboardComponent extends StatefulComponent { this.notify(); } + /** + * Checks if the clipboard is available and returns an object with availability status and a message. + * + * @returns {{ available: boolean, message: string }} + * - `available`: True if the clipboard is available; false otherwise. + * - `message`: A reason if the clipboard is not available, otherwise an empty string. + */ + checkClipboardAvailability() { + if (!this.isContextSecure()) { + throw new Error('Clipboard not available in a non-secure context.'); + } + + if (!this.isClipboardSupported()) { + throw new Error('Clipboard API is not supported in this browser.'); + } + + if (this.isWindowEmbedded()) { + throw new Error('Clipboard access is restricted in iframes.'); + } + return { available: true }; + } + + /** + * Checks if context is secure (HTTPS) + * + * @returns {boolean} Returns `true` if context is secure + */ + isContextSecure() { + return window.isSecureContext; + } + + /** + * Checks if the clipboard API is available in the user's browser. + * + * @returns {boolean} Returns `true` if it is available + */ + isClipboardSupported() { + return Boolean(navigator.clipboard); + } + + /** + * Check if the window is embeded in a frame. + * + * @returns {boolean} Returns `true` if it is embeded + */ + isWindowEmbedded() { + return window !== window.parent; + } + /** * Renders the button that allows copying text to the clipboard. * @@ -50,6 +99,15 @@ export class CopyToClipboardComponent extends StatefulComponent { view(vnode) { const { attrs, children } = vnode; const { value: clipboardTargetValue = '', id } = attrs; + let available = true; + let message = ''; + + try { + this.checkClipboardAvailability(); + } catch ({ message: errorMessage }) { + available = false; + message = errorMessage; + } const defaultContent = [iconLinkIntact(), children]; const successContent = [iconCheck(), h('', 'Copied!')]; @@ -59,6 +117,8 @@ export class CopyToClipboardComponent extends StatefulComponent { { id: `copy-${id}`, onclick: () => this.copyToClipboard(clipboardTargetValue), + disabled: !available, + title: message || null, }, h('div.flex-row.g1', this._successStateTimeout ? successContent : defaultContent), );