Skip to content

Commit 1fd5e37

Browse files
authored
feat: Implement consent page that runs on (firefox) install/update (#138)
This PR implements a dedicated webpage for requesting consent from firefox users on install or update (only if consent version changes). <img width="1509" alt="Screenshot 2025-06-19 at 10 09 43" src="https://github.com/user-attachments/assets/1a77e8dc-e677-4740-b262-2708022902c9" />
1 parent 333d15e commit 1fd5e37

File tree

12 files changed

+214
-72
lines changed

12 files changed

+214
-72
lines changed

.github/workflows/publish.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ jobs:
5555
CLIENT_ID: ${{ secrets.GOOGLE_WEB_STORE_CLIENT_ID }}
5656
CLIENT_SECRET: ${{ secrets.GOOGLE_WEB_STORE_CLIENT_SECRET }}
5757
REFRESH_TOKEN: ${{ secrets.GOOGLE_WEB_STORE_REFRESH_TOKEN }}
58-
# - name: Publish to Firefox
59-
# working-directory: dist-firefox
60-
# run: npx web-ext sign
61-
# env:
62-
# WEB_EXT_API_KEY: ${{ secrets.WEB_EXT_JWT_ISSUER }}
63-
# WEB_EXT_API_SECRET: ${{ secrets.WEB_EXT_JWT_SECRET }}
64-
# WEB_EXT_CHANNEL: listed
65-
# WEB_EXT_API_UPLOAD_SOURCE_CODE: ../codecov-browser-extension-${{ github.event.release.tag_name }}.tar.gz
66-
# WEB_EXT_APPROVAL_TIMEOUT: 0 # Disable timeout for approval
58+
- name: Publish to Firefox
59+
working-directory: dist-firefox
60+
run: npx web-ext sign
61+
env:
62+
WEB_EXT_API_KEY: ${{ secrets.WEB_EXT_JWT_ISSUER }}
63+
WEB_EXT_API_SECRET: ${{ secrets.WEB_EXT_JWT_SECRET }}
64+
WEB_EXT_CHANNEL: listed
65+
WEB_EXT_API_UPLOAD_SOURCE_CODE: ../codecov-browser-extension-${{ github.event.release.tag_name }}.tar.gz
66+
WEB_EXT_APPROVAL_TIMEOUT: 0 # Disable timeout for approval

public/consent.html

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>Codecov Browser Extension</title>
5+
<script src="consent.js"></script>
6+
<link rel="preconnect" href="https://fonts.googleapis.com" />
7+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
8+
<link
9+
href="https://fonts.googleapis.com/css2?family=Inter:[email protected]&display=swap"
10+
rel="stylesheet"
11+
/>
12+
<style>
13+
html {
14+
height: 100vh;
15+
}
16+
17+
body {
18+
font-family: "Inter", sans-serif;
19+
font-weight: 400;
20+
font-size: 20px;
21+
line-height: 30px;
22+
letter-spacing: 0%;
23+
text-align: center;
24+
color: rgb(14, 27, 41);
25+
display: flex;
26+
justify-content: center;
27+
align-items: center;
28+
height: 100%;
29+
margin: 0;
30+
}
31+
32+
div#content {
33+
max-width: 796px;
34+
flex-grow: 0;
35+
flex-shrink: 1;
36+
}
37+
38+
h1 {
39+
font-size: 60px;
40+
line-height: 100%;
41+
}
42+
43+
a {
44+
text-decoration: none;
45+
}
46+
47+
button {
48+
width: 214px;
49+
height: 49px;
50+
border-radius: 4px;
51+
border-width: 1px;
52+
padding-top: 4px;
53+
padding-right: 16px;
54+
padding-bottom: 4px;
55+
padding-left: 16px;
56+
background: rgba(0, 149, 255, 1);
57+
border: 1px solid rgba(1, 88, 150, 1);
58+
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.1);
59+
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.06);
60+
61+
font-size: 18px;
62+
line-height: 150%;
63+
text-align: center;
64+
vertical-align: middle;
65+
color: rgba(255, 255, 255, 1);
66+
67+
cursor: pointer;
68+
}
69+
</style>
70+
</head>
71+
<body>
72+
<div id="content">
73+
<svg
74+
width="108"
75+
height="103"
76+
viewBox="0 0 108 103"
77+
fill="none"
78+
xmlns="http://www.w3.org/2000/svg"
79+
>
80+
<path
81+
d="M54.1288 0C24.2973 0 0 24.0054 0 53.2965V53.5371L9.14057 58.8228H9.38155C15.1567 54.9809 22.1368 53.5371 29.1086 54.7403C33.9198 55.7028 38.4901 57.8602 42.0965 61.2208L43.7833 62.6646L44.7472 60.7478C45.7112 58.8311 46.6751 57.1466 47.6307 55.4622C48.1126 54.7403 48.5946 54.259 49.0766 53.5454L50.0405 52.3422L48.8356 51.3797C43.7833 47.2972 37.7672 44.4179 31.2691 43.2147C25.0119 42.0115 18.7548 42.2522 12.9879 44.1772C17.3255 25.2086 34.1608 11.7662 54.1288 11.7662C65.4382 11.7662 76.0246 16.0893 83.9603 24.0137C89.7355 29.5317 93.5828 36.5018 95.2697 44.1772C91.6633 42.9741 87.8076 42.4928 83.9603 42.4928H83.2374C81.7915 42.4928 80.3539 42.7334 78.6671 42.7334H78.4261C77.9441 42.7334 77.2212 42.9741 76.7392 42.9741C75.7753 43.2147 75.0524 43.2147 74.0968 43.4553L73.3739 43.696C72.6509 43.9366 71.928 44.1772 71.205 44.4179H70.9641C69.2772 44.8991 67.8397 45.621 66.1528 46.3347C65.4299 46.5753 64.7069 47.0566 63.984 47.5378H63.743C60.1366 49.6952 56.7629 52.3422 54.1205 55.7028L53.8795 56.1841C53.1566 57.1466 52.6746 57.8685 52.1927 58.3415C51.7107 58.8228 51.4697 59.5447 50.9878 60.2583L50.7468 60.7395C50.2648 61.4615 50.0239 62.1834 49.7829 62.6563V62.897C49.0599 64.3408 48.337 66.0169 47.855 67.7014V67.942C46.6502 71.7839 45.9272 75.8664 45.9272 80.1895V84.7532C45.9272 85.2345 46.1682 85.9564 46.1682 86.4377C47.3731 92.1963 50.0155 97.7226 54.1039 102.759L54.3448 103L54.5858 102.759C56.2727 100.843 60.12 94.835 60.602 91.2338C58.6742 87.6326 57.7185 83.5501 57.7185 79.7082C57.7185 66.2659 68.305 54.9809 82.0158 54.259H82.9798C88.514 54.0184 94.0482 55.7028 98.6185 58.8228H98.8594L108 53.5371V53.2965C108 39.1322 102.466 25.6898 92.1203 15.6081C82.0408 5.51801 68.5709 0 54.1288 0Z"
82+
fill="#FF0077"
83+
/>
84+
</svg>
85+
<h1>Codecov Browser Extension</h1>
86+
<p>
87+
If you're seeing this, we've updated our
88+
<a
89+
href="https://addons.mozilla.org/en-US/firefox/addon/codecov/privacy/"
90+
target="_blank"
91+
>privacy policy</a
92+
>.
93+
</p>
94+
<p>
95+
By clicking 'Accept', you confirm that you understand and agree to the
96+
privacy policy of the Codecov Browser Extension. Should you decide not
97+
to accept, please note that the extension will not work.
98+
</p>
99+
<br />
100+
<button id="codecov-consent-accept">Accept</button>
101+
</div>
102+
</body>
103+
</html>

