Skip to content

Commit

Permalink
Add a consent popup for Firefox (#375)
Browse files Browse the repository at this point in the history
* [feat] add consent page, block all action until consent is given
* [fix] check if the user still consents every update i guess
* [fix] use a then chain instead of a top-level await
* [fix] remove one call to DispatchEvent
* [fix] use firefox's proprietary message passing stuff if available (This sucks)
* [fix] add consent page to rollup config
* [fix] add a comma
* stylization and reorganization of info
* set a max/min width for text, realign bullet list
* [fix] don't use main element (weird difference between dev and build whoops)
* [fix] update magic numbers for phones
* [fix] button resizing itself
* [fix] add padding, make bullet points not run off the edge
* [fix] add a way to only trigger the consent popup if the old version needs it
* [fix] make "i hate this" comment more explanatory
  • Loading branch information
rougetimelord authored Nov 18, 2024
1 parent 3b3029d commit bd0dc8d
Show file tree
Hide file tree
Showing 12 changed files with 184 additions and 25 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "blue-blocker",
"version": "0.4.12",
"version": "0.4.13",
"author": "DanielleMiu",
"description": "Blocks all Twitter Blue verified users on twitter.com",
"type": "module",
Expand Down
15 changes: 15 additions & 0 deletions src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ api.storage.sync.onChanged.addListener(async items => {

ConnectDb();

const consentRequiredVersions = ['0.3.5']

api.runtime.onInstalled.addListener( ({reason, previousVersion}) => {
/** @ts-ignore I hate that I have to use FF specific APIs to detect FF :)))*/
api.runtime?.getBrowserInfo().then(info => {
if (info.name == 'Firefox') {
api.storage.local.set({holdUntilConsent: true});
if(reason == 'install' || (reason == 'update' && consentRequiredVersions.includes(previousVersion as string))) {
const url = api.runtime.getURL('pages/consent.index.html');
api.tabs.create({url})
}
}
})
})

api.runtime.onMessage.addListener((m, s, r) => {
let response: MessageResponse;
(async (message: RuntimeMessage, sender) => {
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ let _api: {
action: typeof chrome.action | typeof browser.browserAction;
runtime: typeof chrome.runtime;
storage: typeof chrome.storage | typeof browser.storage;
tabs: typeof chrome.tabs | typeof browser.tabs;
management: typeof chrome.management | typeof browser.management;
};
try {
_api = {
Expand Down
25 changes: 21 additions & 4 deletions src/content/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function compileConfig(config: Config): CompiledConfig {
} as CompiledConfig;
}

document.addEventListener('blue-blocker-event', function (e: CustomEvent<BlueBlockerEvent>) {
function eventHandler(e: CustomEvent<BlueBlockerEvent>) {
if (e.detail.status < 300) {
SetHeaders(e.detail.request.headers);
} else {
Expand Down Expand Up @@ -122,17 +122,34 @@ document.addEventListener('blue-blocker-event', function (e: CustomEvent<BlueBlo
});
}
});
});
}

// If we are running in Firefox, expose a function to page scripts
// This is a good test for Firefox since it's non-standard :)
/** @ts-ignore */
if(api?.runtime?.getBrowserInfo) {
/** @ts-ignore Again, non-standard, literally only FF*/
exportFunction(event => {
eventHandler(event)
}, window, {defineAs: 'blueBlockerRequest'})
}
else {
document.addEventListener('blue-blocker-event', eventHandler);
}

// Add support for OldTwitter requests.
window.addEventListener('message', function (ev) {
window.addEventListener('message', async function (ev) {
if (ev.data.type !== 'OLDTWITTER_REQUEST_LOAD') return;
if (Object.keys(await api.storage.local.get('holdUntilConsent')).length != 0) {
// if we still need user consent, we leave
return;
}
if (!ev.data.url || !ev.data.body || !ev.data.headers)
return console.error(logstr, 'OldTwitter sent an invalid payload.', ev.data);

const body_str = JSON.stringify(ev.data.body);

document.dispatchEvent(
eventHandler(
new CustomEvent('blue-blocker-event', {
detail: {
parsedUrl: /(.+)/.exec(ev.data.url)!, // Have to turn the endpoint string into a regex result...
Expand Down
19 changes: 14 additions & 5 deletions src/content/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@ import { api, DefaultOptions } from '../constants';
// TODO: see if we can remove this ignore
// @ts-ignore
import inject from '/src/injected/inject?script&module';
const script = document.createElement('script');
script.src = api.runtime.getURL(inject);
script.id = 'injected-blue-block-xhr';
script.type = 'text/javascript';
document.head.prepend(script);

api.storage.local.get('holdUntilConsent').then(obj => {
const script = document.createElement('script');
script.src = api.runtime.getURL(inject);
script.id = 'injected-blue-block-xhr';
script.type = 'text/javascript';
if (!obj?.holdUntilConsent) {
document.head.prepend(script);
}
else if (obj.holdUntilConsent == true) {
const url = api.runtime.getURL('pages/consent.index.html');
api.tabs.create({url})
}
})

let l = document.createElement('link');
l.href = api.runtime.getURL('src/injected/style.css'); // MUST BE ABSOLUTE PATH
Expand Down
32 changes: 20 additions & 12 deletions src/injected/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,27 @@
// determine if request is a timeline/tweet-returning request
const parsedUrl = RequestRegex.exec(this._url);
if (this._url && parsedUrl && parsedUrl.length > 0) {
document.dispatchEvent(
new CustomEvent('blue-blocker-event', {
detail: {
parsedUrl,
url: this._url,
body: this.response,
request: {
headers: this._requestHeaders,
},
status: this.status,
const event = new CustomEvent('blue-blocker-event', {
detail: {
parsedUrl,
url: this._url,
body: this.response,
request: {
headers: this._requestHeaders,
},
}),
);
status: this.status,
},
});
/** @ts-ignore This feels bad... */
if (window?.blueBlockerRequest) {
/** @ts-ignore This really feels bad */
blueBlockerRequest(event);
}
else {
document.dispatchEvent(
event
);
}
}
});
// TODO: remove this ignore
Expand Down
2 changes: 1 addition & 1 deletion src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineManifest } from '@crxjs/vite-plugin';
export default defineManifest({
name: 'Blue Blocker',
description: 'Blocks all Twitter Blue verified users on twitter.com',
version: '0.4.12',
version: '0.4.13',
manifest_version: 3,
icons: {
'128': 'icon/icon-128.png',
Expand Down
47 changes: 47 additions & 0 deletions src/pages/consent/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Blue Blocker Data Disclosure</title>
<link rel="stylesheet" href="./style.css">
<link href="/icon/icon-128.png" rel="icon" type="image/png" sizes="128x128" />
<script src="./index.ts" type="module"></script>
</head>
<body>
<div class="main">
<div class="center">
<h1>Collection of Data Disclosure</h1>
<br>
<p>Really quickly, before you can use Blue Blocker we have to show you this page. Please read it carefully so that you can understand how Blue Blocker collects, uses and transmits your user data.</p>
<br>
<div>
<p>Blue Blocker collects the following data:
<div class="bullet-list">
<p>- Cookies and tokens used to make requests to Twitter</p>
<p>- The URL of all requests made by the Twitter client</p>
<p>- Responses from Twitter servers</p>
</div>
</p>
</div>
<br>
<p>Your data is used to check if Twitter accounts you run into have Twitter Blue, or match other features that you configure, and then automatically blocks them</p>
<br>
<p>All data that is stored by the extension is stored locally on device</p>
<br>
<p>Your data is only (re)transmitted to Twitter's servers and/or extensions that you consent to sending data to</p>
<br>
<p>All data that Blue Blocker collects, stores, uses, and transmits is mandatory for the function of the extension Because the collection, storage, usage, and transmission of personal data is necessary for the function of this extension, refusing will cause the extension to be uninstalled</p>
<br>
<p>To read the full privacy policy visit the <a href="https://addons.mozilla.org/en-US/firefox/addon/blue-blocker/privacy/" target="_blank" rel="noopener noreferrer">Firefox addons page</a></p>
<br>
<p>Having read the above disclosure, do you consent to Blue Blocker collecting, storing, using, and transmitting your personal data?</p>
<div class="inputs">
<button id="accept">I consent</button>
<button id="refuse">I do not consent</button>
<div>
<label for="refuse">⚠️ as a reminder, refusing will uninstall the extension</label>
</div>
</div>
</div>
</div>
</body>
</html>
15 changes: 15 additions & 0 deletions src/pages/consent/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import '../style.css';
import './style.css';
import { api } from '../../constants';

const accept = document.getElementById('accept');
const refuse = document.getElementById('refuse');

accept?.addEventListener('click', async () => {
await api.storage.local.remove('holdUntilConsent');
window.close();
});

refuse?.addEventListener('click', () => {
api.management.uninstallSelf();
})
45 changes: 45 additions & 0 deletions src/pages/consent/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
p {
font-size: 1rem;
width: auto;
}

.center {
min-width: 50ch;
max-width: 120ch;
padding: 25px;
}

img {
height: 75px;
width: auto;
}

.bullet-list {
width: fit-content;
display: block;
margin-left: calc(50% - 21ch);
& p {
text-align: left;
}
}

button, button:active, button:focus {
font-size: 1.1rem;
}

.inputs {
margin-top: 1.1em;

& div {
margin-top: 5px;
}
}

.main {
height: 100vh;
width: 100vw;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
}
1 change: 1 addition & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default defineConfig(() => {
safelist: './src/pages/safelist/index.html',
history: './src/pages/history/index.html',
integrations: './src/pages/integrations/index.html',
consent: './src/pages/consent/index.html'
},
output: {
chunkFileNames: 'assets/chunk-[hash].js',
Expand Down

0 comments on commit bd0dc8d

Please sign in to comment.