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

prebid / Prebid.js / 21043378589

15 Jan 2026 07:17PM UTC coverage: 96.21% (+33.0%) from 63.163%
21043378589

Pull #14301

github

b4d7ab
web-flow
Merge branch 'master' into constistent-src-on-bid-requested
Pull Request #14301: Core: Added src field to client bidRequests to ensure consistent payload structure

41519 of 51046 branches covered (81.34%)

207825 of 216012 relevant lines covered (96.21%)

71.35 hits per line

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

78.57
/src/adloader.js
1
import { LOAD_EXTERNAL_SCRIPT } from './activities/activities.js';
2
import { activityParams } from './activities/activityParams.js';
3
import { isActivityAllowed } from './activities/rules.js';
4

5
import { insertElement, logError, logWarn, setScriptAttributes } from './utils.js';
6

7
const _requestCache = new WeakMap();
8✔
8
// The below list contains modules or vendors whom Prebid allows to load external JS.
9
const _approvedLoadExternalJSList = [
8✔
10
  // Prebid maintained modules:
11
  'debugging',
12
  'outstream',
13
  // RTD modules:
14
  'aaxBlockmeter',
15
  'adagio',
16
  'adloox',
17
  'arcspan',
18
  'airgrid',
19
  'browsi',
20
  'brandmetrics',
21
  'clean.io',
22
  'humansecurityMalvDefense',
23
  'humansecurity',
24
  'confiant',
25
  'contxtful',
26
  'hadron',
27
  'mediafilter',
28
  'medianet',
29
  'azerionedge',
30
  'a1Media',
31
  'geoedge',
32
  'qortex',
33
  'dynamicAdBoost',
34
  '51Degrees',
35
  'symitridap',
36
  'wurfl',
37
  'nodalsAi',
38
  'anonymised',
39
  'optable',
40
  'oftmedia',
41
  // UserId Submodules
42
  'justtag',
43
  'tncId',
44
  'ftrackId',
45
  'id5',
46
];
47

48
/**
49
 * Loads external javascript. Can only be used if external JS is approved by Prebid. See https://github.com/prebid/prebid-js-external-js-template#policy
50
 * Each unique URL will be loaded at most 1 time.
51
 * @param {string} url the url to load
52
 * @param {string} moduleType moduleType of the module requesting this resource
53
 * @param {string} moduleCode bidderCode or module code of the module requesting this resource
54
 * @param {function} [callback] callback function to be called after the script is loaded
55
 * @param {Document} [doc] the context document, in which the script will be loaded, defaults to loaded document
56
 * @param {object} attributes an object of attributes to be added to the script with setAttribute by [key] and [value]; Only the attributes passed in the first request of a url will be added.
57
 */
58
export function loadExternalScript(url, moduleType, moduleCode, callback, doc, attributes) {
59
  if (!isActivityAllowed(LOAD_EXTERNAL_SCRIPT, activityParams(moduleType, moduleCode))) {
11✔
60
    return;
1✔
61
  }
62

63
  if (!moduleCode || !url) {
10✔
64
    logError('cannot load external script without url and moduleCode');
1✔
65
    return;
1✔
66
  }
67
  if (!_approvedLoadExternalJSList.includes(moduleCode)) {
9✔
68
    logError(`${moduleCode} not whitelisted for loading external JavaScript`);
1✔
69
    return;
1✔
70
  }
71
  if (!doc) {
8✔
72
    doc = document; // provide a "valid" key for the WeakMap
2✔
73
  }
74
  // only load each asset once
75
  const storedCachedObject = getCacheObject(doc, url);
8✔
76
  if (storedCachedObject) {
8✔
77
    if (callback && typeof callback === 'function') {
3✔
78
      if (storedCachedObject.loaded) {
3!
79
        // invokeCallbacks immediately
80
        callback();
×
81
      } else {
82
        // queue the callback
83
        storedCachedObject.callbacks.push(callback);
3✔
84
      }
85
    }
86
    return storedCachedObject.tag;
3✔
87
  }
88
  const cachedDocObj = _requestCache.get(doc) || {};
5✔
89
  const cacheObject = {
5✔
90
    loaded: false,
91
    tag: null,
92
    callbacks: []
93
  };
94
  cachedDocObj[url] = cacheObject;
5✔
95
  _requestCache.set(doc, cachedDocObj);
5✔
96

97
  if (callback && typeof callback === 'function') {
5✔
98
    cacheObject.callbacks.push(callback);
3✔
99
  }
100

101
  logWarn(`module ${moduleCode} is loading external JavaScript`);
5✔
102
  return requestResource(url, function () {
5✔
103
    cacheObject.loaded = true;
×
104
    try {
×
105
      for (let i = 0; i < cacheObject.callbacks.length; i++) {
×
106
        cacheObject.callbacks[i]();
×
107
      }
108
    } catch (e) {
109
      logError('Error executing callback', 'adloader.js:loadExternalScript', e);
×
110
    }
111
  }, doc, attributes);
112

113
  function requestResource(tagSrc, callback, doc, attributes) {
114
    if (!doc) {
5!
115
      doc = document;
×
116
    }
117
    var jptScript = doc.createElement('script');
5✔
118
    jptScript.type = 'text/javascript';
5✔
119
    jptScript.async = true;
5✔
120

121
    const cacheObject = getCacheObject(doc, url);
5✔
122
    if (cacheObject) {
5✔
123
      cacheObject.tag = jptScript;
5✔
124
    }
125

126
    if (jptScript.readyState) {
5!
127
      jptScript.onreadystatechange = function () {
×
128
        if (jptScript.readyState === 'loaded' || jptScript.readyState === 'complete') {
×
129
          jptScript.onreadystatechange = null;
×
130
          callback();
×
131
        }
132
      };
133
    } else {
134
      jptScript.onload = function () {
5✔
135
        callback();
×
136
      };
137
    }
138

139
    jptScript.src = tagSrc;
5✔
140

141
    if (attributes) {
5✔
142
      setScriptAttributes(jptScript, attributes);
1✔
143
    }
144

145
    // add the new script tag to the page
146
    insertElement(jptScript, doc);
5✔
147

148
    return jptScript;
5✔
149
  }
150
  function getCacheObject(doc, url) {
151
    const cachedDocObj = _requestCache.get(doc);
13✔
152
    if (cachedDocObj && cachedDocObj[url]) {
13✔
153
      return cachedDocObj[url];
8✔
154
    }
155
    return null; // return new cache object?
5✔
156
  }
157
};
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