gm-api-content.js 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import bridge, { addBackgroundHandlers, addHandlers } from './bridge';
  2. import { decodeResource, elemByTag, makeElem, nextTask, sendCmd } from './util';
  3. const menus = createNullObj();
  4. const HEAD_TAGS = ['script', 'style', 'link', 'meta'];
  5. const { toLowerCase } = '';
  6. let setPopupThrottle;
  7. let isPopupShown;
  8. addBackgroundHandlers({
  9. PopupShown(state) {
  10. isPopupShown = state;
  11. sendSetPopup();
  12. },
  13. }, true);
  14. addHandlers({
  15. /** @this {Node} */
  16. AddElement({ tag, attrs, cbId }, realm) {
  17. let el;
  18. let res;
  19. try {
  20. const parent = this
  21. || HEAD_TAGS::includes(`${tag}`::toLowerCase()) && elemByTag('head')
  22. || elemByTag('body')
  23. || elemByTag('*');
  24. el = makeElem(tag, attrs);
  25. parent::appendChild(el);
  26. } catch (e) {
  27. // A page-mode userscript can't catch DOM errors in a content script so we pass it explicitly
  28. // TODO: maybe move try/catch to bridge.onHandle and use bridge.sendSync in all web commands
  29. res = [`${e}`, e.stack];
  30. }
  31. bridge.post('Callback', { id: cbId, data: res }, realm, el);
  32. },
  33. GetResource({ id, isBlob, key, raw }) {
  34. if (!raw) raw = bridge.cache[bridge.pathMaps[id]?.[key] || key];
  35. return raw ? decodeResource(raw, isBlob) : true;
  36. },
  37. RegisterMenu({ id, cap }) {
  38. if (IS_TOP) {
  39. ensureNestedProp(menus, id, cap, 1);
  40. sendSetPopup(true);
  41. }
  42. },
  43. UnregisterMenu({ id, cap }) {
  44. if (IS_TOP) {
  45. delete menus[id]?.[cap];
  46. sendSetPopup(true);
  47. }
  48. },
  49. });
  50. export async function sendSetPopup(isDelayed) {
  51. if (isPopupShown) {
  52. if (isDelayed) {
  53. if (setPopupThrottle) return;
  54. // Preventing flicker in popup when scripts re-register menus
  55. setPopupThrottle = nextTask;
  56. await setPopupThrottle;
  57. setPopupThrottle = null;
  58. }
  59. sendCmd('SetPopup', safePickInto({ menus }, bridge, [IDS, INJECT_INTO]));
  60. }
  61. }