Pass release channel information as utm_campaign.

In branded builds, we also add `utm_campaign=<channel>` to outgoing
links to Google owned documentation sets.

Fixed: 402171599
Change-Id: Ia9d5df118d04e56ae2eb7c1403a73b20ff8a9a3a
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6343132
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Alex Rudenko <alexrudenko@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Alex Rudenko <alexrudenko@chromium.org>
This commit is contained in:
Benedikt Meurer
2025-03-11 03:43:47 -07:00
committed by Devtools-frontend LUCI CQ
parent 62d8407a63
commit 5918089492
8 changed files with 94 additions and 37 deletions
+3
View File
@@ -338,6 +338,8 @@ export interface AidaAvailability {
enterprisePolicyValue: number;
}
type Channel = 'stable'|'beta'|'dev'|'canary';
export interface HostConfigConsoleInsights {
modelId: string;
temperature: number;
@@ -442,6 +444,7 @@ interface CSSValueTracing {
*/
export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
aidaAvailability: AidaAvailability,
channel: Channel,
devToolsConsoleInsights: HostConfigConsoleInsights,
devToolsFreestyler: HostConfigFreestyler,
devToolsAiAssistanceNetworkAgent: HostConfigAiAssistanceNetworkAgent,
+1 -1
View File
@@ -555,7 +555,7 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
void SettingsScreen.showSettingsScreen({focusTabHeader: true} as ShowSettingsScreenOptions);
return true;
case 'settings.documentation':
Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(UI.UIUtils.addReferrerToURL(
Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(UI.UIUtils.addUTMParametersToURL(
'https://developer.chrome.com/docs/devtools/' as Platform.DevToolsPath.UrlString));
return true;
case 'settings.shortcuts':
@@ -78,7 +78,7 @@ export class ReleaseNoteView extends UI.Widget.VBox {
<devtools-button
.variant=${Buttons.Button.Variant.PRIMARY}
.jslogContext=${'learn-more'}
@click=${() => input.openNewTab(UI.UIUtils.addReferrerToURLIfNecessary(releaseNote.link as Platform.DevToolsPath.UrlString))}
@click=${() => input.openNewTab(UI.UIUtils.addUTMParametersToURLIfNecessary(releaseNote.link as Platform.DevToolsPath.UrlString))}
>${i18nString(UIStrings.seeFeatures)}</devtools-button>
</div>
@@ -87,7 +87,7 @@ export class ReleaseNoteView extends UI.Widget.VBox {
${releaseNote.videoLinks.map((value: {description: string, link: Platform.DevToolsPath.UrlString, type?: VideoType}) => {
return html`
<x-link
href=${UI.UIUtils.addReferrerToURLIfNecessary(value.link)}
href=${UI.UIUtils.addUTMParametersToURLIfNecessary(value.link)}
jslog=${VisualLogging.link().track({click: true}).context('learn-more')}>
<div class="video">
<img class="thumbnail" src=${input.getThumbnailPath(value.type ?? VideoType.WHATS_NEW)}>
+2 -2
View File
@@ -33,7 +33,7 @@ import type * as Platform from '../../core/platform/platform.js';
import * as VisualLogging from '../visual_logging/visual_logging.js';
import emptyWidgetStyles from './emptyWidget.css.js';
import {addReferrerToURLIfNecessary} from './UIUtils.js';
import {addUTMParametersToURLIfNecessary} from './UIUtils.js';
import {VBox} from './Widget.js';
import {XLink} from './XLink.js';
@@ -64,7 +64,7 @@ export class EmptyWidget extends VBox {
appendLink(link: Platform.DevToolsPath.UrlString): HTMLElement {
const learnMoreLink = XLink.create(
addReferrerToURLIfNecessary(link),
addUTMParametersToURLIfNecessary(link),
i18nString(UIStrings.learnMore),
undefined,
undefined,
+47 -14
View File
@@ -3,50 +3,83 @@
// found in the LICENSE file.
import * as Platform from '../../core/platform/platform.js';
import {updateHostConfig} from '../../testing/EnvironmentHelpers.js';
import * as UI from './legacy.js';
const {urlString} = Platform.DevToolsPath;
describe('UIUtils', () => {
describe('addReferrerToURL', () => {
it('correctly adds referrer info to URLs', () => {
describe('addUTMParametersToURL', () => {
const {addUTMParametersToURL} = UI.UIUtils;
it('correctly adds `utm_source` to URLs', () => {
assert.strictEqual(
UI.UIUtils.addReferrerToURL(urlString`https://www.domain.com/route`),
addUTMParametersToURL(urlString`https://www.domain.com/route`),
'https://www.domain.com/route?utm_source=devtools');
assert.strictEqual(
UI.UIUtils.addReferrerToURL(urlString`https://www.domain.com/route#anchor`),
addUTMParametersToURL(urlString`https://www.domain.com/route#anchor`),
'https://www.domain.com/route?utm_source=devtools#anchor');
assert.strictEqual(
UI.UIUtils.addReferrerToURL(urlString`https://www.domain.com/route?key=value`),
addUTMParametersToURL(urlString`https://www.domain.com/route?key=value`),
'https://www.domain.com/route?key=value&utm_source=devtools');
assert.strictEqual(
UI.UIUtils.addReferrerToURL(urlString`https://www.domain.com/route?key=value#anchor`),
addUTMParametersToURL(urlString`https://www.domain.com/route?key=value#anchor`),
'https://www.domain.com/route?key=value&utm_source=devtools#anchor');
assert.strictEqual(
UI.UIUtils.addReferrerToURL(urlString`https://www.domain.com/route?utm_source=devtools#anchor`),
addUTMParametersToURL(urlString`https://www.domain.com/route?utm_source=devtools#anchor`),
'https://www.domain.com/route?utm_source=devtools#anchor');
assert.strictEqual(
UI.UIUtils.addReferrerToURL(urlString`https://www.domain.com/route?key=value&utm_source=devtools#anchor`),
addUTMParametersToURL(urlString`https://www.domain.com/route?key=value&utm_source=devtools#anchor`),
'https://www.domain.com/route?key=value&utm_source=devtools#anchor');
});
it('correctly adds `utm_campaign` to URLs', () => {
assert.strictEqual(
addUTMParametersToURL(urlString`https://www.domain.com/route`, 'stable'),
'https://www.domain.com/route?utm_source=devtools&utm_campaign=stable');
assert.strictEqual(
addUTMParametersToURL(urlString`https://www.domain.com/route#anchor`, 'canary'),
'https://www.domain.com/route?utm_source=devtools&utm_campaign=canary#anchor');
assert.strictEqual(
addUTMParametersToURL(urlString`https://www.domain.com/route?key=value`, 'beta'),
'https://www.domain.com/route?key=value&utm_source=devtools&utm_campaign=beta');
assert.strictEqual(
addUTMParametersToURL(urlString`https://www.domain.com/route?key=value#anchor`, 'dev'),
'https://www.domain.com/route?key=value&utm_source=devtools&utm_campaign=dev#anchor');
assert.strictEqual(
addUTMParametersToURL(urlString`https://www.domain.com/route?utm_source=devtools#anchor`, 'stable'),
'https://www.domain.com/route?utm_source=devtools&utm_campaign=stable#anchor');
assert.strictEqual(
addUTMParametersToURL(urlString`https://www.domain.com/route?key=value&utm_source=devtools#anchor`, 'beta'),
'https://www.domain.com/route?key=value&utm_source=devtools&utm_campaign=beta#anchor');
});
});
describe('addReferrerToURLIfNecessary', () => {
it('correctly adds referrer for web.dev and developers.google.com', () => {
describe('addUTMParametersToURLIfNecessary', () => {
const {addUTMParametersToURLIfNecessary} = UI.UIUtils;
it('correctly adds `utm_source` for web.dev and developers.google.com', () => {
assert.strictEqual(
UI.UIUtils.addReferrerToURLIfNecessary(urlString`https://web.dev/route`),
addUTMParametersToURLIfNecessary(urlString`https://web.dev/route`),
'https://web.dev/route?utm_source=devtools');
assert.strictEqual(
UI.UIUtils.addReferrerToURLIfNecessary(urlString`https://developers.google.com/route#anchor`),
addUTMParametersToURLIfNecessary(urlString`https://developers.google.com/route#anchor`),
'https://developers.google.com/route?utm_source=devtools#anchor');
assert.strictEqual(
UI.UIUtils.addReferrerToURLIfNecessary(urlString`https://www.domain.com/web.dev/route`),
addUTMParametersToURLIfNecessary(urlString`https://www.domain.com/web.dev/route`),
'https://www.domain.com/web.dev/route');
assert.strictEqual(
UI.UIUtils.addReferrerToURLIfNecessary(urlString`https://foo.developers.google.com/route#anchor`),
addUTMParametersToURLIfNecessary(urlString`https://foo.developers.google.com/route#anchor`),
'https://foo.developers.google.com/route#anchor');
});
it('correctly adds `utm_source` and `utm_campaign` for developer.chrome.com', () => {
updateHostConfig({channel: 'beta'});
assert.strictEqual(
addUTMParametersToURLIfNecessary(urlString`https://developer.chrome.com/docs/devtools/workspaces`),
'https://developer.chrome.com/docs/devtools/workspaces?utm_source=devtools&utm_campaign=beta');
});
});
describe('LongClickController', () => {
+36 -15
View File
@@ -38,6 +38,7 @@ import './Toolbar.js';
import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Platform from '../../core/platform/platform.js';
import * as Root from '../../core/root/root.js';
import * as TextUtils from '../../models/text_utils/text_utils.js';
import * as Buttons from '../components/buttons/buttons.js';
import * as IconButton from '../components/icon_button/icon_button.js';
@@ -1560,29 +1561,49 @@ export function measureTextWidth(context: CanvasRenderingContext2D, text: string
let measureTextWidthCache: Map<string, Map<string, number>>|null = null;
/**
* Adds a 'utm_source=devtools' as query parameter to the url.
* Adds 'utm_source' and optionally 'utm_campaign' search parameters to the `url`.
*
* If the `url` doesn't have an 'utm_source' search parameter already, we'll add
* 'utm_source=devtools' to the `url`.
*
* If the `url` doesn't have an 'utm_campaign' search parameter already and the
* optional `channel` parameter (which defaults to the Chrome release channel
* inferred from the `HostConfig`) is not undefined, we'll append a new search
* parameter 'utm_campagin=<channel>' to the `url`.
*
* @param url the URL to enrich.
* @param channel the optional channel, which defaults to the channel information
* from the `HostConfig` for branded Chrome builds.
* @return the `url` with the additional UTM parameters.
* @see https://en.wikipedia.org/wiki/UTM_parameters
*/
export function addReferrerToURL(url: Platform.DevToolsPath.UrlString): Platform.DevToolsPath.UrlString {
if (/(\?|&)utm_source=devtools/.test(url)) {
return url;
export function addUTMParametersToURL(
url: URL|Platform.DevToolsPath.UrlString,
channel = Root.Runtime.hostConfig.channel): Platform.DevToolsPath.UrlString {
const urlObject = new URL(url);
if (!urlObject.searchParams.has('utm_source')) {
urlObject.searchParams.append('utm_source', 'devtools');
}
if (url.indexOf('?') === -1) {
// If the URL does not contain a query, add the referrer query after path
// and before (potential) anchor.
return url.replace(/^([^#]*)(#.*)?$/g, '$1?utm_source=devtools$2') as Platform.DevToolsPath.UrlString;
if (!urlObject.searchParams.has('utm_campaign') && typeof channel === 'string') {
urlObject.searchParams.append('utm_campaign', channel);
}
// If the URL already contains a query, add the referrer query after the last query
// and before (potential) anchor.
return url.replace(/^([^#]*)(#.*)?$/g, '$1&utm_source=devtools$2') as Platform.DevToolsPath.UrlString;
return Platform.DevToolsPath.urlString`${urlObject}`;
}
/**
* We want to add a referrer query param to every request to
* 'web.dev' or 'developers.google.com'.
* Wrapper around `addUTMParametersToURL`, which only updates URLs to Google
* owned properties.
*
* We only enrich `url` if the host is one of 'web.dev', 'developers.google.com',
* or 'developer.chrome.com'.
*
* @param url the URL to enrich.
* @return the `url` with the additional UTM parameters.
*/
export function addReferrerToURLIfNecessary(url: Platform.DevToolsPath.UrlString): Platform.DevToolsPath.UrlString {
export function addUTMParametersToURLIfNecessary(url: Platform.DevToolsPath.UrlString):
Platform.DevToolsPath.UrlString {
if (/(\/\/developers.google.com\/|\/\/web.dev\/|\/\/developer.chrome.com\/)/.test(url)) {
return addReferrerToURL(url);
return addUTMParametersToURL(url);
}
return url;
}
+2 -2
View File
@@ -12,7 +12,7 @@ import type {ContextMenu, Provider} from './ContextMenu.js';
import {html as xhtml} from './Fragment.js';
import {Tooltip} from './Tooltip.js';
import {
addReferrerToURLIfNecessary,
addUTMParametersToURLIfNecessary,
copyLinkAddressLabel,
MaxLengthForDisplayedURLs,
openLinkExternallyLabel,
@@ -94,7 +94,7 @@ export class XLink extends XElement {
let href: Platform.DevToolsPath.UrlString|null = null;
let url: URL|null = null;
try {
url = new URL(addReferrerToURLIfNecessary(newValue as Platform.DevToolsPath.UrlString));
url = new URL(addUTMParametersToURLIfNecessary(newValue as Platform.DevToolsPath.UrlString));
href = url.toString() as Platform.DevToolsPath.UrlString;
} catch {
}
@@ -369,7 +369,7 @@ export class ContrastDetails extends Common.ObjectWrapper.ObjectWrapper<EventTyp
}
private static showHelp(): void {
Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(UI.UIUtils.addReferrerToURL(
Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(UI.UIUtils.addUTMParametersToURL(
'https://web.dev/color-and-contrast-accessibility/' as Platform.DevToolsPath.UrlString));
}