Skip to content

Commit

Permalink
Make the onDOMContentLoaded even listener a global listener so it per…
Browse files Browse the repository at this point in the history
…sists
  • Loading branch information
jdgarcia committed May 30, 2024
1 parent 3ab19d5 commit f87146d
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 42 deletions.
66 changes: 25 additions & 41 deletions src/background.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { runtime, scripting, tabs, webNavigation } from 'webextension-polyfill';
import { runtime, scripting, storage, tabs, webNavigation } from 'webextension-polyfill';
import { fetchUser } from './gkApi';
import { injectionScope as inject_azureDevops } from './hosts/azureDevops';
import { injectionScope as inject_bitbucket } from './hosts/bitbucket';
import { injectionScope as inject_github } from './hosts/github';
import { injectionScope as inject_gitlab } from './hosts/gitlab';
import { domainToMatchPattern, refreshPermissions } from './permissions-helper';
import { refreshPermissions } from './permissions-helper';
import { getEnterpriseConnections, GKDotDevUrl, PermissionsGrantedMessage, PopupInitMessage } from './shared';
import type { CacheContext } from './types';

Expand All @@ -22,6 +22,24 @@ const DefaultInjectionDomains: InjectionDomains = {
azureDevops: ['dev.azure.com'],
};

webNavigation.onDOMContentLoaded.addListener(async details => {
const { injectionDomains } = (await storage.session.get('injectionDomains')) as {
injectionDomains?: InjectionDomains;
};
if (!injectionDomains) {
return;
}

const injectionFn = getInjectionFn(details.url, injectionDomains);
if (injectionFn) {
void scripting.executeScript({
target: { tabId: details.tabId },
func: injectionFn,
args: [details.url, GKDotDevUrl],
});
}
});

webNavigation.onHistoryStateUpdated.addListener(details => {
// used to detect when the user navigates to a different page in the same tab
const url = new URL(details.url);
Expand Down Expand Up @@ -62,47 +80,14 @@ async function computeInjectionDomains(context: CacheContext) {
return injectionDomains;
}

async function addInjectionListener(context: CacheContext) {
const injectionDomains = await computeInjectionDomains(context);
const allDomains = Object.values<string[]>(injectionDomains as any).flat();

// note: This is a closure over injectionDomains
const injectScript = (tabId: number, tabUrl: string) => {
void scripting.executeScript({
target: { tabId: tabId },
// injectImmediately: true,
func: getInjectionFn(tabUrl, injectionDomains),
args: [tabUrl, GKDotDevUrl],
});
};

webNavigation.onDOMContentLoaded.addListener(details => injectScript(details.tabId, details.url), {
url: allDomains.map(domain => ({ hostContains: domain })),
});

// Immediately inject into the currently open compatible tabs. This is needed because when the background
// script is idle, its event listeners are not active. Opening a compatible tab will cause the background
// script to awaken and setup the event listeners again, but the tab will load before that happens.
const currentTabs = await tabs.query({
url: allDomains.map(domainToMatchPattern),
status: 'complete', // only query tabs that have finished loading
discarded: false, // discarded tabs will reload when focused so we don't need to inject into them now
});
currentTabs.forEach(tab => {
if (tab.id && tab.url) {
injectScript(tab.id, tab.url);
}
});
}

function urlHostHasDomain(url: URL, domains: string[]): boolean {
return domains.some(domain => url.hostname.endsWith(domain));
}

function getInjectionFn(
rawUrl: string,
injectionDomains: InjectionDomains,
): (url: string, gkDotDevUrl: string) => void {
): ((url: string, gkDotDevUrl: string) => void) | null {
const url = new URL(rawUrl);
if (urlHostHasDomain(url, injectionDomains.github)) {
return inject_github;
Expand All @@ -120,8 +105,7 @@ function getInjectionFn(
return inject_azureDevops;
}

console.error('Unsupported host');
throw new Error('Unsupported host');
return null;
}

async function main() {
Expand All @@ -131,9 +115,9 @@ async function main() {
const context: CacheContext = {};
// This removes unneded permissions
await refreshPermissions(context);
// NOTE: This may request hosts that we may not have permissions for, which will log errors for the extension
// This does not cause any issues, and eliminating the errors requires more logic
await addInjectionListener(context);

const injectionDomains = await computeInjectionDomains(context);
void storage.session.set({ injectionDomains: injectionDomains });
}

void main();
2 changes: 1 addition & 1 deletion src/permissions-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { permissions } from 'webextension-polyfill';
import { arrayDifference, CloudProviders, getEnterpriseConnections } from './shared';
import type { CacheContext } from './types';

export function domainToMatchPattern(domain: string): string {
function domainToMatchPattern(domain: string): string {
return `*://*.${domain}/*`;
}

Expand Down

0 comments on commit f87146d

Please sign in to comment.