Skip to content

Commit

Permalink
✨ Pass gdpr_consent and addtl_consent to IMA URL (#37036)
Browse files Browse the repository at this point in the history
  • Loading branch information
alanorozco authored May 25, 2022
1 parent ed0c674 commit 3b136ee
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 219 deletions.
61 changes: 45 additions & 16 deletions ads/google/ima/ima-video.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {loadScript} from '#3p/3p';

import {CONSENT_POLICY_STATE} from '#core/constants/consent-state';
import {
CONSENT_POLICY_STATE,
CONSENT_STRING_TYPE,
} from '#core/constants/consent-state';
import {htmlFor, htmlRefs, svgFor} from '#core/dom/static-template';
import {camelCaseToTitleCase, setStyle, toggle} from '#core/dom/style';
import {isArray, isObject} from '#core/types';
Expand All @@ -9,9 +12,11 @@ import {tryParseJson} from '#core/types/object/json';

import {getData} from '#utils/event-helper';

// Source for this constant is css/amp-ima-video-iframe.css
import {addParamToUrl} from 'src/url';

import {ImaPlayerData} from './ima-player-data';

// Source for this constant is css/amp-ima-video-iframe.css
import {cssText} from '../../../build/amp-ima-video-iframe.css';

/**
Expand Down Expand Up @@ -177,8 +182,8 @@ let adsRequested;
// Flag that tracks if the user tapped and dragged on the overlay button.
let userTappedAndDragged;

// User consent state.
let consentState;
// global context
let context;

// Throttle for the showControls() function
let showControlsThrottled = throttle(window, showControls, 1000);
Expand Down Expand Up @@ -326,6 +331,8 @@ function maybeAppendChildren(document, parent, childrenDef) {
* @param {!Object} data
*/
export function imaVideo(global, data) {
context = global.context;

insertCss(global.document.head, cssText);

videoWidth = global./*OK*/ innerWidth;
Expand Down Expand Up @@ -434,10 +441,7 @@ export function imaVideo(global, data) {
);
});

consentState = global.context.initialConsentState;

