aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/background/Application.ts41
-rw-r--r--src/background/infrastructures/FindPortListener.ts23
-rw-r--r--src/background/operators/impls/FindNextOperator.ts94
-rw-r--r--src/background/operators/impls/FindPrevOperator.ts94
-rw-r--r--src/background/repositories/FindRepository.ts3
-rw-r--r--src/background/repositories/ReadyFrameRepository.ts22
-rw-r--r--src/background/usecases/StartFindUseCase.ts11
-rw-r--r--src/content/Application.ts16
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() {