Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions libraries/rhythmoneMarsUtils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { deepAccess, isArray, parseSizesInput } from '../../src/utils.js';

export function getIsSecureBidRequest(bidderRequest) {
if (bidderRequest?.refererInfo?.stack?.length) {
const el = document.createElement('a');
el.href = bidderRequest.refererInfo.stack[0];
return (el.protocol === 'https:') ? 1 : 0;
}

return 0;
}

export function buildImpressionList(bidRequests, bidderRequest, slotsToBids, options) {
const { frameExt, getFloor, defaultVideoConfig } = options;
const impList = [];
const isSecure = getIsSecureBidRequest(bidderRequest);

for (let i = 0; i < bidRequests.length; i++) {
const bidRequestData = bidRequests[i];
slotsToBids[bidRequestData.adUnitCode] = bidRequestData;

const impObj = {
id: bidRequestData.adUnitCode,
secure: isSecure,
ext: frameExt(bidRequestData)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate media before invoking frameExt

buildImpressionList now calls frameExt before verifying that a bid produced a valid banner/video impression, which changes behavior for malformed bids that should be skipped. In the Marsmedia path, frameExt performs banner-size processing (getMinSize(processedSizes)) and can throw on invalid mediaTypes.banner.sizes shapes; previously frameExt was only reached after frameBanner/frameVideo succeeded, so these bids were dropped instead of crashing request building. This can cause the adapter request flow to fail for bad publisher size configs that were previously tolerated.

Useful? React with 👍 / 👎.

};

if (deepAccess(bidRequestData, 'mediaTypes.banner') || deepAccess(bidRequestData, 'mediaType') === 'banner') {
const banner = frameBanner(bidRequestData);
if (banner) {
impObj.banner = banner;
}
}

if (deepAccess(bidRequestData, 'mediaTypes.video') || deepAccess(bidRequestData, 'mediaType') === 'video') {
impObj.video = frameVideo(bidRequestData, defaultVideoConfig);
}

if (!(impObj.banner || impObj.video)) {
continue;
}

if (typeof getFloor === 'function') {
impObj.bidfloor = getFloor(bidRequestData);
}

impList.push(impObj);
}

return impList;
}

export function interpretCommonResponse(serverResponse, slotsToBids, options = {}) {
const {
baseBidResponse,
videoBidResponse,
bannerBidResponse
} = options;
let responses = serverResponse.body || [];
const bids = [];

if (responses.seatbid) {
const flattenedResponses = [];
for (let i = 0; i < responses.seatbid.length; i++) {
for (let j = 0; j < responses.seatbid[i].bid.length; j++) {
flattenedResponses.push(responses.seatbid[i].bid[j]);
}
}
responses = flattenedResponses;
}

for (let i = 0; i < responses.length; i++) {
const bid = responses[i];
const bidRequest = slotsToBids[bid.impid];
if (!bidRequest) {
continue;
}

const bidResponse = {
requestId: bidRequest.bidId,
cpm: parseFloat(bid.price),
width: bid.w,
height: bid.h,
creativeId: bid.crid,
currency: 'USD',
netRevenue: true,
ttl: 350
};

if (typeof baseBidResponse === 'function') {
Object.assign(bidResponse, baseBidResponse(bid, bidRequest));
}

if (bidRequest.mediaTypes && bidRequest.mediaTypes.video) {
if (typeof videoBidResponse === 'function') {
Object.assign(bidResponse, videoBidResponse(bid, bidRequest));
}
} else {
if (typeof bannerBidResponse === 'function') {
Object.assign(bidResponse, bannerBidResponse(bid, bidRequest));
}
}

bids.push(bidResponse);
}

return bids;
}

export function frameBanner(adUnit) {
// adUnit.sizes is scheduled to be deprecated, continue its support but prefer adUnit.mediaTypes.banner
let sizeList = adUnit.sizes;
if (adUnit.mediaTypes && adUnit.mediaTypes.banner) {
sizeList = adUnit.mediaTypes.banner.sizes;
}

const sizeStringList = parseSizesInput(sizeList);
const format = [];
sizeStringList.forEach(function (size) {
if (size) {
const dimensionList = getValidSizeSet(size.split('x'));
if (dimensionList) {
format.push({
w: dimensionList[0],
h: dimensionList[1],
});
}
}
});

return format.length ? { format } : false;
}

