src/init/consented/prepare-googletag.ts (105 lines of code) (raw):
import type { ConsentState } from '@guardian/libs';
import { getConsentFor, loadScript, log, onConsent } from '@guardian/libs';
import { commercialFeatures } from '../../lib/commercial-features';
import { EventTimer } from '../../lib/event-timer';
import { getGoogleTagId, isUserLoggedIn } from '../../lib/identity/api';
import { getPageTargeting } from '../../lib/page-targeting';
import { checkThirdPartyCookiesEnabled } from '../../lib/third-party-cookies';
import { removeSlots } from './remove-slots';
import { fillStaticAdvertSlots } from './static-ad-slots';
const setPageTargeting = (consentState: ConsentState, isSignedIn: boolean) =>
Object.entries(getPageTargeting(consentState, isSignedIn)).forEach(
([key, value]) => {
if (!value) return;
window.googletag.pubads().setTargeting(key, value);
},
);
/**
* Also known as PPID
*/
const setPublisherProvidedId = (): void => {
void getGoogleTagId().then((googleTagId) => {
if (googleTagId !== null) {
window.googletag.pubads().setPublisherProvidedId(googleTagId);
}
});
};
/**
* Track usage of cookieDeprecationLabel
*/
const setCookieDeprecationLabel = (): void => {
if ('cookieDeprecationLabel' in navigator) {
void navigator.cookieDeprecationLabel?.getValue().then((value) => {
const cookieDeprecationLabel = value || 'empty';
window.googletag
.pubads()
.setTargeting('cookieDeprecationLabel', cookieDeprecationLabel);
});
}
};
const enableTargeting = (consentState: ConsentState) => {
if (consentState.canTarget) {
window.googletag.cmd.push(setPublisherProvidedId);
window.googletag.cmd.push(setCookieDeprecationLabel);
window.googletag.cmd.push(checkThirdPartyCookiesEnabled);
}
};
const isGoogleTagAllowed = (consentState: ConsentState) =>
getConsentFor('googletag', consentState);
const canRunGoogletag = (consentState: ConsentState) => {
if (consentState.tcfv2) {
return isGoogleTagAllowed(consentState);
}
return true;
};
const handleLocalePermissions = (consentState: ConsentState) => {
if (consentState.usnat) {
// US mode- USNAT is a general-purpose consent string for various state laws
window.googletag.cmd.push(() => {
window.googletag.pubads().setPrivacySettings({
restrictDataProcessing: !consentState.canTarget,
});
});
} else if (consentState.aus) {
// AUS mode
window.googletag.cmd.push(() => {
window.googletag.pubads().setPrivacySettings({
nonPersonalizedAds: !isGoogleTagAllowed(consentState),
});
});
}
};
export const init = (): Promise<void> => {
const setupAdvertising = async (): Promise<void> => {
const consentState = await onConsent();
EventTimer.get().mark('googletagInitStart');
const canRun = canRunGoogletag(consentState);
enableTargeting(consentState);
handleLocalePermissions(consentState);
// Prebid will already be loaded, and window.googletag is stubbed in `commercial.js`.
// Just load googletag - it's already added to the window by Prebid.
if (canRun) {
const isSignedIn = await isUserLoggedIn();
window.googletag.cmd.push(
() => EventTimer.get().mark('googletagInitEnd'),
() => setPageTargeting(consentState, isSignedIn),
// Note: this function isn't synchronous like most buffered cmds, it's a promise. It's put in here to ensure
// it strictly follows preceding prepare-googletag work (and the module itself ensures dependencies are
// fulfilled), but don't assume this function is complete when queueing subsequent work using cmd.push
() => void fillStaticAdvertSlots(),
);
// The DuckDuckGo browser blocks ads from loading by default, so it causes a lot of noise in Sentry.
// We filter these errors out here - DuckDuckGo is in the user agent string if someone is using the
// desktop browser, and Ddg is present for those using the mobile browser, so we filter out both.
loadScript(
window.guardian.config.page.libs?.googletag ??
'//securepubads.g.doubleclick.net/tag/js/gpt.js',
{ async: false },
).catch((error: Error) => {
if (
navigator.userAgent.includes('DuckDuckGo') ||
navigator.userAgent.includes('Ddg')
) {
log(
'commercial',
'🦆 Caught loadScript error on DuckDuckGo',
error,
);
} else {
throw error;
}
});
}
};
if (commercialFeatures.shouldLoadGoogletag) {
return (
setupAdvertising()
// on error, remove all slots
.catch(removeSlots)
);
}
return removeSlots();
};