Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)
1/*
2 * Copyright (C) 2023-2025 Yomitan Authors
3 * Copyright (C) 2020-2022 Yomichan Authors
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19import {FrameAncestryHandler} from './frame-ancestry-handler.js';
20
21export class FrameOffsetForwarder {
22 /**
23 * @param {import('../comm/cross-frame-api.js').CrossFrameAPI} crossFrameApi
24 */
25 constructor(crossFrameApi) {
26 /** @type {import('../comm/cross-frame-api.js').CrossFrameAPI} */
27 this._crossFrameApi = crossFrameApi;
28 /** @type {FrameAncestryHandler} */
29 this._frameAncestryHandler = new FrameAncestryHandler(crossFrameApi);
30 }
31
32 /**
33 * @returns {void}
34 */
35 prepare() {
36 this._frameAncestryHandler.prepare();
37 this._crossFrameApi.registerHandlers([
38 ['frameOffsetForwarderGetChildFrameRect', this._onMessageGetChildFrameRect.bind(this)],
39 ]);
40 }
41
42 /**
43 * @returns {Promise<?[x: number, y: number]>}
44 */
45 async getOffset() {
46 if (this._frameAncestryHandler.isRootFrame()) {
47 return [0, 0];
48 }
49
50 const {frameId} = this._crossFrameApi;
51 if (frameId === null) { return null; }
52
53 try {
54 const ancestorFrameIds = await this._frameAncestryHandler.getFrameAncestryInfo();
55
56 let childFrameId = frameId;
57 /** @type {Promise<?import('frame-offset-forwarder').ChildFrameRect>[]} */
58 const promises = [];
59 for (const ancestorFrameId of ancestorFrameIds) {
60 promises.push(this._crossFrameApi.invoke(ancestorFrameId, 'frameOffsetForwarderGetChildFrameRect', {frameId: childFrameId}));
61 childFrameId = ancestorFrameId;
62 }
63
64 const results = await Promise.all(promises);
65
66 let x = 0;
67 let y = 0;
68 for (const result of results) {
69 if (result === null) { return null; }
70 x += result.x;
71 y += result.y;
72 }
73 return [x, y];
74 } catch (e) {
75 return null;
76 }
77 }
78
79 // Private
80
81 /** @type {import('cross-frame-api').ApiHandler<'frameOffsetForwarderGetChildFrameRect'>} */
82 _onMessageGetChildFrameRect({frameId}) {
83 const frameElement = this._frameAncestryHandler.getChildFrameElement(frameId);
84 if (frameElement === null) { return null; }
85
86 const {left, top, width, height} = frameElement.getBoundingClientRect();
87 return {x: left, y: top, width, height};
88 }
89}