if (consentState == 4) {
// UNKNOWN
if (context.initialConsentState == CONSENT_POLICY_STATE.UNKNOWN) {
// On unknown consent state, do not load IMA. Treat this the same as if IMA
// failed to load.
onImaLoadFail();
Expand Down Expand Up @@ -626,16 +630,40 @@ function onOverlayButtonTouchMove() {
export function requestAds() {
adsRequested = true;
adRequestFailed = false;
if (consentState == CONSENT_POLICY_STATE.UNKNOWN) {
const {initialConsentState} = context;
if (initialConsentState == CONSENT_POLICY_STATE.UNKNOWN) {
// We're unaware of the user's consent state - do not request ads.
imaLoadAllowed = false;
return;
} else if (consentState == CONSENT_POLICY_STATE.INSUFFICIENT) {
}
adsRequest.adTagUrl = addParamsToAdTagUrl(adsRequest.adTagUrl);
adsLoader.requestAds(adsRequest);
}

/**
* @param {string} url
* @return {string}
*/
function addParamsToAdTagUrl(url) {
const {initialConsentMetadata, initialConsentState, initialConsentValue} =
context;
if (initialConsentState == CONSENT_POLICY_STATE.INSUFFICIENT) {
// User has provided consent state but has not consented to personalized
// ads.
adsRequest.adTagUrl += '&npa=1';
url = addParamToUrl(url, 'npa', '1');
}
adsLoader.requestAds(adsRequest);
const {additionalConsent, consentStringType} = initialConsentMetadata || {};
const isGdpr =
consentStringType != null &&
consentStringType !== CONSENT_STRING_TYPE.US_PRIVACY_STRING;
if (isGdpr && initialConsentValue != null) {
url = addParamToUrl(url, 'gdpr', '1');
url = addParamToUrl(url, 'gdpr_consent', initialConsentValue);
}
if (additionalConsent != null) {
url = addParamToUrl(url, 'addtl_consent', additionalConsent);
}
return url;
}

/**
Expand Down Expand Up @@ -1615,12 +1643,13 @@ export function setHideControlsTimeoutForTesting(newTimeout) {
}

/**
* Sets the consent state.
* @param {*} newConsentState
* @param {Object} newContext
* @visibleForTesting
*/
export function setConsentStateForTesting(newConsentState) {
consentState = newConsentState;
export function setContextForTesting(newContext) {
for (const k in newContext) {
context[k] = newContext[k];
}
}

/**
Expand Down
45 changes: 28 additions & 17 deletions extensions/amp-ima-video/0.1/amp-ima-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import {installVideoManagerForDoc} from '#service/video-manager-impl';
import {getData, listen} from '#utils/event-helper';

import {getIframe, preloadBootstrap} from '../../../src/3p-frame';
import {getConsentPolicyState} from '../../../src/consent';
import {
getConsentMetadata,
getConsentPolicyInfo,
getConsentPolicyState,
} from '../../../src/consent';
import {addUnsafeAllowAutoplay} from '../../../src/iframe-video';
import {assertHttpsUrl} from '../../../src/url';
import {VideoEvents_Enum} from '../../../src/video-interface';
Expand Down Expand Up @@ -177,30 +181,37 @@ class AmpImaVideo extends AMP.BaseElement {
return isLayoutSizeDefined(layout);
}

/** @override */
getConsentPolicy() {
return null;
/**
* @return {Promise<Object|undefined>}
* @private
*/
getIframeContext_() {
const consentPolicyId = this.getConsentPolicy();
if (!consentPolicyId) {
return Promise.resolve();
}
return Promise.all([
getConsentPolicyState(this.element, consentPolicyId),
getConsentMetadata(this.element, consentPolicyId),
getConsentPolicyInfo(this.element, consentPolicyId),
]).then((result) => ({
initialConsentState: result[0],
initialConsentMetadata: result[1],
initialConsentValue: result[2],
}));
}

/** @override */
layoutCallback() {
const {element, win} = this;
const consentPolicyId = super.getConsentPolicy();
const consentPromise = consentPolicyId
? getConsentPolicyState(element, consentPolicyId)
: Promise.resolve(null);
const {element} = this;
element.setAttribute(
'data-source-children',
JSON.stringify(this.sourceChildren_)
);
return consentPromise.then((initialConsentState) => {
const iframe = getIframe(
win,
element,
TYPE,
{initialConsentState},
{allowFullscreen: true}
);
return this.getIframeContext_().then((context) => {
const iframe = getIframe(this.win, element, TYPE, context, {
allowFullscreen: true,
});
iframe.title = this.element.title || 'IMA video';

applyFillContent(iframe);
Expand Down
54 changes: 49 additions & 5 deletions extensions/amp-ima-video/0.1/test/test-amp-ima-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import '../amp-ima-video';
import {waitForChildPromise} from '#core/dom';
import {htmlFor} from '#core/dom/static-template';

import {Services} from '#service';

import {installResizeObserverStub} from '#testing/resize-observer-stub';

describes.realWin(
Expand All @@ -15,6 +17,15 @@ describes.realWin(
(env) => {
let html;

async function waitForChild(element, selector) {
let child;
await waitForChildPromise(
element,
() => (child = element.querySelector(selector))
);
return child;
}

beforeEach(() => {
html = htmlFor(env.win.document);
installResizeObserverStub(env.sandbox, env.win);
Expand All @@ -32,13 +43,9 @@ describes.realWin(
env.win.document.body.appendChild(element);
await element.whenBuilt();

let iframe;
element.layoutCallback();
await waitForChildPromise(
element,
() => (iframe = element.querySelector('iframe'))
);

const iframe = await waitForChild(element, 'iframe');
const parsedName = JSON.parse(iframe.name);
const sourceChildrenSerialized = parsedName?.attributes?.sourceChildren;
expect(sourceChildrenSerialized).to.not.be.null;
Expand All @@ -50,6 +57,43 @@ describes.realWin(
expect(sourceChildren[1][1]).to.eql({'any-attribute': ''});
});

it('sets consent data in context object', async () => {
const initialConsentState = 'foo_getConsentPolicyState';
const initialConsentMetadata = {'foo_getConsentMetadata': 'bar'};
const initialConsentValue = 'foo_getConsentPolicyInfo';

env.sandbox.stub(Services, 'consentPolicyServiceForDocOrNull').resolves({
whenPolicyResolved: () => Promise.resolve(initialConsentState),
getConsentMetadataInfo: () => Promise.resolve(initialConsentMetadata),
getConsentStringInfo: () => Promise.resolve(initialConsentValue),
whenPolicyUnblock: () => Promise.resolve(true),
whenPurposesUnblock: () => Promise.resolve(true),
});

const element = html`
<amp-ima-video
data-block-on-consent
data-tag="https://example.com"
data-poster="https://example.com/foo.png"
width="1"
height="1"
></amp-ima-video>
`;

env.win.document.body.appendChild(element);
await element.whenBuilt();

element.layoutCallback();

const iframe = await waitForChild(element, 'iframe');
const parsedName = JSON.parse(iframe.name);
expect(parsedName.attributes._context).to.deep.include({
initialConsentState,
initialConsentMetadata,
initialConsentValue,
});
});

it('creates placeholder image from data-poster attribute', async () => {
const element = html`
<amp-ima-video
Expand Down
Loading

0 comments on commit 3b136ee

Please sign in to comment.