From 01242a2f0d174b4bf8b51fd5627edced465757e9 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Thu, 23 Sep 2021 12:44:49 +0900 Subject: Search a content from frames successfully loaded --- src/background/Application.ts | 20 ++++++++- src/background/di.ts | 6 ++- src/background/operators/impls/FindNextOperator.ts | 11 +++-- .../operators/impls/FindOperatorFactoryChain.ts | 10 ++--- src/background/operators/impls/FindPrevOperator.ts | 10 +++-- src/background/presenters/FramePresenter.ts | 12 ------ .../repositories/ReadyFrameRepository.ts | 49 ++++++++++++++++++++++ src/background/usecases/ReadyFrameUseCase.ts | 18 ++++++++ src/background/usecases/StartFindUseCase.ts | 12 ++++-- 9 files changed, 118 insertions(+), 30 deletions(-) delete mode 100644 src/background/presenters/FramePresenter.ts create mode 100644 src/background/repositories/ReadyFrameRepository.ts create mode 100644 src/background/usecases/ReadyFrameUseCase.ts (limited to 'src') diff --git a/src/background/Application.ts b/src/background/Application.ts index 2006965..87865e6 100644 --- a/src/background/Application.ts +++ b/src/background/Application.ts @@ -4,6 +4,7 @@ import SettingController from "./controllers/SettingController"; import VersionController from "./controllers/VersionController"; import SettingRepository from "./repositories/SettingRepository"; import FindRepositoryImpl from "./repositories/FindRepository"; +import ReadyFrameRepository from "./repositories/ReadyFrameRepository"; @injectable() export default class Application { @@ -14,7 +15,9 @@ export default class Application { @inject("SyncSettingRepository") private syncSettingRepository: SettingRepository, @inject("FindRepository") - private readonly findRepository: FindRepositoryImpl + private readonly findRepository: FindRepositoryImpl, + @inject("ReadyFrameRepository") + private readonly frameRepository: ReadyFrameRepository ) {} run() { @@ -22,6 +25,7 @@ export default class Application { browser.tabs.onUpdated.addListener((tabId: number, info) => { if (info.status == "loading") { + this.frameRepository.clearFrameIds(tabId); this.findRepository.deleteLocalState(tabId); } }); @@ -31,6 +35,20 @@ export default class Application { } this.versionController.notify(); }); + browser.webNavigation.onCompleted.addListener((detail) => { + // The console iframe embedded by Vim-Vixen has url starting with + // 'moz-extensions://'. The add-on should ignore it from search targets. + // + // When a browser blocks to load an iframe by x-frame options or a + // content security policy, the URL begins with 'about:neterror', and + // a background script fails to send a message to iframe. + if ( + detail.url.startsWith("http://") || + detail.url.startsWith("https://") + ) { + this.frameRepository.addFrameId(detail.tabId, detail.frameId); + } + }); this.contentMessageListener.run(); this.syncSettingRepository.onChange(() => { diff --git a/src/background/di.ts b/src/background/di.ts index e97c4a8..495de7c 100644 --- a/src/background/di.ts +++ b/src/background/di.ts @@ -18,10 +18,10 @@ import { BrowserSettingRepositoryImpl } from "./repositories/BrowserSettingRepos import { RepeatRepositoryImpl } from "./repositories/RepeatRepository"; import { ZoomPresenterImpl } from "./presenters/ZoomPresenter"; import { WindowPresenterImpl } from "./presenters/WindowPresenter"; -import { FramePresenterImpl } from "./presenters/FramePresenter"; import { FindClientImpl } from "./clients/FindClient"; import { ConsoleFrameClientImpl } from "./clients/ConsoleFrameClient"; import { FindRepositoryImpl } from "./repositories/FindRepository"; +import { ReadyFrameRepositoryImpl } from "./repositories/ReadyFrameRepository"; container.register("LocalSettingRepository", { useClass: LocalSettingRepository, @@ -43,10 +43,12 @@ container.register("TabRepository", { useClass: TabRepositoryImpl }); container.register("ZoomPresenter", { useClass: ZoomPresenterImpl }); container.register("TabPresenter", { useClass: TabPresenterImpl }); container.register("WindowPresenter", { useClass: WindowPresenterImpl }); -container.register("FramePresenter", { useClass: FramePresenterImpl }); container.register("FindRepository", { useClass: FindRepositoryImpl }); container.register("FindClient", { useClass: FindClientImpl }); container.register("NavigateClient", { useClass: NavigateClientImpl }); container.register("ConsoleClient", { useClass: ConsoleClientImpl }); container.register("ConsoleFrameClient", { useClass: ConsoleFrameClientImpl }); container.register("OperatorFactory", { useClass: OperatorFactoryImpl }); +container.register("ReadyFrameRepository", { + useClass: ReadyFrameRepositoryImpl, +}); diff --git a/src/background/operators/impls/FindNextOperator.ts b/src/background/operators/impls/FindNextOperator.ts index 241f71d..2aed9fb 100644 --- a/src/background/operators/impls/FindNextOperator.ts +++ b/src/background/operators/impls/FindNextOperator.ts @@ -3,7 +3,7 @@ import TabPresenter from "../../presenters/TabPresenter"; import FindRepository from "../../repositories/FindRepository"; import FindClient from "../../clients/FindClient"; import ConsoleClient from "../../infrastructures/ConsoleClient"; -import FramePresenter from "../../presenters/FramePresenter"; +import ReadyFrameRepository from "../../repositories/ReadyFrameRepository"; export default class FindNextOperator implements Operator { constructor( @@ -11,7 +11,7 @@ export default class FindNextOperator implements Operator { private readonly findRepository: FindRepository, private readonly findClient: FindClient, private readonly consoleClient: ConsoleClient, - private readonly framePresenter: FramePresenter + private readonly frameRepository: ReadyFrameRepository ) {} async run(): Promise { @@ -65,7 +65,12 @@ export default class FindNextOperator implements Operator { const keyword = await this.findRepository.getGlobalKeyword(); if (keyword) { - const frameIds = await this.framePresenter.getAllFrameIds(tabId); + const frameIds = await this.frameRepository.getFrameIds(tabId); + if (typeof frameIds === "undefined") { + // No frames are ready + return; + } + for (const frameId of frameIds) { await this.findClient.clearSelection(tabId, frameId); } diff --git a/src/background/operators/impls/FindOperatorFactoryChain.ts b/src/background/operators/impls/FindOperatorFactoryChain.ts index b71f032..cc169dd 100644 --- a/src/background/operators/impls/FindOperatorFactoryChain.ts +++ b/src/background/operators/impls/FindOperatorFactoryChain.ts @@ -7,8 +7,8 @@ import FindNextOperator from "./FindNextOperator"; import FindPrevOperator from "./FindPrevOperator"; import FindRepository from "../../repositories/FindRepository"; import FindClient from "../../clients/FindClient"; -import FramePresenter from "../../presenters/FramePresenter"; import ConsoleClient from "../../infrastructures/ConsoleClient"; +import ReadyFrameRepository from "../../repositories/ReadyFrameRepository"; @injectable() export default class FindOperatorFactoryChain implements OperatorFactoryChain { @@ -21,8 +21,8 @@ export default class FindOperatorFactoryChain implements OperatorFactoryChain { private readonly findClient: FindClient, @inject("ConsoleClient") private readonly consoleClient: ConsoleClient, - @inject("FramePresenter") - private readonly framePresenter: FramePresenter + @inject("ReadyFrameRepository") + private readonly frameRepository: ReadyFrameRepository ) {} create(op: operations.Operation): Operator | null { @@ -33,7 +33,7 @@ export default class FindOperatorFactoryChain implements OperatorFactoryChain { this.findRepository, this.findClient, this.consoleClient, - this.framePresenter + this.frameRepository ); case operations.FIND_PREV: return new FindPrevOperator( @@ -41,7 +41,7 @@ export default class FindOperatorFactoryChain implements OperatorFactoryChain { this.findRepository, this.findClient, this.consoleClient, - this.framePresenter + this.frameRepository ); } return null; diff --git a/src/background/operators/impls/FindPrevOperator.ts b/src/background/operators/impls/FindPrevOperator.ts index 822c386..3c4411d 100644 --- a/src/background/operators/impls/FindPrevOperator.ts +++ b/src/background/operators/impls/FindPrevOperator.ts @@ -3,7 +3,7 @@ import TabPresenter from "../../presenters/TabPresenter"; import FindRepository from "../../repositories/FindRepository"; import FindClient from "../../clients/FindClient"; import ConsoleClient from "../../infrastructures/ConsoleClient"; -import FramePresenter from "../../presenters/FramePresenter"; +import ReadyFrameRepository from "../../repositories/ReadyFrameRepository"; export default class FindPrevOperator implements Operator { constructor( @@ -11,7 +11,7 @@ export default class FindPrevOperator implements Operator { private readonly findRepository: FindRepository, private readonly findClient: FindClient, private readonly consoleClient: ConsoleClient, - private readonly framePresenter: FramePresenter + private readonly frameRepository: ReadyFrameRepository ) {} async run(): Promise { @@ -65,7 +65,11 @@ export default class FindPrevOperator implements Operator { const keyword = await this.findRepository.getGlobalKeyword(); if (keyword) { - const frameIds = await this.framePresenter.getAllFrameIds(tabId); + const frameIds = await this.frameRepository.getFrameIds(tabId); + if (typeof frameIds === "undefined") { + // No frames are ready + return; + } for (const frameId of frameIds) { await this.findClient.clearSelection(tabId, frameId); } diff --git a/src/background/presenters/FramePresenter.ts b/src/background/presenters/FramePresenter.ts deleted file mode 100644 index c94f8dd..0000000 --- a/src/background/presenters/FramePresenter.ts +++ /dev/null @@ -1,12 +0,0 @@ -export default interface FramePresenter { - getAllFrameIds(tabId: number): Promise>; -} - -export class FramePresenterImpl implements FramePresenter { - async getAllFrameIds(tabId: number): Promise> { - const frames = await browser.webNavigation.getAllFrames({ tabId: tabId }); - return frames - .filter((f) => !f.url.startsWith("moz-extension://")) - .map((f) => f.frameId); - } -} diff --git a/src/background/repositories/ReadyFrameRepository.ts b/src/background/repositories/ReadyFrameRepository.ts new file mode 100644 index 0000000..725f604 --- /dev/null +++ b/src/background/repositories/ReadyFrameRepository.ts @@ -0,0 +1,49 @@ +import MemoryStorage from "../infrastructures/MemoryStorage"; + +const REPOSITORY_KEY = "readyFrameRepository"; + +type State = { [tabId: number]: number[] }; + +export default interface ReadyFrameRepository { + clearFrameIds(tabId: number): Promise; + + addFrameId(tabId: number, fraemId: number): Promise; + + getFrameIds(tabId: number): Promise; +} + +export class ReadyFrameRepositoryImpl implements ReadyFrameRepository { + private cache: MemoryStorage; + + constructor() { + this.cache = new MemoryStorage(); + } + + clearFrameIds(tabId: number): Promise { + let state: State | undefined = this.cache.get(REPOSITORY_KEY); + if (typeof state === "undefined") { + state = {}; + } + delete state[tabId]; + this.cache.set(REPOSITORY_KEY, state); + return Promise.resolve(); + } + + addFrameId(tabId: number, fraemId: number): Promise { + let state: State | undefined = this.cache.get(REPOSITORY_KEY); + if (typeof state === "undefined") { + state = {}; + } + state[tabId] = (state[tabId] || []).concat(fraemId); + this.cache.set(REPOSITORY_KEY, state); + return Promise.resolve(); + } + + getFrameIds(tabId: number): Promise { + const state: State | undefined = this.cache.get(REPOSITORY_KEY); + if (typeof state === "undefined") { + return Promise.resolve(undefined); + } + return Promise.resolve(state[tabId]); + } +} diff --git a/src/background/usecases/ReadyFrameUseCase.ts b/src/background/usecases/ReadyFrameUseCase.ts new file mode 100644 index 0000000..81bee0c --- /dev/null +++ b/src/background/usecases/ReadyFrameUseCase.ts @@ -0,0 +1,18 @@ +import { inject, injectable } from "tsyringe"; +import ReadyFrameRepository from "../repositories/ReadyFrameRepository"; + +@injectable() +export default class ReadyFrameUseCase { + constructor( + @inject("ReadyFrameRepository") + private readonly frameRepository: ReadyFrameRepository + ) {} + + async addReadyFrame(tabId: number, frameId: number): Promise { + return this.frameRepository.addFrameId(tabId, frameId); + } + + async clearReadyFrame(tabId: number): Promise { + return this.frameRepository.clearFrameIds(tabId); + } +} diff --git a/src/background/usecases/StartFindUseCase.ts b/src/background/usecases/StartFindUseCase.ts index 066d930..a62462f 100644 --- a/src/background/usecases/StartFindUseCase.ts +++ b/src/background/usecases/StartFindUseCase.ts @@ -2,7 +2,7 @@ import { inject, injectable } from "tsyringe"; import ConsoleClient from "../infrastructures/ConsoleClient"; import FindRepositoryImpl from "../repositories/FindRepository"; import FindClient from "../clients/FindClient"; -import FramePresenter from "../presenters/FramePresenter"; +import ReadyFrameRepository from "../repositories/ReadyFrameRepository"; @injectable() export default class StartFindUseCase { @@ -13,8 +13,8 @@ export default class StartFindUseCase { private readonly findRepository: FindRepositoryImpl, @inject("ConsoleClient") private readonly consoleClient: ConsoleClient, - @inject("FramePresenter") - private readonly framePresenter: FramePresenter + @inject("ReadyFrameRepository") + private readonly frameRepository: ReadyFrameRepository ) {} async startFind(tabId: number, keyword?: string): Promise { @@ -31,7 +31,11 @@ export default class StartFindUseCase { this.findRepository.setGlobalKeyword(keyword); - const frameIds = await this.framePresenter.getAllFrameIds(tabId); + const frameIds = await this.frameRepository.getFrameIds(tabId); + if (typeof frameIds === "undefined") { + // No frames are ready + return; + } for (const frameId of frameIds) { await this.findClient.clearSelection(tabId, frameId); } -- cgit v1.2.3