Skip to content

Commit

Permalink
Merge pull request #13 from wsg-ariadne/v0.1.1
Browse files Browse the repository at this point in the history
v0.1.1
  • Loading branch information
jareddantis committed May 12, 2023
2 parents 2b07ffc + 1129aa7 commit d540dc0
Show file tree
Hide file tree
Showing 29 changed files with 868 additions and 264 deletions.
13 changes: 13 additions & 0 deletions background/api/classifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiPost } from "./request";

export const classifyText = (text, callback) => {
ApiPost('/classify/text', { text }, callback, () => {
console.error('api/calliope: Could not classify cookie banner text');
});
};

export const classifyImage = (imageData, callback) => {
ApiPost('/classify/image', { image_data: imageData }, callback, () => {
console.error('api/janus: Could not classify cookie banner screenshot');
});
};
35 changes: 35 additions & 0 deletions background/api/request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const baseUrl = import.meta.env.VITE_API_URL + '/api/v1';

export const ApiGet = (endpoint, callback, errorCallback) => {
if (typeof callback !== 'function') {
console.error('api/request(ApiGet): Expected function callback, got ' + typeof callback);
}

fetch(baseUrl + endpoint, {
method: 'GET'
}).then((response) => response.json())
.then((data) => callback(data))
.catch((error) => {
console.error('api/request(ApiGet) Error fetching ' + endpoint + ':', error);
if (typeof errorCallback === 'function') errorCallback(error);
});
}

export const ApiPost = (endpoint, body, callback, errorCallback) => {
if (typeof callback !== 'function') {
console.error('api/request(ApiPost): Expected function callback, got ' + typeof callback);
}

fetch(baseUrl + endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}).then((response) => response.json())
.then((data) => callback(data))
.catch((error) => {
console.error('api/request(ApiPost) Error fetching ' + endpoint + ':', error);
if (typeof errorCallback === 'function') errorCallback(error);
});
}
16 changes: 16 additions & 0 deletions background/api/stats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApiPost } from './request';
import { updateBadgeText } from '../browser/badge';
import { setTransaction } from '../browser/storage';

export const getStats = (tabUrl, successCallback, errorCallback) => {
ApiPost('/reports/by-url', {'page_url': tabUrl}, (data) => {
console.log('api/stats: Got stats for URL ' + tabUrl + ':', data);
setTransaction('stats', {
url: tabUrl,
stats: data
}).then(() => {
updateBadgeText(data);
if (successCallback !== undefined) successCallback(data);
}).catch((e) => errorCallback(e))
}, errorCallback);
}
183 changes: 9 additions & 174 deletions background/background.js
Original file line number Diff line number Diff line change
@@ -1,175 +1,10 @@
import * as browser from 'webextension-polyfill';

class AriadneBackground {
constructor() {
this._tabStates = [];
this._reportStats = {};
this._API_URL = import.meta.env.VITE_API_URL + '/api/v1';

// Alias if browser.action is defined, i.e. in Manifest V3
this.BrowserAction = browser.browserAction;
if (browser.action !== undefined) {
this.BrowserAction = browser.action;
}
}

addListeners() {
// Update badge on tab change
browser.tabs.onActivated.addListener((activeInfo) => {
// Get URL of active tab
browser.tabs.get(activeInfo.tabId)
.then((tab) => {
console.log('[bg] Tab changed to', tab.url);
this.toggleBadge(this._tabStates[activeInfo.tabId]);
this.updateBadgeText(this._reportStats[tab.url]);
this.getStats(tab.url);
});
});

// Tab URL change listener
browser.tabs.onUpdated.addListener((tabId, changeInfo, _) => {
if (changeInfo.url) {
// Request fresh stats
console.log('[bg] Tab ' + tabId + ' URL changed to', changeInfo.url);
this.getStats(changeInfo.url);
}
});

// Message listeners
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log("[bg] Received message with action", request.action);
if (request.action === "updateBadge") {
// Listen to updateBadge requests
this.toggleBadge(request.args.enabled);

// Update the tab state
this._tabStates[sender.tab.id] = request.args.enabled;
} else if (request.action === "detection") {
// Listen to detection requests from content scripts
const cookieBannerText = request.args.body;
console.log('[bg] Detection request received from tab', sender.tab.id, 'with body:', cookieBannerText);

// POST to API
fetch(this._API_URL + '/classify/text', {
method: 'POST',
body: JSON.stringify({
text: cookieBannerText
}),
headers: {
'Content-Type': 'application/json'
}
}).then((response) => response.json())
.then((data) => {
console.log('[bg] Detection result from API:', data);
sendResponse(data);
});
} else if (request.action === "visualDetection") {
// Listen to visual detection requests from content scripts
const imageData = request.args.screenshot;
console.log('[bg] Visual detection request received from tab', sender.tab.id);

// POST to API
fetch(this._API_URL + '/classify/image', {
method: 'POST',
body: JSON.stringify({
image_data: imageData
}),
headers: {
'Content-Type': 'application/json'
}
}).then((response) => response.json())
.then((data) => {
console.log('[bg] Detection result from API:', data);
sendResponse(data);
});
} else if (request.action === "requestStats") {
console.log("[bg] Received stats request from popup", request, sender);

// If we have cached stats, send them before requesting new ones
const tabUrl = request.args.url;
let deferSending = false;
if (this._reportStats[tabUrl]) {
console.log("[bg] Sending cached stats to tab", tabUrl, this._reportStats[tabUrl]);
sendResponse(this._reportStats[tabUrl]);
deferSending = true;
}

this.getStats(tabUrl, (stats) => {
if (!deferSending) {
console.log('[bg] Sending stats to tab', tabUrl, this._reportStats[tabUrl])
sendResponse(stats);
} else {
console.log('[bg] Revalidated cache for tab', tabUrl, this._reportStats[tabUrl])
}
}, (error) => {
sendResponse({
success: false,
error
});
});
}

return true;
});
}

