this script running, and on its
* own it conflates the server (DNS+TCP+TLS+Vault+Remote Config+render) with the
* client (HTML transfer, script download, parse). Prod showed boot_ms at 98.7% of
* total_ms with everything downstream costing ~7 ms, so this split is the whole
* remaining question and there is nothing else left worth instrumenting.
*
* ttfb_ms is measured from fetchStart, NOT from timeOrigin. Ad clicks routinely
* arrive via one or more upstream 302s (Meta's link shim), and those hops sit
* between timeOrigin and fetchStart. Counting them as our server time would
* inflate ttfb_ms by an amount we neither control nor can fix — and for a
* cross-origin redirect without Timing-Allow-Origin the redirect timings are
* zeroed, so it would not even be visible as such.
*
* redirect_ms carries that upstream time separately, so the three add up:
* boot_ms = redirect_ms + ttfb_ms + (client: HTML transfer, scripts, parse)
*/
function navTiming() {
try {
return performance.getEntriesByType('navigation')[0];
} catch (e) {
return undefined;
}
}
function ttfb(nav) {
return nav && nav.responseStart > 0 && nav.fetchStart >= 0
? nav.responseStart - nav.fetchStart
: undefined;
}
function upstreamRedirect(nav) {
return nav && nav.fetchStart > 0 ? nav.fetchStart : undefined;
}
function sendTrace(finalUrl) {
try {
var nav = navTiming();
var payload = {
traceId: props.traceId,
finalUrl: finalUrl,
flow: FLOW,
// Distinguishes these lines from the React flow's in the same log stream.
impl: 'html',
total_ms: round(t.open),
boot_ms: round(t.boot),
ttfb_ms: round(ttfb(nav)),
redirect_ms: round(upstreamRedirect(nav)),
ss_ms: 0,
params_ms: round(t.params - t.ssReady),
cookie_ms: round(t.cookie),
// Resolved in getServerSideProps now, so this is server-side duration and
// costs the client nothing. Kept under the same key so existing log
// queries and percentiles keep working; `rc_server` marks the difference.
rc_ms: round(props.rcMs),
onelink_ms: round(t.onelink - t.params)
};
// rcMs is null when the server skipped the fetch; only tag a real number.
if (payload.rc_ms !== undefined) {
payload.rc_server = 1;
}
// Remote Config could not be resolved server-side, so af_ios_store_cpp was
// dropped. Watch this against af_ios_store_cpp coverage (80.9% pre-move).
if (props.rcFail) {
payload.rc_fail = 1;
}
// Deliberately withheld rather than failed — the request did not look like
// an ad click. Distinct from rc_fail so af_ios_store_cpp coverage can be
// attributed to the right cause. See looksLikeAdClick.
if (props.rcSkipped) {
payload.rc_skipped = 1;
}
var params = new URLSearchParams();
for (var key in payload) {
if (payload[key] !== undefined && payload[key] !== null && payload[key] !== '') {
params.set(key, String(payload[key]));
}
}
// form-urlencoded, not a JSON Blob: WKWebView has historically downgraded
// Blob content types, which Next's bodyParser then drops silently.
if (navigator.sendBeacon && navigator.sendBeacon('/api/trace', params)) {
return;
}
fetch('/api/trace', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params.toString(),
keepalive: true
})['catch'](function () {});
} catch (e) {
/* logging must never break the redirect */
}
}
/*
* NAVIGATION — the one thing that must never fail.
*
* This used to be `window.open(finalUrl, '_top', 'noopener,noreferrer')`, copied
* from the React flow. That worked there only because a useEffect runs AFTER the
* document has finished loading. Running from a deferred script we fire while
* document.readyState is still 'loading', and in that window window.open is not
* dependable in the WKWebView-based in-app browsers that are most of our traffic:
* observed on a real device in the Instagram browser, the call cancelled the
* in-flight load without navigating, leaving the user on a blank page with the
* progress bar stuck on "Loading..." indefinitely.
*
* location.replace is the right primitive here. It is a same-window navigation
* rather than a popup request (so no popup heuristics and no WKUIDelegate
* involvement), it is specified to abort the in-flight load and navigate, and it
* leaves no history entry — so the back button does not return to this blank page.
*
* REFERRER: window.open carried 'noreferrer'; location.replace sends a Referer
* (this origin) to the OneLink instead. That is a deliberate, minor change —
* AppsFlyer is the destination and the referring w2a host is not sensitive. A
* document-wide would fix it but is deliberately NOT used,
* since it would also strip the Referer from the Facebook pixel calls and hurt
* event matching. If the referrer ever needs suppressing, do it with an anchor
* carrying rel="noopener noreferrer" rather than document-wide.
*
* NO WATCHDOG, DELIBERATELY. A timer that re-tries the navigation looks tempting,
* but there is no reliable way from script to tell "the navigation was ignored"
* from "the navigation is in flight": `pagehide` only fires once the next document
* commits, so on a slow connection a perfectly healthy location.replace would not
* have fired it yet and the timer would restart the navigation — repeatedly, and
* worst for exactly the slow-network users this whole effort is about. One
* primitive, one code path.
*/
function go(finalUrl) {
t.open = now();
sendTrace(finalUrl);
W.location.replace(finalUrl);
}
function waitForFbp(done) {
var startedAt = now();
var pixelID = qs.get('fb_pixel_id') || props.facebookPixelID;
// getFacebookParams returns {} with no pixel id, so there is nothing to wait
// for and no fb params to send.
if (!pixelID) {
t.cookie = 0;
done(null, pixelID);
return;
}
function finish() {
var elapsed = now();
t.cookie =
startedAt !== undefined && elapsed !== undefined ? Math.round(elapsed - startedAt) : 0;
done(readCookie('_fbp'), pixelID);
}
(function tick() {
var elapsed = now();
var overBudget =
startedAt === undefined || elapsed === undefined || elapsed - startedAt >= FBP_MAX_WAIT_MS;
if (readCookie('_fbp') || overBudget) {
finish();
return;
}
setTimeout(tick, FBP_STEP_MS);
})();
}
function buildParams(fbp, pixelID) {
var loc = W.location;
var eventSourceURL = encodeURIComponent(loc.protocol + '//' + loc.hostname + loc.pathname);
var fbclid = qs.get('fbclid');
var fbc = readCookie('_fbc');
if (!fbc && fbclid && fbclid !== 'null') {
// Mirrors helpers/analytics/common/generateFbc.js.
fbc = 'fb.1.' + new Date().getTime() + '.' + fbclid;
}
/*
* MUST mirror src/helpers/analytics/common/createOneLinkAdditionalParams.js.
* `country` / `client_ip_address` are absent because the geolocation fetch they
* came from has never worked — 0% coverage across 1700 prod finals, from a
* relative fetch('?key=...') that returns page HTML plus a prop-name mismatch.
* Porting it would only reproduce the no-op; it wants fixing server-side.
*/
var additionalParams = [
{ paramKey: 'fb_pixel_id', keys: ['fb_pixel_id'], defaultValue: '' },
{ paramKey: 'fbp', keys: [], defaultValue: pixelID ? fbp : undefined },
{ paramKey: 'fbc', keys: [], defaultValue: pixelID ? fbc : undefined },
{ paramKey: 'fbclid', keys: [], defaultValue: pixelID ? fbclid : undefined },
{ paramKey: 'utm_source', keys: [], defaultValue: 'onelink_source_error' },
{ paramKey: 'utm_campaign', keys: [], defaultValue: 'onelink_campaign_error' },
{ paramKey: 'no_adset', keys: [], defaultValue: 'onelink_adset_error' },
{ paramKey: 'original_url_ad', keys: [], defaultValue: 'onelink_ad_error' },
{ paramKey: 'client_user_agent', keys: [], defaultValue: W.navigator.userAgent },
{ paramKey: 'external_id', keys: [], defaultValue: W.fbExternalID },
{ paramKey: 'event_source_url', keys: [], defaultValue: eventSourceURL },
{ paramKey: 'test_event_code', keys: ['test_event_code'], defaultValue: '' },
// custom ad params
{ paramKey: 'af_c_id', keys: ['af_c_id'], defaultValue: '' },
{ paramKey: 'af_adset_id', keys: ['af_adset_id'], defaultValue: '' },
{ paramKey: 'af_ad_id', keys: ['af_ad_id'], defaultValue: '' },
{ paramKey: 'af_ios_store_cpp', keys: ['campaign_cpp'], defaultValue: '' }
];
var cpp = (props.remoteConfig && props.remoteConfig.ab_landing_cpp) || '';
if (cpp && cpp.toLowerCase().indexOf('none') === -1) {
for (var i = 0; i < additionalParams.length; i++) {
if (additionalParams[i].paramKey === 'af_ios_store_cpp') {
additionalParams[i].defaultValue = cpp;
break;
}
}
}
return additionalParams;
}
// Mirrors src/helpers/analytics/instant-redirect/createOneLinkPromise.js.
var MEDIA_SOURCE_KEYS = [
'utm_source',
'serene_web2app_fb',
'botan_web2app_fb',
'calorie_web2app_fb',
'fasting_web2app_fb',
'pdfscanner_web2app_fb',
'mathsolver_web2app_fb',
'airemodel_web2app_fb',
'mt_web2app_fb', // tapemeasureapp.com
'locatemyphone_web2app_fb',
'scanner_web2app_fb'
];
try {
waitForFbp(function (fbp, pixelID) {
try {
var additionalParams = buildParams(fbp, pixelID);
t.params = now();
var result = af.generateOneLinkURL({
oneLinkURL: props.oneLinkURL,
afParameters: {
mediaSource: { keys: MEDIA_SOURCE_KEYS, defaultValue: 'onelink_source_error' },
campaign: { keys: ['utm_campaign'], defaultValue: 'onelink_campaign_error' },
adSet: { keys: ['no_adset'], defaultValue: 'onelink_adset_error' },
ad: { keys: ['original_url_ad'], defaultValue: 'onelink_ad_error' },
deepLinkValue: { keys: ['deep_link_value', 'utm_deeplink'], defaultValue: '' },
afCustom: additionalParams
}
});
Promise.resolve(result).then(function (resolved) {
t.onelink = now();
/*
* generateOneLinkURL resolves to null when oneLinkURL is empty or
* malformed — reachable in prod on a Vault failure. Reading .clickURL
* off it unguarded used to throw, skip window.open entirely and leave
* the user on a blank page. Optional chaining is the fix; here the
* fallback is to hand off to React, which owns the no_click_url path.
*/
if (resolved && resolved.clickURL) {
go(resolved.clickURL);
} else {
fail('no_click_url');
}
}, fail);
} catch (error) {
fail(error);
}
});
} catch (error) {
fail(error);
}
})();