diff --git a/src/prebid.js b/src/prebid.js index 5cc6822c6b3..e59ee6cded6 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -402,10 +402,23 @@ function emitAdRenderSucceeded({ doc, bid, id }) { events.emit(AD_RENDER_SUCCEEDED, data); } +/** + * This function will check for presence of given node in given parent. If not present - will inject it. + * @param {Node} node node, whose existance is in question + * @param {Document} doc document element do look in + * @param {string} tagName tag name to look in + */ +function reinjectNodeIfRemoved(node, doc, tagName) { + const injectionNode = doc.querySelector(tagName); + if (!node.parentNode || node.parentNode !== injectionNode) { + insertElement(node, doc, tagName); + } +} + /** * This function will render the ad (based on params) in the given iframe document passed through. * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously - * @param {HTMLDocument} doc document + * @param {Document} doc document * @param {string} id bid id to locate the ad * @alias module:pbjs.renderAd */ @@ -449,10 +462,11 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { const {height, width, ad, mediaType, adUrl, renderer} = bid; const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); + insertElement(creativeComment, doc, 'html'); if (isRendererRequired(renderer)) { executeRenderer(renderer, bid); - insertElement(creativeComment, doc, 'html'); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); emitAdRenderSucceeded({ doc, bid, id }); } else if ((doc === document && !inIframe()) || mediaType === 'video') { const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; @@ -471,7 +485,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { doc.write(ad); doc.close(); setRenderSize(doc, width, height); - insertElement(creativeComment, doc, 'html'); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); callBurl(bid); emitAdRenderSucceeded({ doc, bid, id }); } else if (adUrl) { @@ -484,7 +498,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) { insertElement(iframe, doc, 'body'); setRenderSize(doc, width, height); - insertElement(creativeComment, doc, 'html'); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); callBurl(bid); emitAdRenderSucceeded({ doc, bid, id }); } else { diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 962fae60db1..2522887bb98 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1135,13 +1135,15 @@ describe('Unit: Prebid Module', function () { height: 0 } }, - getElementsByTagName: sinon.stub() + getElementsByTagName: sinon.stub(), + querySelector: sinon.stub() }; elStub = { insertBefore: sinon.stub() }; doc.getElementsByTagName.returns([elStub]); + doc.querySelector.returns(elStub); spyLogError = sinon.spy(utils, 'logError'); spyLogMessage = sinon.spy(utils, 'logMessage');