content-script.js 2.9 KB

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