-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prebid Core: add documentResolver callback and allow the user to supply a different document object to render #8262
Conversation
* cache url's per resolvable context * pass options as config to Renderer * pass renderDocument to render * utility function to resolve the containing window object of a document
* support user-provided renderer context * handle when there is no div[id^='google_ads'] element
src/utils.js
Outdated
if (!doc) { | ||
return null; | ||
} | ||
return doc.defaultView || doc.parentWindow; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where does parentWindow
come from? I don't see it specified on MDN: https://developer.mozilla.org/en-US/docs/Web/API/Document
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parentWindow
is used on older IE browsers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not support older IE browsers - in fact we don't support IE at all - many other things would not work. IMO it can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. We shouldnt be adding support back in for old IE versions. Can the contents of this function also just be one ternary line saying return (doc) ? doc.defaultView : null
src/Renderer.js
Outdated
context = renderer.config.contextResolver(bid, document, doc);// a user provided callback, which should return a Document, and expect the parameters; bid, sourceDocument, renderDocument | ||
} | ||
if (!context) { | ||
context = document; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO doc
would be a better default; what is the reasoning for using document
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
document
is the current default, changing it to doc
would introduce a possible breaking change in the current behaviour. But generally i agree, in fact one of the main reasons this PR exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right - I misread and thought it would default to the document passed to renderAd. Nevermind.
modules/appnexusBidAdapter.js
Outdated
@@ -590,6 +591,7 @@ function formatRequest(payload, bidderRequest) { | |||
} | |||
|
|||
function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { | |||
debugger; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah-ehm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
src/adloader.js
Outdated
*/ | ||
export function loadExternalScript(url, moduleCode, callback) { | ||
export function loadExternalScript(url, moduleCode, callback, context) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this need to be aware of the context? (also, is context
a good name for what is always a document?)
Would it work if instead of keeping track of context
, we add an option to always run the callback, and let the caller decide if they need to run their logic again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the renderer can load scripts, when the config contains a url property, like;
renderer: {
url: 'https://cdn.adnxs.com/renderer/video/ANOutstreamVideo.js'
}
in which case the script should be loaded in the correct/desired document context.
I'm not sure if calling the callback always would be the best solution, since other modules also make use of this method.
Also this would push the (repeating) logic of caching or not, always to the callback.
However there is 1 advantage to that, since once a script is loaded, it could reference/re initiate the already loaded code in the new context. But this could also be implemented else if desired, like in a pre-render hook. Which would save a url load, if the script needs to be loaded in different document contexts.
Also one could say, you could simply rely on the caching behaviour of the browser.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed that you are using context.createScript
- you are right. I have two suggestions:
- change the name from
context
to something that's clearly a document (likedoc
) - use
WeakMap
to separate caches by document, e.g.const caches = new WeakMap(); const _requestCache = caches.get(doc) || caches.set(doc, {}).get(doc)
. It's a smaller change, easier to follow, and marginally more efficient.
modules/appnexusBidAdapter.js
Outdated
@@ -1138,12 +1147,13 @@ function hideSASIframe(elementId) { | |||
} | |||
} | |||
|
|||
function outstreamRender(bid) { | |||
function outstreamRender(bid, context) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this new context
option needs support from individual adapters anyway,, it looks to me like you could reduce the scope of this change if you look for the document / context in here, without requiring it to be passed around as an argument. By that I mean look at this.options.context
in here to see what document / window to use, leaving almost everything in core unchanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered that, but only the fields url, renderder and config/options are passed allong, so adding another field would mean to make sure the new context
field is always passed in all circumstances. having it a prop of config, ensures it's always passed.
While it's true Adapter should implement this to support it fully, and the ones i've checked it seems implementations vary a lot surrounding this subject.
The core part, for loading the url, when there is not an adapter specific renderer defined, requires this change to live in the core part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I agree after giving it some more thought, if the idea is that the same renderer instance could be used on different documents. The only pushback I'll give on is the naming again - context
sound a lot like object
, while the arument is very well defined as a document. Same for contextResolver
.
* use WeakMap for caching of url's and documents * flipped key order of url cache * added unit test
i've renamed all context's to doc's, implemented the WeakMap for the caching store, and added a unit test. |
function requestResource(tagSrc, callback) { | ||
var jptScript = document.createElement('script'); | ||
function requestResource(tagSrc, callback, doc) { | ||
if (!doc) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here you check that doc
and cacheObject
are defined before using them. It doesn't hurt, except maybe for adding a couple bytes, but they should always be defined here - conceptually, if you're going to check for that, it would make more sense to throw an error when they are undefined (but I wouldn't do that either).
It's not that consequential so I don't mind if it stays this way, just noting it as an odd choice IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends, doc
being not set defaults to the loaded document
, which in theory does also happen down the line in insertElement
and some other methods. But now it's specific from here on. But i do agree it could also be an Argument Exception if there is a need to be specific about this.
The cacheObject
could theoeratically be gone for what ever reason it's resolved there. This could actually be an error, since the tag
property won't be set, but the callback would flag the object as loaded, which is sort of inconsistent. But not failing there and just loading the script, without failing there is i think not that troublesome.
@@ -212,5 +212,20 @@ describe('Renderer', function () { | |||
testRenderer.render() | |||
expect(loadExternalScript.called).to.be.true; | |||
}); | |||
|
|||
it('call\'s documentResolver when configured', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a case to adloater_spec
to check that when the same script, but different document is passed to loadExternalScript
, it does not hit the cache?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
modules/appnexusBidAdapter.js
Outdated
if (el[0]) { | ||
el[0].style.setProperty('display', 'none'); | ||
try { | ||
var el = document.getElementById(elementId).querySelectorAll("div[id^='google_ads']"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you are making changes here anyway, can this be updated to use a const instead of a var?
src/utils.js
Outdated
if (!doc) { | ||
return null; | ||
} | ||
return doc.defaultView || doc.parentWindow; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. We shouldnt be adding support back in for old IE versions. Can the contents of this function also just be one ternary line saying return (doc) ? doc.defaultView : null
* drop IE support
i've made the requested amendments. |
Type of change
Description of change
This PR, allows the user to supply a different document object to render the ad/outstream video in. This can be usefull if the prebid instance is loaded in a different document/iframe, while the ad/video should be rendered in another iframe/document.
This implements this optional change also in the Appnexus bid adapter, and allows the Appnexus bid adapter to fail silently when certain elements don't exist.
The feature add's an optional
contextResolver
callback method to the config of theRenderer
, which receives the current Bid, the document where prebid is loaded, and the document object which is passed to the renderAd method. And should return the document object in which it should render the adConfiguration can be on the adunit or mediaType level.
For example;