refactor: migracja wywołań API przeglądarki do abstrakcji browserAPI #127

Open
am0 wants to merge 1 commits from refactor/build_time_abstraction into develop
5 changed files with 100 additions and 93 deletions
Showing only changes of commit 3512386b2b - Show all commits

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Tab } from '../../util'; import browserAPI, { Tab } from '../../lib/browser-api';
export default function TabDropdown({ export default function TabDropdown({
setPickedTab, setPickedTab,
@ -10,7 +10,7 @@ export default function TabDropdown({
}) { }) {
const [tabs, setTabs] = React.useState<Tab[]>([]); const [tabs, setTabs] = React.useState<Tab[]>([]);
React.useEffect(() => { React.useEffect(() => {
browser.tabs.query({ currentWindow: true }).then(setTabs); browserAPI.tabs.query({ currentWindow: true }).then(setTabs);
}, []); }, []);
return ( return (
<select <select
@ -27,4 +27,4 @@ export default function TabDropdown({
))} ))}
</select> </select>
); );
} }

View File

@ -1,20 +1,18 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { useEmitter } from '../../util';
import { getMemory } from '../../memory'; import { getMemory } from '../../memory';
import { useEmitter, getshorthost } from '../../util';
import browserAPI from '../../lib/browser-api';
async function getCurrentTab() { async function getCurrentTab() {
const [tab] = await browser.tabs.query({ const [tab] = await browserAPI.tabs.query({
active: true, active: true,
windowId: browser.windows.WINDOW_ID_CURRENT, windowId: browserAPI.windows.WINDOW_ID_CURRENT,
}); });
return tab; return tab;
} }
import './../../styles/global.scss'; function isDomainHighlySuspicious(domain: string) {
import './toolbar.scss';
function isDomainHighlySuspicious(domain: string): boolean {
return ( return (
domain.includes('facebook') || domain.includes('facebook') ||
domain.includes('twitter') || domain.includes('twitter') ||
@ -27,19 +25,19 @@ const Toolbar = () => {
const [origin, setOrigin] = React.useState<string | null>(null); const [origin, setOrigin] = React.useState<string | null>(null);
const [eventCounts] = useEmitter(getMemory()); const [eventCounts] = useEmitter(getMemory());
const [cookieDomainCopy, setCookieDomainCopy] = React.useState<string | null>(null); const [cookieDomainCopy, setCookieDomainCopy] = React.useState<string | null>(null);
const [_, setMarksOccurrence] = React.useState<boolean>(false); const [_, setMarksOccurrence] = React.useState(false);
const [exposedOriginDomainCopy, setExposedOriginDomainCopy] = React.useState<string | null>( const [exposedOriginDomainCopy, setExposedOriginDomainCopy] = React.useState<string | null>(
null null
); );
const first_sentence_cookie = 'Strona dokonała zapisu i odczytu plików Cookie dla domen '; const first_sentence_cookie =
'Strona dokonała zapisu i odczytu plików Cookie dla domen ';
const first_sentence_history = const first_sentence_history =
'Część informacji o Twojej historii przeglądania została wysłana do '; 'Część informacji o Twojej historii przeglądania została wysłana do ';
React.useEffect(() => { React.useEffect(() => {
const listener = async () => { const listener = async () => {
const tab = await getCurrentTab(); const tab = await getCurrentTab();
if (tab !== undefined && tab.url) { if (tab !== undefined && tab.url) {
const url = new URL(tab.url); const url = new URL(tab.url);
if (url.origin.startsWith('moz-extension')) { if (url.origin.startsWith('moz-extension')) {
@ -50,26 +48,27 @@ const Toolbar = () => {
console.warn('Out of the tab scope'); console.warn('Out of the tab scope');
} }
}; };
browserAPI.tabs.onUpdated.addListener(listener);
browser.tabs.onUpdated.addListener(listener);
listener(); listener();
return () => { return () => {
browser.tabs.onUpdated.removeListener(listener); browserAPI.tabs.onUpdated.removeListener(listener);
}; };
}); });
React.useEffect(() => { React.useEffect(() => {
if (!origin) return; if (!origin) return;
const exposedOriginDomains = Object.values(getMemory().getClustersForOrigin(origin)) const exposedOriginDomains = Object.values(getMemory().getClustersForOrigin(origin))
.filter((cluster) => cluster.exposesOrigin()) .filter((cluster) => cluster.exposesOrigin())
.sort((cluster1, cluster2) => .sort((cluster1, cluster2) =>
isDomainHighlySuspicious(cluster1.id) isDomainHighlySuspicious(cluster1.id)
? -1 ? -1
: isDomainHighlySuspicious(cluster2.id) : isDomainHighlySuspicious(cluster2.id)
? 1 ? 1
: 0 : 0
) )
.map((cluster) => cluster.id); .map((cluster) => cluster.id);
setExposedOriginDomainCopy(''); setExposedOriginDomainCopy('');
switch (exposedOriginDomains.length) { switch (exposedOriginDomains.length) {
@ -100,16 +99,18 @@ const Toolbar = () => {
React.useEffect(() => { React.useEffect(() => {
if (!origin) return; if (!origin) return;
const cookieDomains = Object.values(getMemory().getClustersForOrigin(origin)) const cookieDomains = Object.values(getMemory().getClustersForOrigin(origin))
.filter((cluster) => cluster.hasCookies()) .filter((cluster) => cluster.hasCookies())
.sort((cluster1, cluster2) => .sort((cluster1, cluster2) =>
isDomainHighlySuspicious(cluster1.id) isDomainHighlySuspicious(cluster1.id)
? -1 ? -1
: isDomainHighlySuspicious(cluster2.id) : isDomainHighlySuspicious(cluster2.id)
? 1 ? 1
: 0 : 0
) )
.map((cluster) => cluster.id); .map((cluster) => cluster.id);
setCookieDomainCopy(''); setCookieDomainCopy('');
switch (cookieDomains.length) { switch (cookieDomains.length) {
@ -128,7 +129,7 @@ const Toolbar = () => {
break; break;
default: default:
setCookieDomainCopy( setCookieDomainCopy(
`${cookieDomains[0]}, ${cookieDomains[1]} (i ${ `${cookieDomains[0]}, ${cookieDomains[1]} (i ${
cookieDomains.length - 2 < 2 ? 2 : cookieDomains.length - 2 cookieDomains.length - 2 < 2 ? 2 : cookieDomains.length - 2
} innych).` } innych).`
); );
@ -136,44 +137,37 @@ const Toolbar = () => {
} }
}, [eventCounts['*'], origin]); }, [eventCounts['*'], origin]);
React.useEffect(() => { const autoMark = () => {
if (!origin) return; Object.values(getMemory().getClustersForOrigin(origin || '')).forEach((cluster) =>
for (const cluster of Object.values(getMemory().getClustersForOrigin(origin))) { cluster.autoMark()
if (cluster.hasMarks()) { );
return setMarksOccurrence(true); setMarksOccurrence(true);
} };
}
return setMarksOccurrence(false);
}, [eventCounts['*']]);
function autoMark() {
if (!origin) return;
for (const cluster of Object.values(getMemory().getClustersForOrigin(origin))) {
cluster.autoMark();
}
return setMarksOccurrence(true);
}
return ( return (
<div className="toolbar"> <div className="toolbar">
<header className={origin ? 'header' : 'header header--no-page'}> <header className={origin ? 'header' : 'header header--no-page'}>
<img src="../../assets/icon-addon.svg" height={32}></img> <img src="../../assets/icon-addon.svg" height="24" />
<div className="webpage-metadata"> <div className="webpage-metadata">
{origin ? ( {origin ? (
<> <div className="webpage-metadata--hyperlink">{origin}</div>
<span>Analiza strony</span>
<span className="webpage-metadata--hyperlink">{origin}</span>
</>
) : ( ) : (
<span>Przejdź do wybranej strony internetowej</span> <div>Rentgen - wtyczka do przeglądania</div>
)} )}
</div> </div>
{origin ? ( {origin ? (
<button
onClick={() => {
window.close();
}}
>
<img src="../../assets/icons/x_thick.svg" width="12" height="12" />
</button>
) : (
<a href="https://internet-czas-dzialac.pl"> <a href="https://internet-czas-dzialac.pl">
<img src="/assets/icons/info_circle_outline.svg" width="20" height="20" /> <img src="/assets/icons/info_circle_outline.svg" width="20" height="20" />
</a> </a>
) : null} )}
</header> </header>
{origin ? ( {origin ? (
@ -183,30 +177,22 @@ const Toolbar = () => {
<div className="counters-wrapper"> <div className="counters-wrapper">
<div className="counters"> <div className="counters">
<div className="counter counter--cookies"> <div className="counter counter--cookies">
<img <img src="/assets/icons/cookie.svg#color" width="24" height="24" />
src="/assets/icons/cookie.svg#color"
width="24"
height="24"
/>
<span data-event={`${eventCounts['*']}`}> <span data-event={`${eventCounts['*']}`}>
{ {
Object.values( Object.values(getMemory().getClustersForOrigin(origin)).filter(
getMemory().getClustersForOrigin(origin) (cluster) => cluster.hasCookies()
).filter((cluster) => cluster.hasCookies()).length ).length
} }
</span> </span>
</div> </div>
<div className="counter counter--browser-history"> <div className="counter counter--browser-history">
<img <img src="/assets/icons/warning.svg#color" width="24" height="24" />
src="/assets/icons/warning.svg#color"
width="24"
height="24"
/>
<span data-event={`${eventCounts['*']}`}> <span data-event={`${eventCounts['*']}`}>
{ {
Object.values( Object.values(getMemory().getClustersForOrigin(origin)).filter(
getMemory().getClustersForOrigin(origin) (cluster) => cluster.exposesOrigin()
).filter((cluster) => cluster.exposesOrigin()).length ).length
} }
</span> </span>
</div> </div>
@ -247,9 +233,9 @@ const Toolbar = () => {
<Fragment> <Fragment>
<section className="about"> <section className="about">
<p> <p>
Takie przetwarzanie danych może być niezgodne z prawem. Przejdź Takie przetwarzanie danych może być niezgodne z&nbsp;prawem.
do analizy aby pomóc ustalić, czy ta strona nie narusza RODO lub Przejdź do analizy aby pomóc ustalić, czy ta strona nie narusza
ustawy Prawo Komunikacji Elektronicznej. RODO lub ustawy Prawo Komunikacji Elektronicznej.
</p> </p>
</section> </section>
<section className="actions"> <section className="actions">
@ -261,7 +247,7 @@ const Toolbar = () => {
`/components/sidebar/sidebar.html?origin=${origin}`, `/components/sidebar/sidebar.html?origin=${origin}`,
'new_tab' 'new_tab'
); );
window.close(); // close toolbar popup window.close();
}} }}
> >
Przejdź do analizy Przejdź do analizy
@ -281,4 +267,4 @@ const Toolbar = () => {
); );
}; };
ReactDOM.render(<Toolbar />, document.getElementById('toolbar')); ReactDOM.render(<Toolbar />, document.getElementById('toolbar'));

View File

@ -8,6 +8,33 @@
* - memory.ts: browserAction.*, webRequest.*, cookies.*, extension.* * - memory.ts: browserAction.*, webRequest.*, cookies.*, extension.*
*/ */
// Import full Request type from util.ts
export type Request = {
cookieStoreId?: string;
documentUrl?: string;
frameId: number;
incognito?: boolean;
method: string;
originUrl: string;
parentFrameId: number;
proxyInfo?: {
host: string;
port: number;
type: string;
username: string;
proxyDNS: boolean;
failoverTimeout: number;
};
requestHeaders?: { name: string; value?: string; binaryValue?: number[] }[];
requestId: string;
tabId: number;
thirdParty?: boolean;
timeStamp: number;
type: string;
url: string;
urlClassification?: { firstParty: string[]; thirdParty: string[] };
};
// === Tab API (util.ts, tab-dropdown.tsx, toolbar.tsx) === // === Tab API (util.ts, tab-dropdown.tsx, toolbar.tsx) ===
export interface Tab { export interface Tab {
id?: number; // util.ts: tab.id, tab-dropdown.tsx: tab.id id?: number; // util.ts: tab.id, tab-dropdown.tsx: tab.id
@ -37,23 +64,11 @@ export interface BadgeColorDetails {
} }
// === WebRequest API (memory.ts) === // === WebRequest API (memory.ts) ===
export interface RequestDetails {
requestId: string; // memory.ts: request.requestId
requestHeaders?: RequestHeader[]; // memory.ts: request.requestHeaders
// Note: ExtendedRequest konstruktor używa więcej pól,
// ale tu skupiamy się na tym co bezpośrednio używa browser API
}
export interface RequestHeader {
name: string;
value?: string;
}
export interface RequestFilter { export interface RequestFilter {
urls: string[]; // memory.ts: { urls: ['<all_urls>'] } urls: string[]; // memory.ts: { urls: ['<all_urls>'] }
} }
export type RequestListener = (details: RequestDetails) => void; export type RequestListener = (details: Request) => void;
// === Cookies API (memory.ts) === // === Cookies API (memory.ts) ===
export interface Cookie { export interface Cookie {

View File

@ -2,10 +2,11 @@ import ExtendedRequest from './extended-request';
import { getshorthost } from './util'; import { getshorthost } from './util';
import { RequestCluster } from './request-cluster'; import { RequestCluster } from './request-cluster';
import { SaferEmitter } from './safer-emitter'; import { SaferEmitter } from './safer-emitter';
import browserAPI from './lib/browser-api';
function setDomainsCount(counter: number, tabId: number) { function setDomainsCount(counter: number, tabId: number) {
browser.browserAction.setBadgeText({ text: counter < 0 ? '0' : counter.toString(), tabId }); browserAPI.badge.setBadgeText({ text: counter < 0 ? '0' : counter.toString(), tabId });
browser.browserAction.setTitle({ browserAPI.badge.setTitle({
title: 'Rentgen', title: 'Rentgen',
tabId, tabId,
}); });
@ -32,8 +33,8 @@ export default class Memory extends SaferEmitter {
Object.values(this.getClustersForOrigin(request.origin)).some((cluster) => Object.values(this.getClustersForOrigin(request.origin)).some((cluster) =>
cluster.hasCookies() cluster.hasCookies()
) )
? browser.browserAction.setBadgeBackgroundColor({ color: '#ff726b' }) ? browserAPI.badge.setBadgeBackgroundColor({ color: '#ff726b' })
: browser.browserAction.setBadgeBackgroundColor({ color: '#ffb900' }); : browserAPI.badge.setBadgeBackgroundColor({ color: '#ffb900' });
if (request.tabId >= 0) { if (request.tabId >= 0) {
setDomainsCount( setDomainsCount(
@ -46,14 +47,14 @@ export default class Memory extends SaferEmitter {
constructor() { constructor() {
super(); super();
browser.webRequest.onBeforeRequest.addListener( browserAPI.webRequest.onBeforeRequest.addListener(
async (request) => { async (request) => {
new ExtendedRequest(request); new ExtendedRequest(request);
}, },
{ urls: ['<all_urls>'] }, { urls: ['<all_urls>'] },
['requestBody'] ['requestBody']
); );
browser.webRequest.onBeforeSendHeaders.addListener( browserAPI.webRequest.onBeforeSendHeaders.addListener(
async (request) => { async (request) => {
const extendedRequest = ExtendedRequest.by_id[request.requestId].addHeaders( const extendedRequest = ExtendedRequest.by_id[request.requestId].addHeaders(
request.requestHeaders || [] request.requestHeaders || []
@ -76,9 +77,9 @@ export default class Memory extends SaferEmitter {
async removeCookiesFor(origin: string, shorthost?: string): Promise<void> { async removeCookiesFor(origin: string, shorthost?: string): Promise<void> {
if (shorthost) { if (shorthost) {
const cookies = await browser.cookies.getAll({ domain: shorthost }); const cookies = await browserAPI.cookies.getAll({ domain: shorthost });
for (const cookie of cookies) { for (const cookie of cookies) {
await browser.cookies.remove({ await browserAPI.cookies.remove({
name: cookie.name, name: cookie.name,
url: `https://${cookie.domain}`, url: `https://${cookie.domain}`,
}); });
@ -106,5 +107,9 @@ export function init() {
} }
export function getMemory(): Memory { export function getMemory(): Memory {
return (browser.extension.getBackgroundPage().window as any).memory as Memory; const backgroundPage = browserAPI.extension.getBackgroundPage();
} if (!backgroundPage) {
throw new Error('Background page not available');
}
return (backgroundPage.window as any).memory as Memory;
}

View File

@ -1,11 +1,12 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import React from 'react'; import React from 'react';
import { DataLocation, Sources } from './stolen-data-entry'; import { DataLocation, Sources } from './stolen-data-entry';
import browserAPI from './lib/browser-api';
export type Unpromisify<T> = T extends Promise<infer R> ? R : T; export type Unpromisify<T> = T extends Promise<infer R> ? R : T;
export type Unarray<T> = T extends Array<infer R> ? R : T; export type Unarray<T> = T extends Array<infer R> ? R : T;
export type Tab = Unarray<Unpromisify<ReturnType<typeof browser.tabs.query>>>; export type Tab = Unarray<Unpromisify<ReturnType<typeof browserAPI.tabs.query>>>;
export type Request = { export type Request = {
cookieStoreId?: string; cookieStoreId?: string;
documentUrl?: string; // RL of the document in which the resource will be loaded. For example, if the web page at "https://example.com" contains an image or an iframe, then the documentUrl for the image or iframe will be "https://example.com". For a top-level document, documentUrl is undefined. documentUrl?: string; // RL of the document in which the resource will be loaded. For example, if the web page at "https://example.com" contains an image or an iframe, then the documentUrl for the image or iframe will be "https://example.com". For a top-level document, documentUrl is undefined.
@ -89,7 +90,7 @@ export function parseCookie(cookie: string): Record<string, string> {
} }
export async function getTabByID(id: number) { export async function getTabByID(id: number) {
const tabs = await browser.tabs.query({ currentWindow: true }); const tabs = await browserAPI.tabs.query({ currentWindow: true });
return tabs.find((tab) => tab.id == id); return tabs.find((tab) => tab.id == id);
} }
@ -317,4 +318,4 @@ export function downloadText(filename: string, text: string) {
element.click(); element.click();
document.body.removeChild(element); document.body.removeChild(element);
} }