export function frameVideo(bid, defaultVideoConfig) {
let size = [];
if (deepAccess(bid, 'mediaTypes.video.playerSize')) {
let dimensionSet = bid.mediaTypes.video.playerSize;
if (isArray(bid.mediaTypes.video.playerSize[0])) {
dimensionSet = bid.mediaTypes.video.playerSize[0];
}
const validSize = getValidSizeSet(dimensionSet)
if (validSize) {
size = validSize;
}
}
return {
mimes: deepAccess(bid, 'mediaTypes.video.mimes') || defaultVideoConfig.SUPPORTED_VIDEO_MIMES,
protocols: deepAccess(bid, 'mediaTypes.video.protocols') || defaultVideoConfig.SUPPORTED_VIDEO_PROTOCOLS,
w: size[0],
h: size[1],
startdelay: deepAccess(bid, 'mediaTypes.video.startdelay') || 0,
skip: deepAccess(bid, 'mediaTypes.video.skip') || 0,
playbackmethod: deepAccess(bid, 'mediaTypes.video.playbackmethod') || defaultVideoConfig.SUPPORTED_VIDEO_PLAYBACK_METHODS,
delivery: deepAccess(bid, 'mediaTypes.video.delivery') || defaultVideoConfig.SUPPORTED_VIDEO_DELIVERY,
api: deepAccess(bid, 'mediaTypes.video.api') || defaultVideoConfig.SUPPORTED_VIDEO_API,
}
}

function getValidSizeSet(dimensionList) {
const w = parseInt(dimensionList[0]);
const h = parseInt(dimensionList[1]);
// clever check for NaN
if (!(w !== w || h !== h)) { // eslint-disable-line
return [w, h];
}
return false;
}
168 changes: 33 additions & 135 deletions modules/marsmediaBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
'use strict';
import { getDNT } from '../libraries/dnt/index.js';
import { deepAccess, parseSizesInput, isArray, getWindowTop, deepSetValue, triggerPixel, getWindowSelf, isPlainObject } from '../src/utils.js';
import { deepAccess, isArray, getWindowTop, deepSetValue, triggerPixel, getWindowSelf, isPlainObject } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { config } from '../src/config.js';
import { percentInView } from '../libraries/percentInView/percentInView.js';
import { getMinSize } from '../libraries/sizeUtils/sizeUtils.js';
import { buildImpressionList, interpretCommonResponse } from '../libraries/rhythmoneMarsUtils/index.js';

