diff options
Diffstat (limited to 'src/background')
| -rw-r--r-- | src/background/Application.ts | 32 | ||||
| -rw-r--r-- | src/background/di.ts | 6 | ||||
| -rw-r--r-- | src/background/infrastructures/FindPortListener.ts | 23 | ||||
| -rw-r--r-- | src/background/operators/impls/FindNextOperator.ts | 93 | ||||
| -rw-r--r-- | src/background/operators/impls/FindOperatorFactoryChain.ts | 10 | ||||
| -rw-r--r-- | src/background/operators/impls/FindPrevOperator.ts | 94 | ||||
| -rw-r--r-- | src/background/presenters/FramePresenter.ts | 12 | ||||
| -rw-r--r-- | src/background/repositories/FindRepository.ts | 3 | ||||
| -rw-r--r-- | src/background/repositories/ReadyFrameRepository.ts | 70 | ||||
| -rw-r--r-- | src/background/usecases/StartFindUseCase.ts | 23 | 
10 files changed, 236 insertions, 130 deletions
| diff --git a/src/background/Application.ts b/src/background/Application.ts index 2006965..b439d19 100644 --- a/src/background/Application.ts +++ b/src/background/Application.ts @@ -1,9 +1,11 @@  import { injectable, inject } from "tsyringe";  import ContentMessageListener from "./infrastructures/ContentMessageListener"; +import FindPortListener from "./infrastructures/FindPortListener";  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,9 +16,16 @@ 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    ) {} +  private readonly findPortListener = new FindPortListener( +    this.onFindPortConnect.bind(this), +    this.onFindPortDisconnect.bind(this) +  ); +    run() {      this.settingController.reload(); @@ -36,5 +45,26 @@ export default class Application {      this.syncSettingRepository.onChange(() => {        this.settingController.reload();      }); +    this.findPortListener.run(); +  } + +  private onFindPortConnect(port: browser.runtime.Port) { +    const tabId = port.sender?.tab?.id; +    const frameId = port.sender?.frameId; +    if (typeof tabId === "undefined" || typeof frameId === "undefined") { +      return; +    } + +    this.frameRepository.addFrameId(tabId, frameId); +  } + +  private onFindPortDisconnect(port: browser.runtime.Port) { +    const tabId = port.sender?.tab?.id; +    const frameId = port.sender?.frameId; +    if (typeof tabId === "undefined" || typeof frameId === "undefined") { +      return; +    } + +    this.frameRepository.removeFrameId(tabId, frameId);    }  } 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/infrastructures/FindPortListener.ts b/src/background/infrastructures/FindPortListener.ts new file mode 100644 index 0000000..ca82439 --- /dev/null +++ b/src/background/infrastructures/FindPortListener.ts @@ -0,0 +1,23 @@ +import { injectable } from "tsyringe"; + +type OnConnectFunc = (port: browser.runtime.Port) => void; +type OnDisconnectFunc = (port: browser.runtime.Port) => void; + +@injectable() +export default class FindPortListener { +  constructor( +    private readonly onConnect: OnConnectFunc, +    private readonly onDisconnect: OnDisconnectFunc +  ) {} + +  run(): void { +    browser.runtime.onConnect.addListener((port) => { +      if (port.name !== "vimvixen-find") { +        return; +      } + +      port.onDisconnect.addListener(this.onDisconnect); +      this.onConnect(port); +    }); +  } +} diff --git a/src/background/operators/impls/FindNextOperator.ts b/src/background/operators/impls/FindNextOperator.ts index 241f71d..99f1759 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<void> { @@ -21,67 +21,64 @@ export default class FindNextOperator implements Operator {        return;      } +    const frameIds = await this.frameRepository.getFrameIds(tabId); +    if (typeof frameIds === "undefined") { +      // No frames are ready +      return; +    } +      const state = await this.findRepository.getLocalState(tabId);      if (state) { -      // Start to find the keyword from the current frame which last found on, -      // and concat it to end of frame ids to perform a wrap-search -      // -      //                ,- keyword should be in this frame -      //                | -      // [100, 101, 0, 100] -      //   | -      //   `- continue from frame id 100 -      // -      const targetFrameIds = state.frameIds -        .slice(state.framePos) -        .concat( -          state.frameIds.slice(0, state.framePos), -          state.frameIds[state.framePos] -        ); +      const framePos = frameIds.indexOf(state.frameId); +      if (framePos !== -1) { +        // Start to find the keyword from the current frame which last found on, +        // and concat it to end of frame ids to perform a wrap-search +        // +        //                ,- keyword should be in this frame +        //                | +        // [100, 101, 0, 100] +        //   | +        //   `- continue from frame id 100 +        // +        const targetFrameIds = frameIds +          .slice(framePos) +          .concat(frameIds.slice(0, framePos), frameIds[framePos]); -      for (let i = 0; i < targetFrameIds.length; ++i) { -        const found = await this.findClient.findNext( +        for (const frameId of targetFrameIds) { +          const found = await this.findClient.findNext( +            tabId, +            frameId, +            state.keyword +          ); +          if (found) { +            this.findRepository.setLocalState(tabId, { +              keyword: state.keyword, +              frameId, +            }); +            return; +          } +          this.findClient.clearSelection(tabId, frameId); +        } + +        // The keyword is gone. +        this.consoleClient.showError(            tabId, -          targetFrameIds[i], -          state.keyword +          "Pattern not found: " + state.keyword          ); -        if (found) { -          this.findRepository.setLocalState(tabId, { -            ...state, -            framePos: (i + state.framePos) % state.frameIds.length, // save current frame position or first -          }); -          return; -        } -        this.findClient.clearSelection(tabId, targetFrameIds[i]); +        return;        } - -      // The keyword is gone. -      this.consoleClient.showError( -        tabId, -        "Pattern not found: " + state.keyword -      ); -      return;      }      const keyword = await this.findRepository.getGlobalKeyword();      if (keyword) { -      const frameIds = await this.framePresenter.getAllFrameIds(tabId);        for (const frameId of frameIds) {          await this.findClient.clearSelection(tabId, frameId);        } -      for (let framePos = 0; framePos < frameIds.length; ++framePos) { -        const found = await this.findClient.findNext( -          tabId, -          frameIds[framePos], -          keyword -        ); +      for (const frameId of frameIds) { +        const found = await this.findClient.findNext(tabId, frameId, keyword);          if (found) { -          await this.findRepository.setLocalState(tabId, { -            frameIds, -            framePos, -            keyword, -          }); +          await this.findRepository.setLocalState(tabId, { frameId, keyword });            await this.consoleClient.showInfo(tabId, "Pattern found: " + keyword);            return;          } 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..f8506b9 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<void> { @@ -21,67 +21,65 @@ export default class FindPrevOperator implements Operator {        return;      } +    let frameIds = await this.frameRepository.getFrameIds(tabId); +    if (typeof frameIds === "undefined") { +      // No frames are ready +      return; +    } +    frameIds = frameIds.slice(0).reverse(); +      const state = await this.findRepository.getLocalState(tabId);      if (state) { -      // Start to find the keyword from the current frame which last found on, -      // and concat it to end of frame ids to perform a wrap-search -      // -      //                ,- keyword should be in this frame -      //                | -      // [100, 101, 0, 100] -      //   | -      //   `- continue from frame id 100 -      // -      const targetFrameIds = state.frameIds -        .slice(state.framePos) -        .concat( -          state.frameIds.slice(0, state.framePos), -          state.frameIds[state.framePos] -        ); +      const framePos = frameIds.indexOf(state.frameId); +      if (framePos !== -1) { +        // Start to find the keyword from the current frame which last found on, +        // and concat it to end of frame ids to perform a wrap-search +        // +        //                ,- keyword should be in this frame +        //                | +        // [100, 101, 0, 100] +        //   | +        //   `- continue from frame id 100 +        // +        const targetFrameIds = frameIds +          .slice(framePos) +          .concat(frameIds.slice(0, framePos), frameIds[framePos]); -      for (let i = targetFrameIds.length - 1; i >= 0; --i) { -        const found = await this.findClient.findPrev( +        for (const frameId of targetFrameIds) { +          const found = await this.findClient.findPrev( +            tabId, +            frameId, +            state.keyword +          ); +          if (found) { +            this.findRepository.setLocalState(tabId, { +              keyword: state.keyword, +              frameId, +            }); +            return; +          } +          this.findClient.clearSelection(tabId, frameId); +        } + +        // The keyword is gone. +        this.consoleClient.showError(            tabId, -          targetFrameIds[i], -          state.keyword +          "Pattern not found: " + state.keyword          ); -        if (found) { -          this.findRepository.setLocalState(tabId, { -            ...state, -            framePos: (i + state.framePos) % state.frameIds.length, // save current frame position or first -          }); -          return; -        } -        this.findClient.clearSelection(tabId, targetFrameIds[i]); +        return;        } - -      // The keyword is gone. -      this.consoleClient.showError( -        tabId, -        "Pattern not found: " + state.keyword -      ); -      return;      }      const keyword = await this.findRepository.getGlobalKeyword();      if (keyword) { -      const frameIds = await this.framePresenter.getAllFrameIds(tabId);        for (const frameId of frameIds) {          await this.findClient.clearSelection(tabId, frameId);        } -      for (let framePos = frameIds.length - 1; framePos >= 0; --framePos) { -        const found = await this.findClient.findPrev( -          tabId, -          frameIds[framePos], -          keyword -        ); +      for (const frameId of frameIds) { +        const found = await this.findClient.findPrev(tabId, frameId, keyword);          if (found) { -          await this.findRepository.setLocalState(tabId, { -            frameIds, -            framePos, -            keyword, -          }); +          await this.findRepository.setLocalState(tabId, { frameId, keyword });            await this.consoleClient.showInfo(tabId, "Pattern found: " + keyword);            return;          } 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<Array<number>>; -} - -export class FramePresenterImpl implements FramePresenter { -  async getAllFrameIds(tabId: number): Promise<Array<number>> { -    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/FindRepository.ts b/src/background/repositories/FindRepository.ts index 46ee390..3492759 100644 --- a/src/background/repositories/FindRepository.ts +++ b/src/background/repositories/FindRepository.ts @@ -6,8 +6,7 @@ const FIND_LOCAL_KEYWORD_KEY = "find-local-keyword";  export type FindState = {    keyword: string; -  framePos: number; -  frameIds: number[]; +  frameId: number;  };  export default interface FindRepository { diff --git a/src/background/repositories/ReadyFrameRepository.ts b/src/background/repositories/ReadyFrameRepository.ts new file mode 100644 index 0000000..c993858 --- /dev/null +++ b/src/background/repositories/ReadyFrameRepository.ts @@ -0,0 +1,70 @@ +import MemoryStorage from "../infrastructures/MemoryStorage"; + +const REPOSITORY_KEY = "readyFrameRepository"; + +type State = { [tabId: number]: { [frameId: number]: number } }; + +export default interface ReadyFrameRepository { +  addFrameId(tabId: number, frameId: number): Promise<void>; + +  removeFrameId(tabId: number, frameId: number): Promise<void>; + +  getFrameIds(tabId: number): Promise<number[] | undefined>; +} + +export class ReadyFrameRepositoryImpl implements ReadyFrameRepository { +  private cache: MemoryStorage; + +  constructor() { +    this.cache = new MemoryStorage(); +  } + +  addFrameId(tabId: number, frameId: number): Promise<void> { +    let state: State | undefined = this.cache.get(REPOSITORY_KEY); +    if (typeof state === "undefined") { +      state = {}; +    } +    const tab = state[tabId] || {}; +    tab[frameId] = (tab[frameId] || 0) + 1; +    state[tabId] = tab; +    this.cache.set(REPOSITORY_KEY, state); +    return Promise.resolve(); +  } + +  removeFrameId(tabId: number, frameId: number): Promise<void> { +    const state: State | undefined = this.cache.get(REPOSITORY_KEY); +    if (typeof state === "undefined") { +      return Promise.resolve(); +    } +    const ids = state[tabId]; +    if (typeof ids === "undefined") { +      return Promise.resolve(); +    } +    const tab = state[tabId] || {}; +    tab[frameId] = (tab[frameId] || 0) - 1; +    if (tab[frameId] == 0) { +      delete tab[frameId]; +    } +    if (Object.keys(tab).length === 0) { +      delete state[tabId]; +    } + +    this.cache.set(REPOSITORY_KEY, state); +    return Promise.resolve(); +  } + +  getFrameIds(tabId: number): Promise<number[] | undefined> { +    const state: State | undefined = this.cache.get(REPOSITORY_KEY); +    if (typeof state === "undefined") { +      return Promise.resolve(undefined); +    } +    const tab = state[tabId]; +    if (typeof tab === "undefined") { +      return Promise.resolve(undefined); +    } +    const frameIds = Object.keys(tab) +      .map((v) => Number(v)) +      .sort(); +    return Promise.resolve(frameIds); +  } +} diff --git a/src/background/usecases/StartFindUseCase.ts b/src/background/usecases/StartFindUseCase.ts index 066d930..6aad962 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<void> { @@ -31,21 +31,20 @@ 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);      } -    for (let framePos = 0; framePos < frameIds.length; ++framePos) { -      const found = await this.findClient.findNext( -        tabId, -        frameIds[framePos], -        keyword -      ); +    for (const frameId of frameIds) { +      const found = await this.findClient.findNext(tabId, frameId, keyword);        if (found) {          await this.findRepository.setLocalState(tabId, { -          frameIds, -          framePos, +          frameId,            keyword,          });          await this.consoleClient.showInfo(tabId, "Pattern found: " + keyword); | 
