Skip to content

Commit

Permalink
fix bug with old way to store rules (scheme = https), conflicted with…
Browse files Browse the repository at this point in the history
… new ones (scheme = *), with resolve conflict before add rule
  • Loading branch information
maximelebreton committed Oct 20, 2024
1 parent aa8b90a commit d548774
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 39 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "qjs",
"version": "2.0.0",
"version": "2.0.1",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
Expand Down
45 changes: 25 additions & 20 deletions src/entry/background/actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
getDomainSetting,
getJavascriptRuleSetting,
getSubdomainSetting,

clearJavascriptRule,
getTabSetting,
removeJavascriptRule,
setJavascriptRule,
Expand Down Expand Up @@ -353,27 +352,33 @@ export const handleAllowUrl = async (tab: chrome.tabs.Tab) => {

export const handleClearSubdomain = async (tab: chrome.tabs.Tab) => {
cl("CLEAR SUBDOMAIN", Log.ACTIONS);
const primaryPattern = getSubdomainPatternFromUrl(tab.url!);
if (primaryPattern) {
await removeJavascriptRule({
primaryPattern,
scope: getScopeSetting(tab.incognito),
});
await updateContextMenus(); //not needed because we update tab
reloadTab(tab);
if (tab.url) {

const primaryPattern = getSubdomainPatternFromUrl(tab.url);
if (primaryPattern) {
await clearJavascriptRule({
primaryPattern,
scope: getScopeSetting(tab.incognito),
tab
});
// await updateContextMenus(); //not needed because we update tab
reloadTab(tab);
}
}
};
export const handleClearDomain = async (tab: chrome.tabs.Tab) => {
cl("CLEAR DOMAIN", Log.ACTIONS);

const primaryPattern = getDomainPatternFromUrl(tab.url!);
if (primaryPattern) {
await removeJavascriptRule({
primaryPattern,
scope: getScopeSetting(tab.incognito),
});
await updateIcon(tab); //not needed because we update tab
reloadTab(tab);
if (tab.url) {
const primaryPattern = getDomainPatternFromUrl(tab.url);
if (primaryPattern) {
await clearJavascriptRule({
primaryPattern,
scope: getScopeSetting(tab.incognito),
tab
});
// await updateIcon(tab); //not needed because we update tab
reloadTab(tab);
}
}
};

Expand Down
63 changes: 60 additions & 3 deletions src/entry/background/contentsettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { clearJavascriptRules, handleClear, reloadTab } from "./actions";
import { cl, isAllowedPattern, Log } from "./utils";
import { updateIcon } from "./icon";
import {
getDomainStorageRulesFromUrl,
getStorageRules,
QJS,
setStorageRule,
Expand Down Expand Up @@ -34,13 +35,34 @@ export const getJavascriptRuleSetting = async ({
incognito,
},
(details) => {
cl(details, Log.RULES);
cl('Current content setting:' + JSON.stringify(details), Log.RULES, 'CONTENT SETTINGS RULE: '+primaryUrl);
resolve(details.setting);
}
);
});
};

export const removeConflictedRulesFromPattern = async (tabPattern: string) => {
const { scheme: tabScheme, subdomain: tabSubdomain, domain: tabDomain } = getUrlAsObject(tabPattern);

const existingDomainRules = await getDomainStorageRulesFromUrl(tabPattern)

Object.entries(existingDomainRules).forEach(async ([storagePattern, rule]) => {
const { scheme: storageScheme, subdomain: storageSubdomain, domain: storageDomain } = getUrlAsObject(storagePattern);
cl({urlScheme: tabScheme, patternScheme: storageScheme, urlSubdomain: tabSubdomain, patternSubdomain: storageSubdomain, urlDomain: tabDomain, patternDomain: storageDomain}, Log.RULES, "Potential conflicted rules with: "+tabPattern)
if (tabScheme !== storageScheme && tabSubdomain === storageSubdomain && tabDomain === storageDomain) {
await removeJavascriptRule(rule)
cl(`Conflicted rule removed: ${storagePattern} (conflict with url: ${tabPattern})`, Log.RULES)
}
if ((tabSubdomain === '*.' && storageSubdomain === '') && tabDomain === storageDomain) {
console.warn(`Potential conflicted rule: ${storagePattern} (conflict with url: ${tabPattern})`)
}
})

//subdomain: `${scheme}${schemeSuffix}${subdomain}${domain}/*`,
}


export const removePrimaryPatternFromRules = (primaryPattern: string) => {};

export const setJavascriptRule = ({
Expand Down Expand Up @@ -71,6 +93,8 @@ export const setJavascriptRule = ({
// } else {
// await addJavascriptRule(rule);
// }
await removeConflictedRulesFromPattern(rule.primaryPattern)

await addJavascriptRule(rule);
if (tab) {
await updateIcon(tab); //not needed because we update tab
Expand All @@ -80,10 +104,42 @@ export const setJavascriptRule = ({
});
};

export const clearJavascriptRule = ({
primaryPattern,
scope,
tab,
}: {
primaryPattern: QJS.ContentSettingRule["primaryPattern"];
scope: QJS.ContentSettingRule["scope"];
tab?: chrome.tabs.Tab;
}) => {

return new Promise<void>(async (resolve, reject) => {
const rule = {
primaryPattern,
scope,
};

if (!isAllowedPattern(primaryPattern)) {
return;
}

// await removeConflictedRulesFromPattern(rule.primaryPattern)

await removeJavascriptRule(rule);
if (tab) {
await updateIcon(tab); //not needed because we update tab
reloadTab(tab);
}
resolve();
});
}

export const addJavascriptRule = async (rule: QJS.ContentSettingRule) => {
cl(rule, Log.RULES);
return new Promise<void>((resolve, reject) => {
return new Promise<void>(async (resolve, reject) => {
console.log(chrome.contentSettings, "chrome.contentSettings");

chrome.contentSettings.javascript.set(rule, async () => {
console.info(
`${rule.setting} ${rule.primaryPattern} rule added to content settings`
Expand Down Expand Up @@ -114,9 +170,10 @@ export const rebaseJavascriptSettingsFromStorage = async () => {
};

export const removeJavascriptRule = async (
rule: Omit<QJS.ContentSettingRule, "setting">
rule: Pick<QJS.ContentSettingRule, "primaryPattern" | "scope">
) => {
return new Promise<void>(async (resolve, reject) => {

const storageRules = await getStorageRules();
if (
storageRules &&
Expand Down
2 changes: 1 addition & 1 deletion src/entry/background/contextmenus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ export const updateContextMenus = async () => {
export const getDefaultShortcut = async () => {
return new Promise((resolve, reject) => {
chrome.commands.getAll((commands) => {
console.log(commands, "commands");
//console.info(commands, "commands");
const shortcut =
commands.find(
// (command) => command.name === "_execute_bowser_action"
Expand Down
2 changes: 1 addition & 1 deletion src/entry/background/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const isPausedTab = async (tab: chrome.tabs.Tab) => {
(stateTab) => stateTab && stateTab.id === tab.id && stateTab.paused === true
);

cl(isPaused, undefined, "is paused?");
//cl(isPaused, undefined, "is paused?");
return isPaused;
};
export const isPausedTabs = async () => {
Expand Down
15 changes: 14 additions & 1 deletion src/entry/background/storage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { merge } from "lodash";
import { RuleSetting } from "./contentsettings";
import { getState, State } from "./state";
import { cl, Log } from "./utils";
import { cl, getDomainAndSubdomain, getUrlAsObject, Log } from "./utils";
import { Options } from "./_types";

// eslint-disable-next-line @typescript-eslint/no-namespace
Expand Down Expand Up @@ -98,6 +98,19 @@ export const getStorageRules = async () => {
});
};

export const getDomainStorageRulesFromUrl = async (url: string) => {
const rules = await getStorageRules()
return Object.entries(rules).reduce<QJS.ContentSettingRules>((acc, [pattern, setting]) => {
const { domain: urlDomain } = getUrlAsObject(url);
const { domain: patternDomain } = getUrlAsObject(pattern);

//const urlDomainAndSubdomain = getDomainAndSubdomain(url)
cl(pattern, Log.STORAGE, 'Options rules contains primary url domain')
//const isTrue = urlDomainAndSubdomain ? pattern.includes(urlDomainAndSubdomain) : false
return urlDomain === patternDomain ? {...acc, ...{[pattern]: setting}} : acc
}, {})
}

export const getStorageOptions = async () => {
return new Promise<Options>(async (resolve, reject) => {
const options = ((await getStorage("options")) as Options) || {};
Expand Down
102 changes: 93 additions & 9 deletions src/entry/background/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const cl = (message: any, type?: Log, name?: string) => {
[Log.ACTIONS]: true,
[Log.TABS]: false,
[Log.RULES]: true,
[Log.STORAGE]: false,
[Log.EVENTS]: false,
[Log.STORAGE]: true,
[Log.EVENTS]: true,
[Log.ICON]: false,
[Log.CONTEXT_MENUS]: false,
};
Expand Down Expand Up @@ -113,30 +113,74 @@ export const getScopeSetting = (incognito: chrome.tabs.Tab["incognito"]) => {
return incognito ? "incognito_session_only" : "regular";
};
export const getUrlAsObject = (url: string) => {
const { domain, subdomain } = parse(url);
const { pathname, protocol, hostname } = new URL(url);
// Utiliser une regex pour capturer les différentes parties de l'URL manuellement
const urlRegex = /^(.*?):\/\/([^\/]*)(\/?.*)$/;
const matches = url.match(urlRegex);

/** http://, https:// etc... */
const scheme = protocol.replace(/\:$/, "");
let scheme = "";
let hostname = "";
let pathname = "";

const schemeSuffix = scheme === "file" ? ":///" : "://";
if (matches) {
scheme = matches[1]; // Récupérer le scheme (ex: http, https, *)
hostname = matches[2]; // Récupérer le hostname (domaine + sous-domaine)
pathname = matches[3]; // Récupérer le chemin (path)
}

const schemeSuffix = scheme === "file" ? ":///" : "://";
const pathnameUntilLastSlash = pathname.substr(0, pathname.lastIndexOf("/"));

const fixedSubdomain = subdomain && subdomain.length ? `${subdomain}.` : "";
// Diviser le hostname en sous-domaine et domaine
const domainParts = hostname.split(".");
let domain = "";
let subdomain = "";

if (domainParts.length > 2) {
domain = domainParts.slice(-2).join(".");
subdomain = domainParts.slice(0, -2).join(".");
} else {
domain = hostname;
subdomain = "";
}

const fixedSubdomain = subdomain.length ? `${subdomain}.` : "";

return {
hostname,
scheme,
schemeSuffix,
protocol,
domain,
subdomain: fixedSubdomain,
pathname,
path: pathname,
pathnameUntilLastSlash,
};
};
// export const getUrlAsObject = (url: string) => {
// const { domain, subdomain } = parse(url);
// const { pathname, protocol, hostname } = new URL(url);

// /** http://, https:// etc... */
// const scheme = protocol.replace(/\:$/, "");

// const schemeSuffix = scheme === "file" ? ":///" : "://";

// const pathnameUntilLastSlash = pathname.substr(0, pathname.lastIndexOf("/"));

// const fixedSubdomain = subdomain && subdomain.length ? `${subdomain}.` : "";

// return {
// hostname,
// scheme,
// schemeSuffix,
// //protocol,
// domain,
// subdomain: fixedSubdomain,
// pathname,
// path: pathname,
// pathnameUntilLastSlash,
// };
// };

export const isValidScheme = (scheme: string) => {
return ["*", "http", "https", "file", "ftp", "urn"].includes(scheme)
Expand Down Expand Up @@ -201,3 +245,43 @@ export const retry = <T>(fn: () => Promise<T>, ms: number): Promise<T> =>
}, ms);
});
});

export function sortUrlsByPatternPrecedence(urls: string[]) {
// Fonction pour déterminer la priorité des motifs
function getPatternScore(url: string) {
let score = 0;

// Priorité par schéma (https > http)
if (url.startsWith("https://")) score += 3;
else if (url.startsWith("http://")) score += 2;

// Priorité par la spécificité du domaine
const domainParts = url.split("/")[2].split(".");
if (domainParts.length > 2) {
// Plus le sous-domaine est spécifique, plus le score est élevé
score += 1 * (domainParts.length - 2);
}

// Priorité par longueur du chemin (plus c'est long, plus c'est spécifique)
const pathLength = url.split("/").length - 3;
score += pathLength;

return score;
}

// Trier les URLs selon la priorité (score décroissant)
return urls.sort((a, b) => getPatternScore(b) - getPatternScore(a));
}


export function getDomainAndSubdomain(url: string) {
try {
const parsedUrl = new URL(url);
const hostname = parsedUrl.hostname;

return hostname;
} catch (error) {
console.error('Invalid url:', error);
return null;
}
}
2 changes: 1 addition & 1 deletion src/manifest.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
manifest_version: 3,
name: "Quick Javascript Switcher",
description: "The one-click JavaScript Switcher",
description: "Disable JavaScript on any site in one click",
version: process.env.VUE_APP_VERSION.replace("-beta", ""),
minimum_chrome_version: "88.0",
homepage_url: "https://github.com/maximelebreton/quick-javascript-switcher",
Expand Down
2 changes: 0 additions & 2 deletions src/view/options.vue
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,8 @@ export default defineComponent({
setup() {
const { fetchRules } = useMethods();
initState()
const isMounted = ref(false)
onMounted(async () => {
isMounted.value = true
await fetchRules();
chrome.storage.onChanged.addListener(async () => {
Expand Down
Loading

0 comments on commit d548774

Please sign in to comment.