Skip to content

Commit

Permalink
enable postMessage listener for cross-domain iframe support (#885)
Browse files Browse the repository at this point in the history
Support SafeFrame / x-domain on prebid creatives
  • Loading branch information
Nate Cozi authored and Matt Kendall committed Jan 12, 2017
1 parent a42655b commit 3ec356f
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
56 changes: 56 additions & 0 deletions integrationExamples/gpt/x-domain/creative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// this script can be returned by an ad server delivering a cross domain iframe, into which the
// creative will be rendered, e.g. DFP delivering a SafeFrame

// set these domains as fits your environment and ensure matching protocols
// alternatively this can be passed as a macro on the query string of the ad server call, for
// example `%%PUBLISHER_DOMAIN%%`.
const publisherDomain = 'http://localhost:9999';
const adServerDomain = 'http://tpc.googlesyndication.com';

function renderAd(ev) {
var key = ev.message ? 'message' : 'data';
var adObject = {};
try {
adObject = JSON.parse(ev[key]);
} catch (e) {
return;
}

if (adObject.ad || adObject.adUrl) {
var doc = window.document;
var ad = adObject.ad;
var url = adObject.adUrl;
var width = adObject.width;
var height = adObject.height;

if (adObject.mediaType === 'video') {
console.log('Error trying to write ad.');
} else

if (ad) {
doc.write(ad);
doc.close();
} else if (url) {
doc.write('<IFRAME SRC="' + url + '" FRAMEBORDER="0" SCROLLING="no" MARGINHEIGHT="0" MARGINWIDTH="0" TOPMARGIN="0" LEFTMARGIN="0" ALLOWTRANSPARENCY="true" WIDTH="' + width + '" HEIGHT="' + height + '"></IFRAME>');
doc.close();
} else {
console.log('Error trying to write ad. No ad for bid response id: ' + id);
}
}
}

function requestAdFromPrebid() {
var message = JSON.stringify({
message: 'Prebid Request',
adId: '%%PATTERN:hb_adid%%',
adServerDomain
});
window.parent.postMessage(message, publisherDomain);
}

function listenAdFromPrebid() {
window.addEventListener('message', renderAd, false);
}

listenAdFromPrebid();
requestAdFromPrebid();
5 changes: 5 additions & 0 deletions src/prebid.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { videoAdUnit, hasNonVideoBidder } from './video';
import 'polyfill';
import {parse as parseURL, format as formatURL} from './url';
import {isValidePriceConfig} from './cpmBucketManager';
import {listenMessagesFromCreative} from './secure-creatives';

var $$PREBID_GLOBAL$$ = getGlobal();
var CONSTANTS = require('./constants.json');
Expand Down Expand Up @@ -55,6 +56,9 @@ $$PREBID_GLOBAL$$.timeoutBuffer = 200;

$$PREBID_GLOBAL$$.logging = $$PREBID_GLOBAL$$.logging || false;

// domain where prebid is running for cross domain iframe communication
$$PREBID_GLOBAL$$.publisherDomain = $$PREBID_GLOBAL$$.publisherDomain || window.location.origin;

This comment has been minimized.

Copy link
@ehoch

ehoch Jan 17, 2017

Contributor

Where is this used / referenced again?

This comment has been minimized.

Copy link
@protonate

protonate Jan 18, 2017

Collaborator

The publisher domain is used to set the targetOrigin for the creative to postMessage to Prebid.

This comment has been minimized.

Copy link
@ehoch

ehoch Feb 22, 2017

Contributor

@protonate Are you sure? I'm not seeing $PREBID_GLOBAL$$.publisherDomain referenced anywhere again. It's redeclared in the creative sample but nowhere in prebid.js


//let the world know we are loaded
$$PREBID_GLOBAL$$.libLoaded = true;

Expand Down Expand Up @@ -836,4 +840,5 @@ $$PREBID_GLOBAL$$.getHighestCpmBids = function (adUnitCode) {
return getWinningBids(adUnitCode);
};

$$PREBID_GLOBAL$$.que.push(() => listenMessagesFromCreative());
processQue();
61 changes: 61 additions & 0 deletions src/secure-creatives.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* Secure Creatives
Provides support for rendering creatives into cross domain iframes such as SafeFrame to prevent
access to a publisher page from creative payloads.
*/

import events from './events';
import { EVENTS } from './constants';

const BID_WON = EVENTS.BID_WON;


export function listenMessagesFromCreative() {
addEventListener('message', receiveMessage, false);
}

function receiveMessage(ev) {
var key = ev.message ? 'message' : 'data';
var data = {};
try {
data = JSON.parse(ev[key]);
} catch (e) {
return;
}

if (data.adId) {
const adObject = $$PREBID_GLOBAL$$._bidsReceived.find(function (bid) {
return bid.adId === data.adId;
});

if (data.message === 'Prebid Request') {
sendAdToCreative(adObject, data.adServerDomain, ev.source);
events.emit(BID_WON, adObject);
}
}
}

function sendAdToCreative(adObject, remoteDomain, source) {
const { adId, ad, adUrl, width, height } = adObject;

if (adId) {
resizeRemoteCreative(adObject);
source.postMessage(JSON.stringify({
message: 'Prebid Response',
ad,
adUrl,
width,
height
}), remoteDomain);
}
}

function resizeRemoteCreative({ adUnitCode, width, height }) {
const iframe = document.getElementById(window.googletag.pubads()
.getSlots().find(slot => {
return slot.getAdUnitPath() === adUnitCode ||
slot.getSlotElementId() === adUnitCode;
}).getSlotElementId()).querySelector('iframe');

iframe.width = '' + width;
iframe.height = '' + height;
}

0 comments on commit 3ec356f

Please sign in to comment.