prebid.js 8.0.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 (232) 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/bedigitechBidAdapter.js +1 -1
  21. package/dist/beopBidAdapter.js +1 -1
  22. package/dist/bidglassBidAdapter.js +1 -1
  23. package/dist/bidwatchAnalyticsAdapter.js +1 -1
  24. package/dist/big-richmediaBidAdapter.js +1 -1
  25. package/dist/bridBidAdapter.js +1 -0
  26. package/dist/bridgewellBidAdapter.js +1 -1
  27. package/dist/brightMountainMediaBidAdapter.js +1 -1
  28. package/dist/carodaBidAdapter.js +1 -1
  29. package/dist/categoryTranslation.js +1 -1
  30. package/dist/chtnwBidAdapter.js +1 -1
  31. package/dist/cmp.js +1 -0
  32. package/dist/colossussspBidAdapter.js +1 -1
  33. package/dist/conceptxBidAdapter.js +1 -1
  34. package/dist/concertBidAdapter.js +1 -1
  35. package/dist/connectadBidAdapter.js +1 -1
  36. package/dist/consentManagement.js +1 -1
  37. package/dist/consentManagementGpp.js +1 -1
  38. package/dist/consentManagementUsp.js +1 -1
  39. package/dist/consumableBidAdapter.js +1 -1
  40. package/dist/conversantAnalyticsAdapter.js +1 -1
  41. package/dist/conversantBidAdapter.js +1 -1
  42. package/dist/craftBidAdapter.js +1 -1
  43. package/dist/criteoBidAdapter.js +1 -1
  44. package/dist/cwireBidAdapter.js +1 -1
  45. package/dist/dependencies.json +15 -0
  46. package/dist/dspxBidAdapter.js +1 -1
  47. package/dist/eplanningBidAdapter.js +1 -1
  48. package/dist/euidIdSystem.js +1 -1
  49. package/dist/feedadBidAdapter.js +1 -1
  50. package/dist/finativeBidAdapter.js +1 -1
  51. package/dist/freepassBidAdapter.js +1 -0
  52. package/dist/freewheel-sspBidAdapter.js +1 -1
  53. package/dist/gmosspBidAdapter.js +1 -1
  54. package/dist/goldbachBidAdapter.js +1 -1
  55. package/dist/gppControl_usnat.js +1 -0
  56. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  57. package/dist/greenbidsRtdProvider.js +1 -1
  58. package/dist/gridBidAdapter.js +1 -1
  59. package/dist/gumgumBidAdapter.js +1 -1
  60. package/dist/h12mediaBidAdapter.js +1 -1
  61. package/dist/hypelabBidAdapter.js +1 -1
  62. package/dist/id5IdSystem.js +1 -1
  63. package/dist/imdsBidAdapter.js +1 -1
  64. package/dist/improvedigitalBidAdapter.js +1 -1
  65. package/dist/insticatorBidAdapter.js +1 -1
  66. package/dist/intentIqIdSystem.js +1 -1
  67. package/dist/ixBidAdapter.js +1 -1
  68. package/dist/justpremiumBidAdapter.js +1 -1
  69. package/dist/kargoBidAdapter.js +1 -1
  70. package/dist/kiviadsBidAdapter.js +1 -0
  71. package/dist/konduitAnalyticsAdapter.js +1 -1
  72. package/dist/kueezBidAdapter.js +1 -1
  73. package/dist/kueezRtbBidAdapter.js +1 -1
  74. package/dist/kulturemediaBidAdapter.js +1 -1
  75. package/dist/lassoBidAdapter.js +1 -1
  76. package/dist/lifestreetBidAdapter.js +1 -1
  77. package/dist/liveIntentIdSystem.js +1 -1
  78. package/dist/logicadBidAdapter.js +1 -1
  79. package/dist/loglyliftBidAdapter.js +1 -1
  80. package/dist/magniteAnalyticsAdapter.js +1 -1
  81. package/dist/malltvAnalyticsAdapter.js +1 -1
  82. package/dist/marsmediaBidAdapter.js +1 -1
  83. package/dist/mediafuseBidAdapter.js +1 -1
  84. package/dist/mediasquareBidAdapter.js +1 -1
  85. package/dist/mgidBidAdapter.js +1 -1
  86. package/dist/mgidXBidAdapter.js +1 -0
  87. package/dist/minutemediaBidAdapter.js +1 -1
  88. package/dist/minutemediaplusBidAdapter.js +1 -1
  89. package/dist/mspa.js +1 -0
  90. package/dist/nexx360BidAdapter.js +1 -1
  91. package/dist/not-for-prod/prebid.js +146 -139
  92. package/dist/oguryBidAdapter.js +1 -1
  93. package/dist/onetagBidAdapter.js +1 -1
  94. package/dist/ooloAnalyticsAdapter.js +1 -1
  95. package/dist/optidigitalBidAdapter.js +1 -1
  96. package/dist/outbrainBidAdapter.js +1 -1
  97. package/dist/oxxionAnalyticsAdapter.js +1 -1
  98. package/dist/oxxionRtdProvider.js +1 -1
  99. package/dist/parrableIdSystem.js +1 -1
  100. package/dist/pixfutureBidAdapter.js +1 -1
  101. package/dist/prebid-core.js +1 -1
  102. package/dist/publinkIdSystem.js +1 -1
  103. package/dist/pubmaticBidAdapter.js +1 -1
  104. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  105. package/dist/pxyzBidAdapter.js +1 -1
  106. package/dist/quantcastBidAdapter.js +1 -1
  107. package/dist/readpeakBidAdapter.js +1 -1
  108. package/dist/relaidoBidAdapter.js +1 -1
  109. package/dist/retailspotBidAdapter.js +1 -1
  110. package/dist/rhythmoneBidAdapter.js +1 -1
  111. package/dist/riseBidAdapter.js +1 -1
  112. package/dist/rtbhouseBidAdapter.js +1 -1
  113. package/dist/rubiconBidAdapter.js +1 -1
  114. package/dist/seedingAllianceBidAdapter.js +1 -1
  115. package/dist/seedtagBidAdapter.js +1 -1
  116. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  117. package/dist/sharethroughBidAdapter.js +1 -1
  118. package/dist/shinezBidAdapter.js +1 -1
  119. package/dist/smaatoBidAdapter.js +1 -1
  120. package/dist/smartadserverBidAdapter.js +1 -1
  121. package/dist/smartxBidAdapter.js +1 -1
  122. package/dist/smilewantedBidAdapter.js +1 -1
  123. package/dist/sonobiBidAdapter.js +1 -1
  124. package/dist/sovrnAnalyticsAdapter.js +1 -1
  125. package/dist/sovrnBidAdapter.js +1 -1
  126. package/dist/sspBCBidAdapter.js +1 -1
  127. package/dist/stvBidAdapter.js +1 -1
  128. package/dist/sublimeBidAdapter.js +1 -1
  129. package/dist/targetVideoBidAdapter.js +1 -1
  130. package/dist/teadsBidAdapter.js +1 -1
  131. package/dist/trionBidAdapter.js +1 -1
  132. package/dist/tripleliftBidAdapter.js +1 -1
  133. package/dist/ttdBidAdapter.js +1 -1
  134. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  135. package/dist/uid2IdSystem.js +1 -1
  136. package/dist/underdogmediaBidAdapter.js +1 -1
  137. package/dist/undertoneBidAdapter.js +1 -1
  138. package/dist/userId.js +1 -1
  139. package/dist/vidazooBidAdapter.js +1 -1
  140. package/dist/videobyteBidAdapter.js +1 -1
  141. package/dist/visxBidAdapter.js +1 -1
  142. package/dist/vuukleBidAdapter.js +1 -1
  143. package/dist/widespaceBidAdapter.js +1 -1
  144. package/dist/winrBidAdapter.js +1 -1
  145. package/dist/yahoosspBidAdapter.js +1 -1
  146. package/dist/yieldmoBidAdapter.js +1 -1
  147. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  148. package/libraries/cmp/cmpClient.js +139 -0
  149. package/libraries/mspa/activityControls.js +91 -0
  150. package/modules/admanBidAdapter.js +5 -0
  151. package/modules/amxBidAdapter.js +11 -12
  152. package/modules/bedigitechBidAdapter.js +15 -12
  153. package/modules/bedigitechBidAdapter.md +2 -2
  154. package/modules/beopBidAdapter.js +0 -4
  155. package/modules/bidwatchAnalyticsAdapter.js +33 -5
  156. package/modules/bridBidAdapter.js +223 -0
  157. package/modules/categoryTranslation.js +3 -2
  158. package/modules/colossussspBidAdapter.js +9 -0
  159. package/modules/conceptxBidAdapter.js +6 -3
  160. package/modules/consentManagement.js +12 -86
  161. package/modules/consentManagementGpp.js +47 -126
  162. package/modules/consentManagementUsp.js +19 -97
  163. package/modules/freepassBidAdapter.js +98 -0
  164. package/modules/freepassBidAdapter.md +31 -0
  165. package/modules/freepassIdSystem.md +1 -1
  166. package/modules/gppControl_usnat.js +11 -0
  167. package/modules/greenbidsAnalyticsAdapter.js +7 -13
  168. package/modules/gridBidAdapter.js +3 -4
  169. package/modules/intentIqIdSystem.js +11 -9
  170. package/modules/ixBidAdapter.js +284 -55
  171. package/modules/kiviadsBidAdapter.js +212 -0
  172. package/modules/kiviadsBidAdapter.md +79 -0
  173. package/modules/liveIntentIdSystem.js +8 -3
  174. package/modules/mediasquareBidAdapter.js +19 -23
  175. package/modules/mgidXBidAdapter.js +263 -0
  176. package/modules/mgidXBidAdapter.md +79 -0
  177. package/modules/oxxionAnalyticsAdapter.js +33 -5
  178. package/modules/oxxionRtdProvider.js +124 -11
  179. package/modules/oxxionRtdProvider.md +19 -4
  180. package/modules/pubmaticBidAdapter.js +18 -2
  181. package/modules/rtbhouseBidAdapter.js +9 -2
  182. package/modules/rtbhouseBidAdapter.md +27 -8
  183. package/modules/rubiconBidAdapter.js +13 -1
  184. package/modules/sharethroughBidAdapter.js +14 -0
  185. package/modules/sonobiBidAdapter.js +4 -7
  186. package/modules/tripleliftBidAdapter.js +21 -1
  187. package/modules/userId/eids.js +29 -0
  188. package/modules/userId/eids.md +19 -2
  189. package/modules/userId/index.js +78 -29
  190. package/modules/userId/userId.md +3 -0
  191. package/package.json +1 -1
  192. package/src/activities/params.js +4 -1
  193. package/test/spec/libraries/cmp/cmpClient_spec.js +233 -0
  194. package/test/spec/libraries/mspa/activityControls_spec.js +315 -0
  195. package/test/spec/modules/admanBidAdapter_spec.js +8 -2
  196. package/test/spec/modules/adqueryBidAdapter_spec.js +5 -1
  197. package/test/spec/modules/amxBidAdapter_spec.js +6 -3
  198. package/test/spec/modules/bedigitechBidAdapter_spec.js +25 -36
  199. package/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +21 -1
  200. package/test/spec/modules/bridBidAdapter_spec.js +129 -0
  201. package/test/spec/modules/byDataAnalyticsAdapter_spec.js +9 -7
  202. package/test/spec/modules/chtnwBidAdapter_spec.js +4 -1
  203. package/test/spec/modules/colossussspBidAdapter_spec.js +29 -0
  204. package/test/spec/modules/consentManagementGpp_spec.js +84 -7
  205. package/test/spec/modules/consentManagement_spec.js +8 -18
  206. package/test/spec/modules/datablocksBidAdapter_spec.js +7 -3
  207. package/test/spec/modules/eids_spec.js +87 -0
  208. package/test/spec/modules/freepassBidAdapter_spec.js +161 -0
  209. package/test/spec/modules/greenbidsAnalyticsAdapter_spec.js +0 -15
  210. package/test/spec/modules/gridBidAdapter_spec.js +29 -28
  211. package/test/spec/modules/insticatorBidAdapter_spec.js +6 -2
  212. package/test/spec/modules/intentIqIdSystem_spec.js +36 -2
  213. package/test/spec/modules/ixBidAdapter_spec.js +785 -15
  214. package/test/spec/modules/kiviadsBidAdapter_spec.js +404 -0
  215. package/test/spec/modules/lassoBidAdapter_spec.js +6 -4
  216. package/test/spec/modules/liveIntentIdMinimalSystem_spec.js +17 -2
  217. package/test/spec/modules/liveIntentIdSystem_spec.js +11 -6
  218. package/test/spec/modules/mediasquareBidAdapter_spec.js +3 -0
  219. package/test/spec/modules/mgidXBidAdapter_spec.js +426 -0
  220. package/test/spec/modules/onetagBidAdapter_spec.js +81 -75
  221. package/test/spec/modules/orbidderBidAdapter_spec.js +6 -3
  222. package/test/spec/modules/oxxionAnalyticsAdapter_spec.js +21 -2
  223. package/test/spec/modules/oxxionRtdProvider_spec.js +113 -1
  224. package/test/spec/modules/pubmaticBidAdapter_spec.js +25 -0
  225. package/test/spec/modules/relaidoBidAdapter_spec.js +4 -3
  226. package/test/spec/modules/rtbhouseBidAdapter_spec.js +35 -0
  227. package/test/spec/modules/rubiconBidAdapter_spec.js +41 -0
  228. package/test/spec/modules/sharethroughBidAdapter_spec.js +75 -0
  229. package/test/spec/modules/sonobiBidAdapter_spec.js +15 -15
  230. package/test/spec/modules/tripleliftBidAdapter_spec.js +20 -1
  231. package/test/spec/modules/ucfunnelBidAdapter_spec.js +37 -16
  232. package/test/spec/modules/userId_spec.js +393 -6
