Skip to content

Commit

Permalink
Fix a bug that the interaction service can't distinguish if the side …
Browse files Browse the repository at this point in the history
…panel is opened on the specific window id
  • Loading branch information
Thunnini committed Aug 9, 2024
1 parent 92b6ade commit 3af131a
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 19 deletions.
63 changes: 45 additions & 18 deletions apps/extension/src/stores/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,24 @@ import {
SwapUsageQueries,
} from "@keplr-wallet/stores-internal";
import { setInteractionDataHref } from "../utils";
import { InteractionPingMsg } from "@keplr-wallet/background";

let _sidePanelWindowId: number | undefined;
async function getSidePanelWindowId(): Promise<number | undefined> {
if (_sidePanelWindowId != null) {
return _sidePanelWindowId;
}

const current = await browser.windows.getCurrent();
_sidePanelWindowId = current.id;
return _sidePanelWindowId;
}
// 실행되는 순간 바로 window id를 초기화한다.
// 현재 실행되는 ui의 window id를 알아내야 하는데
// 문제는 extension api에 그런 기능을 찾을수가 없다.
// 대충 유저가 사용하고 있는 window에서 side panel이 열리는게 당연하니
// 일단 이렇게 처리한다.
getSidePanelWindowId();

export class RootStore {
public readonly uiConfigStore: UIConfigStore;
Expand Down Expand Up @@ -128,7 +146,33 @@ export class RootStore {
public readonly analyticsStore: AnalyticsStore;

constructor() {
const router = new ExtensionRouter(ContentScriptEnv.produceEnv);
const router = new ExtensionRouter(ContentScriptEnv.produceEnv, (msg) => {
// background에서 ping을 보낼때
// side panel이라면 window id를 구분해야한다.
// 하지만 이게 기존의 message system이 sender/receiver가 한개씩만 존재한다고 생각하고 만들었기 때문에
// background에서 여러 side panel에 ping을 보낼수는 없다. (보낼수는 있는데 sender에서 반환되는 값은 단순히 가장 먼저 반응한 receiver의 결과일 뿐이다...)
// 이 문제를 최소한의 변화로 해결하기 위해서
// side panel일 경우 ping message를 받았을때 window id를 체크해서 원하는 값이 아니라면 무시하도록 한다.
// XXX: _sidePanelWindowId는 처음에 undefined일 수 있다.
// 근데 그렇다고 이 함수를 promise로 바꾸는건 router 쪽에서 큰 변화가 필요하기 때문에
// 당장은 이 문제는 무시하도록 한다. _sidePanelWindowId의 값이 설정되는건 처음에 매우 빠를 것이고
// background에서 이 ping msg를 보내는 것 자체가 interval로 보내면서 확인하는 용도이기 때문에
// 큰 문제가 되지는 않을 것이다.
if (
msg instanceof InteractionPingMsg &&
!msg.ignoreWindowIdAndForcePing
) {
const url = new URL(window.location.href);
if (url.pathname === "/sidePanel.html") {
if (!_sidePanelWindowId) {
return true;
}
return msg.windowId !== _sidePanelWindowId;
}
}

return false;
});
router.addGuard(ContentScriptGuards.checkMessageIsInternal);

// Initialize the interaction addon service.
Expand Down Expand Up @@ -554,20 +598,3 @@ export class RootStore {
export function createRootStore() {
return new RootStore();
}

let _sidePanelWindowId: number | undefined;
async function getSidePanelWindowId(): Promise<number | undefined> {
if (_sidePanelWindowId != null) {
return _sidePanelWindowId;
}

const current = await browser.windows.getCurrent();
_sidePanelWindowId = current.id;
return _sidePanelWindowId;
}
// 실행되는 순간 바로 window id를 초기화한다.
// 현재 실행되는 ui의 window id를 알아내야 하는데
// 문제는 extension api에 그런 기능을 찾을수가 없다.
// 대충 유저가 사용하고 있는 window에서 side panel이 열리는게 당연하니
// 일단 이렇게 처리한다.
getSidePanelWindowId();
25 changes: 25 additions & 0 deletions packages/background/src/interaction/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,17 @@ export class InteractionService {
})();
let succeeded = false;
try {
// sendMessage는 처음에는 UI에서 background로 통신을 위한 용도 뿐이였기 때문에
// http 처럼 1:1 연결만 가능하고 보내는 쪽에서 결과를 받는 것만 가능했었다...
// background processs는 하나밖에 존재할 수 없기 때문에 지금까지는 문제가 없었지만
// side panel을 위해서 UI가 특정 window id 위에 켜져있는지 알려면
// background에서 ping msg를 UI로 보내는 형식으로 바뀌게 되면서 문제가 되었는데...
// window가 여러개 켜져있고 각각 window에 side panel이 열려있다고 생각해보면
// 어느 window의 side panel에서 handler가 먼저 발생하고 결과를 반환했느냐에 따라서
// 먼저 반환된 결과가 여기서 반환될 뿐이다...
// 이 문제를 해결하기 위해서 UI 쪽에 trick이 들어가 있다.
// extension의 store root.tsx에서 Router를 만드는 쪽을 참고
// 그 부분에 trick이 들어가 있다.
const res = await this.extensionMessageRequesterToUI!.sendMessage(
APP_PORT,
// XXX: popup에서는 위에 로직에서 window id를 -1로 대충 처리 했었다.
Expand Down Expand Up @@ -382,6 +393,9 @@ export class InteractionService {

let succeeded = false;
try {
// 이 경우는 window id를 구분하지 않고 ping을 하기 때문에 window id 별로 구분할 필요가 없다.
// 어차피 아무것도 안켜져있으면 오류가 던져지고
// 하나라도 켜져있으면 무조건 true를 반환하게 되기 때문에 한번만 보내도 된다.
const res = await this.extensionMessageRequesterToUI!.sendMessage(
APP_PORT,
new InteractionPingMsg(0, true)
Expand Down Expand Up @@ -426,6 +440,17 @@ export class InteractionService {
}

try {
// sendMessage는 처음에는 UI에서 background로 통신을 위한 용도 뿐이였기 때문에
// http 처럼 1:1 연결만 가능하고 보내는 쪽에서 결과를 받는 것만 가능했었다...
// background processs는 하나밖에 존재할 수 없기 때문에 지금까지는 문제가 없었지만
// side panel을 위해서 UI가 특정 window id 위에 켜져있는지 알려면
// background에서 ping msg를 UI로 보내는 형식으로 바뀌게 되면서 문제가 되었는데...
// window가 여러개 켜져있고 각각 window에 side panel이 열려있다고 생각해보면
// 어느 window의 side panel에서 handler가 먼저 발생하고 결과를 반환했느냐에 따라서
// 먼저 반환된 결과가 여기서 반환될 뿐이다...
// 이 문제를 해결하기 위해서 UI 쪽에 trick이 들어가 있다.
// extension의 store root.tsx에서 Router를 만드는 쪽을 참고
// 그 부분에 trick이 들어가 있다.
return await this.extensionMessageRequesterToUI.sendMessage(
APP_PORT,
new InteractionPingMsg(tab.windowId, false)
Expand Down
14 changes: 13 additions & 1 deletion packages/router-extension/src/router/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ import {
EnvProducer,
KeplrError,
EthereumProviderRpcError,
Message,
JSONUint8Array,
} from "@keplr-wallet/router";
import { getKeplrExtensionRouterId } from "../utils";

export class ExtensionRouter extends Router {
constructor(envProducer: EnvProducer) {
constructor(
envProducer: EnvProducer,
protected msgIgnoreCheck?: (msg: Message<any>) => boolean
) {
super(envProducer);
}

Expand Down Expand Up @@ -90,6 +95,13 @@ export class ExtensionRouter extends Router {
});
}

if (this.msgIgnoreCheck) {
const msg = this.msgRegistry.parseMessage(JSONUint8Array.unwrap(message));
if (this.msgIgnoreCheck(msg)) {
return;
}
}

return this.onMessageHandler(message, sender);
};

Expand Down

0 comments on commit 3af131a

Please sign in to comment.