prebid.js 8.1.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/.devcontainer/devcontainer.json +3 -1
  2. package/dist/33acrossBidAdapter.js +1 -1
  3. package/dist/33acrossIdSystem.js +1 -1
  4. package/dist/adagioBidAdapter.js +1 -1
  5. package/dist/adbookpspBidAdapter.js +1 -1
  6. package/dist/adgenerationBidAdapter.js +1 -1
  7. package/dist/admanBidAdapter.js +1 -1
  8. package/dist/adqueryBidAdapter.js +1 -1
  9. package/dist/adrelevantisBidAdapter.js +1 -1
  10. package/dist/adtrgtmeBidAdapter.js +1 -1
  11. package/dist/adxcgBidAdapter.js +1 -1
  12. package/dist/adyoulikeBidAdapter.js +1 -1
  13. package/dist/ajaBidAdapter.js +1 -1
  14. package/dist/amxBidAdapter.js +1 -1
  15. package/dist/amxIdSystem.js +1 -1
  16. package/dist/appierAnalyticsAdapter.js +1 -1
  17. package/dist/appnexusBidAdapter.js +1 -1
  18. package/dist/asoBidAdapter.js +1 -1
  19. package/dist/axonixBidAdapter.js +1 -1
  20. package/dist/bidglassBidAdapter.js +1 -1
  21. package/dist/big-richmediaBidAdapter.js +1 -1
  22. package/dist/bridBidAdapter.js +1 -0
  23. package/dist/bridgewellBidAdapter.js +1 -1
  24. package/dist/brightMountainMediaBidAdapter.js +1 -1
  25. package/dist/carodaBidAdapter.js +1 -1
  26. package/dist/categoryTranslation.js +1 -1
  27. package/dist/chtnwBidAdapter.js +1 -1
  28. package/dist/cmp.js +1 -0
  29. package/dist/concertBidAdapter.js +1 -1
  30. package/dist/connectadBidAdapter.js +1 -1
  31. package/dist/consentManagement.js +1 -1
  32. package/dist/consentManagementGpp.js +1 -1
  33. package/dist/consentManagementUsp.js +1 -1
  34. package/dist/consumableBidAdapter.js +1 -1
  35. package/dist/conversantAnalyticsAdapter.js +1 -1
  36. package/dist/conversantBidAdapter.js +1 -1
  37. package/dist/craftBidAdapter.js +1 -1
  38. package/dist/criteoBidAdapter.js +1 -1
  39. package/dist/cwireBidAdapter.js +1 -1
  40. package/dist/dependencies.json +12 -0
  41. package/dist/dspxBidAdapter.js +1 -1
  42. package/dist/eplanningBidAdapter.js +1 -1
  43. package/dist/euidIdSystem.js +1 -1
  44. package/dist/feedadBidAdapter.js +1 -1
  45. package/dist/finativeBidAdapter.js +1 -1
  46. package/dist/freewheel-sspBidAdapter.js +1 -1
  47. package/dist/gmosspBidAdapter.js +1 -1
  48. package/dist/goldbachBidAdapter.js +1 -1
  49. package/dist/gppControl_usnat.js +1 -0
  50. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  51. package/dist/greenbidsRtdProvider.js +1 -1
  52. package/dist/gridBidAdapter.js +1 -1
  53. package/dist/gumgumBidAdapter.js +1 -1
  54. package/dist/h12mediaBidAdapter.js +1 -1
  55. package/dist/hypelabBidAdapter.js +1 -1
  56. package/dist/id5IdSystem.js +1 -1
  57. package/dist/imdsBidAdapter.js +1 -1
  58. package/dist/improvedigitalBidAdapter.js +1 -1
  59. package/dist/insticatorBidAdapter.js +1 -1
  60. package/dist/intentIqIdSystem.js +1 -1
  61. package/dist/ixBidAdapter.js +1 -1
  62. package/dist/justpremiumBidAdapter.js +1 -1
  63. package/dist/kargoBidAdapter.js +1 -1
  64. package/dist/konduitAnalyticsAdapter.js +1 -1
  65. package/dist/kueezBidAdapter.js +1 -1
  66. package/dist/kueezRtbBidAdapter.js +1 -1
  67. package/dist/kulturemediaBidAdapter.js +1 -1
  68. package/dist/lassoBidAdapter.js +1 -1
  69. package/dist/lifestreetBidAdapter.js +1 -1
  70. package/dist/liveIntentIdSystem.js +1 -1
  71. package/dist/logicadBidAdapter.js +1 -1
  72. package/dist/loglyliftBidAdapter.js +1 -1
  73. package/dist/magniteAnalyticsAdapter.js +1 -1
  74. package/dist/malltvAnalyticsAdapter.js +1 -1
  75. package/dist/marsmediaBidAdapter.js +1 -1
  76. package/dist/mediafuseBidAdapter.js +1 -1
  77. package/dist/mediasquareBidAdapter.js +1 -1
  78. package/dist/mgidBidAdapter.js +1 -1
  79. package/dist/minutemediaBidAdapter.js +1 -1
  80. package/dist/minutemediaplusBidAdapter.js +1 -1
  81. package/dist/mspa.js +1 -0
  82. package/dist/nexx360BidAdapter.js +1 -1
  83. package/dist/not-for-prod/prebid.js +136 -132
  84. package/dist/oguryBidAdapter.js +1 -1
  85. package/dist/onetagBidAdapter.js +1 -1
  86. package/dist/ooloAnalyticsAdapter.js +1 -1
  87. package/dist/optidigitalBidAdapter.js +1 -1
  88. package/dist/outbrainBidAdapter.js +1 -1
  89. package/dist/oxxionRtdProvider.js +1 -1
  90. package/dist/parrableIdSystem.js +1 -1
  91. package/dist/pixfutureBidAdapter.js +1 -1
  92. package/dist/prebid-core.js +1 -1
  93. package/dist/publinkIdSystem.js +1 -1
  94. package/dist/pubmaticBidAdapter.js +1 -1
  95. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  96. package/dist/pxyzBidAdapter.js +1 -1
  97. package/dist/quantcastBidAdapter.js +1 -1
  98. package/dist/readpeakBidAdapter.js +1 -1
  99. package/dist/relaidoBidAdapter.js +1 -1
  100. package/dist/retailspotBidAdapter.js +1 -1
  101. package/dist/rhythmoneBidAdapter.js +1 -1
  102. package/dist/riseBidAdapter.js +1 -1
  103. package/dist/rubiconBidAdapter.js +1 -1
  104. package/dist/seedingAllianceBidAdapter.js +1 -1
  105. package/dist/seedtagBidAdapter.js +1 -1
  106. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  107. package/dist/sharethroughBidAdapter.js +1 -1
  108. package/dist/shinezBidAdapter.js +1 -1
  109. package/dist/smaatoBidAdapter.js +1 -1
  110. package/dist/smartadserverBidAdapter.js +1 -1
  111. package/dist/smartxBidAdapter.js +1 -1
  112. package/dist/smilewantedBidAdapter.js +1 -1
  113. package/dist/sonobiBidAdapter.js +1 -1
  114. package/dist/sovrnAnalyticsAdapter.js +1 -1
  115. package/dist/sovrnBidAdapter.js +1 -1
  116. package/dist/sspBCBidAdapter.js +1 -1
  117. package/dist/stvBidAdapter.js +1 -1
  118. package/dist/sublimeBidAdapter.js +1 -1
  119. package/dist/targetVideoBidAdapter.js +1 -1
  120. package/dist/teadsBidAdapter.js +1 -1
  121. package/dist/trionBidAdapter.js +1 -1
  122. package/dist/tripleliftBidAdapter.js +1 -1
  123. package/dist/ttdBidAdapter.js +1 -1
  124. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  125. package/dist/uid2IdSystem.js +1 -1
  126. package/dist/underdogmediaBidAdapter.js +1 -1
  127. package/dist/undertoneBidAdapter.js +1 -1
  128. package/dist/userId.js +1 -1
  129. package/dist/vidazooBidAdapter.js +1 -1
  130. package/dist/videobyteBidAdapter.js +1 -1
  131. package/dist/visxBidAdapter.js +1 -1
  132. package/dist/vuukleBidAdapter.js +1 -1
  133. package/dist/widespaceBidAdapter.js +1 -1
  134. package/dist/winrBidAdapter.js +1 -1
  135. package/dist/yahoosspBidAdapter.js +1 -1
  136. package/dist/yieldmoBidAdapter.js +1 -1
  137. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  138. package/libraries/cmp/cmpClient.js +139 -0
  139. package/libraries/mspa/activityControls.js +91 -0
  140. package/modules/admanBidAdapter.js +5 -0
  141. package/modules/bridBidAdapter.js +223 -0
  142. package/modules/categoryTranslation.js +3 -2
  143. package/modules/consentManagement.js +12 -86
  144. package/modules/consentManagementGpp.js +47 -126
  145. package/modules/consentManagementUsp.js +19 -97
  146. package/modules/gppControl_usnat.js +11 -0
  147. package/modules/intentIqIdSystem.js +6 -5
  148. package/modules/liveIntentIdSystem.js +8 -3
  149. package/modules/mediasquareBidAdapter.js +19 -23
  150. package/modules/oxxionRtdProvider.js +124 -11
  151. package/modules/oxxionRtdProvider.md +19 -4
  152. package/modules/tripleliftBidAdapter.js +21 -1
  153. package/modules/userId/eids.js +29 -0
  154. package/modules/userId/eids.md +19 -2
  155. package/modules/userId/index.js +78 -29
  156. package/modules/userId/userId.md +3 -0
  157. package/package.json +1 -1
  158. package/src/activities/params.js +4 -1
  159. package/test/spec/libraries/cmp/cmpClient_spec.js +233 -0
  160. package/test/spec/libraries/mspa/activityControls_spec.js +315 -0
  161. package/test/spec/modules/admanBidAdapter_spec.js +8 -2
  162. package/test/spec/modules/adqueryBidAdapter_spec.js +5 -1
  163. package/test/spec/modules/bridBidAdapter_spec.js +129 -0
  164. package/test/spec/modules/byDataAnalyticsAdapter_spec.js +9 -7
  165. package/test/spec/modules/chtnwBidAdapter_spec.js +4 -1
  166. package/test/spec/modules/consentManagementGpp_spec.js +84 -7
  167. package/test/spec/modules/consentManagement_spec.js +8 -18
  168. package/test/spec/modules/datablocksBidAdapter_spec.js +7 -3
  169. package/test/spec/modules/eids_spec.js +87 -0
  170. package/test/spec/modules/insticatorBidAdapter_spec.js +6 -2
  171. package/test/spec/modules/intentIqIdSystem_spec.js +36 -2
  172. package/test/spec/modules/ixBidAdapter_spec.js +40 -12
  173. package/test/spec/modules/lassoBidAdapter_spec.js +6 -4
  174. package/test/spec/modules/liveIntentIdMinimalSystem_spec.js +17 -2
  175. package/test/spec/modules/liveIntentIdSystem_spec.js +11 -6
  176. package/test/spec/modules/mediasquareBidAdapter_spec.js +3 -0
  177. package/test/spec/modules/onetagBidAdapter_spec.js +81 -75
  178. package/test/spec/modules/orbidderBidAdapter_spec.js +6 -3
  179. package/test/spec/modules/oxxionRtdProvider_spec.js +113 -1
  180. package/test/spec/modules/relaidoBidAdapter_spec.js +4 -3
  181. package/test/spec/modules/tripleliftBidAdapter_spec.js +20 -1
  182. package/test/spec/modules/ucfunnelBidAdapter_spec.js +37 -16
  183. package/test/spec/modules/userId_spec.js +393 -6