@@ -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) {
@@ -116,6 +116,15 @@ export const spec = {
116
116
  request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL';
117
117
  request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0;
118
118
  }
119
+
120
+ // Add GPP consent
121
+ if (bidderRequest.gppConsent) {
122
+ request.gpp = bidderRequest.gppConsent.gppString;
123
+ request.gpp_sid = bidderRequest.gppConsent.applicableSections;
124
+ } else if (bidderRequest.ortb2?.regs?.gpp) {
125
+ request.gpp = bidderRequest.ortb2.regs.gpp;
126
+ request.gpp_sid = bidderRequest.ortb2.regs.gpp_sid;
127
+ }
119
128
  }
120
129
 
121
130
  for (let i = 0; i < validBidRequests.length; i++) {
@@ -45,10 +45,13 @@ export const spec = {
45
45
  },
46
46
 
47
47
  interpretResponse: function (serverResponse, bidRequest) {
48
- const bidResponsesFromServer = serverResponse.body.bidResponses;
49
- const firstDummy = bidResponsesFromServer[0]
50
- const firstSeat = firstDummy.ads[0]
51
48
  const bidResponses = [];
49
+ const bidResponsesFromServer = serverResponse.body.bidResponses;
50
+ if (Array.isArray(bidResponsesFromServer) && bidResponsesFromServer.length === 0) {
51
+ return bidResponses
52
+ }
53
+ const firstBid = bidResponsesFromServer[0]
54
+ const firstSeat = firstBid.ads[0]
52
55
  const bidResponse = {
53
56
  requestId: firstSeat.requestId,
54
57
  cpm: firstSeat.cpm,
@@ -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
  /**
@@ -7,10 +7,12 @@
7
7
  import {deepSetValue, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js';
8
8
  import {config} from '../src/config.js';
9
9
  import {gppDataHandler} from '../src/adapterManager.js';
10
- import {includes} from '../src/polyfill.js';
11
10
  import {timedAuctionHook} from '../src/utils/perfMetrics.js';
12
11
  import { enrichFPD } from '../src/fpd/enrichment.js';
13
12
  import {getGlobal} from '../src/prebidGlobal.js';
13
+ import {cmpClient} from '../libraries/cmp/cmpClient.js';
14
+ import {GreedyPromise} from '../src/utils/promise.js';
15
+ import {buildActivityParams} from '../src/activities/params.js';
14
16
 
15
17
  const DEFAULT_CMP = 'iab';
16
18
  const DEFAULT_CONSENT_TIMEOUT = 10000;
@@ -18,7 +20,7 @@ const CMP_VERSION = 1;
18
20
 
19
21
  export let userCMP;
20
22
  export let consentTimeout;
21
- export let staticConsentData;
23
+ let staticConsentData;
22
24
 
23
25
  let consentData;
24
26
  let addedConsentHook = false;
@@ -29,28 +31,16 @@ const cmpCallMap = {
29
31
  'static': lookupStaticConsentData
30
32
  };
31
33
 
32
- /**
33
- * This function checks the state of the IAB gppData's applicableSection field (to ensure it's populated and has a valid value).
34
- * section === 0 represents a CMP's default value when CMP is loading, it shoud not be used a real user's section.
35
- *
36
- * TODO --- The initial version of the GPP CMP API spec used this naming convention, but it was later changed as an update to the spec.
37
- * CMPs should adjust their logic to use the new format (applicableSecctions), but that may not be the case with the initial release.
38
- * Added support just in case for this transition period, can likely be removed at a later date...
39
- * @param gppData represents the IAB gppData object
40
- * @returns true|false
41
- */
42
- function checkApplicableSectionIsReady(gppData) {
43
- return gppData && Array.isArray(gppData.applicableSection) && gppData.applicableSection.length > 0 && gppData.applicableSection[0] !== 0;
44
- }
45
-
46
34
  /**
47
35
  * This function checks the state of the IAB gppData's applicableSections field (to ensure it's populated and has a valid value).
48
36
  * section === 0 represents a CMP's default value when CMP is loading, it shoud not be used a real user's section.
49
37
  * @param gppData represents the IAB gppData object
50
- * @returns true|false
38
+ * @returns {Array}
51
39
  */
52
- function checkApplicableSectionsIsReady(gppData) {
53
- return gppData && Array.isArray(gppData.applicableSections) && gppData.applicableSections.length > 0 && gppData.applicableSections[0] !== 0;
40
+ function applicableSections(gppData) {
41
+ return gppData && Array.isArray(gppData.applicableSections) && gppData.applicableSections.length > 0 && gppData.applicableSections[0] !== 0
42
+ ? gppData.applicableSections
43
+ : [];
54
44
  }
55
45
 
56
46
  /**
@@ -68,107 +58,36 @@ function lookupStaticConsentData({onSuccess, onError}) {
68
58
  * @param {function({})} onSuccess acts as a success callback when CMP returns a value; pass along consentObjectfrom CMP
69
59
  * @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)
70
60
  */
71
- function lookupIabConsent({onSuccess, onError}) {
72
- const cmpApiName = '__gpp';
73
- const cmpCallbacks = {};
74
- let registeredPostMessageResponseListener = false;
75
-
76
- function findCMP() {
77
- let f = window;
78
- let cmpFrame;
79
- let cmpDirectAccess = false;
80
- while (true) {
81
- try {
82
- if (typeof f[cmpApiName] === 'function') {
83
- cmpFrame = f;
84
- cmpDirectAccess = true;
85
- break;
86
- }
87
- } catch (e) {}
88
-
89
- // 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
90
- try {
91
- if (f.frames['__gppLocator']) {
92
- cmpFrame = f;
93
- break;
94
- }
95
- } catch (e) {}
96
-
97
- if (f === window.top) break;
98
- f = f.parent;
99
- }
100
-
101
- return {
102
- cmpFrame,
103
- cmpDirectAccess
104
- };
105
- }
106
-
107
- const {cmpFrame, cmpDirectAccess} = findCMP();
108
-
109
- if (!cmpFrame) {
61
+ export function lookupIabConsent({onSuccess, onError}, mkClient = cmpClient) {
62
+ const cmp = mkClient({
63
+ apiName: '__gpp',
64
+ apiVersion: CMP_VERSION,
65
+ });
66
+ if (!cmp) {
110
67
  return onError('GPP CMP not found.');
111
68
  }
112
69
 
113
- const invokeCMP = (cmpDirectAccess) ? invokeCMPDirect : invokeCMPFrame;
114
-
115
- function invokeCMPDirect({command, callback, parameter, version = CMP_VERSION}, resultCb) {
116
- if (typeof resultCb === 'function') {
117
- resultCb(cmpFrame[cmpApiName](command, callback, parameter, version));
118
- } else {
119
- cmpFrame[cmpApiName](command, callback, parameter, version);
120
- }
121
- }
122
-
123
- function invokeCMPFrame({command, callback, parameter, version = CMP_VERSION}, resultCb) {
124
- const callName = `${cmpApiName}Call`;
125
- if (!registeredPostMessageResponseListener) {
126
- // when we get the return message, call the stashed callback;
127
- window.addEventListener('message', readPostMessageResponse, false);
128
- registeredPostMessageResponseListener = true;
129
- }
130
-
131
- // call CMP via postMessage
132
- const callId = Math.random().toString();
133
- const msg = {
134
- [callName]: {
135
- command: command,
136
- parameter,
137
- version,
138
- callId: callId
139
- }
140
- };
141
-
142
- // TODO? - add logic to check if random was already used in the same session, and roll another if so?
143
- cmpCallbacks[callId] = (typeof callback === 'function') ? callback : resultCb;
144
- cmpFrame.postMessage(msg, '*');
145
-
146
- function readPostMessageResponse(event) {
147
- const cmpDataPkgName = `${cmpApiName}Return`;
148
- const json = (typeof event.data === 'string' && event.data.includes(cmpDataPkgName)) ? JSON.parse(event.data) : event.data;
149
- if (json[cmpDataPkgName] && json[cmpDataPkgName].callId) {
150
- const payload = json[cmpDataPkgName];
151
-
152
- if (cmpCallbacks.hasOwnProperty(payload.callId)) {
153
- cmpCallbacks[payload.callId](payload.returnValue);
154
- }
155
- }
156
- }
157
- }
158
-
159
- const startupMsg = (cmpDirectAccess) ? 'Detected GPP CMP API is directly accessible, calling it now...'
70
+ const startupMsg = (cmp.isDirect) ? 'Detected GPP CMP API is directly accessible, calling it now...'
160
71
  : 'Detected GPP CMP is outside the current iframe where Prebid.js is located, calling it now...';
161
72
  logInfo(startupMsg);
162
73
 
163
- invokeCMP({
74
+ cmp({
164
75
  command: 'addEventListener',
165
76
  callback: function (evt) {
166
77
  if (evt) {
167
- logInfo(`Received a ${(cmpDirectAccess ? 'direct' : 'postmsg')} response from GPP CMP for event`, evt);
78
+ logInfo(`Received a ${(cmp.isDirect ? 'direct' : 'postmsg')} response from GPP CMP for event`, evt);
168
79
  if (evt.eventName === 'sectionChange' || evt.pingData.cmpStatus === 'loaded') {
169
- invokeCMP({command: 'getGPPData'}, function (gppData) {
170
- logInfo(`Received a ${cmpDirectAccess ? 'direct' : 'postmsg'} response from GPP CMP for getGPPData`, gppData);
171
- processCmpData(gppData, {onSuccess, onError});
80
+ cmp({command: 'getGPPData'}).then((gppData) => {
81
+ logInfo(`Received a ${cmp.isDirect ? 'direct' : 'postmsg'} response from GPP CMP for getGPPData`, gppData);
82
+ return GreedyPromise.all(
83
+ (gppData?.pingData?.supportedAPIs || [])
84
+ .map((name) => cmp({command: 'getSection', parameter: name})
85
+ .catch(() => { logError(`Could not retrieve section data for GPP section '${name}'`) })
86
+ .then((res) => [name, res]))
87
+ ).then((sections) => {
88
+ const sectionData = Object.fromEntries(sections.filter(([_, val]) => val != null));
89
+ processCmpData({gppData, sectionData}, {onSuccess, onError});
90
+ })
172
91
  });
173
92
  } else if (evt.pingData.cmpStatus === 'error') {
174
93
  onError('CMP returned with a cmpStatus:error response. Please check CMP setup.');
@@ -199,7 +118,7 @@ function loadConsentData(cb) {
199
118
  }
200
119
  }
201
120
 
202
- if (!includes(Object.keys(cmpCallMap), userCMP)) {
121
+ if (!cmpCallMap.hasOwnProperty(userCMP)) {
203
122
  done(null, false, `GPP CMP framework (${userCMP}) is not a supported framework. Aborting consentManagement module and resuming auction.`);
204
123
  return;
205
124
  }
@@ -219,7 +138,7 @@ function loadConsentData(cb) {
219
138
  }
220
139
  processCmpData(consentData, {
221
140
  onSuccess: continueToAuction,
222
- onError: () => continueToAuction(storeConsentData(undefined))
141
+ onError: () => continueToAuction(storeConsentData())
223
142
  })
224
143
  }
225
144
  if (consentTimeout === 0) {
@@ -281,11 +200,10 @@ export const requestBidsHook = timedAuctionHook('gpp', function requestBidsHook(
281
200
  * If it's bad, we call `onError`
282
201
  * If it's good, then we store the value and call `onSuccess`
283
202
  */
284
- function processCmpData(consentObject, {onSuccess, onError}) {
203
+ function processCmpData(consentData, {onSuccess, onError}) {
285
204
  function checkData() {
286
- const gppString = consentObject && consentObject.gppString;
287
- const gppSection = (checkApplicableSectionsIsReady(consentObject)) ? consentObject.applicableSections
288
- : (checkApplicableSectionIsReady(consentObject)) ? consentObject.applicableSection : [];
205
+ const gppString = consentData?.gppData?.gppString;
206
+ const gppSection = consentData?.gppData?.applicableSections;
289
207
 
290
208
  return !!(
291
209
  (!Array.isArray(gppSection)) ||
@@ -294,25 +212,25 @@ function processCmpData(consentObject, {onSuccess, onError}) {
294
212
  }
295
213
 
296
214
  if (checkData()) {
297
- onError(`CMP returned unexpected value during lookup process.`, consentObject);
215
+ onError(`CMP returned unexpected value during lookup process.`, consentData);
298
216
  } else {
299
- onSuccess(storeConsentData(consentObject));
217
+ onSuccess(storeConsentData(consentData));
300
218
  }
301
219
  }
302
220
 
303
221
  /**
304
222
  * Stores CMP data locally in module to make information available in adaptermanager.js for later in the auction
305
- * @param {object} cmpConsentObject required; an object representing user's consent choices (can be undefined in certain use-cases for this function only)
223
+ * @param {{}} gppData the result of calling a CMP's `getGPPData` (or equivalent)
224
+ * @param {{}} sectionData map from GPP section name to the result of calling a CMP's `getSection` (or equivalent)
306
225
  */
307
- function storeConsentData(cmpConsentObject) {
226
+ export function storeConsentData({gppData, sectionData} = {}) {
308
227
  consentData = {
309
- gppString: (cmpConsentObject) ? cmpConsentObject.gppString : undefined,
310
-
311
- fullGppData: (cmpConsentObject) || undefined,
228
+ gppString: (gppData) ? gppData.gppString : undefined,
229
+ gppData: (gppData) || undefined,
312
230
  };
313
- consentData.applicableSections = (checkApplicableSectionsIsReady(cmpConsentObject)) ? cmpConsentObject.applicableSections
314
- : (checkApplicableSectionIsReady(cmpConsentObject)) ? cmpConsentObject.applicableSection : [];
231
+ consentData.applicableSections = applicableSections(gppData);
315
232
  consentData.apiVersion = CMP_VERSION;
233
+ consentData.sectionData = sectionData;
316
234
  return consentData;
317
235
  }
318
236
 
@@ -353,7 +271,7 @@ export function setConsentConfig(config) {
353
271
 
354
272
  if (userCMP === 'static') {
355
273
  if (isPlainObject(config.consentData)) {
356
- staticConsentData = config.consentData;
274
+ staticConsentData = {gppData: config.consentData, sectionData: config.sectionData};
357
275
  consentTimeout = 0;
358
276
  } else {
359
277
  logError(`consentManagement.gpp config with cmpApi: 'static' did not specify consentData. No consents will be available to adapters.`);
@@ -364,6 +282,9 @@ export function setConsentConfig(config) {
364
282
 
365
283
  if (!addedConsentHook) {
366
284
  getGlobal().requestBids.before(requestBidsHook, 50);
285
+ buildActivityParams.before((next, params) => {
286
+ return next(Object.assign({gppConsent: gppDataHandler.getConsentData()}, params));
287
+ });
367
288
  }
368
289
  addedConsentHook = true;
369
290
  gppDataHandler.enable();