media/js/base/consent/utils.es6.js (132 lines of code) (raw):
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import MozAllowList from './allow-list.es6';
const COOKIE_ID = 'moz-consent-pref'; // Cookie name
const COOKIE_EXPIRY_DAYS = 182; // 6 months expiry
/**
* Determines if the current page requires consent.
* Looks for a data attribute on the <html> tag.
*/
function consentRequired() {
const attr = document
.getElementsByTagName('html')[0]
.getAttribute('data-needs-consent');
return attr ? attr.toLowerCase() === 'true' : false;
}
/**
* Determines if Do Not Track is enabled.
* @returns {Boolean}
*/
function dntEnabled() {
return (
typeof window.Mozilla.dntEnabled === 'function' &&
window.Mozilla.dntEnabled()
);
}
/**
* Determines the hostname to set the consent cookie on.
* Typically, this is either .mozilla.org or .allizom.org.
* But otherwise, it returns null so the cookie will apply
* to the current domain.
* @param {String} hostname - The hostname of the current page.
* @returns {String|null} - Returns the domain or null.
*/
function getHostName(hostname) {
let domain = null;
if (typeof hostname !== 'string') {
return domain;
}
if (hostname.indexOf('.allizom.org') !== -1) {
domain = '.allizom.org';
}
if (hostname.indexOf('.mozilla.org') !== -1) {
domain = '.mozilla.org';
}
return domain;
}
/**
* Determines if the consent cookie exists.
* @returns {Boolean}
*/
function hasConsentCookie() {
return (
window.Mozilla.Cookies.enabled() &&
window.Mozilla.Cookies.hasItem(COOKIE_ID)
);
}
/**
* Gets the consent cookie object.
* @returns {Object|Boolean} - Returns the consent cookie object or false.
*/
function getConsentCookie() {
try {
return JSON.parse(window.Mozilla.Cookies.getItem(COOKIE_ID));
} catch (e) {
return false;
}
}
/**
* Sets consent cookie with data provided.
* @param {Object} data - Object containing consent data.
* @returns {Boolean}
*/
function setConsentCookie(data) {
try {
if (typeof data !== 'object') {
return false;
}
const date = new Date();
date.setDate(date.getDate() + COOKIE_EXPIRY_DAYS);
const expires = date.toUTCString();
window.Mozilla.Cookies.setItem(
COOKIE_ID,
JSON.stringify(data),
expires,
'/',
getHostName(window.location.hostname),
false,
'lax'
);
return true;
} catch (e) {
return false;
}
}
/**
* Determines if Global Privacy Control is enabled.
* @returns {Boolean}
*/
function gpcEnabled() {
return (
typeof window.Mozilla.gpcEnabled === 'function' &&
window.Mozilla.gpcEnabled()
);
}
/**
* Determine if the current page is /firefox/download/thanks/.
* @param {String} location - The current page URL.
* @return {Boolean}.
*/
function isFirefoxDownloadThanks(location) {
if (typeof location !== 'string') {
return false;
}
return location.indexOf('/firefox/download/thanks/') > -1;
}
/**
* Determines if the current page URL contains a query string
* that allows the consent banner to be displayed.
* @param {String} search - The current page URL search string.
* @returns {Boolean}
*/
function isURLExceptionAllowed(search) {
if (typeof search !== 'string') {
return false;
}
return search.indexOf('mozcb=y') > -1;
}
/**
* Takes a given path name and checks it against the allow-list for
* displaying the consent banner.
* @param {String} pathname
* @returns {Boolean}
*/
function isURLPermitted(pathname) {
let currentPath = pathname;
// Remove locale from current URL pathname;
currentPath = currentPath.replace(/^(\/\w{2}-\w{2}\/|\/\w{2,3}\/)/, '/');
for (let i = 0; i < MozAllowList.length; i++) {
// Turn allowlist path into a regex, supporting wildcards.
const pathRegex = new RegExp(
'^' + MozAllowList[i].replace(/\*/g, '.*') + '$'
);
if (pathRegex.test(currentPath)) {
return true;
}
}
return false;
}
/**
* A very primitive state machine to determine if a consent
* banner needs to be displayed or if analytics can be loaded.
* @param {Object} obj - Object containing consent state flags.
* @returns {String} Consent state message.
*/
function getConsentState(obj) {
/**
* If GPC or DNT is enabled, there's no need to show
* a banner or load analytics since we take that signal
* as a rejection to non-essential cookies and analytics.
*/
if (obj.gpcEnabled) {
return 'STATE_GPC_ENABLED';
}
if (obj.dntEnabled) {
return 'STATE_DNT_ENABLED';
}
/**
* If the visitor is in the EU, and page is on the allow-list,
* then show the cookie banner.
*/
if (obj.consentRequired) {
if (obj.isURLPermitted || obj.isURLExceptionAllowed) {
if (obj.hasConsentCookie) {
return 'STATE_HAS_CONSENT_COOKIE';
} else {
return 'STATE_SHOW_COOKIE_BANNER';
}
} else {
return 'STATE_BANNER_NOT_PERMITTED';
}
} else {
/**
* For remaining users outside of EU,
* load analytics by default.
*/
if (obj.hasConsentCookie) {
return 'STATE_HAS_CONSENT_COOKIE';
} else {
return 'STATE_COOKIES_PERMITTED';
}
}
}
export {
consentRequired,
dntEnabled,
getConsentCookie,
getConsentState,
getHostName,
gpcEnabled,
hasConsentCookie,
isFirefoxDownloadThanks,
isURLExceptionAllowed,
isURLPermitted,
setConsentCookie
};