Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(brave): not updating tab to the new redirect url in some cases. #1285

Merged
merged 3 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 31 additions & 4 deletions add-on/src/lib/redirect-handler/blockOrObserve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ const log = debug('ipfs-companion:redirect-handler:blockOrObserve')
log.error = debug('ipfs-companion:redirect-handler:blockOrObserve:error')

export const DEFAULT_NAMESPACES = new Set(['ipfs', 'ipns'])
export const GLOBAL_STATE_OPTION_CHANGE = 'GLOBAL_STATE_OPTION_CHANGE'
export const DELETE_RULE_REQUEST = 'DELETE_RULE_REQUEST'
export const DELETE_RULE_REQUEST_SUCCESS = 'DELETE_RULE_REQUEST_SUCCESS'
export const GLOBAL_STATE_OPTION_CHANGE = 'GLOBAL_STATE_OPTION_CHANGE'
export const MAX_RETRIES_TO_UPDATE_TAB = 5

// We need to match the rest of the URL, so we can use a wildcard.
export const RULE_REGEX_ENDING = '((?:[^\\.]|$).*)$'
Expand All @@ -25,9 +26,12 @@ interface regexFilterMap {
regexSubstitution: string
}

interface redirectHandlerInput {
interface redirectPair {
originUrl: string
redirectUrl: string
}

interface redirectHandlerInput extends redirectPair {
getPort: (state: CompanionState) => string
}

Expand Down Expand Up @@ -362,6 +366,29 @@ export function addRuleToDynamicRuleSetGenerator (
}
})

/**
* Update the tab that matches the origin URL to the redirect URL.
*
* @param param0 redirectPair
* @param numTry number of times we have tried to update the tab.
* @returns
*/
async function updateTabToNewUrl ({ originUrl, redirectUrl }: redirectPair, numTry: number = 1): Promise<void> {
// Don't get stuck in a loop.
if (numTry > MAX_RETRIES_TO_UPDATE_TAB) {
return
}

const tabs = await browser.tabs.query({ url: `${originUrl}*` })
if (tabs.length === 0) {
// wait for the tab to be created or a redirect to happen. Check every 100ms.
await new Promise(resolve => setTimeout(resolve, 100))
await updateTabToNewUrl({ originUrl, redirectUrl }, numTry + 1)
} else {
await Promise.all(tabs.map(async tab => await browser.tabs.update(tab.id, { url: redirectUrl })))
}
}

// returning a closure to avoid passing `getState` as an argument to `addRuleToDynamicRuleSet`.
return async function ({ originUrl, redirectUrl }: redirectHandlerInput): Promise<void> {
// update the rules so that the next request is handled correctly.
Expand All @@ -376,8 +403,8 @@ export function addRuleToDynamicRuleSetGenerator (
}

// first update all the matching tabs to apply the new rule.
const tabs = await browser.tabs.query({ url: `${originUrl}*` })
await Promise.all(tabs.map(async tab => await browser.tabs.update(tab.id, { url: redirectUrl })))
// don't await this as this can happen in parallel.
void updateTabToNewUrl({ originUrl, redirectUrl })

// Then update the rule set for future, we need to construct the regex filter and substitution.
const { regexSubstitution, regexFilter } = constructRegexFilter({ originUrl, redirectUrl })
Expand Down
26 changes: 25 additions & 1 deletion test/functional/lib/redirect-handler/blockOrObserve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { expect } from 'chai';
import { before, describe, it } from 'mocha';
import sinon from 'sinon';
import browserMock from 'sinon-chrome';
import { MAX_RETRIES_TO_UPDATE_TAB } from './../../../../add-on/src/lib/redirect-handler/blockOrObserve';

import { optionDefaults } from '../../../../add-on/src/lib/options.js';
import { RULE_REGEX_ENDING, addRuleToDynamicRuleSetGenerator, cleanupRules, isLocalHost } from '../../../../add-on/src/lib/redirect-handler/blockOrObserve';
Expand Down Expand Up @@ -116,7 +117,6 @@ describe('lib/redirect-handler/blockOrObserve', () => {
beforeEach(async () => {
sinonSandbox.restore()
browserMock.tabs.query.resetHistory()
browserMock.tabs.reload.resetHistory()
browserMock.tabs.update.resetHistory()
browserMock.declarativeNetRequest = sinonSandbox.spy(new DeclarativeNetRequestMock())
// this cleans up the rules from the previous test stored in memory.
Expand Down Expand Up @@ -184,6 +184,30 @@ describe('lib/redirect-handler/blockOrObserve', () => {
})
})

it('Should update tab from originUrl to redirectUrl for the first time', async () => {
await addRuleToDynamicRuleSet({
originUrl: 'https://ipfs.io/ipns/en.wikipedia-on-ipfs.org',
redirectUrl: 'http://localhost:8080/ipns/en.wikipedia-on-ipfs.org'
})
expect(browserMock.tabs.query.called).to.be.true
expect(browserMock.tabs.update.called).to.be.true
expect(browserMock.tabs.update.lastCall.args).to.deep.equal([40, { url: 'http://localhost:8080/ipns/en.wikipedia-on-ipfs.org' }])
})

it('Should wait for the tab to exist before updating tab from originUrl to redirectUrl for the first time', async () => {
const clock = sinon.useFakeTimers();
browserMock.tabs.query.onCall(0).resolves([])
browserMock.tabs.query.onCall(1).resolves([{ id: 40 }])
await addRuleToDynamicRuleSet({
originUrl: 'https://ipfs.io/ipns/en.wikipedia-on-ipfs.org',
redirectUrl: 'http://localhost:8080/ipns/en.wikipedia-on-ipfs.org'
})
await clock.tickAsync(100 * MAX_RETRIES_TO_UPDATE_TAB)
expect(browserMock.tabs.query.callCount).to.be.equal(2)
expect(browserMock.tabs.update.called).to.be.true
expect(browserMock.tabs.update.lastCall.args).to.deep.equal([40, { url: 'http://localhost:8080/ipns/en.wikipedia-on-ipfs.org' }])
})

it('Should add redirect rules for local gateway', async () => {
await addRuleToDynamicRuleSet({
originUrl: 'https://ipfs.io/ipns/en.wikipedia-on-ipfs.org',
Expand Down