Skip to content

Commit

Permalink
more
Browse files Browse the repository at this point in the history
  • Loading branch information
bershanskiy committed Oct 2, 2022
1 parent 8cb71f4 commit c3e6dae
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 31 deletions.
9 changes: 8 additions & 1 deletion src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ type TestMessage = {
type: 'setDataIsMigratedForTesting';
data: boolean;
id: number;
} | {
type: 'getManifest';
id: number;
};

// Start extension
Expand Down Expand Up @@ -129,7 +132,7 @@ if (__WATCH__) {
};

listen();
} else if (!__DEBUG__){
} else if (!__DEBUG__ && !__TEST__) {
chrome.runtime.onInstalled.addListener(({reason}) => {
if (reason === 'install') {
chrome.tabs.create({url: getHelpURL()});
Expand Down Expand Up @@ -181,6 +184,10 @@ if (__TEST__) {
DevTools.setDataIsMigratedForTesting(message.data);
respond({type: 'setDataIsMigratedForTesting-response', id: message.id});
break;
case 'getManifest': {
const data = chrome.runtime.getManifest();
respond({type: 'getManifest-response', data, id: message.id});
}
}
} catch (err) {
respond({type: 'error', data: String(err)});
Expand Down
58 changes: 37 additions & 21 deletions src/background/tab-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,14 @@ import type {FetchRequestParameters} from './utils/network';
import type {Message} from '../definitions';
import {isFirefox} from '../utils/platform';
import {MessageType} from '../utils/message';
import {logWarn} from './utils/log';
import {logInfo, logWarn} from './utils/log';
import {StateManager} from '../utils/state-manager';
import {getURLHostOrProtocol} from '../utils/url';
import {isPanel} from './utils/tab';

declare const __CHROMIUM_MV3__: boolean;
declare const __THUNDERBIRD__: boolean;

async function queryTabs(query: chrome.tabs.QueryInfo = {}) {
return new Promise<chrome.tabs.Tab[]>((resolve) =>
chrome.tabs.query(query, resolve)
);
}

interface ConnectionMessageOptions {
url: string;
frameURL: string;
Expand Down Expand Up @@ -52,7 +46,7 @@ enum DocumentState {
HIDDEN = 2,
FROZEN = 3,
TERMINATED = 4,
DISCARDED = 5
DISCARDED = 5,
}

export default class TabManager {
Expand Down Expand Up @@ -134,7 +128,7 @@ export default class TabManager {
const frameId = sender.frameId;
const frameURL = sender.url;
if (this.tabs[tabId][frameId].timestamp < this.timestamp) {
const message = this.getTabMessage(this.getTabURL(sender.tab), frameURL);
const message = this.getTabMessage(await this.getTabURL(sender.tab), frameURL);
chrome.tabs.sendMessage<Message>(tabId, message, {frameId});
}
this.tabs[sender.tab.id][sender.frameId] = {
Expand Down Expand Up @@ -207,6 +201,12 @@ export default class TabManager {
chrome.tabs.onRemoved.addListener(async (tabId) => this.removeFrame(tabId, 0));
}

private static async queryTabs(query: chrome.tabs.QueryInfo = {}) {
return new Promise<chrome.tabs.Tab[]>((resolve) =>
chrome.tabs.query(query, resolve)
);
}

private static addFrame(tabId: number, frameId: number, senderURL: string, timestamp: number) {
let frames: {[frameId: number]: FrameInfo};
if (this.tabs[tabId]) {
Expand Down Expand Up @@ -238,16 +238,32 @@ export default class TabManager {
this.stateManager.saveState();
}

static getTabURL(tab: chrome.tabs.Tab): string {
private static async getTabURL(tab: chrome.tabs.Tab): Promise<string> {
if (__CHROMIUM_MV3__) {
try {
if (this.tabs[tab.id] && this.tabs[tab.id][0]) {
return this.tabs[tab.id][0].url || 'about:blank';
}
return (await chrome.scripting.executeScript({
target: {
tabId: tab.id,
frameIds: [0],
},
func: () => window.location.href,
}))[0].result;
} catch (e) {
return 'about:blank';
}
}
// It can happen in cases whereby the tab.url is empty.
// Luckily this only and will only happen on `about:blank`-like pages.
// Due to this we can safely use `about:blank` as fallback value.
return tab.url || 'about:blank';
}

static async updateContentScript(options: {runOnProtectedPages: boolean}) {
(await queryTabs())
.filter((tab) => options.runOnProtectedPages || canInjectScript(tab.url))
(await this.queryTabs())
.filter((tab) => __CHROMIUM_MV3__ || options.runOnProtectedPages || canInjectScript(tab.url))
.filter((tab) => !Boolean(this.tabs[tab.id]))
.forEach((tab) => {
if (!tab.discarded) {
Expand All @@ -258,7 +274,7 @@ export default class TabManager {
allFrames: true,
},
files: ['/inject/index.js'],
});
}, () => logInfo('Could not update content script in tab', tab, chrome.runtime.lastError));
} else {
chrome.tabs.executeScript(tab.id, {
runAt: 'document_start',
Expand All @@ -283,23 +299,23 @@ export default class TabManager {
// sendMessage will send a tab messages to all active tabs and their frames.
// If onlyUpdateActiveTab is specified, it will only send a new message to any
// tab that matches the active tab's hostname. This is to ensure that when a user
// has multiple tabs of the same website, it will ensure that every tab will receive
// the new message and not just that tab as Dark Reader currently doesn't have per-tab
// operations, this should be the expected behavior.
// has multiple tabs of the same website, every tab will receive the new message
// and not just that tab as Dark Reader currently doesn't have per-tab operations,
// this should be the expected behavior.
static async sendMessage(onlyUpdateActiveTab = false) {
this.timestamp++;

const activeTabHostname = onlyUpdateActiveTab ? getURLHostOrProtocol(await this.getActiveTabURL()) : null;

(await queryTabs())
(await this.queryTabs())
.filter((tab) => Boolean(this.tabs[tab.id]))
.forEach((tab) => {
const frames = this.tabs[tab.id];
Object.entries(frames)
.filter(([, {state}]) => state === DocumentState.ACTIVE || state === DocumentState.PASSIVE)
.forEach(([id, {url}]) => {
.forEach(async ([id, {url}]) => {
const frameId = Number(id);
const tabURL = this.getTabURL(tab);
const tabURL = await this.getTabURL(tab);
// Check if hostname are equal when we only want to update active tab.
if (onlyUpdateActiveTab && getURLHostOrProtocol(tabURL) !== activeTabHostname) {
return;
Expand Down Expand Up @@ -335,7 +351,7 @@ export default class TabManager {
}

static async getActiveTab() {
let tab = (await queryTabs({
let tab = (await this.queryTabs({
active: true,
lastFocusedWindow: true,
// Explicitly exclude Dark Reader's Dev Tools and other special windows from the query
Expand All @@ -344,7 +360,7 @@ export default class TabManager {
if (!tab) {
// When Dark Reader's DevTools are open, last focused window might be the DevTools window
// so we lift this restriction and try again (with the best guess)
tab = (await queryTabs({
tab = (await this.queryTabs({
active: true,
windowType: 'normal',
}))[0];
Expand Down
4 changes: 3 additions & 1 deletion src/ui/popup/components/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {AutomationMode} from '../../../../utils/automation';
import {isLocalFile} from '../../../../utils/url';
import {isChromium} from '../../../../utils/platform';

declare const __CHROMIUM_MV3__: boolean;

function multiline(...lines: string[]) {
return lines.join('\n');
}
Expand Down Expand Up @@ -44,7 +46,7 @@ function Header({data, actions, onMoreToggleSettingsClick}: HeaderProps) {
data={data}
actions={actions}
/>
{!isFile && (tab.isProtected || !tab.isInjected) ? (
{!isFile && (tab.isProtected || (!__CHROMIUM_MV3__ && !tab.isInjected)) ? (
<span class="header__site-toggle__unable-text">
{getLocalMessage('page_protected')}
</span>
Expand Down
16 changes: 11 additions & 5 deletions tasks/bundle-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,30 @@ async function writeJSON(path, json) {
return await writeFile(path, content);
}

async function patchManifest(platform, debug, watch) {
async function patchManifest(platform, debug, watch, test) {
const manifest = await readJSON(`${srcDir}/manifest.json`);
const manifestPatch = platform === PLATFORM.CHROME ? {} : await readJSON(`${srcDir}/manifest-${platform}.json`);
const patched = {...manifest, ...manifestPatch};
if (platform === PLATFORM.CHROME_MV3) {
patched.browser_action = undefined;
}
if (debug) {
patched.version = '0.0.0.0';
patched.version = '1';
patched.description = `Debug build, platform: ${platform}, watch: ${watch ? 'yes' : 'no'}.`;
}
if (debug && !test && platform === PLATFORM.CHROME_MV3) {
patched.permissions.push('tabs');
}
if (debug && (platform === PLATFORM.CHROME || platform === PLATFORM.CHROME_MV3)) {
patched.version_name = 'Debug';
}
return patched;
}

async function manifests({platforms, debug, watch}) {
async function manifests({platforms, debug, watch, test}) {
const enabledPlatforms = Object.values(PLATFORM).filter((platform) => platform !== PLATFORM.API && platforms[platform]);
for (const platform of enabledPlatforms) {
const manifest = await patchManifest(platform, debug, watch);
const manifest = await patchManifest(platform, debug, watch, test);
const destDir = getDestDir({debug, platform});
await writeJSON(`${destDir}/manifest.json`, manifest);
}
Expand All @@ -52,7 +58,7 @@ const bundleManifestTask = createTask(
const changed = chrome || changedFiles.some((file) => file.endsWith(`manifest-${platform}.json`));
platforms[platform] = changed && buildPlatforms[platform];
}
await manifests({platforms, debug: true, watch: true});
await manifests({platforms, debug: true, watch: true, test: false});
reload.reload({type: reload.FULL});
},
);
Expand Down
3 changes: 2 additions & 1 deletion tests/browser/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class PuppeteerEnvironment extends JestNodeEnvironment.TestEnvironment {

/**
* @param {string} path
* @returns {puppeteer.Page}
* @returns {Promise<puppeteer.Page>}
*/
async openExtensionPage(path) {
if (this.global.product === 'chrome') {
Expand Down Expand Up @@ -236,6 +236,7 @@ class PuppeteerEnvironment extends JestNodeEnvironment.TestEnvironment {
const bg = await this.getBackgroundPage();
await bg.emulateMediaFeatures([{name, value}]);
},
getManifest: async () => await sendToUIPage({type: 'getManifest'}),
};
});
}
Expand Down
1 change: 1 addition & 0 deletions tests/browser/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ declare global {
getChromeStorage: (region: 'local' | 'sync', keys: string[]) => Promise<{[key: string]: any}>;
setDataIsMigratedForTesting: (value: boolean) => Promise<void>;
emulateMedia: (name: string, value: string) => Promise<void>;
getManifest: () => Promise<chrome.runtime.Manifest>;
};
}
6 changes: 4 additions & 2 deletions tests/inject/dynamic/image-analysis.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,9 @@ describe('IMAGE ANALYSIS', () => {
);
createOrUpdateDynamicTheme(theme, null, false);
await waitForEvent('__darkreader__test__asyncQueueComplete');
expect(getComputedStyle(container.querySelector('h1')).backgroundImage.startsWith(isFirefox ? `url("about:invalid"), url("about:invalid")` : `url("http://localhost`)).toBeTrue();
expect(getComputedStyle(container.querySelector('h1')).backgroundImage.endsWith(`url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iOCIgaGVpZ2h0PSI4Ij48ZGVmcz48ZmlsdGVyIGlkPSJkYXJrcmVhZGVyLWltYWdlLWZpbHRlciI+PGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAuMzMzIC0wLjY2NyAtMC42NjcgMC4wMDAgMS4wMDAgLTAuNjY3IDAuMzMzIC0wLjY2NyAwLjAwMCAxLjAwMCAtMC42NjcgLTAuNjY3IDAuMzMzIDAuMDAwIDEuMDAwIDAuMDAwIDAuMDAwIDAuMDAwIDEuMDAwIDAuMDAwIiAvPjwvZmlsdGVyPjwvZGVmcz48aW1hZ2Ugd2lkdGg9IjgiIGhlaWdodD0iOCIgZmlsdGVyPSJ1cmwoI2RhcmtyZWFkZXItaW1hZ2UtZmlsdGVyKSIgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhacFpYZENiM2c5SWpBZ01DQTRJRGdpSUhkcFpIUm9QU0k0SWlCb1pXbG5hSFE5SWpnaVBnb2dJQ0FnUEhKbFkzUWdabWxzYkQwaWQyaHBkR1VpSUhkcFpIUm9QU0l4TURBbElpQm9aV2xuYUhROUlqRXdNQ1VpSUM4K0Nqd3ZjM1puUGc9PSIgLz48L3N2Zz4=")`)).toBeTrue();
expect(getComputedStyle(container.querySelector('h1')).backgroundImage === (isFirefox ?
'url("about:invalid"), url("about:invalid"), url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iOCIgaGVpZ2h0PSI4Ij48ZGVmcz48ZmlsdGVyIGlkPSJkYXJrcmVhZGVyLWltYWdlLWZpbHRlciI+PGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAuMzMzIC0wLjY2NyAtMC42NjcgMC4wMDAgMS4wMDAgLTAuNjY3IDAuMzMzIC0wLjY2NyAwLjAwMCAxLjAwMCAtMC42NjcgLTAuNjY3IDAuMzMzIDAuMDAwIDEuMDAwIDAuMDAwIDAuMDAwIDAuMDAwIDEuMDAwIDAuMDAwIiAvPjwvZmlsdGVyPjwvZGVmcz48aW1hZ2Ugd2lkdGg9IjgiIGhlaWdodD0iOCIgZmlsdGVyPSJ1cmwoI2RhcmtyZWFkZXItaW1hZ2UtZmlsdGVyKSIgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhacFpYZENiM2c5SWpBZ01DQTRJRGdpSUhkcFpIUm9QU0k0SWlCb1pXbG5hSFE5SWpnaVBnb2dJQ0FnUEhKbFkzUWdabWxzYkQwaWQyaHBkR1VpSUhkcFpIUm9QU0l4TURBbElpQm9aV2xuYUhROUlqRXdNQ1VpSUM4K0Nqd3ZjM1puUGc9PSIgLz48L3N2Zz4=")'
:
`url("about:invalid"), url("about:invalid")`));
});
});

0 comments on commit c3e6dae

Please sign in to comment.