2024-07-24 13:57:40 +00:00

856 lines
18 KiB
JavaScript

/**
*
* Source below is based on Mozilla source code:
* https://searchfox.org/mozilla-central/rev/d317e93d9a59c9e4c06ada85fbff9f6a1ceaaad1/browser/extensions/webcompat/shims/google-ima.js
*
* Modifications to the original code below this comment:
* - Avoid JS syntax not supported by older browser versions
* - Add missing shim event
* - Modified to avoid jshint warnings as per uBO's config
* - Added `OmidVerificationVendor` to `ima`
* - Have `AdError.getInnerError()` return `null`
* - Have `AdDisplayContainer` constructor add DIV element to container
* - Added missing event dispatcher functionality
* - Corrected return type of `Ad.getUniversalAdIds()`
* - Corrected typo in `UniversalAdIdInfo.getAdIdValue()` method name
* - Corrected dispatch of LOAD event when preloading is enabled
* - Corrected dispatch of CONTENT_PAUSE/RESUME_REQUESTED events
*
* Related issue:
* - https://github.com/uBlockOrigin/uBlock-issues/issues/2158
*
**/
'use strict';
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Bug 1713690 - Shim Google Interactive Media Ads ima3.js
*
* Many sites use ima3.js for ad bidding and placement, often in conjunction
* with Google Publisher Tags, Prebid.js and/or other scripts. This shim
* provides a stubbed-out version of the API which helps work around related
* site breakage, such as black bxoes where videos ought to be placed.
*/
if (!window.google || !window.google.ima || !window.google.ima.VERSION) {
const VERSION = "3.517.2";
const CheckCanAutoplay = (function() {
// Sourced from: https://searchfox.org/mozilla-central/source/dom/media/gtest/negative_duration.mp4
const TEST_VIDEO = new Blob(
[
new Uint32Array([
469762048,
1887007846,
1752392036,
0,
913273705,
1717987696,
828601953,
-1878917120,
1987014509,
1811939328,
1684567661,
0,
0,
0,
-402456576,
0,
256,
1,
0,
0,
256,
0,
0,
0,
256,
0,
0,
0,
64,
0,
0,
0,
0,
0,
0,
33554432,
-201261056,
1801548404,
1744830464,
1684564852,
251658241,
0,
0,
0,
0,
16777216,
0,
-1,
-1,
0,
0,
0,
0,
256,
0,
0,
0,
256,
0,
0,
0,
64,
5,
53250,
-2080309248,
1634296941,
738197504,
1684563053,
1,
0,
0,
0,
0,
-2137614336,
-1,
-1,
50261,
754974720,
1919706216,
0,
0,
1701079414,
0,
0,
0,
1701079382,
1851869295,
1919249508,
16777216,
1852402979,
102,
1752004116,
100,
1,
0,
0,
1852400676,
102,
1701995548,
102,
0,
1,
1819440396,
32,
1,
1651799011,
108,
1937011607,
100,
0,
1,
1668702599,
49,
0,
1,
0,
0,
0,
33555712,
4718800,
4718592,
0,
65536,
0,
0,
0,
0,
0,
0,
0,
0,
16776984,
1630601216,
21193590,
-14745500,
1729626337,
-1407254428,
89161945,
1049019,
9453056,
-251611125,
27269507,
-379058688,
-1329024392,
268435456,
1937011827,
0,
0,
268435456,
1668510835,
0,
0,
335544320,
2054386803,
0,
0,
0,
268435456,
1868788851,
0,
0,
671088640,
2019915373,
536870912,
2019914356,
0,
16777216,
16777216,
0,
0,
0,
]),
],
{ type: "video/mp4" }
);
let testVideo;
return function() {
if (!testVideo) {
testVideo = document.createElement("video");
testVideo.style =
"position:absolute; width:0; height:0; left:0; right:0; z-index:-1; border:0";
testVideo.setAttribute("muted", "muted");
testVideo.setAttribute("playsinline", "playsinline");
testVideo.src = URL.createObjectURL(TEST_VIDEO);
document.body.appendChild(testVideo);
}
return testVideo.play();
};
})();
const ima = {};
class AdDisplayContainer {
constructor(containerElement) {
const divElement = document.createElement("div");
divElement.style.setProperty("display", "none", "important");
divElement.style.setProperty("visibility", "collapse", "important");
containerElement.appendChild(divElement);
}
destroy() {}
initialize() {}
}
class ImaSdkSettings {
constructor() {
this.c = true;
this.f = {};
this.i = false;
this.l = "";
this.p = "";
this.r = 0;
this.t = "";
this.v = "";
}
getCompanionBackfill() {}
getDisableCustomPlaybackForIOS10Plus() {
return this.i;
}
getFeatureFlags() {
return this.f;
}
getLocale() {
return this.l;
}
getNumRedirects() {
return this.r;
}
getPlayerType() {
return this.t;
}
getPlayerVersion() {
return this.v;
}
getPpid() {
return this.p;
}
isCookiesEnabled() {
return this.c;
}
setAutoPlayAdBreaks() {}
setCompanionBackfill() {}
setCookiesEnabled(c) {
this.c = !!c;
}
setDisableCustomPlaybackForIOS10Plus(i) {
this.i = !!i;
}
setFeatureFlags(f) {
this.f = f;
}
setLocale(l) {
this.l = l;
}
setNumRedirects(r) {
this.r = r;
}
setPlayerType(t) {
this.t = t;
}
setPlayerVersion(v) {
this.v = v;
}
setPpid(p) {
this.p = p;
}
setSessionId(/*s*/) {}
setVpaidAllowed(/*a*/) {}
setVpaidMode(/*m*/) {}
// https://github.com/uBlockOrigin/uBlock-issues/issues/2265#issuecomment-1637094149
getDisableFlashAds() {
}
setDisableFlashAds() {
}
}
ImaSdkSettings.CompanionBackfillMode = {
ALWAYS: "always",
ON_MASTER_AD: "on_master_ad",
};
ImaSdkSettings.VpaidMode = {
DISABLED: 0,
ENABLED: 1,
INSECURE: 2,
};
class EventHandler {
constructor() {
this.listeners = new Map();
}
_dispatch(e) {
let listeners = this.listeners.get(e.type);
listeners = listeners ? Array.from(listeners.values()) : [];
for (const listener of listeners) {
try {
listener(e);
} catch (r) {
console.error(r);
}
}
}
addEventListener(types, c, options, context) {
if (!Array.isArray(types)) {
types = [types];
}
for (const t of types) {
if (!this.listeners.has(t)) {
this.listeners.set(t, new Map());
}
this.listeners.get(t).set(c, c.bind(context || this));
}
}
removeEventListener(types, c) {
if (!Array.isArray(types)) {
types = [types];
}
for (const t of types) {
const typeSet = this.listeners.get(t);
if (typeSet) {
typeSet.delete(c);
}
}
}
}
class AdsLoader extends EventHandler {
constructor() {
super();
this.settings = new ImaSdkSettings();
}
contentComplete() {}
destroy() {}
getSettings() {
return this.settings;
}
getVersion() {
return VERSION;
}
requestAds(/*r, c*/) {
// If autoplay is disabled and the page is trying to autoplay a tracking
// ad, then IMA fails with an error, and the page is expected to request
// ads again later when the user clicks to play.
CheckCanAutoplay().then(
() => {
const { ADS_MANAGER_LOADED } = AdsManagerLoadedEvent.Type;
this._dispatch(new ima.AdsManagerLoadedEvent(ADS_MANAGER_LOADED));
},
() => {
const e = new ima.AdError(
"adPlayError",
1205,
1205,
"The browser prevented playback initiated without user interaction."
);
this._dispatch(new ima.AdErrorEvent(e));
}
);
}
}
class AdsManager extends EventHandler {
constructor() {
super();
this.volume = 1;
this._enablePreloading = false;
}
collapse() {}
configureAdsManager() {}
destroy() {}
discardAdBreak() {}
expand() {}
focus() {}
getAdSkippableState() {
return false;
}
getCuePoints() {
return [0];
}
getCurrentAd() {
return currentAd;
}
getCurrentAdCuePoints() {
return [];
}
getRemainingTime() {
return 0;
}
getVolume() {
return this.volume;
}
init(/*w, h, m, e*/) {
if (this._enablePreloading) {
this._dispatch(new ima.AdEvent(AdEvent.Type.LOADED));
}
}
isCustomClickTrackingUsed() {
return false;
}
isCustomPlaybackUsed() {
return false;
}
pause() {}
requestNextAdBreak() {}
resize(/*w, h, m*/) {}
resume() {}
setVolume(v) {
this.volume = v;
}
skip() {}
start() {
requestAnimationFrame(() => {
for (const type of [
AdEvent.Type.LOADED,
AdEvent.Type.STARTED,
AdEvent.Type.CONTENT_PAUSE_REQUESTED,
AdEvent.Type.AD_BUFFERING,
AdEvent.Type.FIRST_QUARTILE,
AdEvent.Type.MIDPOINT,
AdEvent.Type.THIRD_QUARTILE,
AdEvent.Type.COMPLETE,
AdEvent.Type.ALL_ADS_COMPLETED,
AdEvent.Type.CONTENT_RESUME_REQUESTED,
]) {
try {
this._dispatch(new ima.AdEvent(type));
} catch (e) {
console.error(e);
}
}
});
}
stop() {}
updateAdsRenderingSettings(/*s*/) {}
}
class AdsRenderingSettings {}
class AdsRequest {
setAdWillAutoPlay() {}
setAdWillPlayMuted() {}
setContinuousPlayback() {}
}
class AdPodInfo {
getAdPosition() {
return 1;
}
getIsBumper() {
return false;
}
getMaxDuration() {
return -1;
}
getPodIndex() {
return 1;
}
getTimeOffset() {
return 0;
}
getTotalAds() {
return 1;
}
}
class Ad {
constructor() {
this._pi = new AdPodInfo();
}
getAdId() {
return "";
}
getAdPodInfo() {
return this._pi;
}
getAdSystem() {
return "";
}
getAdvertiserName() {
return "";
}
getApiFramework() {
return null;
}
getCompanionAds() {
return [];
}
getContentType() {
return "";
}
getCreativeAdId() {
return "";
}
getCreativeId() {
return "";
}
getDealId() {
return "";
}
getDescription() {
return "";
}
getDuration() {
return 8.5;
}
getHeight() {
return 0;
}
getMediaUrl() {
return null;
}
getMinSuggestedDuration() {
return -2;
}
getSkipTimeOffset() {
return -1;
}
getSurveyUrl() {
return null;
}
getTitle() {
return "";
}
getTraffickingParameters() {
return {};
}
getTraffickingParametersString() {
return "";
}
getUiElements() {
return [""];
}
getUniversalAdIdRegistry() {
return "unknown";
}
getUniversalAdIds() {
return [new UniversalAdIdInfo()];
}
getUniversalAdIdValue() {
return "unknown";
}
getVastMediaBitrate() {
return 0;
}
getVastMediaHeight() {
return 0;
}
getVastMediaWidth() {
return 0;
}
getWidth() {
return 0;
}
getWrapperAdIds() {
return [""];
}
getWrapperAdSystems() {
return [""];
}
getWrapperCreativeIds() {
return [""];
}
isLinear() {
return true;
}
isSkippable() {
return true;
}
}
class CompanionAd {
getAdSlotId() {
return "";
}
getContent() {
return "";
}
getContentType() {
return "";
}
getHeight() {
return 1;
}
getWidth() {
return 1;
}
}
class AdError {
constructor(type, code, vast, message) {
this.errorCode = code;
this.message = message;
this.type = type;
this.vastErrorCode = vast;
}
getErrorCode() {
return this.errorCode;
}
getInnerError() {
return null;
}
getMessage() {
return this.message;
}
getType() {
return this.type;
}
getVastErrorCode() {
return this.vastErrorCode;
}
toString() {
return `AdError ${this.errorCode}: ${this.message}`;
}
}
AdError.ErrorCode = {};
AdError.Type = {};
const isEngadget = () => {
try {
for (const ctx of Object.values(window.vidible._getContexts())) {
const player = ctx.getPlayer();
if (!player) { continue;}
const div = player.div;
if (!div) { continue; }
if (div.innerHTML.includes("www.engadget.com")) {
return true;
}
}
} catch (_) {}
return false;
};
const currentAd = isEngadget() ? undefined : new Ad();
class AdEvent {
constructor(type) {
this.type = type;
}
getAd() {
return currentAd;
}
getAdData() {
return {};
}
}
AdEvent.Type = {
AD_BREAK_READY: "adBreakReady",
AD_BUFFERING: "adBuffering",
AD_CAN_PLAY: "adCanPlay",
AD_METADATA: "adMetadata",
AD_PROGRESS: "adProgress",
ALL_ADS_COMPLETED: "allAdsCompleted",
CLICK: "click",
COMPLETE: "complete",
CONTENT_PAUSE_REQUESTED: "contentPauseRequested",
CONTENT_RESUME_REQUESTED: "contentResumeRequested",
DURATION_CHANGE: "durationChange",
EXPANDED_CHANGED: "expandedChanged",
FIRST_QUARTILE: "firstQuartile",
IMPRESSION: "impression",
INTERACTION: "interaction",
LINEAR_CHANGE: "linearChange",
LINEAR_CHANGED: "linearChanged",
LOADED: "loaded",
LOG: "log",
MIDPOINT: "midpoint",
PAUSED: "pause",
RESUMED: "resume",
SKIPPABLE_STATE_CHANGED: "skippableStateChanged",
SKIPPED: "skip",
STARTED: "start",
THIRD_QUARTILE: "thirdQuartile",
USER_CLOSE: "userClose",
VIDEO_CLICKED: "videoClicked",
VIDEO_ICON_CLICKED: "videoIconClicked",
VIEWABLE_IMPRESSION: "viewable_impression",
VOLUME_CHANGED: "volumeChange",
VOLUME_MUTED: "mute",
};
class AdErrorEvent {
constructor(error) {
this.type = "adError";
this.error = error;
}
getError() {
return this.error;
}
getUserRequestContext() {
return {};
}
}
AdErrorEvent.Type = {
AD_ERROR: "adError",
};
const manager = new AdsManager();
class AdsManagerLoadedEvent {
constructor(type) {
this.type = type;
}
getAdsManager(c, settings) {
if (settings && settings.enablePreloading) {
manager._enablePreloading = true;
}
return manager;
}
getUserRequestContext() {
return {};
}
}
AdsManagerLoadedEvent.Type = {
ADS_MANAGER_LOADED: "adsManagerLoaded",
};
class CustomContentLoadedEvent {}
CustomContentLoadedEvent.Type = {
CUSTOM_CONTENT_LOADED: "deprecated-event",
};
class CompanionAdSelectionSettings {}
CompanionAdSelectionSettings.CreativeType = {
ALL: "All",
FLASH: "Flash",
IMAGE: "Image",
};
CompanionAdSelectionSettings.ResourceType = {
ALL: "All",
HTML: "Html",
IFRAME: "IFrame",
STATIC: "Static",
};
CompanionAdSelectionSettings.SizeCriteria = {
IGNORE: "IgnoreSize",
SELECT_EXACT_MATCH: "SelectExactMatch",
SELECT_NEAR_MATCH: "SelectNearMatch",
};
class AdCuePoints {
getCuePoints() {
return [];
}
}
class AdProgressData {}
class UniversalAdIdInfo {
getAdIdRegistry() {
return "";
}
getAdIdValue() {
return "";
}
}
Object.assign(ima, {
AdCuePoints,
AdDisplayContainer,
AdError,
AdErrorEvent,
AdEvent,
AdPodInfo,
AdProgressData,
AdsLoader,
AdsManager: manager,
AdsManagerLoadedEvent,
AdsRenderingSettings,
AdsRequest,
CompanionAd,
CompanionAdSelectionSettings,
CustomContentLoadedEvent,
gptProxyInstance: {},
ImaSdkSettings,
OmidAccessMode: {
DOMAIN: "domain",
FULL: "full",
LIMITED: "limited",
},
OmidVerificationVendor: {
1: "OTHER",
2: "GOOGLE",
GOOGLE: 2,
OTHER: 1
},
settings: new ImaSdkSettings(),
UiElements: {
AD_ATTRIBUTION: "adAttribution",
COUNTDOWN: "countdown",
},
UniversalAdIdInfo,
VERSION,
ViewMode: {
FULLSCREEN: "fullscreen",
NORMAL: "normal",
},
});
if (!window.google) {
window.google = {};
}
window.google.ima = ima;
}