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`3bc5e2db73/packages/core/analytics/core-analytics-browser-internal/src/get_session_id.ts (L17)
and
`isSidenavCollapsed$`3bc5e2db73/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`
6ef0369746/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`6ef0369746/packages/core/fatal-errors/core-fatal-errors-browser-internal/src/fatal_errors_service.tsx (L32)
will never instantiate and the
`failure`6ef0369746/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.
This commit is contained in:
Krzysztof Kowalczyk 2024-10-30 15:02:28 +01:00 committed by GitHub
parent 79e64b85dc
commit 3a8bd70e83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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,