Skip to content

Commit

Permalink
feat: more work on Manifest V3 support
Browse files Browse the repository at this point in the history
  • Loading branch information
m4tx committed Oct 30, 2024
1 parent 7e02918 commit e2c6f5d
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 92 deletions.
6 changes: 6 additions & 0 deletions ext/html/offscreen.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!doctype html>
<script src="/vendor/browser-polyfill.js"></script>
<script src="/js/config.js"></script>
<script src="/vendor/bower/jquery.min.js"></script>
<script src="/js/common.js"></script>
<script src="/js/offscreen.js"></script>
4 changes: 3 additions & 1 deletion ext/js/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ if (typeof module !== 'undefined') {
};
}

function parseProblemList(jqueryHandles) {
function parseProblemList(htmlText) {
const jqueryHandles = $.parseHTML(htmlText);

return Object.fromEntries(
jqueryHandles.flatMap((el) =>
[
Expand Down
2 changes: 0 additions & 2 deletions ext/js/offscreen.html

This file was deleted.

50 changes: 7 additions & 43 deletions ext/js/offscreen.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,12 @@
importScripts(
"../vendor/browser-polyfill.js",
"config.js",
"../vendor/bower/jquery.min.js"
);

browser.runtime.onMessage.addListener(handleMessages);

async function handleMessages(message) {
if (message.target !== 'offscreen') {
browser.runtime.onMessage.addListener(async (request) => {
if (request.target !== 'offscreen') {
return false;
}

if (message.type === 'parseProblemList') {
parseProblemList(message.data);
} else {
console.warn(`Unexpected message type received: '${message.type}'.`);
return false;
if (request.type === 'parseProblemList') {
return parseProblemList(request.data);
}
}

function parseProblemList(htmlText) {
const jqueryHandles = $.parseHTML(htmlText);

return Object.fromEntries(
jqueryHandles.flatMap((el) =>
[
...$(el).find('#content table.results tr:not(:first-of-type)'),
].map((tr) => [
$(tr).find('td:nth-child(1)').text(),
{
title: $(tr).find('td:nth-child(2)').text(),
href: $(tr).find('td:nth-child(2) a').attr('href'),
pdfHref: $(tr).find('td:nth-child(3) a').attr('href'),
submitHref: $(tr).find('td:nth-child(5) a').attr('href'),
},
]),
),
);
}

function sendToBackground(type, data) {
browser.runtime.sendMessage({
type,
target: 'background',
data
});
}
console.warn(`Unexpected message type received: '${request.type}'.`);
return false;
});
2 changes: 0 additions & 2 deletions ext/js/results.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
let problemStatus = tr.find('td:last').text();

let url = document.location.href;
console.log("XD");
const contestID = getContestID(url);
console.log("XD2");


/**
Expand Down
142 changes: 98 additions & 44 deletions ext/js/service-worker.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
importScripts(
"../vendor/browser-polyfill.js",
"config.js",
"common.js",
);

(function () {
Expand All @@ -21,6 +22,11 @@ importScripts(

const contestProblemList = {};

/**
* The offscreen document being currently created, if any.
*/
let offscreenBeingCreated;

function displayStatusNotification(submitID, problemCode, status) {
browser.notifications.create({
type: 'basic',
Expand All @@ -35,20 +41,21 @@ importScripts(
* variable.
* @see setUpLastContestRedirect
*/
function retrieveLastContestID() {
storage.get('lastContestID').then((response) => {
lastContestID = response.lastContestID;
});
async function retrieveLastContestID() {
const response = await storage.get('lastContestID');
lastContestID = response.lastContestID;
}

/**
* Save last contest ID both in Storage and our local variable.
* Save last contest ID both in Storage and our local variable
* and update redirect rules.
* @param {string} contestID last contest ID
* @see setUpLastContestRedirect
*/
function saveLastContestID(contestID) {
storage.set({ lastContestID: contestID });
lastContestID = contestID;
updateLastContestRedirectRules();
}

/**
Expand Down Expand Up @@ -125,13 +132,48 @@ importScripts(
}

/**
* Add an onBeforeRequest listener that redirects to the last contest
* Sets up a declarative net request rules to redirect to the last contest
* if the user just entered Satori webpage.
*/
function updateLastContestRedirectRules() {
const addRules = [];
if (typeof lastContestID !== 'undefined' && lastContestID !== null) {
addRules.push(
...[
{ id: 1, urlFilter: '||satori.tcs.uj.edu.pl/|' },
{ id: 2, urlFilter: '||satori.tcs.uj.edu.pl/news' },
].map(({ id, urlFilter }) => ({
id,
condition: {
urlFilter,
// Redirect only if the user just opened Satori on a given tab
excludedTabIds: [...satoriTabs.values()],
resourceTypes: ['main_frame'],
},
action: {
type: 'redirect',
redirect: {
url: SATORI_URL_CONTEST + lastContestID + '/',
},
},
}))
);
}
browser.declarativeNetRequest.updateSessionRules({
removeRuleIds: [1, 2],
addRules,
});
}

/**
* Enables the last contest redirect.
*
* Also, the function adds webNavigation.onCommitted and tabs.onRemoved
* listeners to keep the list of Satori tabs.
*/
function setUpLastContestRedirect() {
updateLastContestRedirectRules();

// Store which tabs have Satori open
browser.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (
Expand All @@ -143,36 +185,13 @@ importScripts(
} else {
satoriTabs.delete(tabId);
}
updateLastContestRedirectRules();
});

browser.tabs.onRemoved.addListener((tabId) => satoriTabs.delete(tabId));

// browser.webRequest.onBeforeRequest.addListener(
// function (details) {
// if (
// typeof lastContestID === 'undefined' ||
// lastContestID === null ||
// satoriTabs.has(details.tabId)
// ) {
// // If we haven't saved any contest yet, then do nothing
// // Also, don't redirect if the user is already browsing
// // Satori
// return;
// }
//
// return {
// redirectUrl: SATORI_URL_CONTEST + lastContestID + '/',
// };
// },
// {
// urls: [
// '*://satori.tcs.uj.edu.pl/',
// '*://satori.tcs.uj.edu.pl/news',
// ],
// types: ['main_frame'],
// },
// ['blocking'],
// );
browser.tabs.onRemoved.addListener((tabId) => {
satoriTabs.delete(tabId);
updateLastContestRedirectRules();
});
}

/**
Expand Down Expand Up @@ -284,19 +303,15 @@ importScripts(
if (!response.ok) {
throw new Error(`HTTP Status ${response.status}`);
}
const responseText = await response.text();

await chrome.offscreen.createDocument({
url: "offscreen.html",
reasons: [chrome.offscreen.Reason.DOM_PARSER],
justification: 'Parse DOM'
});
await setupOffscreenDocument("/html/offscreen.html");
const parseResponse = await chrome.runtime.sendMessage({
type: 'parseProblemList',
target: 'offscreen',
data: await response.text()
data: responseText
});
await chrome.offscreen.closeDocument();
console.log(parseResponse);

contestProblemList[contestID] = parseResponse;
} catch (error) {
Expand All @@ -306,6 +321,35 @@ importScripts(
return contestProblemList[contestID] ?? {};
}

/**
* Setup offscreen document for parsing HTML
* @param path path to the offscreen document
* @returns {Promise<void>} promise that resolves when the document is ready
*/
async function setupOffscreenDocument(path) {
const offscreenUrl = chrome.runtime.getURL(path);
const existingContexts = await chrome.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
documentUrls: [offscreenUrl]
});

if (existingContexts.length > 0) {
return;
}

if (offscreenBeingCreated) {
await offscreenBeingCreated;
} else {
offscreenBeingCreated = chrome.offscreen.createDocument({
url: path,
reasons: [chrome.offscreen.Reason.DOM_PARSER],
justification: 'Parse DOM',
});
await offscreenBeingCreated;
offscreenBeingCreated = null;
}
}

/**
* Save joined contest list in Storage
* @param {array} contestList contest list
Expand All @@ -322,28 +366,33 @@ importScripts(
return (await storage.get('contestList')).contestList ?? [];
}

retrieveLastContestID();
setUpLastContestRedirect();
retrieveLastContestID().then(() => {
setUpLastContestRedirect();
});
setUpSessionCookies();

browser.runtime.onMessage.addListener(async (request, sender) => {
if (request.action === 'enablePageAction') {
enablePageAction(sender.tab);
return new Promise((resolve) => resolve(null));
} else if (request.action === 'saveLastContestID') {
saveLastContestID(getContestID(sender.url));
return new Promise((resolve) => resolve(null));
} else if (request.action === 'displayStatusNotification') {
displayStatusNotification(
request.submitID,
request.problemCode,
request.problemStatus,
);
return new Promise((resolve) => resolve(null));
} else if (request.action === 'modifyContestItemList') {
modifyContestItemList(
request.listName,
getContestID(sender.url),
request.value,
request.add,
);
return new Promise((resolve) => resolve(null));
} else if (request.action === 'getContestItemList') {
return new Promise((resolve) => {
getContestItemList(
Expand All @@ -354,15 +403,20 @@ importScripts(
});
} else if (request.action === 'injectHighlightJsCss') {
await injectHighlightJsCss(sender.tab);
return new Promise((resolve) => resolve(null));
} else if (request.action === 'saveContestProblemList') {
saveContestProblemList(request.contestID, request.problems);
return new Promise((resolve) => resolve(null));
} else if (request.action === 'getContestProblemList') {
return getProblemList(request.contestID);
} else if (request.action === 'setJoinedContestList') {
saveJoinedContestList(request.contestList);
return new Promise((resolve) => resolve(null));
} else if (request.action === 'getJoinedContestList') {
return getJoinedContestList();
}
return new Promise((resolve) => resolve(null));

console.warn(`Unexpected message type received: '${request.action}'.`);
return false;
});
})();
1 change: 1 addition & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ let distFiles = [
'icon48.png',
'icon128.png',
'options.html',
'html/**/*',
'css/**/*',
'js/**/*',
'images/**/*',
Expand Down

0 comments on commit e2c6f5d

Please sign in to comment.