public/consent.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
document.addEventListener("DOMContentLoaded", function () {
2+
var button = document.getElementById("codecov-consent-accept");
3+
button.addEventListener("click", async function () {
4+
const result = await browser.runtime.sendMessage({
5+
type: "set_consent",
6+
payload: true,
7+
});
8+
9+
if (result) {
10+
console.log("Consent granted, closing tab");
11+
close();
12+
} else {
13+
console.error(
14+
"Something went wrong saving consent. Please report this at https://github.com/codecov/codecov-browser-extension/issues"
15+
);
16+
}
17+
});
18+
});

src/background/main.ts

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,27 @@ import {
1212
unregisterContentScriptIfExists,
1313
} from "./dynamic_content_scripts";
1414

15-
async function main(): Promise<void> {
16-
browser.runtime.onMessage.addListener(handleMessages);
17-
18-
sentryInit({
19-
dsn: process.env.SENTRY_DSN,
20-
21-
integrations: [
22-
browserTracingIntegration({
23-
// disable automatic span creation
24-
instrumentNavigation: false,
25-
instrumentPageLoad: false,
26-
}),
27-
],
15+
async function handleConsent(): Promise<void> {
16+
const consent = await new Codecov().getConsent();
17+
if (!consent) {
18+
const url = browser.runtime.getURL("consent.html");
19+
await browser.tabs.create({ url, active: true });
20+
}
21+
}
2822

29-
tracesSampleRate: 1.0,
23+
async function main(): Promise<void> {
24+
browser.runtime.onInstalled.addListener(async ({ reason, temporary }) => {
25+
switch (reason) {
26+
case "install": {
27+
await handleConsent();
28+
}
29+
case "update": {
30+
await handleConsent();
31+
}
32+
}
3033
});
34+
35+
browser.runtime.onMessage.addListener(handleMessages);
3136
}
3237

3338
async function handleMessages(message: {
@@ -36,6 +41,25 @@ async function handleMessages(message: {
3641
referrer?: string;
3742
}) {
3843
const codecov = new Codecov();
44+
if (await codecov.getConsent()) {
45+
console.log("Have data consent, initializing Sentry");
46+
sentryInit({
47+
dsn: process.env.SENTRY_DSN,
48+
49+
integrations: [
50+
browserTracingIntegration({
51+
// disable automatic span creation
52+
instrumentNavigation: false,
53+
instrumentPageLoad: false,
54+
}),
55+
],
56+
57+
tracesSampleRate: 1.0,
58+
});
59+
} else {
60+
console.log("Do not have data consent, not initializing Sentry");
61+
}
62+
3963
return startSpan({ name: message.type }, async () => {
4064
switch (message.type) {
4165
case MessageType.FETCH_COMMIT_REPORT:
@@ -48,6 +72,10 @@ async function handleMessages(message: {
4872
return codecov.listComponents(message.payload, message.referrer!);
4973
case MessageType.CHECK_AUTH:
5074
return codecov.checkAuth(message.payload);
75+
case MessageType.GET_CONSENT:
76+
return codecov.getConsent();
77+
case MessageType.SET_CONSENT:
78+
return codecov.setConsent(message.payload);
5179
case MessageType.REGISTER_CONTENT_SCRIPTS:
5280
return registerContentScript(message.payload);
5381
case MessageType.UNREGISTER_CONTENT_SCRIPTS:

src/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// The version number here should represent the last version where consent requirement was updated.
2+
// We must update the key's version to re-request consent whenever the data we collect changes.
3+
export const consentStorageKey = "codecov-consent-0.5.9";
4+
15
export const codecovApiTokenStorageKey = "self_hosted_codecov_api_token";
26
export const selfHostedCodecovURLStorageKey = "self_hosted_codecov_url";
37
export const selfHostedGitHubURLStorageKey = "self_hosted_github_url";

src/content/github/common/consent.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

src/content/github/common/constants.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
// The version number here should represent the last version where consent requirement was updated.
2-
// We must update the key's version to re-request consent whenever the data we collect changes.
3-
export const consentStorageKey = "codecov-consent-0.5.9";
4-
export const consentDialogCopy =
5-
"By clicking OK, you agree to the Codecov browser extension's privacy policy (https://addons.mozilla.org/en-US/firefox/addon/codecov/privacy/). This message will reappear if you click Cancel. If you do not wish to agree, please uninstall the extension.";
6-
71
export const animationName = "codecov-gh-observer";
82

93
export const animationDefinitionId = `${animationName}-keyframe`;

src/content/github/common/fetchers.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,11 @@ export async function getPRReport(url: any) {
101101

102102
return response.data;
103103
}
104+
105+
export async function getConsent() {
106+
const response = await browser.runtime.sendMessage({
107+
type: MessageType.GET_CONSENT,
108+
});
109+
110+
return response;
111+
}

src/content/github/file/main.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ import {
2929
getCommitReport,
3030
getFlags,
3131
getBranchReport,
32+
getConsent,
3233
} from "../common/fetchers";
3334
import { print } from "src/utils";
3435
import Sentry from "../../common/sentry";
35-
import { ensureConsent } from "../common/consent";
3636

3737
const globals: {
3838
coverageReport?: FileCoverageReport;
@@ -60,8 +60,7 @@ function init(): Promise<void> {
6060

6161
async function main(): Promise<void> {
6262
try {
63-
const consent = await ensureConsent();
64-
if (!consent) {
63+
if (!(await getConsent())) {
6564
return;
6665
}
6766

src/content/github/pr/main.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,17 @@ import {
1212
import { lineSelector } from "./constants";
1313
import { colors } from "../common/constants";
1414
import { print } from "src/utils";
15-
import { getPRReport } from "../common/fetchers";
15+
import { getConsent, getPRReport } from "../common/fetchers";
1616
import { isPrUrl } from "../common/utils";
1717
import Sentry from "src/content/common/sentry";
18-
import { ensureConsent } from "../common/consent";
1918

2019
const globals: {
2120
coverageReport?: PullCoverageReport;
2221
} = {};
2322

2423
async function main() {
2524
try {
26-
const consent = await ensureConsent({ checkOnly: true });
27-
if (!consent) {
25+
if (!(await getConsent())) {
2826
return;
2927
}
3028

0 commit comments

Comments
 (0)