XKit-Rewritten icon indicating copy to clipboard operation
XKit-Rewritten copied to clipboard

refactor: Create and use generic portal traversal utility

Open marcustyphoon opened this issue 2 months ago • 1 comments

Description

A little messy, but it works? Maybe helpful for #1931? I dunno. Possibly not worth it otherwise?

This:

  • Places both xkit-injection-request and xkit-injection-response handlers on document.documentElement (yes, this could mean a few extra handlers firing once in a blue moon; one could make xkit-injection-element-response and add the event listener twice to prevent this I guess but I don't think that would save any cycles edit: see comment)
  • Allows injected functions to return DOM elements by passing them from the main world to the isolated world in the same way we pass the target DOM element from the isolated to the main world: event bubbling.
  • Uses this ability to replace the overly-specific injected function in the meatballs menu util with, essentially a version of .closest() that traverses portals.

Testing steps

  • Confirm that meatballs menu items are still added to post meatball menus.

More tests could be run with #677/#1662, I guess.

marcustyphoon avatar Oct 23 '25 08:10 marcustyphoon

Eh, you know, maybe this actually is more elegant. Leave the normal path's listeners where they are so calls don't trigger each other's listeners.

diff --git a/src/main_world/index.js b/src/main_world/index.js
index 329566b6..0e0a5858 100644
--- a/src/main_world/index.js
+++ b/src/main_world/index.js
@@ -19,10 +19,10 @@ document.documentElement.addEventListener('xkit-injection-request', async event
 
     if (result instanceof Element) {
       result.dispatchEvent(
-        new CustomEvent('xkit-injection-response', { detail: JSON.stringify({ id }), bubbles: true })
+        new CustomEvent('xkit-injection-element-response', { detail: JSON.stringify({ id }), bubbles: true })
       );
     } else {
-      document.documentElement.dispatchEvent(
+      target.dispatchEvent(
         new CustomEvent('xkit-injection-response', { detail: JSON.stringify({ id, result }) })
       );
     }
diff --git a/src/utils/inject.js b/src/utils/inject.js
index b0d49079..1591935a 100644
--- a/src/utils/inject.js
+++ b/src/utils/inject.js
@@ -13,20 +13,23 @@ export const inject = (path, args = [], target = document.documentElement) =>
     const requestId = String(Math.random());
     const data = { path: browser.runtime.getURL(path), args, id: requestId };
 
-    const responseHandler = ({ detail, bubbles, target }) => {
+    const responseHandler = ({ detail, type, target }) => {
       const { id, result, exception } = JSON.parse(detail);
       if (id !== requestId) return;
 
       target.removeEventListener('xkit-injection-response', responseHandler);
+      document.documentElement.removeEventListener('xkit-injection-element-response', responseHandler);
+
       if (exception) {
         reject(exception);
-      } else if (bubbles) {
+      } else if (type === 'xkit-injection-element-response') {
         resolve(target);
       } else if (result) {
         resolve(result);
       }
     };
-    document.documentElement.addEventListener('xkit-injection-response', responseHandler);
+    target.addEventListener('xkit-injection-response', responseHandler);
+    document.documentElement.addEventListener('xkit-injection-element-response', responseHandler);
 
     target.dispatchEvent(
       new CustomEvent('xkit-injection-request', { detail: JSON.stringify(data), bubbles: true })

marcustyphoon avatar Oct 23 '25 08:10 marcustyphoon