From 22cd8742ea728b9963d87140c00d17b92c8f182d Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Mon, 12 Dec 2022 12:46:40 +0200 Subject: [PATCH 01/22] VT-28545 Implement MGID Widget inside AMP Stories --- ads/_a4a-config.js | 1 + .../compile/bundles.config.extensions.json | 5 + build-system/test-configs/forbidden-terms.js | 2 + docs/spec/amp-localstorage.md | 13 +- examples/amp-story/mgid.html | 169 ++++++++++ examples/amp-story/mgid_response.html | 26 ++ .../0.1/amp-ad-network-mgid-impl.js | 312 ++++++++++++++++++ .../0.1/test/test-amp-ad-network-mgid-impl.js | 0 extensions/amp-ad-network-mgid-impl/OWNERS | 12 + .../amp-ad-network-mgid-impl-internal.md | 3 + .../amp-story-auto-ads/0.1/story-ad-config.js | 1 + 11 files changed, 538 insertions(+), 6 deletions(-) create mode 100644 examples/amp-story/mgid.html create mode 100644 examples/amp-story/mgid_response.html create mode 100644 extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js create mode 100644 extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js create mode 100644 extensions/amp-ad-network-mgid-impl/OWNERS create mode 100644 extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md diff --git a/ads/_a4a-config.js b/ads/_a4a-config.js index 7eaa40fb5323..ac653d0ce3f2 100644 --- a/ads/_a4a-config.js +++ b/ads/_a4a-config.js @@ -27,6 +27,7 @@ export function getA4ARegistry() { 'dianomi': () => true, 'doubleclick': () => true, 'fake': () => true, + 'mgid': () => true, 'nws': () => true, 'smartadserver': () => true, 'valueimpression': () => true, diff --git a/build-system/compile/bundles.config.extensions.json b/build-system/compile/bundles.config.extensions.json index 68904689a405..b7bf3beabae0 100644 --- a/build-system/compile/bundles.config.extensions.json +++ b/build-system/compile/bundles.config.extensions.json @@ -113,6 +113,11 @@ "version": "0.1", "latestVersion": "0.1" }, + { + "name": "amp-ad-network-mgid-impl", + "version": "0.1", + "latestVersion": "0.1" + }, { "name": "amp-ad-network-nws-impl", "version": "0.1", diff --git a/build-system/test-configs/forbidden-terms.js b/build-system/test-configs/forbidden-terms.js index a685b786ac46..b78be97818ad 100644 --- a/build-system/test-configs/forbidden-terms.js +++ b/build-system/test-configs/forbidden-terms.js @@ -451,6 +451,7 @@ const forbiddenTermsGlobal = { 'src/service/index.js', 'src/service/cid-impl.js', 'extensions/amp-ad-network-adsense-impl/0.1/responsive-state.js', + 'extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js', 'extensions/amp-analytics/0.1/session-manager.js', 'extensions/amp-app-banner/0.1/amp-app-banner.js', 'extensions/amp-consent/0.1/consent-state-manager.js', @@ -479,6 +480,7 @@ const forbiddenTermsGlobal = { allowlist: [ 'extensions/amp-access/0.1/amp-access-iframe.js', 'extensions/amp-accordion/0.1/amp-accordion.js', + 'extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js', 'extensions/amp-script/0.1/amp-script.js', 'testing/fake-dom.js', ], diff --git a/docs/spec/amp-localstorage.md b/docs/spec/amp-localstorage.md index 82f233cc8653..d098b12019ce 100644 --- a/docs/spec/amp-localstorage.md +++ b/docs/spec/amp-localstorage.md @@ -37,12 +37,13 @@ The following AMP components and service are using the localStorage. \*\* Please add all future usage to the following list -- `` : Store publisher ad size opt in status. -- `` : Store the user decision on dismiss banner -- `` : Store the user decision on dismiss notification -- `` : Store the user decision and granular information on consent -- Client ID Service : Store the user decision to opt-out CID service. -- Consent Instance : Store the user consent decision. +- `` : Store publisher ad size opt in status. +- `` : Store the user decision on dismiss banner +- `` : Store the user decision on dismiss notification +- `` : Store the user decision and granular information on consent +- Client ID Service : Store the user decision to opt-out CID service. +- Consent Instance : Store the user consent decision. +- `` : Store user ID for proper ad targeting. ## Guidance on localStorage Usage in AMP diff --git a/examples/amp-story/mgid.html b/examples/amp-story/mgid.html new file mode 100644 index 000000000000..250fb72ac050 --- /dev/null +++ b/examples/amp-story/mgid.html @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + +

1

+
+
+ + + +

2

+
+
+ + + +

3

+
+
+ + + +

4

+
+
+ + + +

5

+
+
+ + + +

6

+
+
+ + + +

7

+
+
+ + + +

8

+
+
+ + + +

9

+
+
+ + + +

10

+
+
+ + + +

11

+
+
+ + + +

12

+
+
+ + + +

13

+
+
+ + + +

14

+
+
+ + + +

15

+
+
+ + + +

16

+
+
+ + + +

17

+
+
+ + + +

18

+
+
+ + + +

19

+
+
+ + + +

20

+
+
+ +
+ + + diff --git a/examples/amp-story/mgid_response.html b/examples/amp-story/mgid_response.html new file mode 100644 index 000000000000..e35cba4f03c7 --- /dev/null +++ b/examples/amp-story/mgid_response.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js new file mode 100644 index 000000000000..6ab30149a442 --- /dev/null +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -0,0 +1,312 @@ +import {CONSENT_POLICY_STATE, CONSENT_STRING_TYPE} from '#core/constants/consent-state'; +import {removeElement} from "#core/dom"; +import {Services} from '#service'; +import {user} from '#utils/log'; +import {devAssert} from "#utils/log"; +import {insertAnalyticsElement} from "../../../src/extension-analytics"; +import {AmpA4A} from '../../amp-a4a/0.1/amp-a4a'; +import {CookieWriter} from '../../amp-consent/0.1/cookie-writer'; + +/** @const {string} */ +const TAG = 'amp-ad-network-mgid-impl'; + +const BASE_URL_ = 'https://servicer.mgid.com/'; + +export class AmpAdNetworkMgidImpl extends AmpA4A { + + /** + * @param {!Element} element + */ + constructor(element) { + super(element); + + /** @private {?Element} */ + this.ampAnalyticsElement_ = null; + + /** @private */ + this.mgidMetadata = { + "h": "", + "muidn": "", + "h2": "", + "rid": "", + "tt": "", + "ts": "", + }; + } + + /** @override */ + tearDownSlot() { + super.tearDownSlot(); + if (this.ampAnalyticsElement_) { + removeElement(this.ampAnalyticsElement_); + this.ampAnalyticsElement_ = null; + } + } + + /** @override */ + getAdUrl(consentTuple, opt_rtcResponsesPromise) { + const adUrlParams = []; + + const consentParams = this.getConsents_(consentTuple); + if (consentParams.length === 0) { + user().info(TAG, 'Ad request suppressed due to unknown consent'); + return Promise.resolve(''); + } + + const widget = this.element.getAttribute('data-widget'); + + let servicerUrl = BASE_URL_ + widget + '/' + this.getPageParam_(); + + adUrlParams.concat(this.getNetworkInfoParams_()); + adUrlParams.push(this.getCacheBusterParam_()); + adUrlParams.push(this.getDevicePixelRatioParam_()); + adUrlParams.push(this.getRefParam_()); + adUrlParams.push(this.getPrParam_()); + adUrlParams.push(this.getLuParam_()); + adUrlParams.push(this.getSessionIdParam_()); + adUrlParams.push(this.getPvidParam_()); + adUrlParams.push('muid=' + this.mgidMetadata.muid); + adUrlParams.push('implVersion=15'); + + return Promise.allSettled(adUrlParams).then((params) => { + const data = []; + params.forEach((result) => data.push(result.value)); + servicerUrl += '?' + data.join('&'); + return servicerUrl; + }); + } + + /** @override */ + onCreativeRender(creativeMetaData, opt_onLoadPromise) { + super.onCreativeRender(creativeMetaData); + + const config = { + 'transport': {'beacon': false, 'xhrpost': false, 'image': true}, + "requests": { + "base": "https://ads.localhost/c" + }, + "triggers": { + "storyAdView": { + "on": "story-ad-view", + "request": "base", + "extraUrlParams": { + "f": 1, + "cid": this.element.getAttribute('data-widget'), + "h2": this.mgidMetadata.h2, + "rid": this.mgidMetadata.rid, + "tt": this.mgidMetadata.tt, + "ts": this.mgidMetadata.ts, + "iv": 13, + "pageImp": 1, + "pvid": "", + "muid": this.mgidMetadata.muidn, + "cbuster": "", + "v": "x|y|0|" + this.mgidMetadata.h, + } + }, + }, + }; + + this.ampAnalyticsElement_ = insertAnalyticsElement( + this.element, + config, + /*loadAnalytics*/ true, + false + ); + } + + + /** @override */ + sendXhrRequest(adUrl) { + return super.sendXhrRequest(adUrl).then((response) => { + if (!response) { + return null; + } + const {headers, status} = + /** @type {{status: number, headers: !Headers}} */ (response); + + return response.text().then((responseText) => { + const doc = new DOMParser().parseFromString(responseText, 'text/html'); + const root = doc.documentElement; + const meta = root.querySelector('#mgid_metadata'); + if (meta) { + this.mgidMetadata = JSON.parse(meta.innerHTML); + } + + return new Response(responseText, { + status, + headers, + }); + }); + }); + } + + getConsents_(consentTuple) { + const result = []; + + let consentState = undefined; + let consentString = undefined; + let gdprApplies = undefined; + let consentStringType = undefined; + if (consentTuple) { + consentState = consentTuple.consentState; + consentString = consentTuple.consentString; + gdprApplies = consentTuple.gdprApplies; + consentStringType = consentTuple.consentStringType; + } + if ( + consentState === CONSENT_POLICY_STATE.UNKNOWN && + this.element.getAttribute('data-npa-on-unknown-consent') !== 'true' + ) { + return result; + } + + result.push( + 'gdprApplies=' + + (gdprApplies === true ? '1' : gdprApplies === false ? '0' : null) + ); + result.push( + 'consentData=' + + (consentStringType != + CONSENT_STRING_TYPE.US_PRIVACY_STRING + ? consentString + : null) + ); + result.push( + 'uspString=' + + (consentStringType == + CONSENT_STRING_TYPE.US_PRIVACY_STRING + ? consentString + : null) + ); + + return result; + } + + getPageParam_() { + const widget = this.element.getAttribute('data-widget'); + let page = 1; + if (sessionStorage[`MG_widget_${widget}_page`]) { + page = sessionStorage[`MG_widget_${widget}_page`]; + if (page++ > 20) { + page = 1; + } + } + + return page; + } + + getCacheBusterParam_() { + return 'cbuster=' + + Date.now().toString() + + Math.floor(Math.random() * 1000000000 + 1); + } + + getNetworkInfoParams_() { + const params = []; + try { + const networkInformation = navigator.connection || navigator.mozConnection || navigator.webkitConnection; + + if (typeof networkInformation.type != 'undefined') { + params.push('nit=' + networkInformation.type); + } + if (typeof networkInformation.effectiveType != 'undefined') { + params.push('niet=' + networkInformation.effectiveType); + } + if (typeof networkInformation.saveData != 'undefined') { + params.push('nisd=' + (networkInformation.saveData ? 1 : 0)); + } + } catch (e) { + } + + return params; + } + + getDevicePixelRatioParam_() { + let ratio = 1; + + if (typeof window.devicePixelRatio !== 'undefined') { + ratio = window.devicePixelRatio; + } else if (typeof window.screen.systemXDPI !== 'undefined' && + typeof window.screen.logicalXDPI !== 'undefined' && + window.screen.systemXDPI > window.screen.logicalXDPI) { + ratio = window.screen.systemXDPI / window.screen.logicalXDPI; + } + + const isInt = ratio % 1 === 0; + + if (!isInt) { + ratio = ratio.toFixed(3); + } + + return 'dpr=' + ratio; + } + + getRefParam_() { + return this.getReferrer_(10).then(referrer => { + return 'ref=' + referrer; + }); + } + + getPrParam_() { + if (sessionStorage.MG_Session_pr) { + return 'pr=' + encodeURIComponent(sessionStorage.MG_Session_pr); + } else { + return this.getReferrer_(10).then(referrer => { + const matchDomain = referrer.match(/:\/\/([^\/:]+)/i); + sessionStorage.MG_Session_pr = matchDomain && matchDomain[1] ? matchDomain[1] : ''; + return 'pr=' + encodeURIComponent(referrer); + }); + } + } + + getLuParam_() { + if (sessionStorage.MG_Session_lu) { + return 'lu=' + encodeURIComponent(sessionStorage.MG_Session_lu); + } else { + const url = Services.documentInfoForDoc(this.element).canonicalUrl; + sessionStorage.MG_Session_lu = url; + return 'lu=' + encodeURIComponent(url); + } + } + + getSessionIdParam_() { + if (sessionStorage.MG_Session_Id) { + return 'sessionId=' + sessionStorage.MG_Session_Id; + } else { + const sessionId = Math.round(Date.now() / 1000).toString(16) + '-' + + ('00000' + Math.round(Math.random() * 100000).toString(16)).slice(-5); + sessionStorage.MG_Session_Id = sessionId; + return 'sessionId=' + sessionId; + } + } + + getPvidParam_() { + return 'pvid=' + Services.documentInfoForDoc(this.element).pageViewId64; + } + + /** + * Returns the referrer or undefined if the referrer is not resolved + * before the given timeout + * @param {number=} opt_timeout + * @return {!(Promise|Promise)} A promise with a referrer or undefined + * if timed out + * @private + */ + getReferrer_(opt_timeout) { + const timeoutInt = parseInt(opt_timeout, 10); + const referrerPromise = Services.viewerForDoc( + this.getAmpDoc() + ).getReferrerUrl(); + if (isNaN(timeoutInt) || timeoutInt < 0) { + return referrerPromise; + } + return Services.timerFor(this.win) + .timeoutPromise(timeoutInt, referrerPromise) + .catch(() => undefined); + } +} + +AMP.extension('amp-ad-network-mgid-impl', '0.1', (AMP) => { + AMP.registerElement('amp-ad-network-mgid-impl', AmpAdNetworkMgidImpl); +}); diff --git a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/extensions/amp-ad-network-mgid-impl/OWNERS b/extensions/amp-ad-network-mgid-impl/OWNERS new file mode 100644 index 000000000000..78fd7c3931aa --- /dev/null +++ b/extensions/amp-ad-network-mgid-impl/OWNERS @@ -0,0 +1,12 @@ +// For an explanation of the OWNERS rules and syntax, see: +// https://github.com/ampproject/amp-github-apps/blob/main/owners/OWNERS.example + +{ + rules: [ + { + owners: [ + {name: 'ampproject/wg-ads-reviewers'}, + ], + }, + ], +} diff --git a/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md b/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md new file mode 100644 index 000000000000..2ae84f43b5f0 --- /dev/null +++ b/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md @@ -0,0 +1,3 @@ +### amp-ad-network-mgid-impl + + diff --git a/extensions/amp-story-auto-ads/0.1/story-ad-config.js b/extensions/amp-story-auto-ads/0.1/story-ad-config.js index 8413d7b7fc83..b97a3c0b6142 100644 --- a/extensions/amp-story-auto-ads/0.1/story-ad-config.js +++ b/extensions/amp-story-auto-ads/0.1/story-ad-config.js @@ -23,6 +23,7 @@ const AllowedAdTypes = { 'doubleclick': true, 'fake': true, 'nws': true, + 'mgid': true, }; export class StoryAdConfig { From 17e4c71d8cb5dd821c353293a34735f11bc916ca Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Mon, 12 Dec 2022 18:31:45 +0200 Subject: [PATCH 02/22] VT-28545 Implement MGID Widget inside AMP Stories --- build-system/test-configs/forbidden-terms.js | 1 + examples/amp-story/mgid.html | 6 +- examples/amp-story/mgid_response.html | 26 --- .../0.1/amp-ad-network-mgid-impl.js | 213 ++++++++++++------ 4 files changed, 148 insertions(+), 98 deletions(-) delete mode 100644 examples/amp-story/mgid_response.html diff --git a/build-system/test-configs/forbidden-terms.js b/build-system/test-configs/forbidden-terms.js index b78be97818ad..60c928e78543 100644 --- a/build-system/test-configs/forbidden-terms.js +++ b/build-system/test-configs/forbidden-terms.js @@ -462,6 +462,7 @@ const forbiddenTermsGlobal = { message: requiresReviewPrivacy, allowlist: [ 'extensions/amp-access/0.1/amp-access-iframe.js', + 'extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js', 'extensions/amp-script/0.1/amp-script.js', 'extensions/amp-story/1.0/history.js', 'extensions/amp-web-push/0.1/amp-web-push-helper-frame.js', diff --git a/examples/amp-story/mgid.html b/examples/amp-story/mgid.html index 250fb72ac050..6d0054c52b16 100644 --- a/examples/amp-story/mgid.html +++ b/examples/amp-story/mgid.html @@ -1,18 +1,17 @@ - - + diff --git a/examples/amp-story/mgid_response.html b/examples/amp-story/mgid_response.html deleted file mode 100644 index e35cba4f03c7..000000000000 --- a/examples/amp-story/mgid_response.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index 6ab30149a442..897ab8366b31 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -1,19 +1,24 @@ -import {CONSENT_POLICY_STATE, CONSENT_STRING_TYPE} from '#core/constants/consent-state'; -import {removeElement} from "#core/dom"; +import { + CONSENT_POLICY_STATE, + CONSENT_STRING_TYPE, +} from '#core/constants/consent-state'; +import {createElementWithAttributes, removeElement} from '#core/dom'; + import {Services} from '#service'; + import {user} from '#utils/log'; -import {devAssert} from "#utils/log"; -import {insertAnalyticsElement} from "../../../src/extension-analytics"; + +import {insertAnalyticsElement} from '../../../src/extension-analytics'; import {AmpA4A} from '../../amp-a4a/0.1/amp-a4a'; -import {CookieWriter} from '../../amp-consent/0.1/cookie-writer'; /** @const {string} */ const TAG = 'amp-ad-network-mgid-impl'; const BASE_URL_ = 'https://servicer.mgid.com/'; +const PV_URL_ = 'https://c.mgid.com/'; +const CAPPING_URL_ = 'https://c.mgid.com/'; export class AmpAdNetworkMgidImpl extends AmpA4A { - /** * @param {!Element} element */ @@ -24,13 +29,14 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { this.ampAnalyticsElement_ = null; /** @private */ - this.mgidMetadata = { - "h": "", - "muidn": "", - "h2": "", - "rid": "", - "tt": "", - "ts": "", + this.mgidMetadata_ = { + 'h': '', + 'muidn': '', + 'h2': '', + 'rid': '', + 'tt': '', + 'ts': '', + 'pvid': '', }; } @@ -56,6 +62,7 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { const widget = this.element.getAttribute('data-widget'); let servicerUrl = BASE_URL_ + widget + '/' + this.getPageParam_(); + let pvUrl = PV_URL_ + 'pv/'; adUrlParams.concat(this.getNetworkInfoParams_()); adUrlParams.push(this.getCacheBusterParam_()); @@ -65,13 +72,26 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { adUrlParams.push(this.getLuParam_()); adUrlParams.push(this.getSessionIdParam_()); adUrlParams.push(this.getPvidParam_()); - adUrlParams.push('muid=' + this.mgidMetadata.muid); + if (localStorage.mgMuidn) { + adUrlParams.push('muid=' + localStorage.mgMuidn); + } adUrlParams.push('implVersion=15'); return Promise.allSettled(adUrlParams).then((params) => { const data = []; params.forEach((result) => data.push(result.value)); - servicerUrl += '?' + data.join('&'); + const joinedParams = '?' + data.join('&'); + servicerUrl += joinedParams; + pvUrl += joinedParams; + + this.getAmpDoc() + .getBody() + .appendChild( + createElementWithAttributes(this.win.document, 'amp-pixel', { + 'src': pvUrl, + }) + ); + return servicerUrl; }); } @@ -82,27 +102,29 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { const config = { 'transport': {'beacon': false, 'xhrpost': false, 'image': true}, - "requests": { - "base": "https://ads.localhost/c" + 'requests': { + 'base': CAPPING_URL_ + 'c', }, - "triggers": { - "storyAdView": { - "on": "story-ad-view", - "request": "base", - "extraUrlParams": { - "f": 1, - "cid": this.element.getAttribute('data-widget'), - "h2": this.mgidMetadata.h2, - "rid": this.mgidMetadata.rid, - "tt": this.mgidMetadata.tt, - "ts": this.mgidMetadata.ts, - "iv": 13, - "pageImp": 1, - "pvid": "", - "muid": this.mgidMetadata.muidn, - "cbuster": "", - "v": "x|y|0|" + this.mgidMetadata.h, - } + 'triggers': { + 'storyAdView': { + 'on': 'story-ad-view', + 'request': 'base', + 'extraUrlParams': { + 'f': 1, + 'cid': this.element.getAttribute('data-widget'), + 'h2': this.mgidMetadata_.h2, + 'rid': this.mgidMetadata_.rid, + 'tt': this.mgidMetadata_.tt, + 'ts': this.mgidMetadata_.ts, + 'iv': 13, + 'pageImp': 1, + 'pvid': this.mgidMetadata_.pvid, + 'muid': this.mgidMetadata_.muidn, + 'cbuster': + Date.now().toString() + + Math.floor(Math.random() * 1000000000 + 1), + 'v': 'x|y|0|' + this.mgidMetadata_.h, + }, }, }, }; @@ -115,7 +137,6 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { ); } - /** @override */ sendXhrRequest(adUrl) { return super.sendXhrRequest(adUrl).then((response) => { @@ -130,7 +151,10 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { const root = doc.documentElement; const meta = root.querySelector('#mgid_metadata'); if (meta) { - this.mgidMetadata = JSON.parse(meta.innerHTML); + this.mgidMetadata_ = JSON.parse(meta./*OK*/ innerHTML); + if (this.mgidMetadata_.muidn != '') { + localStorage.mgMuidn = this.mgidMetadata_.muidn; + } } return new Response(responseText, { @@ -141,6 +165,11 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { }); } + /** + * @param {!ConsentTupleDef=} consentTuple + * @return {string[]} Consents parameters. + * @private + */ getConsents_(consentTuple) { const result = []; @@ -163,26 +192,28 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { result.push( 'gdprApplies=' + - (gdprApplies === true ? '1' : gdprApplies === false ? '0' : null) + (gdprApplies === true ? '1' : gdprApplies === false ? '0' : null) ); result.push( 'consentData=' + - (consentStringType != - CONSENT_STRING_TYPE.US_PRIVACY_STRING - ? consentString - : null) + (consentStringType != CONSENT_STRING_TYPE.US_PRIVACY_STRING + ? consentString + : null) ); result.push( 'uspString=' + - (consentStringType == - CONSENT_STRING_TYPE.US_PRIVACY_STRING - ? consentString - : null) + (consentStringType == CONSENT_STRING_TYPE.US_PRIVACY_STRING + ? consentString + : null) ); return result; } + /** + * @return {string} Page data for ad request + * @private + */ getPageParam_() { const widget = this.element.getAttribute('data-widget'); let page = 1; @@ -193,19 +224,34 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { } } + sessionStorage[`MG_widget_${widget}_page`] = page; + return page; } + /** + * @return {string} Cachebuster for ad request + * @private + */ getCacheBusterParam_() { - return 'cbuster=' + + return ( + 'cbuster=' + Date.now().toString() + - Math.floor(Math.random() * 1000000000 + 1); + Math.floor(Math.random() * 1000000000 + 1) + ); } + /** + * @return {string} Network information data for ad request + * @private + */ getNetworkInfoParams_() { const params = []; try { - const networkInformation = navigator.connection || navigator.mozConnection || navigator.webkitConnection; + const networkInformation = + navigator.connection || + navigator.mozConnection || + navigator.webkitConnection; if (typeof networkInformation.type != 'undefined') { params.push('nit=' + networkInformation.type); @@ -216,20 +262,25 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { if (typeof networkInformation.saveData != 'undefined') { params.push('nisd=' + (networkInformation.saveData ? 1 : 0)); } - } catch (e) { - } + } catch (e) {} return params; } + /** + * @return {string} Device pixel ratio info for ad request + * @private + */ getDevicePixelRatioParam_() { let ratio = 1; if (typeof window.devicePixelRatio !== 'undefined') { ratio = window.devicePixelRatio; - } else if (typeof window.screen.systemXDPI !== 'undefined' && + } else if ( + typeof window.screen.systemXDPI !== 'undefined' && typeof window.screen.logicalXDPI !== 'undefined' && - window.screen.systemXDPI > window.screen.logicalXDPI) { + window.screen.systemXDPI > window.screen.logicalXDPI + ) { ratio = window.screen.systemXDPI / window.screen.logicalXDPI; } @@ -242,47 +293,73 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { return 'dpr=' + ratio; } + /** + * @return {string} Referrer info for ad request + * @private + */ getRefParam_() { - return this.getReferrer_(10).then(referrer => { + return this.getReferrer_(10).then((referrer) => { return 'ref=' + referrer; }); } + /** + * @return {string} Primary referrer info for ad request + * @private + */ getPrParam_() { - if (sessionStorage.MG_Session_pr) { - return 'pr=' + encodeURIComponent(sessionStorage.MG_Session_pr); + if (sessionStorage['MG_Session_pr']) { + return 'pr=' + encodeURIComponent(sessionStorage['MG_Session_pr']); } else { - return this.getReferrer_(10).then(referrer => { + return this.getReferrer_(10).then((referrer) => { const matchDomain = referrer.match(/:\/\/([^\/:]+)/i); - sessionStorage.MG_Session_pr = matchDomain && matchDomain[1] ? matchDomain[1] : ''; + sessionStorage['MG_Session_pr'] = + matchDomain && matchDomain[1] ? matchDomain[1] : ''; return 'pr=' + encodeURIComponent(referrer); }); } } + /** + * @return {string} First session page info for ad request + * @private + */ getLuParam_() { - if (sessionStorage.MG_Session_lu) { - return 'lu=' + encodeURIComponent(sessionStorage.MG_Session_lu); + if (sessionStorage['MG_Session_lu']) { + return 'lu=' + encodeURIComponent(sessionStorage['MG_Session_lu']); } else { const url = Services.documentInfoForDoc(this.element).canonicalUrl; - sessionStorage.MG_Session_lu = url; + sessionStorage['MG_Session_lu'] = url; return 'lu=' + encodeURIComponent(url); } } + /** + * @return {string} Session id info for ad request + * @private + */ getSessionIdParam_() { - if (sessionStorage.MG_Session_Id) { - return 'sessionId=' + sessionStorage.MG_Session_Id; + if (sessionStorage['MG_Session_Id']) { + return 'sessionId=' + sessionStorage['MG_Session_Id']; } else { - const sessionId = Math.round(Date.now() / 1000).toString(16) + '-' + + const sessionId = + Math.round(Date.now() / 1000).toString(16) + + '-' + ('00000' + Math.round(Math.random() * 100000).toString(16)).slice(-5); - sessionStorage.MG_Session_Id = sessionId; + sessionStorage['MG_Session_Id'] = sessionId; return 'sessionId=' + sessionId; } } + /** + * @return {string} Pageview info for ad request + * @private + */ getPvidParam_() { - return 'pvid=' + Services.documentInfoForDoc(this.element).pageViewId64; + Services.documentInfoForDoc(this.element).pageViewId64.then((pvid) => { + this.mgidMetadata_.pvid = pvid; + return 'pvid=' + pvid; + }); } /** @@ -302,8 +379,8 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { return referrerPromise; } return Services.timerFor(this.win) - .timeoutPromise(timeoutInt, referrerPromise) - .catch(() => undefined); + .timeoutPromise(timeoutInt, referrerPromise) + .catch(() => undefined); } } From 60623613082be06ee312d126fb919a318bc54368 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Mon, 12 Dec 2022 19:09:09 +0200 Subject: [PATCH 03/22] VT-28545 Implement MGID Widget inside AMP Stories. Static screen size --- .../amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index 897ab8366b31..34691a332db8 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -123,7 +123,7 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { 'cbuster': Date.now().toString() + Math.floor(Math.random() * 1000000000 + 1), - 'v': 'x|y|0|' + this.mgidMetadata_.h, + 'v': '412|915|0|' + this.mgidMetadata_.h, }, }, }, From d783699e77f147972dedbfe49d54f8cbf51779b9 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Wed, 14 Dec 2022 14:47:02 +0200 Subject: [PATCH 04/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix 3p iframe implementation --- ads/_a4a-config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ads/_a4a-config.js b/ads/_a4a-config.js index ac653d0ce3f2..aeddc19f4653 100644 --- a/ads/_a4a-config.js +++ b/ads/_a4a-config.js @@ -27,7 +27,7 @@ export function getA4ARegistry() { 'dianomi': () => true, 'doubleclick': () => true, 'fake': () => true, - 'mgid': () => true, + 'mgid': (win, adTag) => !adTag.hasAttribute('data-container'), 'nws': () => true, 'smartadserver': () => true, 'valueimpression': () => true, From 8866b63b89ec21c138cffdb90221f1618b5188c5 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Thu, 15 Dec 2022 16:43:47 +0200 Subject: [PATCH 05/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix consents --- .../0.1/amp-ad-network-mgid-impl.js | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index 34691a332db8..4b2c0254f81e 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -51,12 +51,14 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { /** @override */ getAdUrl(consentTuple, opt_rtcResponsesPromise) { - const adUrlParams = []; + let adUrlParams = []; const consentParams = this.getConsents_(consentTuple); if (consentParams.length === 0) { user().info(TAG, 'Ad request suppressed due to unknown consent'); return Promise.resolve(''); + } else { + adUrlParams = adUrlParams.concat(consentParams); } const widget = this.element.getAttribute('data-widget'); @@ -64,7 +66,7 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { let servicerUrl = BASE_URL_ + widget + '/' + this.getPageParam_(); let pvUrl = PV_URL_ + 'pv/'; - adUrlParams.concat(this.getNetworkInfoParams_()); + adUrlParams = adUrlParams.concat(this.getNetworkInfoParams_()); adUrlParams.push(this.getCacheBusterParam_()); adUrlParams.push(this.getDevicePixelRatioParam_()); adUrlParams.push(this.getRefParam_()); @@ -190,22 +192,20 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { return result; } - result.push( - 'gdprApplies=' + - (gdprApplies === true ? '1' : gdprApplies === false ? '0' : null) - ); - result.push( - 'consentData=' + - (consentStringType != CONSENT_STRING_TYPE.US_PRIVACY_STRING - ? consentString - : null) - ); - result.push( - 'uspString=' + - (consentStringType == CONSENT_STRING_TYPE.US_PRIVACY_STRING - ? consentString - : null) - ); + if (gdprApplies) { + result.push( + 'gdprApplies=' + + (gdprApplies === true ? '1' : gdprApplies === false ? '0' : null) + ); + } + + if (consentStringType != CONSENT_STRING_TYPE.US_PRIVACY_STRING) { + result.push('consentData=' + consentString); + } + + if (consentStringType == CONSENT_STRING_TYPE.US_PRIVACY_STRING) { + result.push('uspString=' + consentString); + } return result; } From 39bd971367e3b5584e5317de30b43ddbbeb3ce55 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Thu, 15 Dec 2022 19:08:13 +0200 Subject: [PATCH 06/22] VT-28545 Implement MGID Widget inside AMP Stories. Tests --- .../0.1/amp-ad-network-mgid-impl.js | 27 +++-- .../0.1/test/test-amp-ad-network-mgid-impl.js | 105 ++++++++++++++++++ 2 files changed, 125 insertions(+), 7 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index 4b2c0254f81e..683c5a5b5330 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -49,15 +49,22 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { } } + /** @override */ + isValidElement() { + const id = this.element.getAttribute('data-widget'); + if (!id || parseInt(id, 10) != id) { + user().warn(TAG, 'Undefined or non-numeric data-widget param!'); + return false; + } + return true; + } + /** @override */ getAdUrl(consentTuple, opt_rtcResponsesPromise) { let adUrlParams = []; const consentParams = this.getConsents_(consentTuple); - if (consentParams.length === 0) { - user().info(TAG, 'Ad request suppressed due to unknown consent'); - return Promise.resolve(''); - } else { + if (consentParams.length !== 0) { adUrlParams = adUrlParams.concat(consentParams); } @@ -199,11 +206,17 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { ); } - if (consentStringType != CONSENT_STRING_TYPE.US_PRIVACY_STRING) { + if ( + consentString && + consentStringType != CONSENT_STRING_TYPE.US_PRIVACY_STRING + ) { result.push('consentData=' + consentString); } - if (consentStringType == CONSENT_STRING_TYPE.US_PRIVACY_STRING) { + if ( + consentString && + consentStringType == CONSENT_STRING_TYPE.US_PRIVACY_STRING + ) { result.push('uspString=' + consentString); } @@ -299,7 +312,7 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { */ getRefParam_() { return this.getReferrer_(10).then((referrer) => { - return 'ref=' + referrer; + return 'ref=' + encodeURIComponent(referrer); }); } diff --git a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js index e69de29bb2d1..54a20aa10463 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js @@ -0,0 +1,105 @@ +import {Services} from '#service'; +import { + AmpAdNetworkDoubleclickImpl +} from '../../../amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl'; +import {AmpAd} from '../../../amp-ad/0.1/amp-ad'; // eslint-disable-line @typescript-eslint/no-unused-vars +import {AmpAdNetworkMgidImpl} from '../amp-ad-network-mgid-impl'; + +describes.realWin( + 'amp-ad-network-mgid-impl', + { + amp: { + extensions: ['amp-ad', 'amp-ad-network-mgid-impl'], + }, + }, + (env) => { + let doc; + let win; + let mgidImplElem; + beforeEach(() => { + win = env.win; + doc = win.document; + mgidImplElem = doc.createElement('amp-ad'); + mgidImplElem.setAttribute('type', 'mgid'); + mgidImplElem.setAttribute('layout', 'fixed'); + mgidImplElem.setAttribute('width', '300'); + mgidImplElem.setAttribute('height', '250'); + + env.win.document.body.appendChild(mgidImplElem); + }); + + it('should check for data-widget attribute', () => { + const mgidImpl = new AmpAdNetworkMgidImpl(mgidImplElem); + expect(mgidImpl.isValidElement()).to.be.false; + + mgidImplElem.setAttribute('data-widget', '100'); + const mgidImpl2 = new AmpAdNetworkMgidImpl(mgidImplElem); + expect(mgidImpl2.isValidElement()).to.be.true; + }); + + it('generates correct adUrl', () => { + mgidImplElem.setAttribute('data-widget', '100'); + + const viewer = Services.viewerForDoc(mgidImplElem); + env.sandbox + .stub(viewer, 'getReferrerUrl') + .returns(Promise.resolve('http://fake.example/?foo=bar')); + + const documentInfo = Services.documentInfoForDoc(mgidImplElem); + documentInfo.canonicalUrl = 'http://canonical.example/?abc=xyz'; + + const mgidImpl = new AmpAdNetworkMgidImpl(mgidImplElem); + + return mgidImpl.getAdUrl().then((url) => { + [ + /^https:\/\/servicer\.mgid\.com\/100\/1/, + /(\?|&)niet=(slow-2g|2g|3g|4g)(&|$)/, + /(\?|&)nisd=(0|1)(&|$)/, + /(\?|&)cbuster=\d+(&|$)/, + /(\?|&)dpr=\d+(&|$)/, + /(\?|&)ref=http%3A%2F%2Ffake.example%2F%3Ffoo%3Dbar(&|$)/, + /(\?|&)pr=http%3A%2F%2Ffake.example%2F%3Ffoo%3Dbar(&|$)/, + /(\?|&)lu=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, + /(\?|&)sessionId=[0-9a-f]{8}-[0-9a-f]{5}(&|$)/, + /(\?|&)implVersion=15(&|$)/, + ].forEach((regexp) => { + expect(url).to.match(regexp); + }); + }); + }); + + it('generates correct adUrl (data from storage)', () => { + mgidImplElem.setAttribute('data-widget', '100'); + + const viewer = Services.viewerForDoc(mgidImplElem); + env.sandbox + .stub(viewer, 'getReferrerUrl') + .returns(Promise.resolve('http://fake.example/?foo=bar')); + + sessionStorage.MG_Session_pr = 'http://stored-pr.example/?foo=bar'; + sessionStorage.MG_Session_lu = 'http://stored-lu.example/?abc=xyz'; + sessionStorage.MG_Session_Id = 'stored-session'; + localStorage.mgMuidn = 'qwerty123456'; + + const mgidImpl = new AmpAdNetworkMgidImpl(mgidImplElem); + + return mgidImpl.getAdUrl().then((url) => { + [ + /^https:\/\/servicer\.mgid\.com\/100\/2/, + /(\?|&)niet=(slow-2g|2g|3g|4g)(&|$)/, + /(\?|&)nisd=(0|1)(&|$)/, + /(\?|&)cbuster=\d+(&|$)/, + /(\?|&)dpr=\d+(&|$)/, + /(\?|&)ref=http%3A%2F%2Ffake.example%2F%3Ffoo%3Dbar(&|$)/, + /(\?|&)pr=http%3A%2F%2Fstored-pr.example%2F%3Ffoo%3Dbar(&|$)/, + /(\?|&)lu=http%3A%2F%2Fstored-lu.example%2F%3Fabc%3Dxyz(&|$)/, + /(\?|&)sessionId=stored-session(&|$)/, + /(\?|&)muid=qwerty123456(&|$)/, + /(\?|&)implVersion=15(&|$)/, + ].forEach((regexp) => { + expect(url).to.match(regexp); + }); + }); + }); + } +); From 35de85343dfcd09f5d7220f1a8fa13ba0219da2d Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Thu, 15 Dec 2022 19:19:10 +0200 Subject: [PATCH 07/22] VT-28545 Implement MGID Widget inside AMP Stories. Readme --- .../amp-ad-network-mgid-impl-internal.md | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md b/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md index 2ae84f43b5f0..ab0e65506a05 100644 --- a/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md +++ b/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md @@ -1,3 +1,33 @@ -### amp-ad-network-mgid-impl +# amp-ad-network-mgid-impl +Mgid implementation of AMP Ad, which is only used for amp-stories. 3p-iframe implementation is used for all other cases. + + + + + + + + + + +
AvailabilityIn Development
Required Script<script async custom-element="amp-story" src="https://cdn.ampproject.org/v0/amp-story-1.0.js"></script><script async custom-element="amp-story-auto-ads" src="https://cdn.ampproject.org/v0/amp-story-auto-ads-0.1.js"></script>
+ +## Example + +```html + + + + + ... + +``` From 8ef57c2468f0890c1a315483c798df38e64a2e62 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Fri, 16 Dec 2022 16:38:49 +0200 Subject: [PATCH 08/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix eslint --- .../0.1/test/test-amp-ad-network-mgid-impl.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js index 54a20aa10463..a47e44cb8e12 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js @@ -1,7 +1,5 @@ import {Services} from '#service'; -import { - AmpAdNetworkDoubleclickImpl -} from '../../../amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl'; + import {AmpAd} from '../../../amp-ad/0.1/amp-ad'; // eslint-disable-line @typescript-eslint/no-unused-vars import {AmpAdNetworkMgidImpl} from '../amp-ad-network-mgid-impl'; @@ -76,9 +74,9 @@ describes.realWin( .stub(viewer, 'getReferrerUrl') .returns(Promise.resolve('http://fake.example/?foo=bar')); - sessionStorage.MG_Session_pr = 'http://stored-pr.example/?foo=bar'; - sessionStorage.MG_Session_lu = 'http://stored-lu.example/?abc=xyz'; - sessionStorage.MG_Session_Id = 'stored-session'; + sessionStorage['MG_Session_pr'] = 'http://stored-pr.example/?foo=bar'; + sessionStorage['MG_Session_lu'] = 'http://stored-lu.example/?abc=xyz'; + sessionStorage['MG_Session_Id'] = 'stored-session'; localStorage.mgMuidn = 'qwerty123456'; const mgidImpl = new AmpAdNetworkMgidImpl(mgidImplElem); From 4945022d7bdd3090374b5b7982110f034699be90 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Fri, 16 Dec 2022 17:15:56 +0200 Subject: [PATCH 09/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix eslint --- build-system/test-configs/forbidden-terms.js | 1 - 1 file changed, 1 deletion(-) diff --git a/build-system/test-configs/forbidden-terms.js b/build-system/test-configs/forbidden-terms.js index 60c928e78543..be6c78da96d1 100644 --- a/build-system/test-configs/forbidden-terms.js +++ b/build-system/test-configs/forbidden-terms.js @@ -451,7 +451,6 @@ const forbiddenTermsGlobal = { 'src/service/index.js', 'src/service/cid-impl.js', 'extensions/amp-ad-network-adsense-impl/0.1/responsive-state.js', - 'extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js', 'extensions/amp-analytics/0.1/session-manager.js', 'extensions/amp-app-banner/0.1/amp-app-banner.js', 'extensions/amp-consent/0.1/consent-state-manager.js', From 535c8e4ba1a1110a9122d0af0305f5c7bb44631e Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Fri, 16 Dec 2022 18:14:34 +0200 Subject: [PATCH 10/22] VT-28545 Implement MGID Widget inside AMP Stories. Add tests --- .../0.1/test/test-amp-ad-network-mgid-impl.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js index a47e44cb8e12..8ed776707953 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js @@ -99,5 +99,33 @@ describes.realWin( }); }); }); + + it('test sendXhrRequest', async () => { + env.win.fetch = env.fetchMock.realFetch; + const creative = ` + + + + + + + + `; + + env.fetchMock.mock('begin:https://fake.local', creative); + + mgidImplElem.setAttribute('data-widget', '100'); + + const mgidImpl = new AmpAdNetworkMgidImpl(mgidImplElem); + await mgidImpl.sendXhrRequest('https://fake.local'); + expect(mgidImpl.mgidMetadata_.h).to.equal('hash'); + expect(mgidImpl.mgidMetadata_.h2).to.equal('hash2'); + expect(mgidImpl.mgidMetadata_.rid).to.equal('request_id'); + expect(mgidImpl.mgidMetadata_.tt).to.equal('tt123'); + expect(mgidImpl.mgidMetadata_.ts).to.equal('ts456'); + expect(mgidImpl.mgidMetadata_.muidn).to.equal('muidn123456'); + }); } ); From db0e938cfb586feb859424499fc63e0f467ed9c8 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Fri, 16 Dec 2022 18:18:40 +0200 Subject: [PATCH 11/22] VT-28545 Implement MGID Widget inside AMP Stories. linter fixes --- docs/spec/amp-localstorage.md | 14 +++++++------- extensions/amp-ad-network-mgid-impl/OWNERS | 4 +--- .../amp-ad-network-mgid-impl-internal.md | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/docs/spec/amp-localstorage.md b/docs/spec/amp-localstorage.md index d098b12019ce..18a6eeb50e6e 100644 --- a/docs/spec/amp-localstorage.md +++ b/docs/spec/amp-localstorage.md @@ -37,13 +37,13 @@ The following AMP components and service are using the localStorage. \*\* Please add all future usage to the following list -- `` : Store publisher ad size opt in status. -- `` : Store the user decision on dismiss banner -- `` : Store the user decision on dismiss notification -- `` : Store the user decision and granular information on consent -- Client ID Service : Store the user decision to opt-out CID service. -- Consent Instance : Store the user consent decision. -- `` : Store user ID for proper ad targeting. +- `` : Store publisher ad size opt in status. +- `` : Store the user decision on dismiss banner +- `` : Store the user decision on dismiss notification +- `` : Store the user decision and granular information on consent +- Client ID Service : Store the user decision to opt-out CID service. +- Consent Instance : Store the user consent decision. +- `` : Store user ID for proper ad targeting. ## Guidance on localStorage Usage in AMP diff --git a/extensions/amp-ad-network-mgid-impl/OWNERS b/extensions/amp-ad-network-mgid-impl/OWNERS index 78fd7c3931aa..1b8c3b93f6d9 100644 --- a/extensions/amp-ad-network-mgid-impl/OWNERS +++ b/extensions/amp-ad-network-mgid-impl/OWNERS @@ -4,9 +4,7 @@ { rules: [ { - owners: [ - {name: 'ampproject/wg-ads-reviewers'}, - ], + owners: [{name: 'ampproject/wg-ads-reviewers'}], }, ], } diff --git a/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md b/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md index ab0e65506a05..85faf1aeb517 100644 --- a/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md +++ b/extensions/amp-ad-network-mgid-impl/amp-ad-network-mgid-impl-internal.md @@ -1,6 +1,7 @@ # amp-ad-network-mgid-impl -Mgid implementation of AMP Ad, which is only used for amp-stories. 3p-iframe implementation is used for all other cases. +Mgid implementation of AMP Ad, which is only used for amp-stories. +3p iframe implementation is used for all other cases. @@ -30,4 +31,3 @@ Mgid implementation of AMP Ad, which is only used for amp-stories. 3p-iframe imp ... ``` - From 8cad691e42b3f08fe574a56460fb80f4c3541167 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Fri, 16 Dec 2022 18:34:09 +0200 Subject: [PATCH 12/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix deps --- build-system/test-configs/dep-check-config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/build-system/test-configs/dep-check-config.js b/build-system/test-configs/dep-check-config.js index d497c30665f0..94f2466c883e 100644 --- a/build-system/test-configs/dep-check-config.js +++ b/build-system/test-configs/dep-check-config.js @@ -150,6 +150,7 @@ exports.rules = [ 'extensions/amp-ad-network-valueimpression-impl/0.1/amp-ad-network-valueimpression-impl.js->extensions/amp-a4a/0.1/amp-a4a.js', 'extensions/amp-ad-network-dianomi-impl/0.1/amp-ad-network-dianomi-impl.js->extensions/amp-a4a/0.1/amp-a4a.js', 'extensions/amp-ad-network-smartadserver-impl/0.1/amp-ad-network-smartadserver-impl.js->extensions/amp-a4a/0.1/amp-a4a.js', + 'extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js->extensions/amp-a4a/0.1/amp-a4a.js', // A4A impls importing amp fast fetch header name 'extensions/amp-ad-network-adsense-impl/0.1/amp-ad-network-adsense-impl.js->extensions/amp-a4a/0.1/signature-verifier.js', From 2973d40c8730462a49264e21f834afd2ac46171a Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Tue, 27 Dec 2022 16:57:05 +0200 Subject: [PATCH 13/22] VT-28545 Implement MGID Widget inside AMP Stories. VT-28613 - remove ref param --- .../0.1/amp-ad-network-mgid-impl.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index 683c5a5b5330..96e826a7740a 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -76,7 +76,6 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { adUrlParams = adUrlParams.concat(this.getNetworkInfoParams_()); adUrlParams.push(this.getCacheBusterParam_()); adUrlParams.push(this.getDevicePixelRatioParam_()); - adUrlParams.push(this.getRefParam_()); adUrlParams.push(this.getPrParam_()); adUrlParams.push(this.getLuParam_()); adUrlParams.push(this.getSessionIdParam_()); @@ -306,16 +305,6 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { return 'dpr=' + ratio; } - /** - * @return {string} Referrer info for ad request - * @private - */ - getRefParam_() { - return this.getReferrer_(10).then((referrer) => { - return 'ref=' + encodeURIComponent(referrer); - }); - } - /** * @return {string} Primary referrer info for ad request * @private From 4962f87f7fc522a825f74dcca4db248d155f0c1e Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Tue, 27 Dec 2022 17:31:27 +0200 Subject: [PATCH 14/22] VT-28545 Implement MGID Widget inside AMP Stories. VT-28604 - fix pv request --- .../0.1/amp-ad-network-mgid-impl.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index 96e826a7740a..3acb56bce4ac 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -92,13 +92,17 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { servicerUrl += joinedParams; pvUrl += joinedParams; - this.getAmpDoc() - .getBody() - .appendChild( - createElementWithAttributes(this.win.document, 'amp-pixel', { - 'src': pvUrl, - }) - ); + if (typeof this.win['_mgAmpStoryPV'] == 'undefined') { + this.getAmpDoc() + .getBody() + .appendChild( + createElementWithAttributes(this.win.document, 'amp-pixel', { + 'src': pvUrl, + }) + ); + + this.win['_mgAmpStoryPV'] = 1; + } return servicerUrl; }); From 99fa3c76fd5920d5927ea94920a7b302f01535d2 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Wed, 28 Dec 2022 13:00:26 +0200 Subject: [PATCH 15/22] VT-28545 Implement MGID Widget inside AMP Stories. Dismiss capping --- .../0.1/amp-ad-network-mgid-impl.js | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index 3acb56bce4ac..fa03a86e4e74 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -8,7 +8,6 @@ import {Services} from '#service'; import {user} from '#utils/log'; -import {insertAnalyticsElement} from '../../../src/extension-analytics'; import {AmpA4A} from '../../amp-a4a/0.1/amp-a4a'; /** @const {string} */ @@ -16,7 +15,6 @@ const TAG = 'amp-ad-network-mgid-impl'; const BASE_URL_ = 'https://servicer.mgid.com/'; const PV_URL_ = 'https://c.mgid.com/'; -const CAPPING_URL_ = 'https://c.mgid.com/'; export class AmpAdNetworkMgidImpl extends AmpA4A { /** @@ -108,47 +106,6 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { }); } - /** @override */ - onCreativeRender(creativeMetaData, opt_onLoadPromise) { - super.onCreativeRender(creativeMetaData); - - const config = { - 'transport': {'beacon': false, 'xhrpost': false, 'image': true}, - 'requests': { - 'base': CAPPING_URL_ + 'c', - }, - 'triggers': { - 'storyAdView': { - 'on': 'story-ad-view', - 'request': 'base', - 'extraUrlParams': { - 'f': 1, - 'cid': this.element.getAttribute('data-widget'), - 'h2': this.mgidMetadata_.h2, - 'rid': this.mgidMetadata_.rid, - 'tt': this.mgidMetadata_.tt, - 'ts': this.mgidMetadata_.ts, - 'iv': 13, - 'pageImp': 1, - 'pvid': this.mgidMetadata_.pvid, - 'muid': this.mgidMetadata_.muidn, - 'cbuster': - Date.now().toString() + - Math.floor(Math.random() * 1000000000 + 1), - 'v': '412|915|0|' + this.mgidMetadata_.h, - }, - }, - }, - }; - - this.ampAnalyticsElement_ = insertAnalyticsElement( - this.element, - config, - /*loadAnalytics*/ true, - false - ); - } - /** @override */ sendXhrRequest(adUrl) { return super.sendXhrRequest(adUrl).then((response) => { From 42050beba54180713d478b40bdc55e29b822b5c9 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Wed, 28 Dec 2022 16:12:49 +0200 Subject: [PATCH 16/22] VT-28545 Implement MGID Widget inside AMP Stories. VT-28614 Add cxurl and pvid params --- .../0.1/amp-ad-network-mgid-impl.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index fa03a86e4e74..ab77897640d0 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -74,6 +74,7 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { adUrlParams = adUrlParams.concat(this.getNetworkInfoParams_()); adUrlParams.push(this.getCacheBusterParam_()); adUrlParams.push(this.getDevicePixelRatioParam_()); + adUrlParams.push(this.getCxurlParam_()); adUrlParams.push(this.getPrParam_()); adUrlParams.push(this.getLuParam_()); adUrlParams.push(this.getSessionIdParam_()); @@ -297,6 +298,15 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { } } + /** + * @return {string} Current page url for ad request + * @private + */ + getCxurlParam_() { + const url = Services.documentInfoForDoc(this.element).canonicalUrl; + return 'cxurl=' + encodeURIComponent(url); + } + /** * @return {string} Session id info for ad request * @private @@ -319,10 +329,12 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { * @private */ getPvidParam_() { - Services.documentInfoForDoc(this.element).pageViewId64.then((pvid) => { - this.mgidMetadata_.pvid = pvid; - return 'pvid=' + pvid; - }); + return Services.documentInfoForDoc(this.element).pageViewId64.then( + (pvid) => { + this.mgidMetadata_.pvid = pvid; + return 'pvid=' + pvid; + } + ); } /** From 134e51f17f8f636c40c4e829183dfba9d17d9ee3 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Wed, 28 Dec 2022 17:22:27 +0200 Subject: [PATCH 17/22] VT-28545 Implement MGID Widget inside AMP Stories. VT-28615 fix empty muidn --- .../0.1/amp-ad-network-mgid-impl.js | 6 +----- .../0.1/test/test-amp-ad-network-mgid-impl.js | 7 +------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index ab77897640d0..ead8e26a26c2 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -28,12 +28,7 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { /** @private */ this.mgidMetadata_ = { - 'h': '', 'muidn': '', - 'h2': '', - 'rid': '', - 'tt': '', - 'ts': '', 'pvid': '', }; } @@ -122,6 +117,7 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { const meta = root.querySelector('#mgid_metadata'); if (meta) { this.mgidMetadata_ = JSON.parse(meta./*OK*/ innerHTML); + this.mgidMetadata_.muidn = this.mgidMetadata_.muidn.trim(); if (this.mgidMetadata_.muidn != '') { localStorage.mgMuidn = this.mgidMetadata_.muidn; } diff --git a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js index 8ed776707953..206a8857775a 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js @@ -108,7 +108,7 @@ describes.realWin( @@ -120,11 +120,6 @@ describes.realWin( const mgidImpl = new AmpAdNetworkMgidImpl(mgidImplElem); await mgidImpl.sendXhrRequest('https://fake.local'); - expect(mgidImpl.mgidMetadata_.h).to.equal('hash'); - expect(mgidImpl.mgidMetadata_.h2).to.equal('hash2'); - expect(mgidImpl.mgidMetadata_.rid).to.equal('request_id'); - expect(mgidImpl.mgidMetadata_.tt).to.equal('tt123'); - expect(mgidImpl.mgidMetadata_.ts).to.equal('ts456'); expect(mgidImpl.mgidMetadata_.muidn).to.equal('muidn123456'); }); } From 7a936600cb1759e31813af8689f481e3b1ad33c2 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Wed, 28 Dec 2022 17:56:39 +0200 Subject: [PATCH 18/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix tests --- .../0.1/test/test-amp-ad-network-mgid-impl.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js index 206a8857775a..422a320d1596 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js @@ -55,9 +55,10 @@ describes.realWin( /(\?|&)nisd=(0|1)(&|$)/, /(\?|&)cbuster=\d+(&|$)/, /(\?|&)dpr=\d+(&|$)/, - /(\?|&)ref=http%3A%2F%2Ffake.example%2F%3Ffoo%3Dbar(&|$)/, + /(\?|&)cxurl=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, /(\?|&)pr=http%3A%2F%2Ffake.example%2F%3Ffoo%3Dbar(&|$)/, /(\?|&)lu=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, + /(\?|&)pvid=[A-z0-9\-_]+(&|$)/, /(\?|&)sessionId=[0-9a-f]{8}-[0-9a-f]{5}(&|$)/, /(\?|&)implVersion=15(&|$)/, ].forEach((regexp) => { @@ -74,6 +75,9 @@ describes.realWin( .stub(viewer, 'getReferrerUrl') .returns(Promise.resolve('http://fake.example/?foo=bar')); + const documentInfo = Services.documentInfoForDoc(mgidImplElem); + documentInfo.canonicalUrl = 'http://canonical.example/?abc=xyz'; + sessionStorage['MG_Session_pr'] = 'http://stored-pr.example/?foo=bar'; sessionStorage['MG_Session_lu'] = 'http://stored-lu.example/?abc=xyz'; sessionStorage['MG_Session_Id'] = 'stored-session'; @@ -88,10 +92,11 @@ describes.realWin( /(\?|&)nisd=(0|1)(&|$)/, /(\?|&)cbuster=\d+(&|$)/, /(\?|&)dpr=\d+(&|$)/, - /(\?|&)ref=http%3A%2F%2Ffake.example%2F%3Ffoo%3Dbar(&|$)/, + /(\?|&)cxurl=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, /(\?|&)pr=http%3A%2F%2Fstored-pr.example%2F%3Ffoo%3Dbar(&|$)/, /(\?|&)lu=http%3A%2F%2Fstored-lu.example%2F%3Fabc%3Dxyz(&|$)/, /(\?|&)sessionId=stored-session(&|$)/, + /(\?|&)pvid=[A-z0-9\-_]+(&|$)/, /(\?|&)muid=qwerty123456(&|$)/, /(\?|&)implVersion=15(&|$)/, ].forEach((regexp) => { From 38c1252efd17ac1bcd44f8e388cba0c57ff6da87 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Thu, 29 Dec 2022 15:31:49 +0200 Subject: [PATCH 19/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix pr parameter --- .../amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index ead8e26a26c2..589cf0c0cd7b 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -275,7 +275,7 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { const matchDomain = referrer.match(/:\/\/([^\/:]+)/i); sessionStorage['MG_Session_pr'] = matchDomain && matchDomain[1] ? matchDomain[1] : ''; - return 'pr=' + encodeURIComponent(referrer); + return 'pr=' + encodeURIComponent(sessionStorage['MG_Session_pr']); }); } } From 1d2b6be7ec3f4666b7989a67a85a3ebbcdd1d9a1 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Thu, 29 Dec 2022 16:02:09 +0200 Subject: [PATCH 20/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix pr parameter tests --- .../0.1/test/test-amp-ad-network-mgid-impl.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js index 422a320d1596..4b96c9a74562 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js @@ -56,7 +56,7 @@ describes.realWin( /(\?|&)cbuster=\d+(&|$)/, /(\?|&)dpr=\d+(&|$)/, /(\?|&)cxurl=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, - /(\?|&)pr=http%3A%2F%2Ffake.example%2F%3Ffoo%3Dbar(&|$)/, + /(\?|&)pr=fake.example(&|$)/, /(\?|&)lu=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, /(\?|&)pvid=[A-z0-9\-_]+(&|$)/, /(\?|&)sessionId=[0-9a-f]{8}-[0-9a-f]{5}(&|$)/, @@ -78,7 +78,7 @@ describes.realWin( const documentInfo = Services.documentInfoForDoc(mgidImplElem); documentInfo.canonicalUrl = 'http://canonical.example/?abc=xyz'; - sessionStorage['MG_Session_pr'] = 'http://stored-pr.example/?foo=bar'; + sessionStorage['MG_Session_pr'] = 'stored-pr.example'; sessionStorage['MG_Session_lu'] = 'http://stored-lu.example/?abc=xyz'; sessionStorage['MG_Session_Id'] = 'stored-session'; localStorage.mgMuidn = 'qwerty123456'; @@ -93,7 +93,7 @@ describes.realWin( /(\?|&)cbuster=\d+(&|$)/, /(\?|&)dpr=\d+(&|$)/, /(\?|&)cxurl=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, - /(\?|&)pr=http%3A%2F%2Fstored-pr.example%2F%3Ffoo%3Dbar(&|$)/, + /(\?|&)pr=stored-pr.example(&|$)/, /(\?|&)lu=http%3A%2F%2Fstored-lu.example%2F%3Fabc%3Dxyz(&|$)/, /(\?|&)sessionId=stored-session(&|$)/, /(\?|&)pvid=[A-z0-9\-_]+(&|$)/, From 33dede734e63a78bbfac44a5a989c66fbb0d01cf Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Thu, 12 Jan 2023 14:49:29 +0200 Subject: [PATCH 21/22] VT-28545 Implement MGID Widget inside AMP Stories. Remove localStorage and sessionStorage usage --- build-system/test-configs/forbidden-terms.js | 2 - docs/spec/amp-localstorage.md | 1 - .../0.1/amp-ad-network-mgid-impl.js | 124 ++++-------------- .../0.1/test/test-amp-ad-network-mgid-impl.js | 64 +-------- 4 files changed, 30 insertions(+), 161 deletions(-) diff --git a/build-system/test-configs/forbidden-terms.js b/build-system/test-configs/forbidden-terms.js index be6c78da96d1..a685b786ac46 100644 --- a/build-system/test-configs/forbidden-terms.js +++ b/build-system/test-configs/forbidden-terms.js @@ -461,7 +461,6 @@ const forbiddenTermsGlobal = { message: requiresReviewPrivacy, allowlist: [ 'extensions/amp-access/0.1/amp-access-iframe.js', - 'extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js', 'extensions/amp-script/0.1/amp-script.js', 'extensions/amp-story/1.0/history.js', 'extensions/amp-web-push/0.1/amp-web-push-helper-frame.js', @@ -480,7 +479,6 @@ const forbiddenTermsGlobal = { allowlist: [ 'extensions/amp-access/0.1/amp-access-iframe.js', 'extensions/amp-accordion/0.1/amp-accordion.js', - 'extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js', 'extensions/amp-script/0.1/amp-script.js', 'testing/fake-dom.js', ], diff --git a/docs/spec/amp-localstorage.md b/docs/spec/amp-localstorage.md index 18a6eeb50e6e..82f233cc8653 100644 --- a/docs/spec/amp-localstorage.md +++ b/docs/spec/amp-localstorage.md @@ -43,7 +43,6 @@ The following AMP components and service are using the localStorage. - `` : Store the user decision and granular information on consent - Client ID Service : Store the user decision to opt-out CID service. - Consent Instance : Store the user consent decision. -- `` : Store user ID for proper ad targeting. ## Guidance on localStorage Usage in AMP diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index 589cf0c0cd7b..b16bfe235119 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -25,12 +25,6 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { /** @private {?Element} */ this.ampAnalyticsElement_ = null; - - /** @private */ - this.mgidMetadata_ = { - 'muidn': '', - 'pvid': '', - }; } /** @override */ @@ -71,12 +65,8 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { adUrlParams.push(this.getDevicePixelRatioParam_()); adUrlParams.push(this.getCxurlParam_()); adUrlParams.push(this.getPrParam_()); - adUrlParams.push(this.getLuParam_()); - adUrlParams.push(this.getSessionIdParam_()); adUrlParams.push(this.getPvidParam_()); - if (localStorage.mgMuidn) { - adUrlParams.push('muid=' + localStorage.mgMuidn); - } + adUrlParams.push(this.getMuidParam_()); adUrlParams.push('implVersion=15'); return Promise.allSettled(adUrlParams).then((params) => { @@ -102,35 +92,6 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { }); } - /** @override */ - sendXhrRequest(adUrl) { - return super.sendXhrRequest(adUrl).then((response) => { - if (!response) { - return null; - } - const {headers, status} = - /** @type {{status: number, headers: !Headers}} */ (response); - - return response.text().then((responseText) => { - const doc = new DOMParser().parseFromString(responseText, 'text/html'); - const root = doc.documentElement; - const meta = root.querySelector('#mgid_metadata'); - if (meta) { - this.mgidMetadata_ = JSON.parse(meta./*OK*/ innerHTML); - this.mgidMetadata_.muidn = this.mgidMetadata_.muidn.trim(); - if (this.mgidMetadata_.muidn != '') { - localStorage.mgMuidn = this.mgidMetadata_.muidn; - } - } - - return new Response(responseText, { - status, - headers, - }); - }); - }); - } - /** * @param {!ConsentTupleDef=} consentTuple * @return {string[]} Consents parameters. @@ -185,18 +146,12 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { * @private */ getPageParam_() { - const widget = this.element.getAttribute('data-widget'); - let page = 1; - if (sessionStorage[`MG_widget_${widget}_page`]) { - page = sessionStorage[`MG_widget_${widget}_page`]; - if (page++ > 20) { - page = 1; - } - } - - sessionStorage[`MG_widget_${widget}_page`] = page; - - return page; + const counter = Services.urlReplacementsForDoc( + this.element + )./*OK*/ expandStringSync('COUNTER', undefined, { + 'COUNTER': true, + }); + return parseInt(counter, 10) % 20; } /** @@ -264,34 +219,17 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { } /** - * @return {string} Primary referrer info for ad request + * @return {string} Referrer info for ad request * @private */ getPrParam_() { - if (sessionStorage['MG_Session_pr']) { - return 'pr=' + encodeURIComponent(sessionStorage['MG_Session_pr']); - } else { - return this.getReferrer_(10).then((referrer) => { - const matchDomain = referrer.match(/:\/\/([^\/:]+)/i); - sessionStorage['MG_Session_pr'] = - matchDomain && matchDomain[1] ? matchDomain[1] : ''; - return 'pr=' + encodeURIComponent(sessionStorage['MG_Session_pr']); - }); - } - } - - /** - * @return {string} First session page info for ad request - * @private - */ - getLuParam_() { - if (sessionStorage['MG_Session_lu']) { - return 'lu=' + encodeURIComponent(sessionStorage['MG_Session_lu']); - } else { - const url = Services.documentInfoForDoc(this.element).canonicalUrl; - sessionStorage['MG_Session_lu'] = url; - return 'lu=' + encodeURIComponent(url); - } + return this.getReferrer_(10).then((referrer) => { + const matchDomain = referrer.match(/:\/\/([^\/:]+)/i); + return ( + 'pr=' + + encodeURIComponent(matchDomain && matchDomain[1] ? matchDomain[1] : '') + ); + }); } /** @@ -303,23 +241,6 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { return 'cxurl=' + encodeURIComponent(url); } - /** - * @return {string} Session id info for ad request - * @private - */ - getSessionIdParam_() { - if (sessionStorage['MG_Session_Id']) { - return 'sessionId=' + sessionStorage['MG_Session_Id']; - } else { - const sessionId = - Math.round(Date.now() / 1000).toString(16) + - '-' + - ('00000' + Math.round(Math.random() * 100000).toString(16)).slice(-5); - sessionStorage['MG_Session_Id'] = sessionId; - return 'sessionId=' + sessionId; - } - } - /** * @return {string} Pageview info for ad request * @private @@ -327,12 +248,25 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { getPvidParam_() { return Services.documentInfoForDoc(this.element).pageViewId64.then( (pvid) => { - this.mgidMetadata_.pvid = pvid; return 'pvid=' + pvid; } ); } + /** + * @return {string} Muidn info for ad request + * @private + */ + getMuidParam_() { + return Services.urlReplacementsForDoc(this.element) + ./*OK*/ expandStringAsync('CLIENT_ID(muidn)', undefined, { + 'CLIENT_ID': true, + }) + .then((r) => { + return 'muid=' + r; + }); + } + /** * Returns the referrer or undefined if the referrer is not resolved * before the given timeout diff --git a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js index 4b96c9a74562..b961017f5de2 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/test/test-amp-ad-network-mgid-impl.js @@ -57,75 +57,13 @@ describes.realWin( /(\?|&)dpr=\d+(&|$)/, /(\?|&)cxurl=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, /(\?|&)pr=fake.example(&|$)/, - /(\?|&)lu=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, /(\?|&)pvid=[A-z0-9\-_]+(&|$)/, - /(\?|&)sessionId=[0-9a-f]{8}-[0-9a-f]{5}(&|$)/, + /(\?|&)muid=amp-[A-z0-9\-_]+(&|$)/, /(\?|&)implVersion=15(&|$)/, ].forEach((regexp) => { expect(url).to.match(regexp); }); }); }); - - it('generates correct adUrl (data from storage)', () => { - mgidImplElem.setAttribute('data-widget', '100'); - - const viewer = Services.viewerForDoc(mgidImplElem); - env.sandbox - .stub(viewer, 'getReferrerUrl') - .returns(Promise.resolve('http://fake.example/?foo=bar')); - - const documentInfo = Services.documentInfoForDoc(mgidImplElem); - documentInfo.canonicalUrl = 'http://canonical.example/?abc=xyz'; - - sessionStorage['MG_Session_pr'] = 'stored-pr.example'; - sessionStorage['MG_Session_lu'] = 'http://stored-lu.example/?abc=xyz'; - sessionStorage['MG_Session_Id'] = 'stored-session'; - localStorage.mgMuidn = 'qwerty123456'; - - const mgidImpl = new AmpAdNetworkMgidImpl(mgidImplElem); - - return mgidImpl.getAdUrl().then((url) => { - [ - /^https:\/\/servicer\.mgid\.com\/100\/2/, - /(\?|&)niet=(slow-2g|2g|3g|4g)(&|$)/, - /(\?|&)nisd=(0|1)(&|$)/, - /(\?|&)cbuster=\d+(&|$)/, - /(\?|&)dpr=\d+(&|$)/, - /(\?|&)cxurl=http%3A%2F%2Fcanonical.example%2F%3Fabc%3Dxyz(&|$)/, - /(\?|&)pr=stored-pr.example(&|$)/, - /(\?|&)lu=http%3A%2F%2Fstored-lu.example%2F%3Fabc%3Dxyz(&|$)/, - /(\?|&)sessionId=stored-session(&|$)/, - /(\?|&)pvid=[A-z0-9\-_]+(&|$)/, - /(\?|&)muid=qwerty123456(&|$)/, - /(\?|&)implVersion=15(&|$)/, - ].forEach((regexp) => { - expect(url).to.match(regexp); - }); - }); - }); - - it('test sendXhrRequest', async () => { - env.win.fetch = env.fetchMock.realFetch; - const creative = ` - - - - - - - - `; - - env.fetchMock.mock('begin:https://fake.local', creative); - - mgidImplElem.setAttribute('data-widget', '100'); - - const mgidImpl = new AmpAdNetworkMgidImpl(mgidImplElem); - await mgidImpl.sendXhrRequest('https://fake.local'); - expect(mgidImpl.mgidMetadata_.muidn).to.equal('muidn123456'); - }); } ); From 3858385fabe0e3c6fee20e57bdedb76506cd45f8 Mon Sep 17 00:00:00 2001 From: Alexander Velichkin Date: Tue, 24 Jan 2023 12:05:49 +0200 Subject: [PATCH 22/22] VT-28545 Implement MGID Widget inside AMP Stories. Fix pvurl; hasown usage. --- .../0.1/amp-ad-network-mgid-impl.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js index b16bfe235119..755620a92c39 100644 --- a/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js +++ b/extensions/amp-ad-network-mgid-impl/0.1/amp-ad-network-mgid-impl.js @@ -3,6 +3,7 @@ import { CONSENT_STRING_TYPE, } from '#core/constants/consent-state'; import {createElementWithAttributes, removeElement} from '#core/dom'; +import {hasOwn} from '#core/types/object'; import {Services} from '#service'; @@ -14,7 +15,7 @@ import {AmpA4A} from '../../amp-a4a/0.1/amp-a4a'; const TAG = 'amp-ad-network-mgid-impl'; const BASE_URL_ = 'https://servicer.mgid.com/'; -const PV_URL_ = 'https://c.mgid.com/'; +const PV_URL_ = 'https://c.mgid.com/pv/'; export class AmpAdNetworkMgidImpl extends AmpA4A { /** @@ -58,7 +59,6 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { const widget = this.element.getAttribute('data-widget'); let servicerUrl = BASE_URL_ + widget + '/' + this.getPageParam_(); - let pvUrl = PV_URL_ + 'pv/'; adUrlParams = adUrlParams.concat(this.getNetworkInfoParams_()); adUrlParams.push(this.getCacheBusterParam_()); @@ -74,14 +74,13 @@ export class AmpAdNetworkMgidImpl extends AmpA4A { params.forEach((result) => data.push(result.value)); const joinedParams = '?' + data.join('&'); servicerUrl += joinedParams; - pvUrl += joinedParams; - if (typeof this.win['_mgAmpStoryPV'] == 'undefined') { + if (!hasOwn(this.win, '_mgAmpStoryPV')) { this.getAmpDoc() .getBody() .appendChild( createElementWithAttributes(this.win.document, 'amp-pixel', { - 'src': pvUrl, + 'src': PV_URL_ + joinedParams, }) );