Skip to content

Commit

Permalink
Merge pull request #7 from mozilla-extensions/rewrite
Browse files Browse the repository at this point in the history
Many changes and a rewrite
  • Loading branch information
tomrittervg authored Sep 20, 2024
2 parents b6afe3f + 61aa66f commit 31ccd92
Show file tree
Hide file tree
Showing 39 changed files with 3,870 additions and 3,091 deletions.
8 changes: 3 additions & 5 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
env: { browser: true, es2020: true, webextensions: true },
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
settings: { react: { version: "18.2" } },
parser: "@typescript-eslint/parser",
plugins: ["react-refresh"],
rules: {
"react/jsx-no-target-blank": "off",
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
Expand Down
62 changes: 62 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# How to contribute?

Thank you for considering contributing!

If you choose to develop the extension, you should ideally be familiar with React, TypeScript, state management, Zustand in particular, Tailwind/CSS, and [RFP Targets](https://searchfox.org/mozilla-central/source/toolkit/components/resistfingerprinting/RFPTargets.inc). Depending on what you want to achieve you may only need a subset of these.

In addition, this extension uses the Experimental API ([Firefox's documentation](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/basics.html#adding-experimental-apis-in-privileged-extensions) and [Thunderbird's documentation](https://webextension-api.thunderbird.net/en/stable/experiments/introduction.html)) to set Firefox preferences that are not exposed to extensions normally. It is defined in [src/api.mjs](src/api.mjs) and [src/schema.json](src/schema.json).

## Get started

To get started, simply clone the repo, and run `npm install` to install dependencies. [package.json](package.json) has a few NPM scripts that are defined. During development, you should run both `watch-popup` and `watch-ext`. The first script will build the React code automatically when you make a change, and the second script will automatically install the newly built extension to Firefox Nightly.

### React

Component Tree starting from [App/Root](src/App.tsx)
- [InitialStateLoader](src/components/InitialStateLoader.tsx)
- [BlockingMessage](src/components/BlockingMessage.tsx)
- [Notifications](src/components/Notifications.tsx)
- [Navigation](src/components/Navigation.tsx)
- [Home](src/pages/Home.tsx)
- [Troubleshooter](src/components/Troubleshooter.tsx)
- [SetAllButtons](src/components/SetAllButtons.tsx)
- [TargetList](src/components/TargetList.tsx)
- [TargetCheckbox](src/components/TargetCheckbox.tsx)

Entry point of the application is [src/App.tsx](src/App.tsx). There you'll find the following:

- `<InitialStateLoader />`: Initial state loader component, as its name suggests, loads and initializes the state. This includes fetching RFP Targets from Firefox using [RFPHelper](https://searchfox.org/mozilla-central/source/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs), loading the state of troubleshooter from [browser.storage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage), loading the active tab's domain, checking for invalid RFP targets, and checking whether fingerprinting protection is enabled or not.
- `<BlockingMessage />`: This component shows up over every element to "block" the access to extension while the initial state is being loaded.
- `<Notifications />`: Similar to BlockingMessage component, Notifications component is used to show messages to the user, however, it is not blocking. The user can dismiss or interact with the notification.
- `<Navigation tabs={tabs} />`: This component manages the navigation. Currently, navigation bar is hidden as there's no second page, but we used to have, and decided to keep this component just in case we decide to add another page in the future.

In Home page, you'll find:

- `<Troubleshooter />`: Troubleshooter component is used to run a binary search like algorithm to find out which RFP target is causing the issue with the website the user is currently using. This is useful when a user reports a website that is broken due to RFP, and we need to find out which RFP target is causing the issue.
- `<SetAllButtons />`: This component is used to enable or disable RFP targets. We don't support `+/-AllTargets` due to how it would affect the UI. Instead, we have `SetAll` buttons that allow the user to enable or disable all RFP targets.
- `<TargetList />`: This component is used to list all RFP targets. It uses TargetCheckbox component to render each target.
- `<Search />`: This component is used to search for RFP targets. It filters the targets based on the user's input.
- `<TargetCheckbox />`: This component is used to render a single RFP target. It is shows a checkbox and a button. The checkbox shows whether the target is enabled or not, and the button is used to change the scope of the target. The button's text color will be red when the RFP target was previously set granularly but the scope is set to all, or vice versa. When it is red, the user won't be able to enable or disable the target until they switch the scope to correct one.

### State Management

This extension uses Zustand for state management. The state is defined in [src/store.ts](src/store.ts). Currently, there are 5 components in the state:

- `targets`: Targets store the available RFP targets, globally and granularly enabled targets, default targets, and invalid targets. Note that the following is not an exhaustive list of functions, but the most important ones.
- `load` function fetches whether fingerprinting protection is enabled or not, invalid, default, and available RFP targets from Firefox. We don't load enabled targets here, as we don't have the current tab's domain yet.
- `set` function is used to enable or disable an RFP target globally or granularly. Calling set with `enabled: false` will set the target as `-TargetName`. We use `-TargetNames` to disable default targets globally, or disable a target granularly for a specific domain.
- `remove` function is used to remove an RFP target from the targets. Unlike set, remove will actually remove the target from the overrides and not just disable it. This is useful for removing non-default globally disabled targets.
- `clear` function will clear all the overrides.
- `activeTab`: Active tab is used to store the current tab's domain. This is used to determine which RFP targets are enabled granularly for the current domain.
- `blockingMessage`: Blocking message is used to show a blocking message over the extension while the initial state is being loaded.
- `notifications`: Notifications is used to show messages to the user. It is an array of objects, where each object has a id, message, action, and actionLabel. You may call `remove` function with the id to remove the notification.
- `troubleshooter`: Troubleshooter is used to store the state of the troubleshooter. It stores the current range target, and beginning targets. Range is the 2x the current range of targets we are testing, and beginning targets is used to enable the targets that were enabled previously before starting the troubleshooter. We store the range and beginning targets in `browser.storage` to persist the state between pop-up reloads.

### Experimental API

This extension uses the Experimental API to set Firefox preferences that are not exposed to extensions normally. It is defined in [src/api.mjs](src/api.mjs) and [src/schema.json](src/schema.json). The API file contains the following:

- `OverridesHelper`: This is used to parse, stringify global and granular overrides.
- `Utils`: There's only one function defined at the moment, and it is `tryParseJSON`. It is used to parse JSON, and if it fails, it will return given default value.
- `ExtensionPrefHelper`: This is used to wrap around the `ExtensionPreferencesManager` API. It simplifies the process of setting and getting preferences.
- `this.fppOverrides`: This is the actual Experimental API that is exposed to the extension. For it's detailed documentation, please refer to [src/schema.json](src/schema.json). In short, it is responsible for setting and getting the overrides globally and granularly.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# Fingerprinting Protection Debugger

This is a Firefox extension to easily manage privacy.fingerprintingProtection.overrides using checkboxes. You can search, enable and disable specific targets or all of them at once. The extension is written with React. The experimental API is defined in [public/api.mjs](public/api.mjs).
This is a Firefox extension to easily manage privacy.fingerprintingProtection.overrides using checkboxes and troubleshoot websites. You can search, enable and disable specific targets or all of them at once. You can use the troubleshooting mode to quickly find out which RFP target is causing the breakage. The extension is written with React. The experimental API is defined in [src/api.mjs](src/api.mjs).

### Test it locally

To test the extension locally, you have to be using a local build with [c6d8284bd68b](https://hg.mozilla.org/mozilla-central/rev/8fae218a34cd) commit, or use Nightly 128.0a1 version and the versions after that. Checkout package.json for defined scripts. Ideally, you would run `npm run watch-popup` and `npm run watch-ext` during development/testing.
To test the extension locally, you need at least Firefox 128.0a1 version. Checkout package.json for defined scripts. Ideally, you would run `npm run watch-popup` and `npm run watch-ext` during development/testing.

### Disabled Targets
### Troubleshooting mode

The goal of the troubleshooting mode is to find the breakage causing RFP target. The extension first enables one half of the protections, then depending on the user's response, it either enables the other half (e.g. the user reported that the website is now working) or cuts the enabled protections by half (e.g. the user reported that the website is still not working). Basically a binary search for the breakage causing RFP target.

### Disabled targets

Currently, two targets are disabled:

- IsAlwaysEnabledForPrecompute: This target is used internally. [According to its definition](https://searchfox.org/mozilla-central/rev/fa86401b80f19afb6ed9bfca127ecc5e3a6f0cdc/toolkit/components/resistfingerprinting/RFPTargets.inc#101-110), inluding it in overrides may result in undefined behaviour.
- AllTargets: Including AllTargets breaks the UI logic. An active target is shown with a checked checkbox, but if we include AllTargets, active targets shown would be incorrect. Instead we have an `Active All` and `Deactivate all` butto to quickly enable/disable all the targets.
- IsAlwaysEnabledForPrecompute: This target is used internally. [According to its definition](https://searchfox.org/mozilla-central/rev/fa86401b80f19afb6ed9bfca127ecc5e3a6f0cdc/toolkit/components/resistfingerprinting/RFPTargets.inc#101-110), including it in overrides may result in undefined behavior.
- AllTargets: Including AllTargets breaks the UI logic. An active target is shown with a checked checkbox, but if we include AllTargets, active targets shown would be incorrect. Instead we have an `Active All` and `Deactivate all` button to quickly enable/disable all the targets.

### Targets with yellow background

Expand Down
8 changes: 8 additions & 0 deletions assets/fingerprint.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<body class="bg-white dark:bg-slate-900">
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading

0 comments on commit 31ccd92

Please sign in to comment.