content-script.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /* global chrome */
  2. // This script runs in the context of the web page
  3. // Constants
  4. const MESSAGE_ID = 'three-devtools';
  5. const MESSAGE_REQUEST_STATE = 'request-state';
  6. const MESSAGE_REQUEST_OBJECT_DETAILS = 'request-object-details';
  7. const MESSAGE_SCROLL_TO_CANVAS = 'scroll-to-canvas';
  8. const MESSAGE_HIGHLIGHT_OBJECT = 'highlight-object';
  9. const MESSAGE_UNHIGHLIGHT_OBJECT = 'unhighlight-object';
  10. // Inject the bridge script into the main document or a target (e.g., iframe)
  11. function injectBridge( target = document ) {
  12. const bridgeScript = document.createElement( 'script' );
  13. bridgeScript.src = chrome.runtime.getURL( 'bridge.js' );
  14. bridgeScript.onload = function () {
  15. this.remove();
  16. // Inject highlight.js after bridge.js loads
  17. const highlightScript = document.createElement( 'script' );
  18. highlightScript.src = chrome.runtime.getURL( 'highlight.js' );
  19. highlightScript.onload = function () {
  20. this.remove();
  21. };
  22. ( target.head || target.documentElement ).appendChild( highlightScript );
  23. };
  24. ( target.head || target.documentElement ).appendChild( bridgeScript );
  25. return bridgeScript;
  26. }
  27. // Inject bridge into all existing iframes
  28. function injectIntoIframes() {
  29. document.querySelectorAll( 'iframe' ).forEach( iframe => {
  30. try {
  31. if ( iframe.contentDocument ) injectBridge( iframe.contentDocument );
  32. } catch ( e ) {
  33. // Ignore cross-origin errors when accessing iframe content
  34. }
  35. } );
  36. }
  37. // Initial injection
  38. injectBridge();
  39. injectIntoIframes();
  40. // Watch for new iframes being added
  41. new MutationObserver( mutations => {
  42. mutations.forEach( mutation => {
  43. mutation.addedNodes.forEach( node => {
  44. if ( node.tagName === 'IFRAME' ) {
  45. node.addEventListener( 'load', () => {
  46. try {
  47. if ( node.contentDocument ) injectBridge( node.contentDocument );
  48. } catch ( e ) {
  49. // Ignore cross-origin errors when accessing iframe content
  50. }
  51. } );
  52. }
  53. } );
  54. } );
  55. } ).observe( document.documentElement, { childList: true, subtree: true } );
  56. // Helper to check if extension context is valid
  57. function isExtensionContextValid() {
  58. try {
  59. chrome.runtime.getURL( '' );
  60. return true;
  61. } catch ( error ) {
  62. return false;
  63. }
  64. }
  65. // Unified message handler for window messages
  66. function handleWindowMessage( event ) {
  67. // Only accept messages with the correct id
  68. if ( ! event.data || event.data.id !== MESSAGE_ID ) return;
  69. // Determine source: 'main' for window, 'iframe' otherwise
  70. const source = event.source === window ? 'main' : 'iframe';
  71. if ( ! isExtensionContextValid() ) {
  72. console.warn( 'Extension context invalidated, cannot send message' );
  73. return;
  74. }
  75. event.data.source = source;
  76. chrome.runtime.sendMessage( event.data );
  77. }
  78. // Listener for messages from the background script (originating from panel)
  79. function handleBackgroundMessage( message ) {
  80. const forwardableMessages = new Set( [
  81. MESSAGE_REQUEST_STATE,
  82. MESSAGE_REQUEST_OBJECT_DETAILS,
  83. MESSAGE_SCROLL_TO_CANVAS,
  84. MESSAGE_HIGHLIGHT_OBJECT,
  85. MESSAGE_UNHIGHLIGHT_OBJECT
  86. ] );
  87. if ( forwardableMessages.has( message.name ) ) {
  88. message.id = MESSAGE_ID;
  89. window.postMessage( message, '*' );
  90. }
  91. }
  92. // Add event listeners
  93. window.addEventListener( 'message', handleWindowMessage, false );
  94. chrome.runtime.onMessage.addListener( handleBackgroundMessage );
  95. // Icon color scheme
  96. const isLightTheme = window.matchMedia( '(prefers-color-scheme: light)' ).matches;
  97. chrome.runtime.sendMessage( { scheme: isLightTheme ? 'light' : 'dark' } );
  98. window.matchMedia( '(prefers-color-scheme: light)' ).onchange = event => {
  99. chrome.runtime.sendMessage( { scheme: event.matches ? 'light' : 'dark' } );
  100. };
粤ICP备19079148号