| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /* global chrome */
- // This script runs in the context of the web page
- // Constants
- const MESSAGE_ID = 'three-devtools';
- const MESSAGE_REQUEST_STATE = 'request-state';
- const MESSAGE_REQUEST_OBJECT_DETAILS = 'request-object-details';
- const MESSAGE_SCROLL_TO_CANVAS = 'scroll-to-canvas';
- // Inject the bridge script into the main document or a target (e.g., iframe)
- function injectBridge( target = document ) {
- const script = document.createElement( 'script' );
- script.src = chrome.runtime.getURL( 'bridge.js' );
- script.onload = function () {
- this.remove();
- };
- ( target.head || target.documentElement ).appendChild( script );
- return script;
- }
- // Inject bridge into all existing iframes
- function injectIntoIframes() {
- document.querySelectorAll( 'iframe' ).forEach( iframe => {
- try {
- if ( iframe.contentDocument ) injectBridge( iframe.contentDocument );
- } catch ( e ) {
- // Ignore cross-origin errors when accessing iframe content
- }
- } );
- }
- // Initial injection
- injectBridge();
- injectIntoIframes();
- // Watch for new iframes being added
- new MutationObserver( mutations => {
- mutations.forEach( mutation => {
- mutation.addedNodes.forEach( node => {
- if ( node.tagName === 'IFRAME' ) {
- node.addEventListener( 'load', () => {
- try {
- if ( node.contentDocument ) injectBridge( node.contentDocument );
- } catch ( e ) {
- // Ignore cross-origin errors when accessing iframe content
- }
- } );
- }
- } );
- } );
- } ).observe( document.documentElement, { childList: true, subtree: true } );
- // Helper to check if extension context is valid
- function isExtensionContextValid() {
- try {
- chrome.runtime.getURL( '' );
- return true;
- } catch ( error ) {
- return false;
- }
- }
- // Unified message handler for window messages
- function handleWindowMessage( event ) {
- // Only accept messages with the correct id
- if ( ! event.data || event.data.id !== MESSAGE_ID ) return;
- // Determine source: 'main' for window, 'iframe' otherwise
- const source = event.source === window ? 'main' : 'iframe';
- if ( ! isExtensionContextValid() ) {
- console.warn( 'Extension context invalidated, cannot send message' );
- return;
- }
- event.data.source = source;
- chrome.runtime.sendMessage( event.data );
- }
- // Listener for messages from the background script (originating from panel)
- function handleBackgroundMessage( message ) {
- const forwardableMessages = new Set( [ MESSAGE_REQUEST_STATE, MESSAGE_REQUEST_OBJECT_DETAILS, MESSAGE_SCROLL_TO_CANVAS ] );
- if ( forwardableMessages.has( message.name ) ) {
- message.id = MESSAGE_ID;
- window.postMessage( message, '*' );
- }
- }
- // Add event listeners
- window.addEventListener( 'message', handleWindowMessage, false );
- chrome.runtime.onMessage.addListener( handleBackgroundMessage );
- // Icon color scheme
- const isLightTheme = window.matchMedia( '(prefers-color-scheme: light)' ).matches;
- chrome.runtime.sendMessage( { scheme: isLightTheme ? 'light' : 'dark' } );
- window.matchMedia( '(prefers-color-scheme: light)' ).onchange = event => {
- chrome.runtime.sendMessage( { scheme: event.matches ? 'light' : 'dark' } );
- };
|