index.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import { CustomEvent, jsonDump, jsonLoad } from './helpers';
  2. export { sendMessage, request, throttle } from 'src/common';
  3. export function postData(destId, data) {
  4. // Firefox issue: data must be stringified to avoid cross-origin problem
  5. const e = new CustomEvent(destId, { detail: jsonDump(data) });
  6. document.dispatchEvent(e);
  7. }
  8. let doInject;
  9. export function inject(code) {
  10. if (!doInject) {
  11. const id = getUniqId('VM-');
  12. const detect = domId => {
  13. const span = document.createElement('span');
  14. span.id = domId;
  15. document.documentElement.appendChild(span);
  16. };
  17. injectViaText(`(${detect.toString()})(${jsonDump(id)})`);
  18. const span = document.querySelector(`#${id}`);
  19. if (span) {
  20. span.parentNode.removeChild(span);
  21. doInject = injectViaText;
  22. } else {
  23. // For Firefox in CSP limited pages
  24. doInject = injectViaBlob;
  25. }
  26. }
  27. doInject(code);
  28. }
  29. function injectViaText(code) {
  30. const script = document.createElement('script');
  31. const doc = document.body || document.documentElement;
  32. script.textContent = code;
  33. doc.appendChild(script);
  34. try {
  35. doc.removeChild(script);
  36. } catch (e) {
  37. // ignore if body is changed and script is detached
  38. }
  39. }
  40. // Firefox does not support script injection by `textCode` in CSP limited pages
  41. // have to inject via blob URL, leading to delayed first injection
  42. function injectViaBlob(code) {
  43. const script = document.createElement('script');
  44. const doc = document.body || document.documentElement;
  45. // https://en.wikipedia.org/wiki/Byte_order_mark
  46. const blob = new Blob(['\ufeff', code], { type: 'text/javascript' });
  47. const url = URL.createObjectURL(blob);
  48. script.src = url;
  49. doc.appendChild(script);
  50. try {
  51. doc.removeChild(script);
  52. } catch (e) {
  53. // ignore if body is changed and script is detached
  54. }
  55. URL.revokeObjectURL(url);
  56. }
  57. export function getUniqId() {
  58. return Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
  59. }
  60. export function bindEvents(srcId, destId, handle) {
  61. document.addEventListener(srcId, e => {
  62. const data = jsonLoad(e.detail);
  63. handle(data);
  64. }, false);
  65. return data => { postData(destId, data); };
  66. }
  67. export function attachFunction(id, cb) {
  68. Object.defineProperty(window, id, {
  69. value(...args) {
  70. cb.apply(this, args);
  71. delete window[id];
  72. },
  73. configurable: true,
  74. });
  75. }