Skip to content

Commit 84975c6

Browse files
ladamskiladamski@duckduckgo.com
andauthored
messaging fixes (#209)
* update message (WIP) * allowList of permitted message types * refactor clickFunction to wait for enableSocialTracker * ensure reusableMethodName is not configurable * save original CustomEvent at load * refactor message filtering, fix tests * cleanup console spam * build * builds * preserve original dispatchEvent Co-authored-by: ladamski@duckduckgo.com <ladamski@duckduckgo.com>
1 parent 44e1bd9 commit 84975c6

File tree

13 files changed

+843
-689
lines changed

13 files changed

+843
-689
lines changed

Sources/ContentScopeScripts/dist/contentScope.js

Lines changed: 108 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,14 @@
14281428
// eslint-disable-next-line no-global-assign
14291429
let globalObj = typeof window === 'undefined' ? globalThis : window;
14301430
let Error$1 = globalObj.Error;
1431+
let messageSecret;
1432+
1433+
// save a reference to original CustomEvent amd dispatchEvent so they can't be overriden to forge messages
1434+
const OriginalCustomEvent = typeof CustomEvent === 'undefined' ? null : CustomEvent;
1435+
const originalWindowDispatchEvent = typeof window === 'undefined' ? null : window.dispatchEvent;
1436+
function registerMessageSecret (secret) {
1437+
messageSecret = secret;
1438+
}
14311439

14321440
function getDataKeySync (sessionKey, domainKey, inputData) {
14331441
// eslint-disable-next-line new-cap
@@ -1813,12 +1821,12 @@
18131821

18141822
function createCustomEvent (eventName, eventDetail) {
18151823

1816-
return new CustomEvent(eventName, eventDetail)
1824+
return new OriginalCustomEvent(eventName, eventDetail)
18171825
}
18181826

18191827
function sendMessage (messageType, options) {
18201828
// FF & Chrome
1821-
return window.dispatchEvent(createCustomEvent('sendMessage', { detail: { messageType, options } }))
1829+
return originalWindowDispatchEvent(createCustomEvent('sendMessageProxy' + messageSecret, { detail: { messageType, options } }))
18221830
// TBD other platforms
18231831
}
18241832

@@ -1899,6 +1907,7 @@
18991907
if (!shouldRun()) {
19001908
return
19011909
}
1910+
registerMessageSecret(args.messageSecret);
19021911
initStringExemptionLists(args);
19031912
const resolvedFeatures = await Promise.all(features);
19041913
resolvedFeatures.forEach(({ init, featureName }) => {
@@ -3106,7 +3115,7 @@
31063115
* @returns {Function?} onError
31073116
* Function to be called if the video fails to load.
31083117
*/
3109-
async adjustYouTubeVideoElement (videoElement) {
3118+
adjustYouTubeVideoElement (videoElement) {
31103119
let onError = null;
31113120

31123121
if (!videoElement.src) {
@@ -3185,100 +3194,103 @@
31853194
this.isUnblocked = true;
31863195
clicked = true;
31873196
let isLogin = false;
3197+
const clickElement = e.srcElement; // Object.assign({}, e)
31883198
if (this.replaceSettings.type === 'loginButton') {
31893199
isLogin = true;
31903200
}
3191-
enableSocialTracker({ entity: this.entity, action: 'block-ctl-fb', isLogin });
3192-
const parent = replacementElement.parentNode;
3193-
3194-
// If we allow everything when this element is clicked,
3195-
// notify surrogate to enable SDK and replace original element.
3196-
if (this.clickAction.type === 'allowFull') {
3197-
parent.replaceChild(originalElement, replacementElement);
3198-
this.dispatchEvent(window, 'ddg-ctp-load-sdk');
3199-
return
3200-
}
3201-
// Create a container for the new FB element
3202-
const fbContainer = document.createElement('div');
3203-
fbContainer.style.cssText = styles.wrapperDiv;
3204-
const fadeIn = document.createElement('div');
3205-
fadeIn.style.cssText = 'display: none; opacity: 0;';
3206-
3207-
// Loading animation (FB can take some time to load)
3208-
const loadingImg = document.createElement('img');
3209-
loadingImg.setAttribute('src', loadingImages[this.getMode()]);
3210-
loadingImg.setAttribute('height', '14px');
3211-
loadingImg.style.cssText = styles.loadingImg;
3212-
3213-
// Always add the animation to the button, regardless of click source
3214-
if (e.srcElement.nodeName === 'BUTTON') {
3215-
e.srcElement.firstElementChild.insertBefore(loadingImg, e.srcElement.firstElementChild.firstChild);
3216-
} else {
3217-
// try to find the button
3218-
let el = e.srcElement;
3219-
let button = null;
3220-
while (button === null && el !== null) {
3221-
button = el.querySelector('button');
3222-
el = el.parentElement;
3201+
window.addEventListener('ddg-ctp-enableSocialTracker-complete', () => {
3202+
const parent = replacementElement.parentNode;
3203+
3204+
// If we allow everything when this element is clicked,
3205+
// notify surrogate to enable SDK and replace original element.
3206+
if (this.clickAction.type === 'allowFull') {
3207+
parent.replaceChild(originalElement, replacementElement);
3208+
this.dispatchEvent(window, 'ddg-ctp-load-sdk');
3209+
return
32233210
}
3224-
if (button) {
3225-
button.firstElementChild.insertBefore(loadingImg, button.firstElementChild.firstChild);
3211+
// Create a container for the new FB element
3212+
const fbContainer = document.createElement('div');
3213+
fbContainer.style.cssText = styles.wrapperDiv;
3214+
const fadeIn = document.createElement('div');
3215+
fadeIn.style.cssText = 'display: none; opacity: 0;';
3216+
3217+
// Loading animation (FB can take some time to load)
3218+
const loadingImg = document.createElement('img');
3219+
loadingImg.setAttribute('src', loadingImages[this.getMode()]);
3220+
loadingImg.setAttribute('height', '14px');
3221+
loadingImg.style.cssText = styles.loadingImg;
3222+
3223+
// Always add the animation to the button, regardless of click source
3224+
if (clickElement.nodeName === 'BUTTON') {
3225+
clickElement.firstElementChild.insertBefore(loadingImg, clickElement.firstElementChild.firstChild);
3226+
} else {
3227+
// try to find the button
3228+
let el = clickElement;
3229+
let button = null;
3230+
while (button === null && el !== null) {
3231+
button = el.querySelector('button');
3232+
el = el.parentElement;
3233+
}
3234+
if (button) {
3235+
button.firstElementChild.insertBefore(loadingImg, button.firstElementChild.firstChild);
3236+
}
32263237
}
3227-
}
32283238

3229-
fbContainer.appendChild(fadeIn);
3230-
3231-
let fbElement;
3232-
let onError = null;
3233-
switch (this.clickAction.type) {
3234-
case 'iFrame':
3235-
fbElement = this.createFBIFrame();
3236-
break
3237-
case 'youtube-video':
3238-
onError = await this.adjustYouTubeVideoElement(originalElement);
3239-
fbElement = originalElement;
3240-
break
3241-
default:
3242-
fbElement = originalElement;
3243-
break
3244-
}
3239+
fbContainer.appendChild(fadeIn);
3240+
3241+
let fbElement;
3242+
let onError = null;
3243+
switch (this.clickAction.type) {
3244+
case 'iFrame':
3245+
fbElement = this.createFBIFrame();
3246+
break
3247+
case 'youtube-video':
3248+
onError = this.adjustYouTubeVideoElement(originalElement);
3249+
fbElement = originalElement;
3250+
break
3251+
default:
3252+
fbElement = originalElement;
3253+
break
3254+
}
32453255

3246-
// If hidden, restore the tracking element's styles to make
3247-
// it visible again.
3248-
if (this.originalElementStyle) {
3249-
for (const key of ['display', 'visibility']) {
3250-
const { value, priority } = this.originalElementStyle[key];
3251-
if (value) {
3252-
fbElement.style.setProperty(key, value, priority);
3253-
} else {
3254-
fbElement.style.removeProperty(key);
3256+
// If hidden, restore the tracking element's styles to make
3257+
// it visible again.
3258+
if (this.originalElementStyle) {
3259+
for (const key of ['display', 'visibility']) {
3260+
const { value, priority } = this.originalElementStyle[key];
3261+
if (value) {
3262+
fbElement.style.setProperty(key, value, priority);
3263+
} else {
3264+
fbElement.style.removeProperty(key);
3265+
}
32553266
}
32563267
}
3257-
}
32583268

3259-
/*
3260-
* Modify the overlay to include a Facebook iFrame, which
3261-
* starts invisible. Once loaded, fade out and remove the overlay
3262-
* then fade in the Facebook content
3263-
*/
3264-
parent.replaceChild(fbContainer, replacementElement);
3265-
fbContainer.appendChild(replacementElement);
3266-
fadeIn.appendChild(fbElement);
3267-
fbElement.addEventListener('load', () => {
3268-
this.fadeOutElement(replacementElement)
3269-
.then(v => {
3270-
fbContainer.replaceWith(fbElement);
3271-
this.dispatchEvent(fbElement, 'ddg-ctp-placeholder-clicked');
3272-
this.fadeInElement(fadeIn).then(() => {
3273-
fbElement.focus(); // focus on new element for screen readers
3269+
/*
3270+
* Modify the overlay to include a Facebook iFrame, which
3271+
* starts invisible. Once loaded, fade out and remove the overlay
3272+
* then fade in the Facebook content
3273+
*/
3274+
parent.replaceChild(fbContainer, replacementElement);
3275+
fbContainer.appendChild(replacementElement);
3276+
fadeIn.appendChild(fbElement);
3277+
fbElement.addEventListener('load', () => {
3278+
this.fadeOutElement(replacementElement)
3279+
.then(v => {
3280+
fbContainer.replaceWith(fbElement);
3281+
this.dispatchEvent(fbElement, 'ddg-ctp-placeholder-clicked');
3282+
this.fadeInElement(fadeIn).then(() => {
3283+
fbElement.focus(); // focus on new element for screen readers
3284+
});
32743285
});
3275-
});
3286+
}, { once: true });
3287+
// Note: This event only fires on Firefox, on Chrome the frame's
3288+
// load event will always fire.
3289+
if (onError) {
3290+
fbElement.addEventListener('error', onError, { once: true });
3291+
}
32763292
}, { once: true });
3277-
// Note: This event only fires on Firefox, on Chrome the frame's
3278-
// load event will always fire.
3279-
if (onError) {
3280-
fbElement.addEventListener('error', onError, { once: true });
3281-
}
3293+
enableSocialTracker({ entity: this.entity, action: 'block-ctl-fb', isLogin });
32823294
}
32833295
}.bind(this);
32843296
// If this is a login button, show modal if needed
@@ -3319,7 +3331,7 @@
33193331
}, { capture: true });
33203332

33213333
// Inform surrogate scripts that CTP is ready
3322-
window.dispatchEvent(createCustomEvent('ddg-ctp-ready'));
3334+
originalWindowDispatchEvent(createCustomEvent('ddg-ctp-ready'));
33233335
}
33243336

33253337
function replaceTrackingElement (widget, trackingElement, placeholderElement, hideTrackingElement = false, currentPlaceholder = null) {
@@ -3512,7 +3524,7 @@
35123524

35133525
function runLogin (entity) {
35143526
enableSocialTracker(entity);
3515-
window.dispatchEvent(
3527+
originalWindowDispatchEvent(
35163528
createCustomEvent('ddg-ctp-run-login', {
35173529
detail: {
35183530
entity
@@ -3522,7 +3534,7 @@
35223534
}
35233535

35243536
function cancelModal (entity) {
3525-
window.dispatchEvent(
3537+
originalWindowDispatchEvent(
35263538
createCustomEvent('ddg-ctp-cancel-modal', {
35273539
detail: {
35283540
entity
@@ -4001,10 +4013,7 @@
40014013
);
40024014
previewToggle.addEventListener(
40034015
'click',
4004-
() => makeModal(widget.entity, () => sendMessage('updateSetting', {
4005-
name: 'youtubePreviewsEnabled',
4006-
value: true
4007-
}), widget.entity)
4016+
() => makeModal(widget.entity, () => sendMessage('setYoutubePreviewsEnabled', true), widget.entity)
40084017
);
40094018
bottomRow.appendChild(previewToggle);
40104019

@@ -4131,7 +4140,7 @@
41314140
);
41324141
previewToggle.addEventListener(
41334142
'click',
4134-
() => sendMessage('updateSetting', {
4143+
() => sendMessage('setYoutubePreviewsEnabled', {
41354144
name: 'youtubePreviewsEnabled',
41364145
value: false
41374146
})
@@ -4194,13 +4203,16 @@
41944203
getYoutubePreviewsEnabled: function (resp) {
41954204
isYoutubePreviewsEnabled = resp;
41964205
},
4197-
updateSetting: function (resp) {
4206+
setYoutubePreviewsEnabled: function (resp) {
41984207
if (!resp.messageType || resp.value === undefined) { return }
4199-
window.dispatchEvent(new CustomEvent(resp.messageType, { detail: resp.value }));
4208+
originalWindowDispatchEvent(new OriginalCustomEvent(resp.messageType, { detail: resp.value }));
42004209
},
42014210
getYouTubeVideoDetails: function (resp) {
42024211
if (!resp.status || !resp.videoURL) { return }
4203-
window.dispatchEvent(new CustomEvent('ddg-ctp-youTubeVideoDetails', { detail: resp }));
4212+
originalWindowDispatchEvent(new OriginalCustomEvent('ddg-ctp-youTubeVideoDetails', { detail: resp }));
4213+
},
4214+
enableSocialTracker: function (resp) {
4215+
originalWindowDispatchEvent(new OriginalCustomEvent('ddg-ctp-enableSocialTracker-complete', { detail: resp }));
42044216
}
42054217
};
42064218

0 commit comments

Comments
 (0)