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

Improve __uspapi behavior in iframe #6072

Merged
merged 2 commits into from
Jan 13, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 58 additions & 32 deletions modules/consentManagementUsp.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,35 @@ function lookupStaticConsentData(cmpSuccess, cmpError, hookConfig) {
* @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
*/
function lookupUspConsent(uspSuccess, uspError, hookConfig) {
function findUsp() {
let f = window;
let uspapiFrame;
let uspapiFunction;

while (!uspapiFrame) {
try {
if (typeof f.__uspapi === 'function') {
uspapiFunction = f.__uspapi;
uspapiFrame = f;
break;
}
} catch (e) {}

try {
if (f.frames['__uspapiLocator']) {
uspapiFrame = f;
break;
}
} catch (e) {}
if (f === window.top) break;
f = f.parent;
}
return {
uspapiFrame,
uspapiFunction,
};
}

function handleUspApiResponseCallbacks() {
const uspResponse = {};

Expand All @@ -61,41 +90,43 @@ function lookupUspConsent(uspSuccess, uspError, hookConfig) {
uspResponse.usPrivacy = consentResponse.uspString;
}
afterEach();
}
},
};
}

let callbackHandler = handleUspApiResponseCallbacks();
let uspapiCallbacks = {};

let { uspapiFrame, uspapiFunction } = findUsp();

if (!uspapiFrame) {
return uspError('USP CMP not found.', hookConfig);
}

// to collect the consent information from the user, we perform a call to USPAPI
// to collect the user's consent choices represented as a string (via getUSPData)

// the following code also determines where the USPAPI is located and uses the proper workflow to communicate with it:
// - use the USPAPI locator code to see if USP's located in the current window or an ancestor window. This works in friendly or cross domain iframes
// - use the USPAPI locator code to see if USP's located in the current window or an ancestor window.
// - else assume prebid is in an iframe, and use the locator to see if the CMP is located in a higher parent window. This works in cross domain iframes.
// - if USPAPI is not found, the iframe function will call the uspError exit callback to abort the rest of the USPAPI workflow
// - try to call the __uspapi() function directly, otherwise use the postMessage() api
// find the CMP frame/window

try {
// try to call __uspapi directly
window.__uspapi('getUSPData', USPAPI_VERSION, callbackHandler.consentDataCallback);
} catch (e) {
// must not have been accessible, try using postMessage() api
let f = window;
let uspapiFrame;
while (!uspapiFrame) {
try {
if (f.frames['__uspapiLocator']) uspapiFrame = f;
} catch (e) { }
if (f === window.top) break;
f = f.parent;
}

if (!uspapiFrame) {
return uspError('USP CMP not found.', hookConfig);
}
callUspApiWhileInIframe('getUSPData', uspapiFrame, callbackHandler.consentDataCallback);
if (utils.isFn(uspapiFunction)) {
utils.logInfo('Detected USP CMP is directly accessible, calling it now...');
uspapiFunction(
'getUSPData',
USPAPI_VERSION,
callbackHandler.consentDataCallback
);
} else {
utils.logInfo(
'Detected USP CMP is outside the current iframe where Prebid.js is located, calling it now...'
);
callUspApiWhileInIframe(
'getUSPData',
uspapiFrame,
callbackHandler.consentDataCallback
);
}

function callUspApiWhileInIframe(commandName, uspapiFrame, moduleCallback) {
Expand All @@ -107,19 +138,19 @@ function lookupUspConsent(uspSuccess, uspError, hookConfig) {
__uspapiCall: {
command: cmd,
version: ver,
callId: callId
}
callId: callId,
},
};

uspapiCallbacks[callId] = callback;
uspapiFrame.postMessage(msg, '*');
}
};

/** when we get the return message, call the stashed callback */
window.addEventListener('message', readPostMessageResponse, false);

// call uspapi
window.__uspapi(commandName, USPAPI_VERSION, uspapiCallback);
window.__uspapi(commandName, USPAPI_VERSION, moduleCallback);

function readPostMessageResponse(event) {
const res = event && event.data && event.data.__uspapiReturn;
Expand All @@ -130,11 +161,6 @@ function lookupUspConsent(uspSuccess, uspError, hookConfig) {
}
}
}

function uspapiCallback(consentObject, success) {
window.removeEventListener('message', readPostMessageResponse, false);
moduleCallback(consentObject, success);
}
}
}

Expand Down