fixed fireEvent() in mraid.js. - #942
AdviewOpen wants to merge 1 commit into
Conversation
YuriyVelichkoPI
left a comment
There was a problem hiding this comment.
Hi @AdviewOpen !
First of all - this is definitely a bug, and it's great that it was discovered and addressed.
However, I have two concerns here:
- there is a risk of calling the same listeners twice if, due to some scenario the additional call of
fireEventwill be made before the relation of the listener in another worker. - we need to be sure that this fix doesn't break the current behavior.
I'm not super strong in js so I would like to see your point about the first item, and if you think it makes sense, let's try to find a more robust solution without side effects for the existing flows.
For the second item, I'd like to ask @jsligh about auto tests. Have you tried to run this project https://github.com/prebid/prebid-mobile-autotests ? It contains hundreds of tests including the tests for MRAID ads. So we can use it to verify the current fixes. If we want to deprecate auto tests framework, it makes sense to migrate tests to the iOS and Android projects.
Pushing these changes to prod will be risky without appropriate testing since the changes will be shipped to all SDKs (apps).
|
Hi,@YuriyVelichkoPI |
|
Hi all, @YuriyVelichkoPI , while investigating reported impression-count discrepancies between SSPs and Prebid, I debugged the iOS SDK and found an issue in mraid.eventListeners dispatch. Further research surfaced existing reports #942 and prebid/prebid-mobile-android#732 from @AdviewOpen. In my findings it is concluded that I propose a small fix: mraid.fireEvent = function(event, args) {
- var handlers = mraid.eventListeners[event];
- if (handlers == null) { return; }
+ var handlers = (mraid.eventListeners[event] || []).slice();
+ if (handlers.length === 0) { return; }
for (var handler = 0; handler < handlers.length; handler++) {Which would allow for bidders to have multiple self-removing events fire properly. Test test('fireEvent dispatches over a snapshot when a listener self-removes mid-dispatch', () => {
const mraid = loadMraid(SRC);
const calls = [];
function a(value) {
calls.push(['A', value]);
mraid.removeEventListener('viewableChange', a);
}
function b(value) {
calls.push(['B', value]);
}
mraid.addEventListener('viewableChange', a);
mraid.addEventListener('viewableChange', b);
mraid.fireEvent('viewableChange', true);
assert.deepStrictEqual(calls, [['A', true], ['B', true]]);
calls.length = 0;
mraid.fireEvent('viewableChange', true);
assert.deepStrictEqual(calls, [['B', true]]);
});Current result: |
we found some ads contents with mraid may remove EventListener in listeners function. for example:
function addVchange(){
mraid.isViewable()?viewChangeCB(true):mraid.addEventListener("viewableChange",viewCB);//add cb
mraid.addEventListener("viewableChange",viewCB2);//add cb2
}
function viewCB(n){
console.log('+++++++ viewCB() ++++++++');
mraid.removeEventListener("viewableChange", viewCB));//remove itself
}
function viewCB2(n){
console.log('+++++++ viewCB2() ++++++++');
}
so, if use currently mraid.js, in viewCB(), listener itself will be removed and next item will be null, that mean viewCB2() will not be triggered, we already found some ads contains like this usage, so we change the mraid.fireEvent(), we make a copy when events needs to be fired, if listener function remove itself, the fireEvent()'s array will not be cracked and keep its original order, then all of EventListners will be triggered correctly.
pls review it & check it ,greate thanks.