google-ima.js (13793B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * Bug 1713690 - Shim Google Interactive Media Ads ima3.js 7 * 8 * Many sites use ima3.js for ad bidding and placement, often in conjunction 9 * with Google Publisher Tags, Prebid.js and/or other scripts. This shim 10 * provides a stubbed-out version of the API which helps work around related 11 * site breakage, such as black bxoes where videos ought to be placed. 12 */ 13 14 if (!window.google?.ima?.VERSION) { 15 const VERSION = "3.517.2"; 16 17 const CheckCanAutoplay = (function () { 18 // Sourced from: https://searchfox.org/mozilla-central/source/dom/media/gtest/negative_duration.mp4 19 const TEST_VIDEO = new Blob( 20 [ 21 new Uint32Array([ 22 469762048, 1887007846, 1752392036, 0, 913273705, 1717987696, 23 828601953, -1878917120, 1987014509, 1811939328, 1684567661, 0, 0, 0, 24 -402456576, 0, 256, 1, 0, 0, 256, 0, 0, 0, 256, 0, 0, 0, 64, 0, 0, 0, 25 0, 0, 0, 33554432, -201261056, 1801548404, 1744830464, 1684564852, 26 251658241, 0, 0, 0, 0, 16777216, 0, -1, -1, 0, 0, 0, 0, 256, 0, 0, 0, 27 256, 0, 0, 0, 64, 5, 53250, -2080309248, 1634296941, 738197504, 28 1684563053, 1, 0, 0, 0, 0, -2137614336, -1, -1, 50261, 754974720, 29 1919706216, 0, 0, 1701079414, 0, 0, 0, 1701079382, 1851869295, 30 1919249508, 16777216, 1852402979, 102, 1752004116, 100, 1, 0, 0, 31 1852400676, 102, 1701995548, 102, 0, 1, 1819440396, 32, 1, 1651799011, 32 108, 1937011607, 100, 0, 1, 1668702599, 49, 0, 1, 0, 0, 0, 33555712, 33 4718800, 4718592, 0, 65536, 0, 0, 0, 0, 0, 0, 0, 0, 16776984, 34 1630601216, 21193590, -14745500, 1729626337, -1407254428, 89161945, 35 1049019, 9453056, -251611125, 27269507, -379058688, -1329024392, 36 268435456, 1937011827, 0, 0, 268435456, 1668510835, 0, 0, 335544320, 37 2054386803, 0, 0, 0, 268435456, 1868788851, 0, 0, 671088640, 38 2019915373, 536870912, 2019914356, 0, 16777216, 16777216, 0, 0, 0, 39 ]), 40 ], 41 { type: "video/mp4" } 42 ); 43 44 let testVideo = undefined; 45 46 return function () { 47 if (!testVideo) { 48 testVideo = document.createElement("video"); 49 testVideo.style = 50 "position:absolute; width:0; height:0; left:0; right:0; z-index:-1; border:0"; 51 testVideo.setAttribute("muted", "muted"); 52 testVideo.setAttribute("playsinline", "playsinline"); 53 testVideo.src = URL.createObjectURL(TEST_VIDEO); 54 document.body.appendChild(testVideo); 55 } 56 return testVideo.play(); 57 }; 58 })(); 59 60 let ima = {}; 61 62 class AdDisplayContainer { 63 destroy() {} 64 initialize() {} 65 } 66 67 class ImaSdkSettings { 68 #c = true; 69 #f = {}; 70 #i = false; 71 #l = ""; 72 #p = ""; 73 #r = 0; 74 #t = ""; 75 #v = ""; 76 getCompanionBackfill() {} 77 getDisableCustomPlaybackForIOS10Plus() { 78 return this.#i; 79 } 80 getFeatureFlags() { 81 return this.#f; 82 } 83 getLocale() { 84 return this.#l; 85 } 86 getNumRedirects() { 87 return this.#r; 88 } 89 getPlayerType() { 90 return this.#t; 91 } 92 getPlayerVersion() { 93 return this.#v; 94 } 95 getPpid() { 96 return this.#p; 97 } 98 isCookiesEnabled() { 99 return this.#c; 100 } 101 setAutoPlayAdBreaks() {} 102 setCompanionBackfill() {} 103 setCookiesEnabled(c) { 104 this.#c = !!c; 105 } 106 setDisableCustomPlaybackForIOS10Plus(i) { 107 this.#i = !!i; 108 } 109 setFeatureFlags(f) { 110 this.#f = f; 111 } 112 setLocale(l) { 113 this.#l = l; 114 } 115 setNumRedirects(r) { 116 this.#r = r; 117 } 118 setPlayerType(t) { 119 this.#t = t; 120 } 121 setPlayerVersion(v) { 122 this.#v = v; 123 } 124 setPpid(p) { 125 this.#p = p; 126 } 127 setSessionId(_s) {} 128 setVpaidAllowed(_a) {} 129 setVpaidMode(_m) {} 130 } 131 ImaSdkSettings.CompanionBackfillMode = { 132 ALWAYS: "always", 133 ON_MASTER_AD: "on_master_ad", 134 }; 135 ImaSdkSettings.VpaidMode = { 136 DISABLED: 0, 137 ENABLED: 1, 138 INSECURE: 2, 139 }; 140 141 class EventHandler { 142 #listeners = new Map(); 143 144 _dispatch(e) { 145 const listeners = this.#listeners.get(e.type) || []; 146 for (const listener of Array.from(listeners)) { 147 try { 148 listener(e); 149 } catch (r) { 150 console.error(r); 151 } 152 } 153 } 154 155 addEventListener(t, c) { 156 if (!this.#listeners.has(t)) { 157 this.#listeners.set(t, new Set()); 158 } 159 this.#listeners.get(t).add(c); 160 } 161 162 removeEventListener(t, c) { 163 this.#listeners.get(t)?.delete(c); 164 } 165 } 166 167 class AdsLoader extends EventHandler { 168 #settings = new ImaSdkSettings(); 169 contentComplete() {} 170 destroy() {} 171 getSettings() { 172 return this.#settings; 173 } 174 getVersion() { 175 return VERSION; 176 } 177 requestAds(_r, _c) { 178 // If autoplay is disabled and the page is trying to autoplay a tracking 179 // ad, then IMA fails with an error, and the page is expected to request 180 // ads again later when the user clicks to play. 181 CheckCanAutoplay().then( 182 () => { 183 const { ADS_MANAGER_LOADED } = AdsManagerLoadedEvent.Type; 184 this._dispatch(new ima.AdsManagerLoadedEvent(ADS_MANAGER_LOADED)); 185 }, 186 () => { 187 const e = new ima.AdError( 188 "adPlayError", 189 1205, 190 1205, 191 "The browser prevented playback initiated without user interaction." 192 ); 193 this._dispatch(new ima.AdErrorEvent(e)); 194 } 195 ); 196 } 197 } 198 199 class AdsManager extends EventHandler { 200 #volume = 1; 201 collapse() {} 202 configureAdsManager() {} 203 destroy() {} 204 discardAdBreak() {} 205 expand() {} 206 focus() {} 207 getAdSkippableState() { 208 return false; 209 } 210 getCuePoints() { 211 return [0]; 212 } 213 getCurrentAd() { 214 return currentAd; 215 } 216 getCurrentAdCuePoints() { 217 return []; 218 } 219 getRemainingTime() { 220 return 0; 221 } 222 getVolume() { 223 return this.#volume; 224 } 225 init(_w, _h, _m, _e) {} 226 isCustomClickTrackingUsed() { 227 return false; 228 } 229 isCustomPlaybackUsed() { 230 return false; 231 } 232 pause() {} 233 requestNextAdBreak() {} 234 resize(_w, _h, _m) {} 235 resume() {} 236 setVolume(v) { 237 this.#volume = v; 238 } 239 skip() {} 240 start() { 241 requestAnimationFrame(() => { 242 for (const type of [ 243 AdEvent.Type.LOADED, 244 AdEvent.Type.STARTED, 245 AdEvent.Type.CONTENT_RESUME_REQUESTED, 246 AdEvent.Type.AD_BUFFERING, 247 AdEvent.Type.FIRST_QUARTILE, 248 AdEvent.Type.MIDPOINT, 249 AdEvent.Type.THIRD_QUARTILE, 250 AdEvent.Type.COMPLETE, 251 AdEvent.Type.ALL_ADS_COMPLETED, 252 ]) { 253 try { 254 this._dispatch(new ima.AdEvent(type)); 255 } catch (e) { 256 console.error(e); 257 } 258 } 259 }); 260 } 261 stop() {} 262 updateAdsRenderingSettings(_s) {} 263 } 264 265 class AdsRenderingSettings {} 266 267 class AdsRequest { 268 setAdWillAutoPlay() {} 269 setAdWillPlayMuted() {} 270 setContinuousPlayback() {} 271 } 272 273 class AdPodInfo { 274 getAdPosition() { 275 return 1; 276 } 277 getIsBumper() { 278 return false; 279 } 280 getMaxDuration() { 281 return -1; 282 } 283 getPodIndex() { 284 return 1; 285 } 286 getTimeOffset() { 287 return 0; 288 } 289 getTotalAds() { 290 return 1; 291 } 292 } 293 294 class Ad { 295 _pi = new AdPodInfo(); 296 getAdId() { 297 return ""; 298 } 299 getAdPodInfo() { 300 return this._pi; 301 } 302 getAdSystem() { 303 return ""; 304 } 305 getAdvertiserName() { 306 return ""; 307 } 308 getApiFramework() { 309 return null; 310 } 311 getCompanionAds() { 312 return []; 313 } 314 getContentType() { 315 return ""; 316 } 317 getCreativeAdId() { 318 return ""; 319 } 320 getCreativeId() { 321 return ""; 322 } 323 getDealId() { 324 return ""; 325 } 326 getDescription() { 327 return ""; 328 } 329 getDuration() { 330 return 8.5; 331 } 332 getHeight() { 333 return 0; 334 } 335 getMediaUrl() { 336 return null; 337 } 338 getMinSuggestedDuration() { 339 return -2; 340 } 341 getSkipTimeOffset() { 342 return -1; 343 } 344 getSurveyUrl() { 345 return null; 346 } 347 getTitle() { 348 return ""; 349 } 350 getTraffickingParameters() { 351 return {}; 352 } 353 getTraffickingParametersString() { 354 return ""; 355 } 356 getUiElements() { 357 return [""]; 358 } 359 getUniversalAdIdRegistry() { 360 return "unknown"; 361 } 362 getUniversalAdIds() { 363 return [""]; 364 } 365 getUniversalAdIdValue() { 366 return "unknown"; 367 } 368 getVastMediaBitrate() { 369 return 0; 370 } 371 getVastMediaHeight() { 372 return 0; 373 } 374 getVastMediaWidth() { 375 return 0; 376 } 377 getWidth() { 378 return 0; 379 } 380 getWrapperAdIds() { 381 return [""]; 382 } 383 getWrapperAdSystems() { 384 return [""]; 385 } 386 getWrapperCreativeIds() { 387 return [""]; 388 } 389 isLinear() { 390 return true; 391 } 392 isSkippable() { 393 return true; 394 } 395 } 396 397 class CompanionAd { 398 getAdSlotId() { 399 return ""; 400 } 401 getContent() { 402 return ""; 403 } 404 getContentType() { 405 return ""; 406 } 407 getHeight() { 408 return 1; 409 } 410 getWidth() { 411 return 1; 412 } 413 } 414 415 class AdError { 416 #errorCode = -1; 417 #message = ""; 418 #type = ""; 419 #vastErrorCode = -1; 420 constructor(type, code, vast, message) { 421 this.#errorCode = code; 422 this.#message = message; 423 this.#type = type; 424 this.#vastErrorCode = vast; 425 } 426 getErrorCode() { 427 return this.#errorCode; 428 } 429 getInnerError() {} 430 getMessage() { 431 return this.#message; 432 } 433 getType() { 434 return this.#type; 435 } 436 getVastErrorCode() { 437 return this.#vastErrorCode; 438 } 439 toString() { 440 return `AdError ${this.#errorCode}: ${this.#message}`; 441 } 442 } 443 AdError.ErrorCode = {}; 444 AdError.Type = {}; 445 446 const isEngadget = () => { 447 try { 448 for (const ctx of Object.values(window.vidible._getContexts())) { 449 if (ctx.getPlayer()?.div?.innerHTML.includes("www.engadget.com")) { 450 return true; 451 } 452 } 453 } catch (_) {} 454 return false; 455 }; 456 457 const currentAd = isEngadget() ? undefined : new Ad(); 458 459 class AdEvent { 460 constructor(type) { 461 this.type = type; 462 } 463 getAd() { 464 return currentAd; 465 } 466 getAdData() { 467 return {}; 468 } 469 } 470 AdEvent.Type = { 471 AD_BREAK_READY: "adBreakReady", 472 AD_BUFFERING: "adBuffering", 473 AD_CAN_PLAY: "adCanPlay", 474 AD_METADATA: "adMetadata", 475 AD_PROGRESS: "adProgress", 476 ALL_ADS_COMPLETED: "allAdsCompleted", 477 CLICK: "click", 478 COMPLETE: "complete", 479 CONTENT_PAUSE_REQUESTED: "contentPauseRequested", 480 CONTENT_RESUME_REQUESTED: "contentResumeRequested", 481 DURATION_CHANGE: "durationChange", 482 EXPANDED_CHANGED: "expandedChanged", 483 FIRST_QUARTILE: "firstQuartile", 484 IMPRESSION: "impression", 485 INTERACTION: "interaction", 486 LINEAR_CHANGE: "linearChange", 487 LINEAR_CHANGED: "linearChanged", 488 LOADED: "loaded", 489 LOG: "log", 490 MIDPOINT: "midpoint", 491 PAUSED: "pause", 492 RESUMED: "resume", 493 SKIPPABLE_STATE_CHANGED: "skippableStateChanged", 494 SKIPPED: "skip", 495 STARTED: "start", 496 THIRD_QUARTILE: "thirdQuartile", 497 USER_CLOSE: "userClose", 498 VIDEO_CLICKED: "videoClicked", 499 VIDEO_ICON_CLICKED: "videoIconClicked", 500 VIEWABLE_IMPRESSION: "viewable_impression", 501 VOLUME_CHANGED: "volumeChange", 502 VOLUME_MUTED: "mute", 503 }; 504 505 class AdErrorEvent { 506 type = "adError"; 507 #error = ""; 508 constructor(error) { 509 this.#error = error; 510 } 511 getError() { 512 return this.#error; 513 } 514 getUserRequestContext() { 515 return {}; 516 } 517 } 518 AdErrorEvent.Type = { 519 AD_ERROR: "adError", 520 }; 521 522 const manager = new AdsManager(); 523 524 class AdsManagerLoadedEvent { 525 constructor(type) { 526 this.type = type; 527 } 528 getAdsManager() { 529 return manager; 530 } 531 getUserRequestContext() { 532 return {}; 533 } 534 } 535 AdsManagerLoadedEvent.Type = { 536 ADS_MANAGER_LOADED: "adsManagerLoaded", 537 }; 538 539 class CustomContentLoadedEvent {} 540 CustomContentLoadedEvent.Type = { 541 CUSTOM_CONTENT_LOADED: "deprecated-event", 542 }; 543 544 class CompanionAdSelectionSettings {} 545 CompanionAdSelectionSettings.CreativeType = { 546 ALL: "All", 547 FLASH: "Flash", 548 IMAGE: "Image", 549 }; 550 CompanionAdSelectionSettings.ResourceType = { 551 ALL: "All", 552 HTML: "Html", 553 IFRAME: "IFrame", 554 STATIC: "Static", 555 }; 556 CompanionAdSelectionSettings.SizeCriteria = { 557 IGNORE: "IgnoreSize", 558 SELECT_EXACT_MATCH: "SelectExactMatch", 559 SELECT_NEAR_MATCH: "SelectNearMatch", 560 }; 561 562 class AdCuePoints { 563 getCuePoints() { 564 return []; 565 } 566 } 567 568 class AdProgressData {} 569 570 class UniversalAdIdInfo { 571 getAdIdRegistry() { 572 return ""; 573 } 574 getAdIsValue() { 575 return ""; 576 } 577 } 578 579 Object.assign(ima, { 580 AdCuePoints, 581 AdDisplayContainer, 582 AdError, 583 AdErrorEvent, 584 AdEvent, 585 AdPodInfo, 586 AdProgressData, 587 AdsLoader, 588 AdsManager: manager, 589 AdsManagerLoadedEvent, 590 AdsRenderingSettings, 591 AdsRequest, 592 CompanionAd, 593 CompanionAdSelectionSettings, 594 CustomContentLoadedEvent, 595 gptProxyInstance: {}, 596 ImaSdkSettings, 597 OmidAccessMode: { 598 DOMAIN: "domain", 599 FULL: "full", 600 LIMITED: "limited", 601 }, 602 settings: new ImaSdkSettings(), 603 UiElements: { 604 AD_ATTRIBUTION: "adAttribution", 605 COUNTDOWN: "countdown", 606 }, 607 UniversalAdIdInfo, 608 VERSION, 609 ViewMode: { 610 FULLSCREEN: "fullscreen", 611 NORMAL: "normal", 612 }, 613 }); 614 615 if (!window.google) { 616 window.google = {}; 617 } 618 619 window.google.ima = ima; 620 }