diff --git a/modules/contxtfulRtdProvider.js b/modules/contxtfulRtdProvider.js index 30bc1e87775c..03050a6a64fa 100644 --- a/modules/contxtfulRtdProvider.js +++ b/modules/contxtfulRtdProvider.js @@ -17,12 +17,19 @@ import { isArray, } from '../src/utils.js'; import { loadExternalScript } from '../src/adloader.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; const MODULE_NAME = 'contxtful'; const MODULE = `${MODULE_NAME}RtdProvider`; const CONTXTFUL_RECEPTIVITY_DOMAIN = 'api.receptivity.io'; +const storageManager = getStorageManager({ + moduleType: MODULE_TYPE_RTD, + moduleName: MODULE_NAME +}); + let rxApi = null; let isFirstBidRequestCall = true; @@ -35,10 +42,19 @@ function getRxEngineReceptivity(requester) { return rxApi?.receptivity(requester); } +function getItemFromSessionStorage(key) { + let value = null; + try { + // Use the Storage Manager + value = storageManager.getDataFromSessionStorage(key, null); + } catch (error) { + } + + return value; +} + function loadSessionReceptivity(requester) { - // TODO: commented out because of rule violations - /* - let sessionStorageValue = sessionStorage.getItem(requester); + let sessionStorageValue = getItemFromSessionStorage(requester); if (!sessionStorageValue) { return null; } @@ -56,7 +72,6 @@ function loadSessionReceptivity(requester) { } catch { return null; } - */ } /** diff --git a/modules/contxtfulRtdProvider.md b/modules/contxtfulRtdProvider.md index 71a641db4ad7..622b353c27a2 100644 --- a/modules/contxtfulRtdProvider.md +++ b/modules/contxtfulRtdProvider.md @@ -63,6 +63,7 @@ pbjs.setConfig({ } }); ``` + ## Parameters | Name | Type | Scope | Description | diff --git a/src/storageManager.js b/src/storageManager.js index d2d26461eac7..493c44f056e0 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -88,27 +88,6 @@ export function newStorageManager({moduleName, moduleType} = {}, {isAllowed = is return schedule(cb, STORAGE_TYPE_COOKIES, done); }; - /** - * @returns {boolean} - */ - const localStorageIsEnabled = function (done) { - let cb = function (result) { - if (result && result.valid) { - try { - localStorage.setItem('prebid.cookieTest', '1'); - return localStorage.getItem('prebid.cookieTest') === '1'; - } catch (error) { - } finally { - try { - localStorage.removeItem('prebid.cookieTest'); - } catch (error) {} - } - } - return false; - } - return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); - } - /** * @returns {boolean} */ @@ -122,60 +101,69 @@ export function newStorageManager({moduleName, moduleType} = {}, {isAllowed = is return schedule(cb, STORAGE_TYPE_COOKIES, done); } - /** - * @param {string} key - * @param {string} value - */ - const setDataInLocalStorage = function (key, value, done) { - let cb = function (result) { - if (result && result.valid && hasLocalStorage()) { - window.localStorage.setItem(key, value); - } - } - return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); - } - - /** - * @param {string} key - * @returns {(string|null)} - */ - const getDataFromLocalStorage = function (key, done) { - let cb = function (result) { - if (result && result.valid && hasLocalStorage()) { - return window.localStorage.getItem(key); - } - return null; - } - return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); - } + function storageMethods(name) { + const capName = name.charAt(0).toUpperCase() + name.substring(1); + const backend = () => window[name]; - /** - * @param {string} key - */ - const removeDataFromLocalStorage = function (key, done) { - let cb = function (result) { - if (result && result.valid && hasLocalStorage()) { - window.localStorage.removeItem(key); + const hasStorage = function (done) { + let cb = function (result) { + if (result && result.valid) { + try { + return !!backend(); + } catch (e) { + logError(`${name} api disabled`); + } + } + return false; } + return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); } - return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); - } - /** - * @returns {boolean} - */ - const hasLocalStorage = function (done) { - let cb = function (result) { - if (result && result.valid) { - try { - return !!window.localStorage; - } catch (e) { - logError('Local storage api disabled'); + return { + [`has${capName}`]: hasStorage, + [`${name}IsEnabled`](done) { + let cb = function (result) { + if (result && result.valid) { + try { + backend().setItem('prebid.cookieTest', '1'); + return backend().getItem('prebid.cookieTest') === '1'; + } catch (error) { + } finally { + try { + backend().removeItem('prebid.cookieTest'); + } catch (error) {} + } + } + return false; } + return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); + }, + [`setDataIn${capName}`](key, value, done) { + let cb = function (result) { + if (result && result.valid && hasStorage()) { + backend().setItem(key, value); + } + } + return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); + }, + [`getDataFrom${capName}`](key, done) { + let cb = function (result) { + if (result && result.valid && hasStorage()) { + return backend().getItem(key); + } + return null; + } + return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); + }, + [`removeDataFrom${capName}`](key, done) { + let cb = function (result) { + if (result && result.valid && hasStorage()) { + backend().removeItem(key); + } + } + return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); } - return false; } - return schedule(cb, STORAGE_TYPE_LOCALSTORAGE, done); } /** @@ -211,12 +199,9 @@ export function newStorageManager({moduleName, moduleType} = {}, {isAllowed = is return { setCookie, getCookie, - localStorageIsEnabled, cookiesAreEnabled, - setDataInLocalStorage, - getDataFromLocalStorage, - removeDataFromLocalStorage, - hasLocalStorage, + ...storageMethods('localStorage'), + ...storageMethods('sessionStorage'), findSimilarCookies } } diff --git a/test/spec/unit/core/storageManager_spec.js b/test/spec/unit/core/storageManager_spec.js index edead126c2c3..25471a806772 100644 --- a/test/spec/unit/core/storageManager_spec.js +++ b/test/spec/unit/core/storageManager_spec.js @@ -26,15 +26,15 @@ describe('storage manager', function() { hook.ready(); }); - beforeEach(function() { + beforeEach(function () { resetData(); }); - afterEach(function() { + afterEach(function () { config.resetConfig(); }) - it('should allow to set cookie for core modules without checking gdpr enforcements', function() { + it('should allow to set cookie for core modules without checking gdpr enforcements', function () { const coreStorage = getCoreStorageManager(); let date = new Date(); date.setTime(date.getTime() + (24 * 60 * 60 * 1000)); @@ -43,7 +43,7 @@ describe('storage manager', function() { expect(coreStorage.getCookie('hello')).to.equal('world'); }); - it('should add done callbacks to storageCallbacks array', function() { + it('should add done callbacks to storageCallbacks array', function () { let noop = sinon.spy(); const coreStorage = newStorageManager(); @@ -55,11 +55,15 @@ describe('storage manager', function() { coreStorage.getDataFromLocalStorage('foo', noop); coreStorage.removeDataFromLocalStorage('foo', noop); coreStorage.hasLocalStorage(noop); + coreStorage.setDataInSessionStorage('foo', 'bar', noop); + coreStorage.getDataFromSessionStorage('foo', noop); + coreStorage.removeDataFromSessionStorage('foo', noop); + coreStorage.hasSessionStorage(noop); - expect(storageCallbacks.length).to.equal(8); + expect(storageCallbacks.length).to.equal(12); }); - it('should allow bidder to access device if gdpr enforcement module is not included', function() { + it('should allow bidder to access device if gdpr enforcement module is not included', function () { let deviceAccessSpy = sinon.spy(utils, 'hasDeviceAccess'); const storage = newStorageManager(); storage.setCookie('foo1', 'baz1'); @@ -87,12 +91,16 @@ describe('storage manager', function() { })); }); - it('should deny access if activity is denied', () => { - isAllowed.returns(false); - const mgr = mkManager(MODULE_TYPE_PREBID, 'mockMod'); - mgr.setDataInLocalStorage('testKey', 'val'); - expect(mgr.getDataFromLocalStorage('testKey')).to.not.exist; - }); + ['Local', 'Session'].forEach(type => { + describe(`${type} storage`, () => { + it('should deny access if activity is denied', () => { + isAllowed.returns(false); + const mgr = mkManager(MODULE_TYPE_PREBID, 'mockMod'); + mgr[`setDataIn${type}Storage`]('testKey', 'val'); + expect(mgr[`getDataFrom${type}Storage`]('testKey')).to.not.exist; + }); + }) + }) it('should use bidder aliases when possible', () => { adapterManager.registerBidAdapter({callBids: sinon.stub(), getSpec: () => ({})}, 'mockBidder'); @@ -103,57 +111,66 @@ describe('storage manager', function() { [ACTIVITY_PARAM_COMPONENT_NAME]: 'mockAlias' })) }) - }) - - describe('localstorage forbidden access in 3rd-party context', function() { - let errorLogSpy; - let originalLocalStorage; - const localStorageMock = { get: () => { throw Error } }; + }); - beforeEach(function() { - originalLocalStorage = window.localStorage; - Object.defineProperty(window, 'localStorage', localStorageMock); - errorLogSpy = sinon.spy(utils, 'logError'); - }); + ['localStorage', 'sessionStorage'].forEach(storage => { + const Storage = storage.charAt(0).toUpperCase() + storage.substring(1); - afterEach(function() { - Object.defineProperty(window, 'localStorage', { get: () => originalLocalStorage }); - errorLogSpy.restore(); - }) + describe(`${storage} forbidden access in 3rd-party context`, function () { + let errorLogSpy; + let originalStorage; + const storageMock = { + get: () => { + throw Error + } + }; - it('should not throw if the localstorage is not accessible when setting/getting/removing from localstorage', function() { - const coreStorage = newStorageManager(); + beforeEach(function () { + originalStorage = window[storage]; + Object.defineProperty(window, storage, storageMock); + errorLogSpy = sinon.spy(utils, 'logError'); + }); - coreStorage.setDataInLocalStorage('key', 'value'); - const val = coreStorage.getDataFromLocalStorage('key'); - coreStorage.removeDataFromLocalStorage('key'); + afterEach(function () { + Object.defineProperty(window, storage, {get: () => originalStorage}); + errorLogSpy.restore(); + }) - expect(val).to.be.null; - sinon.assert.calledThrice(errorLogSpy); - }) - }) + it('should not throw if storage is not accessible when setting/getting/removing', function () { + const coreStorage = newStorageManager(); - describe('localstorage is enabled', function() { - let localStorage; + coreStorage[`setDataIn${Storage}`]('key', 'value'); + const val = coreStorage[`getDataFrom${Storage}`]('key'); + coreStorage[`removeDataFrom${Storage}`]('key'); - beforeEach(function() { - localStorage = window.localStorage; - localStorage.clear(); + expect(val).to.be.null; + sinon.assert.calledThrice(errorLogSpy); + }); }); + }); - afterEach(function() { - localStorage.clear(); - }) + ['localStorage', 'sessionStorage'].forEach(storage => { + describe(`${storage} is enabled`, function () { + let store; + beforeEach(function () { + store = window[storage]; + store.clear(); + }); - it('should remove side-effect after checking', function () { - const storage = newStorageManager(); + afterEach(function () { + store.clear(); + }) - localStorage.setItem('unrelated', 'dummy'); - const val = storage.localStorageIsEnabled(); + it('should remove side-effect after checking', function () { + const storageMgr = newStorageManager(); - expect(val).to.be.true; - expect(localStorage.length).to.be.eq(1); - expect(localStorage.getItem('unrelated')).to.be.eq('dummy'); + store.setItem('unrelated', 'dummy'); + const val = storageMgr[`${storage}IsEnabled`](); + + expect(val).to.be.true; + expect(store.length).to.be.eq(1); + expect(store.getItem('unrelated')).to.be.eq('dummy'); + }); }); });