diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/background/Application.ts | 41 | ||||
| -rw-r--r-- | src/background/infrastructures/FindPortListener.ts | 23 | ||||
| -rw-r--r-- | src/background/operators/impls/FindNextOperator.ts | 94 | ||||
| -rw-r--r-- | src/background/operators/impls/FindPrevOperator.ts | 94 | ||||
| -rw-r--r-- | src/background/repositories/FindRepository.ts | 3 | ||||
| -rw-r--r-- | src/background/repositories/ReadyFrameRepository.ts | 22 | ||||
| -rw-r--r-- | src/background/usecases/StartFindUseCase.ts | 11 | ||||
| -rw-r--r-- | src/content/Application.ts | 16 | 
8 files changed, 175 insertions, 129 deletions
diff --git a/src/background/Application.ts b/src/background/Application.ts index 87865e6..c7bcc42 100644 --- a/src/background/Application.ts +++ b/src/background/Application.ts @@ -1,5 +1,6 @@  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"; @@ -20,6 +21,11 @@ export default class Application {      private readonly frameRepository: ReadyFrameRepository    ) {} +  private readonly findPortListener = new FindPortListener( +    this.onFindPortConnect.bind(this), +    this.onFindPortDisconnect.bind(this) +  ); +    run() {      this.settingController.reload(); @@ -35,24 +41,31 @@ 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(() => {        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/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 2aed9fb..99f1759 100644 --- a/src/background/operators/impls/FindNextOperator.ts +++ b/src/background/operators/impls/FindNextOperator.ts @@ -21,72 +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.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, -            keyword, -          }); +          await this.findRepository.setLocalState(tabId, { frameId, keyword });            await this.consoleClient.showInfo(tabId, "Pattern found: " + keyword);            return;          } diff --git a/src/background/operators/impls/FindPrevOperator.ts b/src/background/operators/impls/FindPrevOperator.ts index 3c4411d..f8506b9 100644 --- a/src/background/operators/impls/FindPrevOperator.ts +++ b/src/background/operators/impls/FindPrevOperator.ts @@ -21,71 +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.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 = 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/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 index 725f604..72ae5a4 100644 --- a/src/background/repositories/ReadyFrameRepository.ts +++ b/src/background/repositories/ReadyFrameRepository.ts @@ -7,7 +7,9 @@ type State = { [tabId: number]: number[] };  export default interface ReadyFrameRepository {    clearFrameIds(tabId: number): Promise<void>; -  addFrameId(tabId: number, fraemId: number): Promise<void>; +  addFrameId(tabId: number, frameId: number): Promise<void>; + +  removeFrameId(tabId: number, frameId: number): Promise<void>;    getFrameIds(tabId: number): Promise<number[] | undefined>;  } @@ -29,12 +31,26 @@ export class ReadyFrameRepositoryImpl implements ReadyFrameRepository {      return Promise.resolve();    } -  addFrameId(tabId: number, fraemId: number): Promise<void> { +  addFrameId(tabId: number, frameId: number): Promise<void> {      let state: State | undefined = this.cache.get(REPOSITORY_KEY);      if (typeof state === "undefined") {        state = {};      } -    state[tabId] = (state[tabId] || []).concat(fraemId); +    state[tabId] = (state[tabId] || []).concat(frameId); +    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(); +    } +    state[tabId] = ids.filter((id) => id != frameId);      this.cache.set(REPOSITORY_KEY, state);      return Promise.resolve();    } diff --git a/src/background/usecases/StartFindUseCase.ts b/src/background/usecases/StartFindUseCase.ts index a62462f..6aad962 100644 --- a/src/background/usecases/StartFindUseCase.ts +++ b/src/background/usecases/StartFindUseCase.ts @@ -40,16 +40,11 @@ export default class StartFindUseCase {        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); diff --git a/src/content/Application.ts b/src/content/Application.ts index a12c3c6..6b881fe 100644 --- a/src/content/Application.ts +++ b/src/content/Application.ts @@ -41,7 +41,21 @@ export default class Application {      if (window.self === window.top) {        this.routeMasterComponents();      } -    return this.routeCommonComponents(); +    this.routeCommonComponents(); +    // Make sure the background script sends a message to the content script by +    // establishing a connection.  If the background script tries to send a +    // message to a frame on which cannot run the content script, it fails with +    // a message "Could not establish connection." +    // +    // The port is never used, and the messages are delivered via +    // `browser.tabs.sendMessage` API because sending a message via port cannot +    // receive returned value. +    // +    // /* on background script */ +    // port.sendMessage({ type: "do something" });  <- returns void +    // +    browser.runtime.connect(undefined, { name: "vimvixen-find" }); +    return Promise.resolve();    }    private routeMasterComponents() {  | 
