diff options
Diffstat (limited to 'browser/extensions/webcompat/shims/google-ima.js')
-rw-r--r-- | browser/extensions/webcompat/shims/google-ima.js | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/browser/extensions/webcompat/shims/google-ima.js b/browser/extensions/webcompat/shims/google-ima.js new file mode 100644 index 0000000000..1f5e56239d --- /dev/null +++ b/browser/extensions/webcompat/shims/google-ima.js @@ -0,0 +1,620 @@ +/* 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?.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 = undefined; + + 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(); + }; + })(); + + let ima = {}; + + class AdDisplayContainer { + destroy() {} + initialize() {} + } + + class ImaSdkSettings { + #c = true; + #f = {}; + #i = false; + #l = ""; + #p = ""; + #r = 0; + #t = ""; + #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) {} + } + ImaSdkSettings.CompanionBackfillMode = { + ALWAYS: "always", + ON_MASTER_AD: "on_master_ad", + }; + ImaSdkSettings.VpaidMode = { + DISABLED: 0, + ENABLED: 1, + INSECURE: 2, + }; + + class EventHandler { + #listeners = new Map(); + + _dispatch(e) { + const listeners = this.#listeners.get(e.type) || []; + for (const listener of Array.from(listeners)) { + try { + listener(e); + } catch (r) { + console.error(r); + } + } + } + + addEventListener(t, c) { + if (!this.#listeners.has(t)) { + this.#listeners.set(t, new Set()); + } + this.#listeners.get(t).add(c); + } + + removeEventListener(t, c) { + this.#listeners.get(t)?.delete(c); + } + } + + class AdsLoader extends EventHandler { + #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 { + #volume = 1; + 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) {} + 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_RESUME_REQUESTED, + AdEvent.Type.AD_BUFFERING, + AdEvent.Type.FIRST_QUARTILE, + AdEvent.Type.MIDPOINT, + AdEvent.Type.THIRD_QUARTILE, + AdEvent.Type.COMPLETE, + AdEvent.Type.ALL_ADS_COMPLETED, + ]) { + 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 { + _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 [""]; + } + 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 { + #errorCode = -1; + #message = ""; + #type = ""; + #vastErrorCode = -1; + constructor(type, code, vast, message) { + this.#errorCode = code; + this.#message = message; + this.#type = type; + this.#vastErrorCode = vast; + } + getErrorCode() { + return this.#errorCode; + } + getInnerError() {} + 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())) { + if (ctx.getPlayer()?.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 { + type = "adError"; + #error = ""; + constructor(error) { + 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() { + 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 ""; + } + getAdIsValue() { + 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", + }, + 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; +} |