function MarsmediaAdapter() {
this.code = 'marsmedia';
Expand All @@ -30,38 +31,17 @@ function MarsmediaAdapter() {
};

function frameImp(BRs, bidderRequest) {
var impList = [];
var isSecure = 0;
if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.stack.length) {
// clever trick to get the protocol
// TODO: this should probably use parseUrl
var el = document.createElement('a');
el.href = bidderRequest.refererInfo.stack[0];
isSecure = (el.protocol === 'https:') ? 1 : 0;
}
for (var i = 0; i < BRs.length; i++) {
slotsToBids[BRs[i].adUnitCode] = BRs[i];
var impObj = {};
impObj.id = BRs[i].adUnitCode;
impObj.secure = isSecure;

if (deepAccess(BRs[i], 'mediaTypes.banner') || deepAccess(BRs[i], 'mediaType') === 'banner') {
const banner = frameBanner(BRs[i]);
if (banner) {
impObj.banner = banner;
}
}
if (deepAccess(BRs[i], 'mediaTypes.video') || deepAccess(BRs[i], 'mediaType') === 'video') {
impObj.video = frameVideo(BRs[i]);
return buildImpressionList(BRs, bidderRequest, slotsToBids, {
frameExt,
getFloor: _getFloor,
defaultVideoConfig: {
SUPPORTED_VIDEO_MIMES,
SUPPORTED_VIDEO_PROTOCOLS,
SUPPORTED_VIDEO_PLAYBACK_METHODS,
SUPPORTED_VIDEO_DELIVERY,
SUPPORTED_VIDEO_API
}
if (!(impObj.banner || impObj.video)) {
continue;
}
impObj.bidfloor = _getFloor(BRs[i]);
impObj.ext = frameExt(BRs[i]);
impList.push(impObj);
}
return impList;
});
}

function frameSite(bidderRequest) {
Expand Down Expand Up @@ -97,69 +77,6 @@ function MarsmediaAdapter() {
}
}

function getValidSizeSet(dimensionList) {
const w = parseInt(dimensionList[0]);
const h = parseInt(dimensionList[1]);
// clever check for NaN
if (! (w !== w || h !== h)) { // eslint-disable-line
return [w, h];
}
return false;
}

function frameBanner(adUnit) {
// adUnit.sizes is scheduled to be deprecated, continue its support but prefer adUnit.mediaTypes.banner
var sizeList = adUnit.sizes;
if (adUnit.mediaTypes && adUnit.mediaTypes.banner) {
sizeList = adUnit.mediaTypes.banner.sizes;
}
var sizeStringList = parseSizesInput(sizeList);
var format = [];
sizeStringList.forEach(function(size) {
if (size) {
var dimensionList = getValidSizeSet(size.split('x'));
if (dimensionList) {
format.push({
'w': dimensionList[0],
'h': dimensionList[1],
});
}
}
});
if (format.length) {
return {
'format': format
};
}

return false;
}

function frameVideo(bid) {
var size = [];
if (deepAccess(bid, 'mediaTypes.video.playerSize')) {
var dimensionSet = bid.mediaTypes.video.playerSize;
if (isArray(bid.mediaTypes.video.playerSize[0])) {
dimensionSet = bid.mediaTypes.video.playerSize[0];
}
var validSize = getValidSizeSet(dimensionSet)
if (validSize) {
size = validSize;
}
}
return {
mimes: deepAccess(bid, 'mediaTypes.video.mimes') || SUPPORTED_VIDEO_MIMES,
protocols: deepAccess(bid, 'mediaTypes.video.protocols') || SUPPORTED_VIDEO_PROTOCOLS,
w: size[0],
h: size[1],
startdelay: deepAccess(bid, 'mediaTypes.video.startdelay') || 0,
skip: deepAccess(bid, 'mediaTypes.video.skip') || 0,
playbackmethod: deepAccess(bid, 'mediaTypes.video.playbackmethod') || SUPPORTED_VIDEO_PLAYBACK_METHODS,
delivery: deepAccess(bid, 'mediaTypes.video.delivery') || SUPPORTED_VIDEO_DELIVERY,
api: deepAccess(bid, 'mediaTypes.video.api') || SUPPORTED_VIDEO_API,
}
}

function frameExt(bid) {
if ((bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes)) {
let bidSizes = (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) || bid.sizes;
Expand Down Expand Up @@ -276,51 +193,32 @@ function MarsmediaAdapter() {
};

this.interpretResponse = function (serverResponse) {
let responses = serverResponse.body || [];
const bids = [];
let i = 0;

if (responses.seatbid) {
const temp = [];
for (i = 0; i < responses.seatbid.length; i++) {
for (let j = 0; j < responses.seatbid[i].bid.length; j++) {
temp.push(responses.seatbid[i].bid[j]);
}
}
responses = temp;
}

for (i = 0; i < responses.length; i++) {
const bid = responses[i];
const bidRequest = slotsToBids[bid.impid];
const bidResponse = {
requestId: bidRequest.bidId,
cpm: parseFloat(bid.price),
width: bid.w,
height: bid.h,
creativeId: bid.crid,
currency: 'USD',
netRevenue: true,
ttl: 350,
nurl: bid.nurl
};
return interpretCommonResponse(serverResponse, slotsToBids, {
baseBidResponse(bid) {
return {
nurl: bid.nurl
};
},
videoBidResponse(bid) {
const videoResponse = {
mediaType: 'video',
ttl: 600
};

if (bidRequest.mediaTypes && bidRequest.mediaTypes.video) {
if (bid.adm.charAt(0) === '<') {
bidResponse.vastXml = bid.adm;
videoResponse.vastXml = bid.adm;
} else {
bidResponse.vastUrl = bid.adm;
videoResponse.vastUrl = bid.adm;
}
bidResponse.mediaType = 'video';
bidResponse.ttl = 600;
} else {
bidResponse.ad = bid.adm;
}

bids.push(bidResponse);
}

return bids;
return videoResponse;
},
bannerBidResponse(bid) {
return {
ad: bid.adm
};
}
});
};

function sendbeacon(bid, type) {
Expand Down
Loading
Loading