Improve performance by limiting renders

This commit is contained in:
Kuba Orlik 2022-02-07 15:28:01 +01:00
parent cf47b79052
commit 7330ebf9f2
6 changed files with 79 additions and 109 deletions

View File

@ -5,7 +5,7 @@ import { RequestCluster } from './request-cluster';
export default class Memory extends EventEmitter {
origin_to_history = {} as Record<string, Record<string, RequestCluster>>;
private throttle = makeThrottle(200);
private throttle = makeThrottle(100);
async register(request: ExtendedRequest) {
await request.init();
if (!request.isThirdParty()) {
@ -20,7 +20,7 @@ export default class Memory extends EventEmitter {
this.origin_to_history[request.origin][shorthost] = cluster;
}
this.origin_to_history[request.origin][shorthost].add(request);
this.emit('change');
this.emit('change', false, shorthost, 'registered request(shorthost emit)');
}
constructor() {
@ -35,9 +35,9 @@ export default class Memory extends EventEmitter {
);
browser.webRequest.onBeforeSendHeaders.addListener(
async (request) => {
const extendedRequest = ExtendedRequest.by_id[
request.requestId
].addHeaders(request.requestHeaders || []);
const extendedRequest = ExtendedRequest.by_id[request.requestId].addHeaders(
request.requestHeaders || []
);
this.register(extendedRequest);
},
{ urls: ['<all_urls>'] },
@ -45,13 +45,16 @@ export default class Memory extends EventEmitter {
);
}
emit(eventName: string, immediate = false) {
emit(eventName: string, immediate = false, data = 'any', reason: string) {
console.log('emitting!', eventName, data, reason);
setTimeout(() => super.emit(eventName, data), 0);
return;
try {
if (immediate) {
super.emit(eventName);
super.emit(eventName, data);
return;
} else {
this.throttle(() => super.emit(eventName));
this.throttle(() => super.emit(eventName, data));
}
return true;
} catch (e) {
@ -68,12 +71,7 @@ export default class Memory extends EventEmitter {
if (shorthost) {
const cookies = await browser.cookies.getAll({ domain: shorthost });
for (const cookie of cookies) {
console.log(
'removing cookie',
cookie.name,
'from',
cookie.domain
);
console.log('removing cookie', cookie.name, 'from', cookie.domain);
await browser.cookies.remove({
name: cookie.name,
url: `https://${cookie.domain}`,
@ -102,6 +100,5 @@ export function init() {
}
export function getMemory(): Memory {
return (browser.extension.getBackgroundPage().window as any)
.memory as Memory;
return (browser.extension.getBackgroundPage().window as any).memory as Memory;
}

View File

@ -2,7 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Options from '../options';
import { StolenData } from './stolen-data';
import { useEmitter } from '../util';
import { getshorthost, useEmitter } from '../util';
import { getMemory } from '../memory';
async function getCurrentTab() {
@ -26,7 +26,7 @@ const Sidebar = () => {
const [cookiesOnly, setCookiesOnly] = React.useState<boolean>(false);
const [stolenDataView, setStolenDataView] = React.useState<boolean>(true);
const [cookiesOrOriginOnly, setCookiesOrOriginOnly] = React.useState<boolean>(false);
const [counter, setCounter] = useEmitter(getMemory());
const [eventCounts, setEventCounts] = useEmitter(getMemory());
const [marksOccurrence, setMarksOccurrence] = React.useState<boolean>(false);
const [warningDataDialogAck, setWarningDataDialogAck] = React.useState<boolean>(
localStorage.getItem('warningDataDialogAck') === null
@ -44,7 +44,7 @@ const Sidebar = () => {
);
React.useEffect(() => {
const listener = async (data: any) => {
const listener = async () => {
console.log('tab change!');
const tab = await getCurrentTab();
const url = new URL(tab.url);
@ -66,7 +66,7 @@ const Sidebar = () => {
}
}
return setMarksOccurrence(false);
}, [counter, origin]);
}, [eventCounts['*'], origin]);
return (
<div className="sidebar">
@ -112,7 +112,7 @@ const Sidebar = () => {
<button
onClick={() => {
getMemory().removeRequestsFor(origin);
setCounter((c) => c + 1);
getMemory().emit('change', false, origin, 'clicked trash');
setMarksOccurrence(false);
}}
>
@ -122,7 +122,7 @@ const Sidebar = () => {
<button
onClick={() => {
getMemory().removeCookiesFor(origin);
setCounter((c) => c + 1);
getMemory().emit('change', false, origin, 'clicked clear cookies');
setMarksOccurrence(false);
}}
>
@ -193,8 +193,7 @@ const Sidebar = () => {
) : null}
<StolenData
origin={origin}
refreshToken={counter}
refresh={() => setCounter((c) => c + 1)}
eventCounts={eventCounts}
minValueLength={minValueLength}
cookiesOnly={cookiesOnly}
cookiesOrOriginOnly={cookiesOrOriginOnly}

View File

@ -8,23 +8,14 @@ import './stolen-data-cluster.scss';
const MAX_STRING_VALUE_LENGTH = 100;
function StolenDataValue({
entry,
refresh,
}: {
entry: StolenDataEntry;
refresh: Function;
prefixKey?: string;
}) {
function StolenDataValue({ entry }: { entry: StolenDataEntry; prefixKey?: string }) {
const [version] = useEmitter(entry);
let body = null;
if (!entry.value) {
body = <></>;
} else {
body = (
<div data-version={version}>
{maskString(entry.value, 1, MAX_STRING_VALUE_LENGTH)}
</div>
<div data-version={version}>{maskString(entry.value, 1, MAX_STRING_VALUE_LENGTH)}</div>
);
}
return (
@ -32,7 +23,7 @@ function StolenDataValue({
className="value"
onClick={(e) => {
entry.toggleMark();
refresh();
getMemory().emit('change', false, entry.request.shorthost, 'clicked value');
e.stopPropagation();
}}
title={maskString(entry.value, 1, MAX_STRING_VALUE_LENGTH)}
@ -42,13 +33,7 @@ function StolenDataValue({
);
}
function StolenDataRow({
entry,
refresh,
}: {
entry: StolenDataEntry;
refresh: Function;
}) {
function StolenDataRow({ entry }: { entry: StolenDataEntry }) {
const [version] = useEmitter(entry);
return (
<tr
@ -62,7 +47,12 @@ function StolenDataRow({
checked={entry.isMarked}
onChange={() => {
entry.toggleMark();
refresh();
getMemory().emit(
'change',
false,
entry.request.shorthost,
'clicked checkbox'
);
}}
/>
</td>
@ -70,7 +60,12 @@ function StolenDataRow({
title={`Nazwa: ${entry.name}\nŹródło: ${entry.source}`}
onClick={() => {
entry.toggleMark();
refresh();
getMemory().emit(
'change',
false,
entry.request.shorthost,
'Clicked entry name'
);
}}
>
{entry.name}
@ -86,10 +81,7 @@ function StolenDataRow({
/>
</span>
) : entry.request.hasCookie() ? (
<span
title="Wysłane w zapytaniu opatrzonym Cookies"
style={{ opacity: 0.25 }}
>
<span title="Wysłane w zapytaniu opatrzonym Cookies" style={{ opacity: 0.25 }}>
<img
src="/assets/icons/cookie.svg"
height={16}
@ -121,7 +113,7 @@ function StolenDataRow({
</span>
) : null}
</td>
<StolenDataValue refresh={refresh} entry={entry} />
<StolenDataValue entry={entry} />
</tr>
);
}
@ -130,21 +122,22 @@ export default function StolenDataCluster({
origin,
shorthost,
minValueLength,
refresh,
cookiesOnly,
refreshToken,
cookiesOrOriginOnly,
}: {
origin: string;
shorthost: string;
refreshToken: number;
minValueLength: number;
refresh: Function;
cookiesOnly: boolean;
cookiesOrOriginOnly: boolean;
}) {
const cluster = getMemory().getClustersForOrigin(origin)[shorthost];
const fullHosts = cluster.getFullHosts();
/* console.log('Stolen data cluster!', shorthost, refreshToken); */
return (
<div className="stolen-data-cluster-container">
<header className="domains-container">
@ -153,13 +146,8 @@ export default function StolenDataCluster({
</a>
<div className="subdomains-container">
{fullHosts.map((host, index) => (
<a
className="subdomain"
key={host}
href={`https://${host}`}
>
{host}{' '}
{`${fullHosts.length - 1 !== index ? '· ' : ''}`}
<a className="subdomain" key={host} href={`https://${host}`}>
{host} {`${fullHosts.length - 1 !== index ? '· ' : ''}`}
</a>
))}
</div>
@ -182,7 +170,6 @@ export default function StolenDataCluster({
})
.map((entry) => (
<StolenDataRow
refresh={refresh}
{...{
entry,
key: entry.id,

View File

@ -1,23 +1,19 @@
import React from 'react';
import { getMemory } from '../memory';
import { RequestCluster } from '../request-cluster';
import StolenDataCluster from './stolen-data-cluster';
import { getshorthost } from '../util';
import { getMemory } from '../memory';
import './stolen-data.scss';
export function StolenData({
origin,
minValueLength,
refreshToken,
refresh,
eventCounts,
cookiesOnly,
cookiesOrOriginOnly,
}: {
origin: string;
refreshToken: number;
refresh: () => void;
eventCounts: Record<string, number>;
minValueLength: number;
cookiesOnly: boolean;
cookiesOrOriginOnly: boolean;
@ -33,10 +29,7 @@ export function StolenData({
.sort(RequestCluster.sortCompare)
.filter((cluster) => !cookiesOnly || cluster.hasCookies())
.filter(
(cluster) =>
!cookiesOrOriginOnly ||
cluster.hasCookies() ||
cluster.exposesOrigin()
(cluster) => !cookiesOrOriginOnly || cluster.hasCookies() || cluster.exposesOrigin()
);
return (
<div className="stolen-data-container">
@ -48,8 +41,7 @@ export function StolenData({
origin={origin}
shorthost={cluster.id}
key={cluster.id + origin}
refresh={refresh}
refreshToken={refreshToken}
refreshToken={eventCounts[cluster.id]}
minValueLength={minValueLength}
cookiesOnly={cookiesOnly}
cookiesOrOriginOnly={cookiesOrOriginOnly}

View File

@ -12,12 +12,7 @@ import {
safeDecodeURIComponent,
} from './util';
export type Sources =
| 'cookie'
| 'pathname'
| 'queryparams'
| 'header'
| 'request_body';
export type Sources = 'cookie' | 'pathname' | 'queryparams' | 'header' | 'request_body';
export const Classifications = <const>{
id: 'Identyfikator internetowy',
@ -123,10 +118,7 @@ export class StolenDataEntry extends EventEmitter {
}
).entries()
);
if (
typeof hash !== 'object' &&
Object.keys(searchParams).length === 0
) {
if (typeof hash !== 'object' && Object.keys(searchParams).length === 0) {
return value; // just a string;
}
const object = {
@ -134,11 +126,7 @@ export class StolenDataEntry extends EventEmitter {
host: url.host,
path: url.pathname,
searchParams,
...(hash === ''
? {}
: typeof hash === 'string'
? { hash }
: hash),
...(hash === '' ? {} : typeof hash === 'string' ? { hash } : hash),
};
return object;
} else if (value === null) {
@ -148,16 +136,12 @@ export class StolenDataEntry extends EventEmitter {
}
}
getParsedValue(
key_path: string
): string | Record<string | symbol, unknown> {
getParsedValue(key_path: string): string | Record<string | symbol, unknown> {
let object = StolenDataEntry.parseValue(this.value);
for (const key of key_path.split('.')) {
if (key === '') continue;
if (typeof key === 'string') {
throw new Error(
'something went wrong when parsing ' + key_path
);
throw new Error('something went wrong when parsing ' + key_path);
}
object = StolenDataEntry.parseValue(object[key]);
}
@ -176,7 +160,7 @@ export class StolenDataEntry extends EventEmitter {
const had_been_marked_before = this.marked;
this.marked = false;
if (had_been_marked_before) {
this.emit('change');
this.emit('change', this.request.origin);
}
}
@ -199,9 +183,7 @@ export class StolenDataEntry extends EventEmitter {
}
isRelatedToID() {
return this.request.stolenData.some(
(entry) => entry.classification == 'id'
);
return this.request.stolenData.some((entry) => entry.classification == 'id');
}
matchesHAREntry(har: HAREntry): boolean {
@ -216,10 +198,7 @@ export class StolenDataEntry extends EventEmitter {
: value.toString();
if (typeof value !== 'object' && this.classification == 'id') {
return maskString(value, 1 / 3, ID_PREVIEW_MAX_LENGTH);
} else if (
typeof value === 'object' &&
value[Symbol.for('originalString')]
) {
} else if (typeof value === 'object' && value[Symbol.for('originalString')]) {
return value[Symbol.for('originalString')] as string;
} else {
return str;
@ -267,8 +246,8 @@ export class StolenDataEntry extends EventEmitter {
}
exposesHost() {
return [this.value, safeDecodeURIComponent(this.value)].some(
(haystack) => haystack.includes(getshorthost(this.request.origin))
return [this.value, safeDecodeURIComponent(this.value)].some((haystack) =>
haystack.includes(getshorthost(this.request.origin))
);
}
}

26
util.ts
View File

@ -47,18 +47,34 @@ export function getshorthost(host: string) {
export function useEmitter(
e: EventEmitter
): [number, React.Dispatch<React.SetStateAction<number>>] {
const [counter, setCounter] = React.useState<number>(0);
): [
Record<string, number | undefined>,
React.Dispatch<React.SetStateAction<Record<string, number | undefined>>>
] {
const [eventCounts, setEventCounts] = React.useState<Record<string, number | undefined>>({
'*': 0,
});
React.useEffect(() => {
const callback = () => {
setCounter((counter) => counter + 1);
const callback = (eventSubtype: string) => {
setEventCounts((eventCounts) => {
console.log({
...eventCounts,
...{ [eventSubtype]: (eventCounts[eventSubtype] || 0) + 1 },
...{ '*': (eventCounts['*'] === undefined ? 0 : eventCounts['*']) + 1 },
});
return {
...eventCounts,
...{ [eventSubtype]: (eventCounts[eventSubtype] || 0) + 1 },
...{ '*': (eventCounts['*'] === undefined ? 0 : eventCounts['*']) + 1 },
};
});
};
e.on('change', callback);
return () => {
e.removeListener('change', callback);
};
}, []);
return [counter, setCounter];
return [eventCounts, setEventCounts];
}
export function parseCookie(cookie: string): Record<string, string> {