toggleBadge(state) {
if (state) {
this.BrowserAction.setBadgeBackgroundColor({
color: "#B677FA",
});
} else {
this.BrowserAction.setBadgeBackgroundColor({
color: "#2A272A",
});
}
}

updateBadgeText(stats) {
console.log('[bg] Updating badge text with stats:', stats);
if (stats !== undefined && stats.hasOwnProperty("success") &&
stats.hasOwnProperty("specific_reports") && stats["success"]) {
const count = stats.specific_reports.count;
console.log('[bg] Badge count:', count)
if (count > 0) {
this.BrowserAction.setBadgeText({
text: count.toString(),
});
return;
}
}
this.BrowserAction.setBadgeText({
text: "0",
});
}

getStats(tabUrl, successCallback, errorCallback) {
fetch(this._API_URL + '/reports/by-url', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'page_url': tabUrl
})
})
.then((response) => response.json())
.then((data) => {
console.log('[bg] Report stats from API:', data);
this._reportStats[tabUrl] = data;

// Update badge text
this.updateBadgeText(data);

if (successCallback !== undefined) successCallback(data);
})
.catch((error) => {
console.error('[bg] Error fetching report stats:', error);
if (errorCallback !== undefined) errorCallback(error);
}
);
}
}

const ariadneBackground = new AriadneBackground();
ariadneBackground.addListeners();
import {
messageListener,
tabChangeListener,
tabUrlChangeListener
} from './listeners';

browser.runtime.onMessage.addListener(messageListener);
browser.tabs.onActivated.addListener(tabChangeListener);
browser.tabs.onUpdated.addListener(tabUrlChangeListener);
33 changes: 33 additions & 0 deletions background/browser/badge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getAction } from './getAction';

const action = getAction();

export const toggleBadge = (state) => {
if (state) {
action.setBadgeBackgroundColor({
color: "#B677FA",
});
} else {
action.setBadgeBackgroundColor({
color: "#2A272A",
});
}
}

export const updateBadgeText = (stats) => {
console.log('browser/badge: Updating badge text with stats:', stats);
if (stats !== undefined && stats.hasOwnProperty("success") &&
stats.hasOwnProperty("specific_reports") && stats["success"]) {
const count = stats.specific_reports.count;
console.log('browser/badge: Badge count:', count)
if (count > 0) {
action.setBadgeText({
text: count.toString(),
});
return;
}
}
action.setBadgeText({
text: "0",
});
}
8 changes: 8 additions & 0 deletions background/browser/getAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as browser from 'webextension-polyfill';

export const getAction = () => {
if (chrome.runtime.getManifest().manifest_version === 3) {
return browser.action;
}
return browser.browserAction;
}
80 changes: 80 additions & 0 deletions background/browser/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const openDatabase = () => new Promise((res, rej) => {
const request = indexedDB.open('ariadne', 1);
request.onerror = (event) => {
console.error(`browser/storage: Error ${event.target.errorCode}`)
rej();
};

// Declare schema
request.onupgradeneeded = (event) => {
const db = event.target.result;

// Create storage for stats per URL
const urlStatsStore = db.createObjectStore('stats', { keyPath: 'url' })
urlStatsStore.createIndex('url', 'url', { unique: true })

// Create storage for badge states per tab
const badgeStateStore = db.createObjectStore('badgeStates', { keyPath: 'tabId' })
badgeStateStore.createIndex('tabId', 'tabId', { unique: true })

// Create storage for Calliope results per URL
const calliopeStore = db.createObjectStore('calliope', { keyPath: 'url' })
calliopeStore.createIndex('url', 'url', { unique: true })

// Create storage for Janus results per URL
const janusStore = db.createObjectStore('janus', { keyPath: 'url' })
janusStore.createIndex('url', 'url', { unique: true })
}

request.onsuccess = (event) => {
console.log('browser/storage: Database ready');
res(event.target.result);
}
});


export const setTransaction = async (store, data) => {
console.log('browser/storage(set): Opening DB');

const db = await openDatabase();
const t = db.transaction(store, 'readwrite');
const s = t.objectStore(store);
return await new Promise((res, rej) => {
t.oncomplete = () => console.log('browser/storage(set): Complete');
t.onerror = (e) => rej(e);

const r = s.put(data);
r.onsuccess = () => res();
});
}

export const getTransaction = async (store, key) => {
console.log('browser/storage(get): Opening DB');

const db = await openDatabase();
const t = db.transaction(store, 'readonly');
const s = t.objectStore(store);
return await new Promise((res, rej) => {
t.oncomplete = () => console.log('browser/storage(get): Complete');
t.onerror = (ev) => rej(ev);

const r = s.get(key);
r.onsuccess = (e) => res(e.target.result);
});
}

export const deleteTransaction = async (store, key) => {
console.log('browser/storage(delete): Opening DB');

const db = await openDatabase();
const t = db.transaction(store, 'readwrite');
const s = t.objectStore(store);
return await new Promise((res, rej) => {
t.oncomplete = () => {
console.log('browser/storage(delete): Complete');
res();
};
t.onerror = (e) => rej(e);
s.delete(key);
});
}
9 changes: 9 additions & 0 deletions background/listeners/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import messageListener from './message';
import tabChangeListener from './tabChange';
import tabUrlChangeListener from './tabUrlChange';

export {
messageListener,
tabChangeListener,
tabUrlChangeListener
}
Loading

0 comments on commit d540dc0

Please sign in to comment.