@@ -0,0 +1,139 @@
1
+ import {GreedyPromise} from '../../src/utils/promise.js';
2
+
3
+ /**
4
+ * @typedef {function} CMPClient
5
+ *
6
+ * @param {{}} params CMP parameters. Currently this is a subset of {command, callback, parameter, version}.
7
+ * @returns {Promise<*>} a promise that:
8
+ * - if a `callback` param was provided, resolves (with no result) just before the first time it's run;
9
+ * - if `callback` was *not* provided, resolves to the return value of the CMP command
10
+ * @property {boolean} isDirect true if the CMP is directly accessible (no postMessage required)
11
+ */
12
+
13
+ /**
14
+ * Returns a function that can interface with a CMP regardless of where it's located.
15
+ *
16
+ * @param apiName name of the CMP api, e.g. "__gpp"
17
+ * @param apiVersion? CMP API version
18
+ * @param apiArgs? names of the arguments taken by the api function, in order.
19
+ * @param callbackArgs? names of the cross-frame response payload properties that should be passed as callback arguments, in order
20
+ * @param win
21
+ * @returns {CMPClient} CMP invocation function (or null if no CMP was found).
22
+ */
23
+ export function cmpClient(
24
+ {
25
+ apiName,
26
+ apiVersion,
27
+ apiArgs = ['command', 'callback', 'parameter', 'version'],
28
+ callbackArgs = ['returnValue', 'success'],
29
+ },
30
+ win = window
31
+ ) {
32
+ const cmpCallbacks = {};
33
+ const callName = `${apiName}Call`;
34
+ const cmpDataPkgName = `${apiName}Return`;
35
+
36
+ function handleMessage(event) {
37
+ const json = (typeof event.data === 'string' && event.data.includes(cmpDataPkgName)) ? JSON.parse(event.data) : event.data;
38
+ if (json?.[cmpDataPkgName]?.callId) {
39
+ const payload = json[cmpDataPkgName];
40
+
41
+ if (cmpCallbacks.hasOwnProperty(payload.callId)) {
42
+ cmpCallbacks[payload.callId](...callbackArgs.map(name => payload[name]));
43
+ }
44
+ }
45
+ }
46
+
47
+ function findCMP() {
48
+ let f = win;
49
+ let cmpFrame;
50
+ let isDirect = false;
51
+ while (f != null) {
52
+ try {
53
+ if (typeof f[apiName] === 'function') {
54
+ cmpFrame = f;
55
+ isDirect = true;
56
+ break;
57
+ }
58
+ } catch (e) {
59
+ }
60
+
61
+ // need separate try/catch blocks due to the exception errors thrown when trying to check for a frame that doesn't exist in 3rd party env
62
+ try {
63
+ if (f.frames[`${apiName}Locator`]) {
64
+ cmpFrame = f;
65
+ break;
66
+ }
67
+ } catch (e) {
68
+ }
69
+
70
+ if (f === win.top) break;
71
+ f = f.parent;
72
+ }
73
+
74
+ return [
75
+ cmpFrame,
76
+ isDirect
77
+ ];
78
+ }
79
+
80
+ const [cmpFrame, isDirect] = findCMP();
81
+
82
+ if (!cmpFrame) {
83
+ return;
84
+ }
85
+
86
+ function resolveParams(params) {
87
+ params = Object.assign({version: apiVersion}, params);
88
+ return apiArgs.map(arg => [arg, params[arg]])
89
+ }
90
+
91
+ function wrapCallback(callback, resolve, reject, preamble) {
92
+ return function (result, success) {
93
+ preamble && preamble();
94
+ const resolver = success == null || success ? resolve : reject;
95
+ if (typeof callback === 'function') {
96
+ resolver();
97
+ return callback.apply(this, arguments);
98
+ } else {
99
+ resolver(result);
100
+ }
101
+ }
102
+ }
103
+
104
+ let client;
105
+
106
+ if (isDirect) {
107
+ client = function invokeCMPDirect(params = {}) {
108
+ return new GreedyPromise((resolve, reject) => {
109
+ const ret = cmpFrame[apiName](...resolveParams({
110
+ ...params,
111
+ callback: params.callback && wrapCallback(params.callback, resolve, reject)
112
+ }).map(([_, val]) => val));
113
+ if (params.callback == null) {
114
+ resolve(ret);
115
+ }
116
+ });
117
+ };
118
+ } else {
119
+ win.addEventListener('message', handleMessage, false);
120
+
121
+ client = function invokeCMPFrame(params) {
122
+ return new GreedyPromise((resolve, reject) => {
123
+ // call CMP via postMessage
124
+ const callId = Math.random().toString();
125
+ const msg = {
126
+ [callName]: {
127
+ ...Object.fromEntries(resolveParams(params).filter(([param]) => param !== 'callback')),
128
+ callId: callId
129
+ }
130
+ };
131
+
132
+ cmpCallbacks[callId] = wrapCallback(params?.callback, resolve, reject, params?.callback == null && (() => { delete cmpCallbacks[callId] }));
133
+ cmpFrame.postMessage(msg, '*');
134
+ });
135
+ };
136
+ }
137
+ client.isDirect = isDirect;
138
+ return client;
139
+ }
@@ -0,0 +1,91 @@
1
+ import {registerActivityControl} from '../../src/activities/rules.js';
2
+ import {
3
+ ACTIVITY_ENRICH_EIDS,
4
+ ACTIVITY_ENRICH_UFPD,
5
+ ACTIVITY_SYNC_USER,
6
+ ACTIVITY_TRANSMIT_PRECISE_GEO
7
+ } from '../../src/activities/activities.js';
8
+ import {gppDataHandler} from '../../src/adapterManager.js';
9
+
10
+ // default interpretation for MSPA consent(s):
11
+ // https://docs.google.com/document/d/1yPEVx3bBdSkIw-QNkQR5qi1ztmn9zoXk-LaGQB6iw7Q
12
+
13
+ export function isBasicConsentDenied(cd) {
14
+ // service provider mode is always consent denied
15
+ return ['MspaServiceProviderMode', 'Gpc'].some(prop => cd[prop] === 1) ||
16
+ // you cannot consent to what you were not notified of
17
+ cd.PersonalDataConsents === 2 ||
18
+ // minors 13+ who have not given consent
19
+ cd.KnownChildSensitiveDataConsents[0] === 1 ||
20
+ // minors under 13 cannot consent
21
+ cd.KnownChildSensitiveDataConsents[1] !== 0 ||
22
+ // sensitive category consent impossible without notice
23
+ (cd.SensitiveDataProcessing.some(element => element === 2) && (cd.SensitiveDataLimitUseNotice !== 1 || cd.SensitiveDataProcessingOptOutNotice !== 1));
24
+ }
25
+
26
+ export function isSensitiveNoticeMissing(cd) {
27
+ return ['SensitiveDataProcessingOptOutNotice', 'SensitiveDataLimitUseNotice'].some(prop => cd[prop] === 2)
28
+ }
29
+
30
+ export function isConsentDenied(cd) {
31
+ return isBasicConsentDenied(cd) ||
32
+ // user opts out
33
+ (['SaleOptOut', 'SharingOptOut', 'TargetedAdvertisingOptOut'].some(prop => cd[prop] === 1)) ||
34
+ // notice not given
35
+ (['SaleOptOutNotice', 'SharingNotice', 'SharingOptOutNotice', 'TargetedAdvertisingOptOutNotice'].some(prop => cd[prop] === 2)) ||
36
+ // sale opted in but notice does not apply
37
+ ((cd.SaleOptOut === 2 && cd.SaleOptOutNotice === 0)) ||
38
+ // targeted opted in but notice does not apply
39
+ ((cd.TargetedAdvertisingOptOut === 2 && cd.TargetedAdvertisingOptOutNotice === 0)) ||
40
+ // sharing opted in but notices do not apply
41
+ ((cd.SharingOptOut === 2 && (cd.SharingNotice === 0 || cd.SharingOptOutNotice === 0)));
42
+ }
43
+
44
+ export function isTransmitUfpdConsentDenied(cd) {
45
+ // SensitiveDataProcessing[1-5,11]=1 OR SensitiveDataProcessing[6,7,9,10,12]<>0 OR
46
+ const mustBeZero = [6, 7, 9, 10, 12];
47
+ const mustBeZeroSubtractedVector = mustBeZero.map((number) => number - 1);
48
+ const SensitiveDataProcessingMustBeZero = cd.SensitiveDataProcessing.filter(index => mustBeZeroSubtractedVector.includes(index));
49
+ const cannotBeOne = [1, 2, 3, 4, 5, 11];
50
+ const cannotBeOneSubtractedVector = cannotBeOne.map((number) => number - 1);
51
+ const SensitiveDataProcessingCannotBeOne = cd.SensitiveDataProcessing.filter(index => cannotBeOneSubtractedVector.includes(index));
52
+ return isConsentDenied(cd) ||
53
+ isSensitiveNoticeMissing(cd) ||
54
+ SensitiveDataProcessingCannotBeOne.some(val => val === 1) ||
55
+ SensitiveDataProcessingMustBeZero.some(val => val !== 0);
56
+ }
57
+
58
+ export function isTransmitGeoConsentDenied(cd) {
59
+ return isBasicConsentDenied(cd) ||
60
+ isSensitiveNoticeMissing(cd) ||
61
+ cd.SensitiveDataProcessing[7] === 1
62
+ }
63
+
64
+ const CONSENT_RULES = {
65
+ [ACTIVITY_SYNC_USER]: isConsentDenied,
66
+ [ACTIVITY_ENRICH_EIDS]: isConsentDenied,
67
+ [ACTIVITY_ENRICH_UFPD]: isTransmitUfpdConsentDenied,
68
+ [ACTIVITY_TRANSMIT_PRECISE_GEO]: isTransmitGeoConsentDenied
69
+ }
70
+
71
+ export function mspaRule(sids, getConsent, denies, applicableSids = () => gppDataHandler.getConsentData()?.applicableSections) {
72
+ return function() {
73
+ if (applicableSids().some(sid => sids.includes(sid))) {
74
+ const consent = getConsent();
75
+ if (consent == null) {
76
+ return {allow: false, reason: 'consent data not available'};
77
+ }
78
+ if (denies(consent)) {
79
+ return {allow: false}
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ export function setupRules(api, sids, normalizeConsent = (c) => c, rules = CONSENT_RULES, registerRule = registerActivityControl, getConsentData = () => gppDataHandler.getConsentData()) {
86
+ const unreg = [];
87
+ Object.entries(rules).forEach(([activity, denies]) => {
88
+ unreg.push(registerRule(activity, `MSPA (${api})`, mspaRule(sids, () => normalizeConsent(getConsentData()?.sectionData?.[api]), denies, () => getConsentData()?.applicableSections || [])))
89
+ })
90
+ return () => unreg.forEach(ur => ur())
91
+ }
@@ -109,6 +109,11 @@ export const spec = {
109
109
  bidFloor: getBidFloor(bid)
110
110
  }
111
111
 
112
+ if (bid.transactionId) {
113
+ placement.ext = placement.ext || {};
114
+ placement.ext.tid = bid.transactionId;
115
+ }
116
+
112
117
  if (bid.schain) {
113
118
  placement.schain = bid.schain;
114
119
  }
@@ -0,0 +1,223 @@
1
+ import {createTrackPixelHtml, _each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js';
2
+ import {VIDEO} from '../src/mediaTypes.js';
3
+ import {registerBidder} from '../src/adapters/bidderFactory.js';
4
+ import {getRefererInfo} from '../src/refererDetection.js';
5
+
6
+ const SOURCE = 'pbjs';
7
+ const BIDDER_CODE = 'brid';
8
+ const ENDPOINT_URL = 'https://pbs.prebrid.tv/openrtb2/auction';
9
+ const GVLID = 934;
10
+ const TIME_TO_LIVE = 300;
11
+ const VIDEO_PARAMS = [
12
+ 'api', 'linearity', 'maxduration', 'mimes', 'minduration', 'placement',
13
+ 'playbackmethod', 'protocols', 'startdelay'
14
+ ];
15
+
16
+ export const spec = {
17
+
18
+ code: BIDDER_CODE,
19
+ gvlid: GVLID,
20
+ supportedMediaTypes: [VIDEO],
21
+
22
+ /**
23
+ * Determines whether or not the given bid request is valid.
24
+ *
25
+ * @param {object} bid The bid to validate.
26
+ * @return boolean True if this is a valid bid, and false otherwise.
27
+ */
28
+ isBidRequestValid: function(bid) {
29
+ return !!(bid && bid.params && bid.params.placementId);
30
+ },
31
+
32
+ /**
33
+ * Make a server request from the list of BidRequests.
34
+ *
35
+ * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server.
36
+ * @param {BidderRequest} bidderRequest bidder request object.
37
+ * @return ServerRequest Info describing the request to the server.
38
+ */
39
+ buildRequests: function(bidRequests, bidderRequest) {
40
+ const requests = [];
41
+
42
+ _each(bidRequests, function(bid) {
43
+ const placementId = bid.params.placementId;
44
+ const bidId = bid.bidId;
45
+ let sizes = bid.sizes;
46
+ if (sizes && !Array.isArray(sizes[0])) sizes = [sizes];
47
+
48
+ const site = getSiteObj();
49
+
50
+ const postBody = {
51
+ sdk: {
52
+ source: SOURCE,
53
+ version: '$prebid.version$'
54
+ },
55
+ id: bidderRequest.bidderRequestId,
56
+ site,
57
+ imp: []
58
+ };
59
+
60
+ const imp = {
61
+ ext: {
62
+ prebid: {
63
+ storedrequest: {'id': placementId}
64
+ }
65
+ }
66
+ };
67
+
68
+ const video = deepAccess(bid, 'mediaTypes.video');
69
+ if (video) {
70
+ imp.video = getDefinedParams(video, VIDEO_PARAMS);
71
+ if (video.playerSize) {
72
+ imp.video = Object.assign(
73
+ imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize[0]) || {}
74
+ );
75
+ } else if (video.w && video.h) {
76
+ imp.video.w = video.w;
77
+ imp.video.h = video.h;
78
+ };
79
+ };
80
+
81
+ postBody.imp.push(imp);
82
+
83
+ const gdprConsent = bidderRequest && bidderRequest.gdprConsent;
84
+ const uspConsent = bidderRequest && bidderRequest.uspConsent;
85
+
86
+ if (gdprConsent || uspConsent) {
87
+ postBody.regs = { ext: {} };
88
+
89
+ if (uspConsent) {
90
+ postBody.regs.ext.us_privacy = uspConsent;
91
+ };
92
+
93
+ if (gdprConsent) {
94
+ if (typeof gdprConsent.gdprApplies !== 'undefined') {
95
+ postBody.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0;
96
+ };
97
+
98
+ if (typeof gdprConsent.consentString !== 'undefined') {
99
+ postBody.user = {
100
+ ext: { consent: gdprConsent.consentString }
101
+ };
102
+ };
103
+ };
104
+ };
105
+
106
+ if (bidRequests[0].schain) {
107
+ postBody.schain = bidRequests[0].schain;
108
+ }
109
+
110
+ const params = bid.params;
111
+
112
+ requests.push({
113
+ method: 'POST',
114
+ url: ENDPOINT_URL,
115
+ data: JSON.stringify(postBody),
116
+ options: {
117
+ withCredentials: true
118
+ },
119
+ bidId,
120
+ params
121
+ });
122
+ });
123
+
124
+ return requests;
125
+ },
126
+
127
+ /**
128
+ * Unpack the response from the server into a list of bids.
129
+ *
130
+ * @param {*} serverResponse A successful response from the server.
131
+ * @return {Bid[]} An array of bids which were nested inside the server.
132
+ */
133
+ interpretResponse: function(serverResponse, bidRequest) {
134
+ const response = serverResponse.body;
135
+ const bidResponses = [];
136
+
137
+ _each(response.seatbid, (resp) => {
138
+ _each(resp.bid, (bid) => {
139
+ const requestId = bidRequest.bidId;
140
+ const params = bidRequest.params;
141
+
142
+ const {ad, adUrl, vastUrl, vastXml} = getAd(bid);
143
+
144
+ const bidResponse = {
145
+ requestId,
146
+ params,
147
+ cpm: bid.price,
148
+ width: bid.w,
149
+ height: bid.h,
150
+ creativeId: bid.adid,
151
+ currency: response.cur,
152
+ netRevenue: false,
153
+ ttl: TIME_TO_LIVE,
154
+ meta: {
155
+ advertiserDomains: bid.adomain || []
156
+ }
157
+ };
158
+
159
+ if (vastUrl || vastXml) {
160
+ bidResponse.mediaType = VIDEO;
161
+ if (vastUrl) bidResponse.vastUrl = vastUrl;
162
+ if (vastXml) bidResponse.vastXml = vastXml;
163
+ } else {
164
+ bidResponse.ad = ad;
165
+ bidResponse.adUrl = adUrl;
166
+ };
167
+
168
+ bidResponses.push(bidResponse);
169
+ });
170
+ });
171
+
172
+ return bidResponses;
173
+ },
174
+
175
+ }
176
+
177
+ /**
178
+ * Helper function to get ad
179
+ *
180
+ * @param {object} bid The bid.
181
+ * @return {object} ad object.
182
+ */
183
+ function getAd(bid) {
184
+ let ad, adUrl, vastXml, vastUrl;
185
+
186
+ switch (deepAccess(bid, 'ext.prebid.type')) {
187
+ case VIDEO:
188
+ if (bid.adm.substr(0, 4) === 'http') {
189
+ vastUrl = bid.adm;
190
+ } else {
191
+ vastXml = bid.adm;
192
+ };
193
+ break;
194
+ default:
195
+ if (bid.adm && bid.nurl) {
196
+ ad = bid.adm;
197
+ ad += createTrackPixelHtml(decodeURIComponent(bid.nurl));
198
+ } else if (bid.adm) {
199
+ ad = bid.adm;
200
+ } else if (bid.nurl) {
201
+ adUrl = bid.nurl;
202
+ };
203
+ }
204
+
205
+ return {ad, adUrl, vastXml, vastUrl};
206
+ }
207
+
208
+ /**
209
+ * Helper function to get site object
210
+ *
211
+ * @return {object} siteObj.
212
+ */
213
+ function getSiteObj() {
214
+ const refInfo = (getRefererInfo && getRefererInfo()) || {};
215
+
216
+ return {
217
+ page: refInfo.page,
218
+ ref: refInfo.ref,
219
+ domain: refInfo.domain
220
+ };
221
+ }
222
+
223
+ registerBidder(spec);
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  import {config} from '../src/config.js';
15
- import {hook, setupBeforeHookFnOnce} from '../src/hook.js';
15
+ import {hook, setupBeforeHookFnOnce, ready} from '../src/hook.js';
16
16
  import {ajax} from '../src/ajax.js';
17
17
  import {logError, timestamp} from '../src/utils.js';
18
18
  import {addBidResponse} from '../src/auction.js';
@@ -32,7 +32,8 @@ export const registerAdserver = hook('async', function(adServer) {
32
32
  initTranslation(url, DEFAULT_IAB_TO_FW_MAPPING_KEY);
33
33
  }
34
34
  }, 'registerAdserver');
35
- registerAdserver();
35
+
36
+ ready.then(() => registerAdserver());
36
37
 
37
38
  export const getAdserverCategoryHook = timedBidResponseHook('categoryTranslation', function getAdserverCategoryHook(fn, adUnitCode, bid, reject) {
38
39
  if (!bid) {
@@ -12,6 +12,7 @@ import {timedAuctionHook} from '../src/utils/perfMetrics.js';
12
12
  import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js';
13
13
  import {enrichFPD} from '../src/fpd/enrichment.js';
14
14
  import {getGlobal} from '../src/prebidGlobal.js';
15
+ import {cmpClient} from '../libraries/cmp/cmpClient.js';
15
16
 
16
17
  const DEFAULT_CMP = 'iab';
17
18
  const DEFAULT_CONSENT_TIMEOUT = 10000;
@@ -48,36 +49,6 @@ function lookupStaticConsentData({onSuccess, onError}) {
48
49
  * @param {function(string, ...{}?)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string) and any extra error arguments (purely for logging)
49
50
  */
50
51
  function lookupIabConsent({onSuccess, onError, onEvent}) {
51
- function findCMP() {
52
- let f = window;
53
- let cmpFrame;
54
- let cmpFunction;
55
- while (true) {
56
- try {
57
- if (typeof f.__tcfapi === 'function') {
58
- cmpFunction = f.__tcfapi;
59
- cmpFrame = f;
60
- break;
61
- }
62
- } catch (e) { }
63
-
64
- // need separate try/catch blocks due to the exception errors thrown when trying to check for a frame that doesn't exist in 3rd party env
65
- try {
66
- if (f.frames['__tcfapiLocator']) {
67
- cmpFrame = f;
68
- break;
69
- }
70
- } catch (e) { }
71
-
72
- if (f === window.top) break;
73
- f = f.parent;
74
- }
75
- return {
76
- cmpFrame,
77
- cmpFunction
78
- };
79
- }
80
-
81
52
  function cmpResponseCallback(tcfData, success) {
82
53
  logInfo('Received a response from CMP', tcfData);
83
54
  if (success) {
@@ -90,70 +61,25 @@ function lookupIabConsent({onSuccess, onError, onEvent}) {
90
61
  }
91
62
  }
92
63
 
93
- const cmpCallbacks = {};
94
- const { cmpFrame, cmpFunction } = findCMP();
64
+ const cmp = cmpClient({
65
+ apiName: '__tcfapi',
66
+ apiVersion: CMP_VERSION,
67
+ apiArgs: ['command', 'version', 'callback', 'parameter'],
68
+ });
95
69
 
96
- if (!cmpFrame) {
70
+ if (!cmp) {
97
71
  return onError('TCF2 CMP not found.');
98
72
  }
99
- // to collect the consent information from the user, we perform two calls to the CMP in parallel:
100
- // first to collect the user's consent choices represented in an encoded string (via getConsentData)
101
- // second to collect the user's full unparsed consent information (via getVendorConsents)
102
-
103
- // the following code also determines where the CMP is located and uses the proper workflow to communicate with it:
104
- // check to see if CMP is found on the same window level as prebid and call it directly if so
105
- // check to see if prebid is in a safeframe (with CMP support)
106
- // else assume prebid may be inside an iframe and use the IAB CMP locator code to see if CMP's located in a higher parent window. this works in cross domain iframes
107
- // if the CMP is not found, the iframe function will call the cmpError exit callback to abort the rest of the CMP workflow
108
-
109
- if (typeof cmpFunction === 'function') {
73
+ if (cmp.isDirect) {
110
74
  logInfo('Detected CMP API is directly accessible, calling it now...');
111
- cmpFunction('addEventListener', CMP_VERSION, cmpResponseCallback);
112
75
  } else {
113
76
  logInfo('Detected CMP is outside the current iframe where Prebid.js is located, calling it now...');
114
- callCmpWhileInIframe('addEventListener', cmpFrame, cmpResponseCallback);
115
77
  }
116
78
 
117
- function callCmpWhileInIframe(commandName, cmpFrame, moduleCallback) {
118
- const apiName = '__tcfapi';
119
-
120
- const callName = `${apiName}Call`;
121
-
122
- /* Setup up a __cmp function to do the postMessage and stash the callback.
123
- This function behaves (from the caller's perspective identicially to the in-frame __cmp call */
124
- window[apiName] = function (cmd, cmpVersion, callback, arg) {
125
- const callId = Math.random() + '';
126
- const msg = {
127
- [callName]: {
128
- command: cmd,
129
- version: cmpVersion,
130
- parameter: arg,
131
- callId: callId
132
- }
133
- };
134
-
135
- cmpCallbacks[callId] = callback;
136
- cmpFrame.postMessage(msg, '*');
137
- }
138
-
139
- /** when we get the return message, call the stashed callback */
140
- window.addEventListener('message', readPostMessageResponse, false);
141
-
142
- // call CMP
143
- window[apiName](commandName, CMP_VERSION, moduleCallback);
144
-
145
- function readPostMessageResponse(event) {
146
- const cmpDataPkgName = `${apiName}Return`;
147
- const json = (typeof event.data === 'string' && includes(event.data, cmpDataPkgName)) ? JSON.parse(event.data) : event.data;
148
- if (json[cmpDataPkgName] && json[cmpDataPkgName].callId) {
149
- const payload = json[cmpDataPkgName];
150
- // TODO - clean up this logic (move listeners?); we have duplicate messages responses because 2 eventlisteners are active from the 2 cmp requests running in parallel
151
- if (cmpCallbacks.hasOwnProperty(payload.callId)) {
152
- cmpCallbacks[payload.callId](payload.returnValue, payload.success);
153
- }
154
- }
155
- }
156
- }
79
+ cmp({
80
+ command: 'addEventListener',
81
+ callback: cmpResponseCallback
82
+ })
157
83
  }
158
84
 
159
85
  /**