background.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /* global chrome */
  2. // Constants
  3. const MESSAGE_ID = 'three-devtools';
  4. const MESSAGE_INIT = 'init';
  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. const MESSAGE_REGISTER = 'register';
  11. const MESSAGE_COMMITTED = 'committed';
  12. // Map tab IDs to connections
  13. const connections = new Map();
  14. // Handle extension icon clicks in the toolbar
  15. chrome.action.onClicked.addListener( ( tab ) => {
  16. // Send scroll-to-canvas message to the content script (no UUID = scroll to first canvas)
  17. chrome.tabs.sendMessage( tab.id, {
  18. name: MESSAGE_SCROLL_TO_CANVAS,
  19. tabId: tab.id
  20. } ).catch( () => {
  21. // Ignore error - tab might not have the content script injected
  22. console.log( 'Could not send scroll-to-canvas message to tab', tab.id );
  23. } );
  24. } );
  25. // Listen for connections from the devtools panel
  26. chrome.runtime.onConnect.addListener( port => {
  27. let tabId;
  28. // Messages that should be forwarded to content script
  29. const forwardableMessages = new Set( [
  30. MESSAGE_REQUEST_STATE,
  31. MESSAGE_REQUEST_OBJECT_DETAILS,
  32. MESSAGE_SCROLL_TO_CANVAS,
  33. MESSAGE_HIGHLIGHT_OBJECT,
  34. MESSAGE_UNHIGHLIGHT_OBJECT
  35. ] );
  36. // Listen for messages from the devtools panel
  37. port.onMessage.addListener( message => {
  38. if ( message.name === MESSAGE_INIT ) {
  39. tabId = message.tabId;
  40. connections.set( tabId, port );
  41. } else if ( forwardableMessages.has( message.name ) && tabId ) {
  42. chrome.tabs.sendMessage( tabId, message );
  43. } else if ( tabId === undefined ) {
  44. console.warn( 'Background: Message received from panel before init:', message );
  45. }
  46. } );
  47. // Clean up when devtools is closed
  48. port.onDisconnect.addListener( () => {
  49. if ( tabId ) {
  50. connections.delete( tabId );
  51. }
  52. } );
  53. } );
  54. // Listen for messages from the content script
  55. chrome.runtime.onMessage.addListener( ( message, sender, sendResponse ) => {
  56. if ( message.scheme ) {
  57. chrome.action.setIcon( {
  58. path: {
  59. 128: `icons/128-${message.scheme}.png`
  60. }
  61. } );
  62. }
  63. if ( sender.tab ) {
  64. const tabId = sender.tab.id;
  65. // If three.js is detected, show a badge
  66. if ( message.name === MESSAGE_REGISTER && message.detail && message.detail.revision ) {
  67. const revision = String( message.detail.revision );
  68. const number = revision.replace( /\D+$/, '' );
  69. const isDev = revision.includes( 'dev' );
  70. chrome.action.setBadgeText( { tabId: tabId, text: number } ).catch( () => {
  71. // Ignore error - tab might have been closed
  72. } );
  73. chrome.action.setBadgeTextColor( { tabId: tabId, color: '#ffffff' } ).catch( () => {
  74. // Ignore error - tab might have been closed
  75. } );
  76. chrome.action.setBadgeBackgroundColor( { tabId: tabId, color: isDev ? '#ff0098' : '#049ef4' } ).catch( () => {
  77. // Ignore error - tab might have been closed
  78. } );
  79. }
  80. const port = connections.get( tabId );
  81. if ( port ) {
  82. // Forward the message to the devtools panel
  83. try {
  84. port.postMessage( message );
  85. // Send immediate response to avoid "message channel closed" error
  86. sendResponse( { received: true } );
  87. } catch ( e ) {
  88. console.error( 'Error posting message to devtools:', e );
  89. // If the port is broken, clean up the connection
  90. connections.delete( tabId );
  91. }
  92. }
  93. }
  94. return false; // Return false to indicate synchronous handling
  95. } );
  96. // Listen for page navigation events
  97. chrome.webNavigation.onCommitted.addListener( details => {
  98. const { tabId, frameId } = details;
  99. // Clear badge on navigation, only for top-level navigation
  100. if ( frameId === 0 ) {
  101. chrome.action.setBadgeText( { tabId: tabId, text: '' } ).catch( () => {
  102. // Ignore error - tab might have been closed
  103. } );
  104. }
  105. const port = connections.get( tabId );
  106. if ( port ) {
  107. port.postMessage( {
  108. id: MESSAGE_ID,
  109. name: MESSAGE_COMMITTED,
  110. frameId: frameId
  111. } );
  112. }
  113. } );
  114. // Clear badge when a tab is closed
  115. chrome.tabs.onRemoved.addListener( ( tabId ) => {
  116. chrome.action.setBadgeText( { tabId: tabId, text: '' } ).catch( () => {
  117. // Ignore error - tab is already gone
  118. } );
  119. // Clean up connection if it exists for the closed tab
  120. if ( connections.has( tabId ) ) {
  121. connections.delete( tabId );
  122. }
  123. } );
粤ICP备19079148号