• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 21444465511

28 Jan 2026 03:32PM UTC coverage: 96.22% (+0.003%) from 96.217%
21444465511

push

github

web-flow
Taboola support extra signals (#14299)

* add deferredBilling support using onBidBillable

* update burl setting

* support nurl firing logic

* add extra signals to taboola request

* add extra ad signals

* fix missing semicolon

* use Prebid's built-in counters

* updated detectBot logic

41716 of 51289 branches covered (81.34%)

216 of 221 new or added lines in 2 files covered. (97.74%)

130 existing lines in 9 files now uncovered.

209165 of 217382 relevant lines covered (96.22%)

71.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.11
/src/secureCreatives.js
1
/* Secure Creatives
2
  Provides support for rendering creatives into cross domain iframes such as SafeFrame to prevent
3
   access to a publisher page from creative payloads.
4
 */
5

6
import {getAllAssetsMessage, getAssetMessage} from './native.js';
7
import {BID_STATUS, MESSAGES} from './constants.js';
8
import {isApnGetTagDefined, isGptPubadsDefined, logError, logWarn} from './utils.js';
9
import {
10
  deferRendering,
11
  getBidToRender,
12
  handleCreativeEvent,
13
  handleNativeMessage,
14
  handleRender,
15
  markWinner
16
} from './adRendering.js';
17
import {getCreativeRendererSource, PUC_MIN_VERSION} from './creativeRenderers.js';
18
import {PbPromise} from './utils/promise.js';
19

20
const { REQUEST, RESPONSE, NATIVE, EVENT } = MESSAGES;
8✔
21

22
const HANDLER_MAP = {
8✔
23
  [REQUEST]: handleRenderRequest,
24
  [EVENT]: handleEventRequest,
25
};
26

27
if (FEATURES.NATIVE) {
8✔
28
  Object.assign(HANDLER_MAP, {
8✔
29
    [NATIVE]: handleNativeRequest,
30
  });
31
}
32

33
export function listenMessagesFromCreative() {
34
  window.addEventListener('message', function (ev) {
1✔
35
    receiveMessage(ev);
×
36
  }, false);
37
}
38

39
export function getReplier(ev) {
40
  if (ev.origin == null && ev.ports.length === 0) {
21✔
41
    return function () {
1✔
42
      const msg = 'Cannot post message to a frame with null origin. Please update creatives to use MessageChannel, see https://github.com/prebid/Prebid.js/issues/7870';
1✔
43
      logError(msg);
1✔
44
      throw new Error(msg);
1✔
45
    };
46
  } else if (ev.ports.length > 0) {
20✔
47
    return function (message) {
1✔
48
      ev.ports[0].postMessage(JSON.stringify(message));
1✔
49
    };
50
  } else {
51
    return function (message) {
19✔
52
      ev.source.postMessage(JSON.stringify(message), ev.origin);
7✔
53
    };
54
  }
55
}
56

57
function ensureAdId(adId, reply) {
58
  return function (data, ...args) {
18!
59
    return reply(Object.assign({}, data, {adId}), ...args);
6✔
60
  }
61
}
62

63
export function receiveMessage(ev, cb) {
64
  var key = ev.message ? 'message' : 'data';
18!
65
  var data = {};
18✔
66
  try {
18✔
67
    data = JSON.parse(ev[key]);
18✔
68
  } catch (e) {
69
    return;
×
70
  }
71

72
  if (data && data.adId && data.message && HANDLER_MAP.hasOwnProperty(data.message)) {
18✔
73
    return getBidToRender(data.adId, data.message === MESSAGES.REQUEST, (adObject) => {
18✔
74
      HANDLER_MAP[data.message](ensureAdId(data.adId, getReplier(ev)), data, adObject);
18✔
75
      cb && cb();
18✔
76
    });
77
  }
78
}
79

80
function getResizer(adId, bidResponse) {
81
  // in some situations adId !== bidResponse.adId
82
  // the first is the one that was requested and is tied to the element
83
  // the second is the one that is being rendered (sometimes different, e.g. in some paapi setups)
84
  return function (width, height) {
11✔
85
    resizeRemoteCreative({...bidResponse, width, height, adId});
2✔
86
  }
87
}
88
function handleRenderRequest(reply, message, bidResponse) {
89
  handleRender({
9✔
90
    renderFn(adData) {
91
      reply(Object.assign({
3✔
92
        message: RESPONSE,
93
        renderer: getCreativeRendererSource(bidResponse),
94
        rendererVersion: PUC_MIN_VERSION
95
      }, adData));
96
    },
97
    resizeFn: getResizer(message.adId, bidResponse),
98
    options: message.options,
99
    adId: message.adId,
100
    bidResponse
101
  });
102
}
103

104
function handleNativeRequest(reply, data, adObject) {
105
  // handle this script from native template in an ad server
106
  // window.parent.postMessage(JSON.stringify({
107
  //   message: 'Prebid Native',
108
  //   adId: '%%PATTERN:hb_adid%%'
109
  // }), '*');
110
  if (adObject == null) {
5!
111
    logError(`Cannot find ad for x-origin event request: '${data.adId}'`);
×
UNCOV
112
    return;
×
113
  }
114
  switch (data.action) {
5!
115
    case 'assetRequest':
116
      deferRendering(adObject, () => reply(getAssetMessage(data, adObject)));
×
UNCOV
117
      break;
×
118
    case 'allAssetRequest':
119
      deferRendering(adObject, () => reply(getAllAssetsMessage(data, adObject)));
3✔
120
      break;
3✔
121
    default:
122
      handleNativeMessage(data, adObject, {resizeFn: getResizer(data.adId, adObject)});
2✔
123
      markWinner(adObject);
2✔
124
  }
125
}
126

127
function handleEventRequest(reply, data, adObject) {
128
  if (adObject == null) {
4!
129
    logError(`Cannot find ad '${data.adId}' for x-origin event request`);
×
UNCOV
130
    return;
×
131
  }
132
  if (adObject.status !== BID_STATUS.RENDERED) {
4✔
133
    logWarn(`Received x-origin event request without corresponding render request for ad '${adObject.adId}'`);
2✔
134
    return;
2✔
135
  }
136
  return handleCreativeEvent(data, adObject);
2✔
137
}
138

139
function getDimension(value) {
140
  return value ? value + 'px' : '100%';
9!
141
}
142

143
export function resizeAnchor(ins, width, height) {
144
  /**
145
   * Special handling for google anchor ads
146
   * For anchors, the element to resize is an <ins> element that is an ancestor of the creative iframe
147
   * On desktop this is sized to the creative dimensions;
148
   * on mobile one dimension is fixed to 100%.
149
   */
150
  return new PbPromise((resolve, reject) => {
4✔
151
    let tryCounter = 10;
4✔
152
    // wait until GPT has set dimensions on the ins, otherwise our changes will be overridden
153
    const resizer = setInterval(() => {
4✔
154
      let done = false;
22✔
155
      Object.entries({width, height})
22✔
156
        .forEach(([dimension, newValue]) => {
44✔
157
          if (/\d+px/.test(ins.style[dimension])) {
44✔
158
            ins.style[dimension] = getDimension(newValue);
5✔
159
            done = true;
5✔
160
          }
161
        })
162
      if (done || (tryCounter-- === 0)) {
22✔
163
        clearInterval(resizer);
4✔
164
        done ? resolve() : reject(new Error('Could not resize anchor'))
4✔
165
      }
166
    }, 50)
167
  })
168
}
169

170
export function resizeRemoteCreative({instl, adId, adUnitCode, width, height}) {
7✔
171
  // do not resize interstitials - the creative frame takes the full screen and sizing of the ad should
172
  // be handled within it.
173
  if (instl) return;
7✔
174

175
  function resize(element) {
176
    if (element) {
12✔
177
      const elementStyle = element.style;
2✔
178
      elementStyle.width = getDimension(width)
2✔
179
      elementStyle.height = getDimension(height);
2✔
180
    } else {
181
      logError(`Unable to locate matching page element for adUnitCode ${adUnitCode}.  Can't resize it to ad's dimensions.  Please review setup.`);
10✔
182
    }
183
  }
184

185
  // not select element that gets removed after dfp render
186
  const iframe = getElementByAdUnit('iframe:not([style*="display: none"])');
6✔
187
  resize(iframe);
6✔
188
  const anchorIns = iframe?.closest('ins[data-anchor-status]');
6✔
189
  anchorIns ? resizeAnchor(anchorIns, width, height) : resize(iframe?.parentElement);
6!
190

191
  function getElementByAdUnit(elmType) {
192
    const id = getElementIdBasedOnAdServer(adId, adUnitCode);
6✔
193
    const parentDivEle = document.getElementById(id);
6✔
194
    return parentDivEle && parentDivEle.querySelector(elmType);
6✔
195
  }
196

197
  function getElementIdBasedOnAdServer(adId, adUnitCode) {
198
    if (isGptPubadsDefined()) {
6✔
199
      const dfpId = getDfpElementId(adId);
5✔
200
      if (dfpId) {
5✔
201
        return dfpId;
1✔
202
      }
203
    }
204
    if (isApnGetTagDefined()) {
5✔
205
      const apnId = getAstElementId(adUnitCode);
4✔
206
      if (apnId) {
4✔
207
        return apnId;
1✔
208
      }
209
    }
210
    return adUnitCode;
4✔
211
  }
212

213
  function getDfpElementId(adId) {
214
    const slot = window.googletag.pubads().getSlots().find(slot => {
5✔
215
      return slot.getTargetingKeys().find(key => {
13✔
216
        return slot.getTargeting(key).includes(adId);
3✔
217
      });
218
    });
219
    return slot ? slot.getSlotElementId() : null;
5✔
220
  }
221

222
  function getAstElementId(adUnitCode) {
223
    const astTag = window.apntag.getTag(adUnitCode);
4✔
224
    return astTag && astTag.targetId;
4✔
225
  }
226
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc