dscheerens / ngx-webstorage-service

Module for Angular that provides service wrappers for the Web Storage API
MIT License
65 stars 11 forks source link

Failed to read the 'localStorage' property from 'Window' #13

Closed psalkowski closed 4 years ago

psalkowski commented 5 years ago

Hi, it's related to https://github.com/dscheerens/ngx-webstorage-service/issues/8

I got this problem in 4.1.0. I'm using Sentry for reporting errors in production environemnt. It happens on many environments, especially in Chrome Mobile on Android and Opera / Chrome on Windows 7.

Maybe the trick will be to check if exception occurs i.e.:

isStorageAvailable() {
    try {
        localStorage.setItem('a', 'test');
        localStorage.removeItem('a');
        return true;
    } catch(ignored) {}

    return false;
}
dscheerens commented 5 years ago

Hi @namerci,

What is the exact error you observe in the Sentry logs?

The check you are suggesting is exactly what this library does:

https://github.com/dscheerens/ngx-webstorage-service/blob/master/lib/src/web-storage.service.ts#L80-L99

export function isStorageAvailable(storage: Storage): boolean {
    // Check if storage is available.
    if (!storage) {
        return false;
    }

    // Check if the storage can actually be accessed.
    try {
        const now = Date.now();
        const testItemKey = `storage-test-entry-${now}`;
        const testItemValue = `storage-test-value-${now}`;
        storage.setItem(testItemKey, testItemValue);
        const retrievedItemValue = storage.getItem(testItemKey);
        storage.removeItem(testItemKey);

        return retrievedItemValue === testItemValue;
    } catch (error) {
        return false;
    }
}

The actual factory functions for the session storage and local storage are defined as:

export function sessionStorageFactory(): StorageService {
    try {
        if (typeof sessionStorage as any !== 'undefined' && isStorageAvailable(sessionStorage)) {
            return new WebStorageService(sessionStorage);
        }
    } catch {}

    return new InMemoryStorageService();
}

// ...

export function localStorageFactory(): StorageService {
    try {
        if (typeof localStorage as any !== 'undefined' && isStorageAvailable(localStorage)) {
            return new WebStorageService(localStorage);
        }
    } catch {}

    return new InMemoryStorageService();
}

As far as I can see this should cover all cases in which the web storage isn't available. Also, since everything is wrapped in try-catch blocks, I can't see a code path that would lead to an unhandled error. Are you sure the errors you see in your sentry logs are coming from ngx-webstorage-service? Could it perhaps be another third party library which causes these errors?

nephiw commented 5 years ago

In order to call isStorageAvailable you have to provide localStorage and/or sessionStorage because it is used like so:

isStorageAvailable(localStorage);

However, if the storage is disabled (or the user is using private browsing), just by accessing localStorage or sessionStorage you risk getting an error like this one:

VM6023:1 Uncaught DOMException: Failed to read the 'localStorage' property from 'Window': Access is denied for this document.
    at <anonymous>:1:1

So to use isStorageAvailable you have to wrap it in a try/catch block to make sure it works properly.

It may be better to split this into two methods: isLocalStorageAvailable and isSessionStorageAvailable and wrap that try/catch into the methods.

dscheerens commented 5 years ago

However, if the storage is disabled (or the user is using private browsing), just by accessing localStorage or sessionStorage you risk getting an error like this one:

You are right about that. However, this case has been covered by wrapping those check inside try-catch-blocks:

    try {
        if (typeof localStorage as any !== 'undefined' && isStorageAvailable(localStorage)) {
            return new WebStorageService(localStorage);
        }
    } catch {}

So even when attempting to access localStorage or sessionStorage when storage has been disabled, the error will be caught and it will fall back to the InMemoryStorageService implementation.

nephiw commented 5 years ago

I get that; my use case is to look for localStorage before using it so I can fall back to something like cookies as opposed to in-memory storage. So I call isStorageAvailable directly and so I have to wrap it in a try/catch. It seems that my use case is not I common use case for this library. That is ok, but I thought I'd share my experience as at least one other user appears to have experienced it.

dscheerens commented 5 years ago

@nephiw, ah I didn't understood that you were using it like that. In that case thanks for sharing your experience. 👍

I'll add the isLocalStorageAvailable and isSessionStorageAvailable functions to the library and update the docs.