From 3a8bd70e835e0467bc5446376345ef7fe1ac4069 Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Wed, 30 Oct 2024 15:02:28 +0100 Subject: [PATCH] Handle DOM storage being disabled (#197798) ## Summary This PR aims to improve the message shown to users when Kibana can't be started due to disabled DOM storage (#121189). The visuals here follow the same pattern as other fatal errors (see #186609) ![image](https://github.com/user-attachments/assets/19832830-49e3-4789-9b83-0c1f14d7980d) The `isDomStorageDisabled` check has to be done before `CoreService` gets instantiated because of issues described below. Closes: #121189 ## The issue What actually happens when you disable all cookies in a browser? Aside from cookies, the browser disables the whole DOM storage - `localStorage` and `sessionStorage`. Trying to access those will result in an error. `getSessionId`https://github.com/elastic/kibana/blob/3bc5e2db73799dc9c7831b6f9da4a52063cf112f/packages/core/analytics/core-analytics-browser-internal/src/get_session_id.ts#L17 and `isSidenavCollapsed$`https://github.com/elastic/kibana/blob/3bc5e2db73799dc9c7831b6f9da4a52063cf112f/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx#L91 Both of those try to access either `localStorage` or `sessionStorage` and both of those are triggered when you create an instance of `CoreSystem` which gets instantiated in `kbn_bootstrap` https://github.com/elastic/kibana/blob/6ef03697460aba0d3774c0c03fb7fb58c76c00bd/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts#L42 Trying to access DOM storage in `CoreSystem` will cause it to throw an error and this means that `FatalErrorService`https://github.com/elastic/kibana/blob/6ef03697460aba0d3774c0c03fb7fb58c76c00bd/packages/core/fatal-errors/core-fatal-errors-browser-internal/src/fatal_errors_service.tsx#L32 will never instantiate and the `failure`https://github.com/elastic/kibana/blob/6ef03697460aba0d3774c0c03fb7fb58c76c00bd/packages/core/rendering/core-rendering-server-internal/src/bootstrap/render_template.ts#L68 function which styles the errors and makes them visible will never trigger and all the user will see is permament `Loading Kibana` spinner. Wrapping `getSessionId` and `isSidenavCollapsed$` in `try-catch` block allows `FatalErrorService` to work properly, which will catch an unhandled exception (`Detected an unhandled Promise rejection.`) with an error about `sessionStorage` being disabled, which gets thrown by `LicensingPlugin` (and possibly in other places). This is not an actual solution though - this behavior would happen again if another line of code trying to access DOM storage gets added to `CoreSystem`. I think it would be best to handle this directly in `kbn_bootstrap.ts` by some check like the one below: ```javascript const isDOMStorageDisabled = () => { try { const key = 'kbn_bootrasrap_domStorageEnabled'; sessionStorage.setItem(key, 'true'); sessionStorage.removeItem(key); return false; } catch (e) { return true; } }; const domStorageDisabled = isDOMStorageDisabled() /* ...some additonal logic */ ``` This would then require some error displaying logic that doesn't use `FatalErrorService`. Looking for some feedback on how to properly solve this. --- .../src/kbn_bootstrap.ts | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts b/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts index 80020b79427f5..a06abd107fd06 100644 --- a/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts +++ b/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts @@ -39,6 +39,76 @@ export async function __kbnBootstrap__() { }), ]); + const isDomStorageDisabled = () => { + try { + const key = 'kbn_bootstrap_domStorageEnabled'; + sessionStorage.setItem(key, 'true'); + sessionStorage.removeItem(key); + localStorage.setItem(key, 'true'); + localStorage.removeItem(key); + return false; + } catch (e) { + return true; + } + }; + + if (isDomStorageDisabled()) { + const defaultErrorTitle = `Couldn't load the page`; + const defaultErrorText = `Update your browser's settings to allow storage of cookies and site data, and reload the page.`; + const defaultErrorReload = 'Reload'; + + const errorTitle = i18nError + ? defaultErrorTitle + : i18n.translate('core.ui.welcomeErrorCouldNotLoadPage', { + defaultMessage: defaultErrorTitle, + }); + + const errorText = i18nError + ? defaultErrorText + : i18n.translate('core.ui.welcomeErrorDomStorageDisabled', { + defaultMessage: defaultErrorText, + }); + + const errorReload = i18nError + ? defaultErrorReload + : i18n.translate('core.ui.welcomeErrorReloadButton', { + defaultMessage: defaultErrorReload, + }); + + const err = document.createElement('div'); + err.style.textAlign = 'center'; + err.style.padding = '120px 20px'; + err.style.fontFamily = 'Inter, BlinkMacSystemFont, Helvetica, Arial, sans-serif'; + + const errorTitleEl = document.createElement('h1'); + errorTitleEl.innerText = errorTitle; + errorTitleEl.style.margin = '20px'; + errorTitleEl.style.color = '#1a1c21'; + + const errorTextEl = document.createElement('p'); + errorTextEl.innerText = errorText; + errorTextEl.style.margin = '20px'; + errorTextEl.style.color = '#343741'; + + const errorReloadEl = document.createElement('button'); + errorReloadEl.innerText = errorReload; + errorReloadEl.onclick = function () { + location.reload(); + }; + errorReloadEl.setAttribute( + 'style', + 'cursor: pointer; padding-inline: 12px; block-size: 40px; font-size: 1rem; line-height: 1.4286rem; border-radius: 6px; min-inline-size: 112px; color: rgb(255, 255, 255); background-color: rgb(0, 119, 204); outline-color: rgb(0, 0, 0); border:none' + ); + + err.appendChild(errorTitleEl); + err.appendChild(errorTextEl); + err.appendChild(errorReloadEl); + + document.body.innerHTML = ''; + document.body.appendChild(err); + return; + } + const coreSystem = new CoreSystem({ injectedMetadata, rootDomElement: document.body,