Omit non-essential fields from RTAMO flow (Fixes #14482)

This commit is contained in:
Alex Gibson 2024-04-22 09:54:18 +01:00
Родитель df4d247018
Коммит 75cab111a3
7 изменённых файлов: 236 добавлений и 199 удалений

Просмотреть файл

@ -155,6 +155,14 @@ RTAMO initially worked for only a limited subset of addons recommended by Mozill
functionality was recently expanded by the AMO team to cover all publically listed addons,
under a project called `Extended RTAMO (ERTAMO)`.
.. Important::
Because RTAMO is a user facing feature, expressly requested by the user from the AMO
page, we deem the RTAMO flow as an essential/necessary use of attribution data. We do
however limit the amount of data we collect to only what's essential for the RTAMO to
function as a feature. Non-essential fields such as the Google Analytics client ID are
omitted. We also continue to respect Do Not Track (DNT) as a valid opt-out signal.
How can visitors opt out?
-------------------------

Просмотреть файл

@ -38,40 +38,3 @@ Documentation
See `helpers.py`_ for documentation and supported parameters for both buttons.
.. _helpers.py: https://github.com/mozilla/bedrock/blob/main/bedrock/firefox/templatetags/helpers.py
External referrers
------------------
Generally we encourage other websites in the Mozilla ecosystem to link to the `/firefox/new/`_ page when
prompting visitors to download Firefox, since it provides a consistent user experience and also benefits
:abbr:`SEO (Search Engine Optimization)`. In some circumstances however sites may want to provide a download button that initiates a file
download automatically when clicked. For cases like this, sites can link to the following URL:
.. code-block::
https://www.mozilla.org/firefox/download/thanks/?s=direct
.. Important::
Including the ``s=direct`` query parameter here will ensure that Windows download attribution is
collected and recorded correctly in Telemetry. Also, make sure to **not** include the locale in
the URL, so that bedrock can serve the most suitable language based on the visitor's browser
preference.
.. Note::
This download URL will not automatically trigger a download in older Internet Explorer browsers. If
that's important to your visitors, then you can use a `conditional comment`_ to provide a different link.
.. code-block:: html
<!--[if !IE]><!-->
<a href="https://www.mozilla.org/firefox/download/thanks/?s=direct">Download Firefox</a>
<!--<![endif]-->
<!--[if IE]>
<a href="https://www.mozilla.org/firefox/new/">Download Firefox</a>
<![endif]-->
.. _/firefox/new/: https://www.mozilla.org/firefox/new/
.. _conditional comment: https://en.wikipedia.org/wiki/Conditional_comment

Просмотреть файл

@ -456,19 +456,31 @@ if (typeof window.Mozilla === 'undefined') {
/**
* Gets utm parameters and referrer information from the web page if they exist.
* @param {String} ref - Optional referrer to facilitate testing.
* @param {Boolean} omitNonEssentialFields - Optional flag to omit fields that are nonEssential for RTAMO.
* @return {Object} - Stub attribution data object.
*/
StubAttribution.getAttributionData = function (ref) {
StubAttribution.getAttributionData = function (
ref,
omitNonEssentialFields
) {
var params = new window._SearchParams();
var utms = params.utmParams();
var experiment =
params.get('experiment') || StubAttribution.experimentName;
var variation =
params.get('variation') || StubAttribution.experimentVariation;
var experiment = omitNonEssentialFields
? null
: params.get('experiment') || StubAttribution.experimentName;
var variation = omitNonEssentialFields
? null
: params.get('variation') || StubAttribution.experimentVariation;
var referrer = typeof ref === 'string' ? ref : document.referrer;
var ua = StubAttribution.getUserAgent();
var clientIDUA = StubAttribution.getUAClientID();
var clientIDGA4 = StubAttribution.getGtagClientID();
var ua = omitNonEssentialFields
? 'other'
: StubAttribution.getUserAgent();
var clientIDUA = omitNonEssentialFields
? null
: StubAttribution.getUAClientID();
var clientIDGA4 = omitNonEssentialFields
? null
: StubAttribution.getGtagClientID();
/* eslint-disable camelcase */
var data = {

Просмотреть файл

@ -0,0 +1,165 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* IMPORTANT: This file is a special version of thanks.js that supports making a Firefox
* attribution flow request immediately prior to triggering a download of the Firefox
* installer. This is required in order to support Return to addons.mozilla.org (RTAMO),
* which is a user facing feature that enables easy installation of a Firefox extension
* as a post-install onboarding step. It is triggered via a special `?s=direct` query
* parameter added to the `/thanks/` page when linking from an AMO page.
*
* Because RTAMO is a user-facing feature, expressly requested by the user from the AMO
* page, we deem the RTAMO flow as essential/necessary. Extra measures are taken to remove
* non-essential attribution fields and include only those required for RTAMO to function.
*
* DNT is still respected as an opt-out signal.
*/
let timeout;
let requestComplete = false;
function beginFirefoxDownload() {
const directDownloadLink = document.getElementById('direct-download-link');
let downloadURL;
// Only auto-start the download if a supported platform is detected.
if (
Mozilla.DownloadThanks.shouldAutoDownload(
window.site.platform,
window.site.fxSupported
) &&
typeof Mozilla.Utils !== 'undefined'
) {
downloadURL = Mozilla.DownloadThanks.getDownloadURL(window.site);
if (downloadURL) {
// Pull download link from the download button and add to the 'Try downloading again' link.
// Make sure the 'Try downloading again' link is well formatted! (issue 9615)
if (directDownloadLink && directDownloadLink.href) {
directDownloadLink.href = downloadURL;
directDownloadLink.addEventListener(
'click',
(event) => {
try {
Mozilla.TrackProductDownload.handleLink(event);
} catch (error) {
return;
}
},
false
);
}
// Start the platform-detected download a second after DOM ready event.
Mozilla.Utils.onDocumentReady(() => {
setTimeout(() => {
try {
Mozilla.TrackProductDownload.sendEventFromURL(
downloadURL
);
} catch (error) {
return;
}
window.location.href = downloadURL;
}, 1000);
});
}
}
}
function onSuccess() {
// Make sure we only initiate the download once!
clearTimeout(timeout);
if (requestComplete) {
return;
}
requestComplete = true;
// Fire GA event to log attribution success
// UA
window.dataLayer.push({
event: 'non-interaction',
eAction: 'direct-attribution',
eLabel: 'success'
});
// GA4
window.dataLayer.push({
event: 'widget_action',
type: 'direct-attribution',
action: 'success',
non_interaction: true
});
beginFirefoxDownload();
}
function onTimeout() {
// Make sure we only initiate the download once!
clearTimeout(timeout);
if (requestComplete) {
return;
}
requestComplete = true;
// Fire GA event to log attribution timeout
// UA
window.dataLayer.push({
event: 'non-interaction',
eAction: 'direct-attribution',
eLabel: 'timeout'
});
// GA4
window.dataLayer.push({
event: 'widget_action',
type: 'direct-attribution',
action: 'timeout',
non_interaction: true
});
beginFirefoxDownload();
}
function initAttribution() {
// We pass omitNonEssentialFields=true here to only include the attribution fields
// required for RTAMO to function as a user facing feature.
const data = Mozilla.StubAttribution.getAttributionData(null, true);
// Make sure we check referrer for AMO (issue 11467)
if (
data &&
Mozilla.StubAttribution.withinAttributionRate() &&
Mozilla.StubAttribution.hasValidData(data)
) {
Mozilla.StubAttribution.successCallback = onSuccess;
Mozilla.StubAttribution.timeoutCallback = onTimeout;
// We don't want to delay the download indefinitely for a stub attribution call,
// so we only wait up to an additional 2 seconds (on top of GA) before downloading.
timeout = setTimeout(onTimeout, 2000);
Mozilla.StubAttribution.requestAuthentication(data);
} else {
beginFirefoxDownload();
}
}
/**
* If visitor already has a cookie, or does not meet the typical requirements fo
* stub attribution, then we can start the download as normal. If requirements *are*
* met and the visitor does *not* have a cookie, then attempt to make the attribution
* call before starting the download.
*/
if (
typeof Mozilla.StubAttribution !== 'undefined' &&
Mozilla.StubAttribution.meetsRequirements() &&
!Mozilla.StubAttribution.hasCookie()
) {
initAttribution();
} else {
beginFirefoxDownload();
}
// Bug 1354334 - add a hint for test automation that page has loaded.
document.getElementsByTagName('html')[0].classList.add('download-ready');

Просмотреть файл

@ -1,153 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
(function () {
'use strict';
var timeout;
var requestComplete = false;
function beginFirefoxDownload() {
var directDownloadLink = document.getElementById(
'direct-download-link'
);
var downloadURL;
// Only auto-start the download if a supported platform is detected.
if (
Mozilla.DownloadThanks.shouldAutoDownload(
window.site.platform,
window.site.fxSupported
) &&
typeof Mozilla.Utils !== 'undefined'
) {
downloadURL = Mozilla.DownloadThanks.getDownloadURL(window.site);
if (downloadURL) {
// Pull download link from the download button and add to the 'Try downloading again' link.
// Make sure the 'Try downloading again' link is well formatted! (issue 9615)
if (directDownloadLink && directDownloadLink.href) {
directDownloadLink.href = downloadURL;
directDownloadLink.addEventListener(
'click',
function (event) {
try {
Mozilla.TrackProductDownload.handleLink(event);
} catch (error) {
return;
}
},
false
);
}
// Start the platform-detected download a second after DOM ready event.
Mozilla.Utils.onDocumentReady(function () {
setTimeout(function () {
try {
Mozilla.TrackProductDownload.sendEventFromURL(
downloadURL
);
} catch (error) {
return;
}
window.location.href = downloadURL;
}, 1000);
});
}
}
}
function onSuccess() {
// Make sure we only initiate the download once!
clearTimeout(timeout);
if (requestComplete) {
return;
}
requestComplete = true;
// Fire GA event to log attribution success
// UA
window.dataLayer.push({
event: 'non-interaction',
eAction: 'direct-attribution',
eLabel: 'success'
});
// GA4
window.dataLayer.push({
event: 'widget_action',
type: 'direct-attribution',
action: 'success',
non_interaction: true
});
beginFirefoxDownload();
}
function onTimeout() {
// Make sure we only initiate the download once!
clearTimeout(timeout);
if (requestComplete) {
return;
}
requestComplete = true;
// Fire GA event to log attribution timeout
// UA
window.dataLayer.push({
event: 'non-interaction',
eAction: 'direct-attribution',
eLabel: 'timeout'
});
// GA4
window.dataLayer.push({
event: 'widget_action',
type: 'direct-attribution',
action: 'timeout',
non_interaction: true
});
beginFirefoxDownload();
}
/**
* If visitor already has a cookie, or does not meet the typical requirements fo
* stub attribution, then we can start the download as normal. If requirements *are*
* met and the visitor does *not* have a cookie, then attempt to make the attribution
* call before starting the download.
*/
if (
typeof Mozilla.StubAttribution !== 'undefined' &&
Mozilla.StubAttribution.meetsRequirements() &&
!Mozilla.StubAttribution.hasCookie()
) {
// Wait for GA to load so that we can pass along visit ID.
Mozilla.StubAttribution.waitForGoogleAnalyticsThen(function () {
var data = Mozilla.StubAttribution.getAttributionData();
// make sure we check referrer for AMO (issue 11467)
if (
data &&
Mozilla.StubAttribution.withinAttributionRate() &&
Mozilla.StubAttribution.hasValidData(data)
) {
Mozilla.StubAttribution.successCallback = onSuccess;
Mozilla.StubAttribution.timeoutCallback = onTimeout;
// We don't want to delay the download indefinitely for a stub attribution call,
// so we only wait up to an additional 2 seconds (on top of GA) before downloading.
timeout = setTimeout(onTimeout, 2000);
Mozilla.StubAttribution.requestAuthentication(data);
} else {
beginFirefoxDownload();
}
});
} else {
beginFirefoxDownload();
}
// Bug 1354334 - add a hint for test automation that page has loaded.
document.getElementsByTagName('html')[0].classList.add('download-ready');
})();

Просмотреть файл

@ -1326,7 +1326,7 @@
{
"files": [
"js/firefox/new/common/thanks.js",
"js/firefox/new/common/thanks-direct.js"
"js/firefox/new/common/thanks-direct.es6.js"
],
"name": "firefox_new_thanks_direct"
},

Просмотреть файл

@ -775,6 +775,48 @@ describe('stub-attribution.js', function () {
const result = Mozilla.StubAttribution.getAttributionData(referrer);
expect(result).toEqual(data);
});
it('should omit non-essential attribution data when `omitNonEssentialFields=true` is passed', function () {
const referrer = 'https://addons.mozilla.org/';
/* eslint-disable camelcase */
const utms = {
utm_source: 'addons.mozilla.org',
utm_medium: 'referral',
utm_campaign: 'non-fx-button',
utm_content: 'rta%3Acm9uaW4td2FsbGV0QGF4aWVpbmZpbml0eS5jb20'
};
/* eslint-enable camelcase */
/* eslint-disable camelcase */
const data = {
utm_source: 'addons.mozilla.org',
utm_medium: 'referral',
utm_campaign: 'non-fx-button',
utm_content: 'rta%3Acm9uaW4td2FsbGV0QGF4aWVpbmZpbml0eS5jb20',
referrer: 'https://addons.mozilla.org/',
ua: 'other',
dlsource: DLSOURCE
};
/* eslint-enable camelcase */
spyOn(window._SearchParams.prototype, 'utmParams').and.returnValue(
utms
);
spyOn(window._SearchParams.prototype, 'get').and.callFake(
function (key) {
return key === 'experiment' ? 'firefox-new' : 1;
}
);
spyOn(Mozilla.StubAttribution, 'getUserAgent').and.returnValue(
'chrome'
);
const result = Mozilla.StubAttribution.getAttributionData(
referrer,
true
);
expect(result).toEqual(data);
});
});
describe('requestAuthentication', function () {