Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config option to disable localStorage access #613

Closed
wants to merge 12 commits into from
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [3.56.0] - 2024-03-26

### Added
- Config option `disableStorageApi` to prevent `localStorage` operations

### Changed
- `localStorage` availability check to not create a test-entry anymore

Expand Down
4 changes: 2 additions & 2 deletions src/ts/components/subtitlesettings/subtitlesettingsmanager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ export class SubtitleSettingsManager {
/**
* Loads the settings from local storage
*/
public load(): void {
this.userSettings = StorageUtils.getObject<SubtitleSettings>(this.localStorageKey) || {};
public async load(): Promise<void> {
this.userSettings = await StorageUtils.getObject<SubtitleSettings>(this.localStorageKey) || {};

// Apply the loaded settings
for (let property in this.userSettings) {
Expand Down
40 changes: 32 additions & 8 deletions src/ts/storageutils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
import { UIConfig } from './uiconfig';

export namespace StorageUtils {
/*
At the time of using `storageutils` inside components, the config isn't yet processed.
In order to know if we are allowed to use the `localStorage` API, we wait for the config to be
provided.
*/
let uiConfigSetResolver: (uiConfig: UIConfig) => void;
let uiConfigSetPromise = new Promise<UIConfig>((resolve) => {
uiConfigSetResolver = resolve;
});

export function resolveStorageAccess(uiConfig: UIConfig) {
uiConfigSetResolver?.(uiConfig);
}

function isLocalStorageAvailable(): boolean {
try {
return (
Expand All @@ -7,17 +23,25 @@ export namespace StorageUtils {
typeof localStorage.setItem === 'function'
);
} catch (e) {
console.debug('Error while checking localStorage availablility', e);
return false;
}
}

function shouldUseLocalStorage(): Promise<boolean> {
return uiConfigSetPromise.then((uiConfig) => {
return !uiConfig.disableStorageApi && isLocalStorageAvailable();
});
}

/**
* Stores a string item into localStorage.
* @param key the item's key
* @param data the item's data
*/
export function setItem(key: string, data: string): void {
if (isLocalStorageAvailable()) {
export async function setItem(key: string, data: string): Promise<void> {
if (await shouldUseLocalStorage()) {

try {
window.localStorage.setItem(key, data);
} catch (e) {
Expand All @@ -31,8 +55,8 @@ export namespace StorageUtils {
* @param key the key to look up its associated value
* @return {string | null} Returns the string if found, null if there is no data stored for the key
*/
export function getItem(key: string): string | null {
if (isLocalStorageAvailable()) {
export async function getItem(key: string): Promise<string | null> {
if (await shouldUseLocalStorage()) {
try {
return window.localStorage.getItem(key);
} catch (e) {
Expand All @@ -51,9 +75,9 @@ export namespace StorageUtils {
* @param key the key to store the data to
* @param data the object to store
*/
export function setObject<T>(key: string, data: T): void {
export async function setObject<T>(key: string, data: T): Promise<void> {
let json = JSON.stringify(data);
setItem(key, json);
await setItem(key, json);
}

/**
Expand All @@ -64,8 +88,8 @@ export namespace StorageUtils {
* @param key the key to look up its associated object
* @return {any} Returns the object if found, null otherwise
*/
export function getObject<T>(key: string): T | null {
let json = getItem(key);
export async function getObject<T>(key: string): Promise<T | null> {
let json = await getItem(key);

if (json) {
let object = JSON.parse(json);
Expand Down
5 changes: 5 additions & 0 deletions src/ts/uiconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,9 @@ export interface UIConfig {
* Forces subtitle-labels back into their respective container if they overflow and are therefore cropped.
*/
forceSubtitlesIntoViewContainer?: boolean;

/**
* When set to true, the ad module will never write anything to the browser's `localStorage`.
*/
disableStorageApi?: boolean;
}
2 changes: 2 additions & 0 deletions src/ts/uimanager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { i18n, CustomVocabulary, Vocabularies } from './localization/i18n';
import { FocusVisibilityTracker } from './focusvisibilitytracker';
import { isMobileV3PlayerAPI, MobileV3PlayerAPI, MobileV3PlayerEvent } from './mobilev3playerapi';
import { SpatialNavigation } from './spatialnavigation/spatialnavigation';
import { StorageUtils } from './storageutils';

export interface LocalizationConfig {
/**
Expand Down Expand Up @@ -154,6 +155,7 @@ export class UIManager {
this.uiVariants = <UIVariant[]>playerUiOrUiVariants;
}

StorageUtils.resolveStorageAccess(uiconfig);
this.player = player;
this.managerPlayerWrapper = new PlayerWrapper(player);

Expand Down
Loading