The bridge communication system provides efficient message passing between different DevTools contexts (extension background, content script, injected script) with automatic message batching and event-driven architecture.
Main communication bridge that extends EventEmitter for bidirectional message passing with automatic batching.
/**
* Bridge for communication between DevTools contexts
* Automatically batches messages for improved performance
*/
class Bridge extends EventEmitter {
/**
* Create a new bridge instance
* @param wall - Low-level communication wall implementation
*/
constructor(wall: Wall);
/**
* Send a message through the bridge with optional transferable objects
* Messages are automatically batched for performance
* @param event - Event name/type
* @param payload - Data to send
* @param transferable - Optional transferable objects for performance
*/
send(event: string, payload: any, transferable?: Array<any>): void;
}Usage Examples:
import Bridge from "react-devtools-experimental/src/bridge";
// Create wall implementation for your context
const wall = {
listen: (callback) => {
// Set up message listener (e.g., chrome.runtime.onMessage)
chrome.runtime.onMessage.addListener(callback);
},
send: (event, payload, transferable) => {
// Send message (e.g., chrome.runtime.sendMessage)
chrome.runtime.sendMessage({ event, payload });
}
};
// Create bridge
const bridge = new Bridge(wall);
// Listen for events
bridge.on('element-selected', (data) => {
console.log('Element selected:', data);
});
// Send events
bridge.send('inspect-element', { id: 123, rendererID: 1 });Low-level communication abstraction that bridges must implement for their specific context.
interface Wall {
/**
* Set up listener for incoming messages
* @param fn - Callback function to handle messages
*/
listen: (fn: Function) => void;
/**
* Send message through the wall
* @param event - Event name/type
* @param payload - Data to send
* @param transferable - Optional transferable objects
*/
send: (event: string, payload: any, transferable?: Array<any>) => void;
}The bridge automatically batches messages within a time window to reduce communication overhead.
/**
* Message batching configuration
*/
const BATCH_DURATION = 100; // milliseconds
/**
* Internal message structure
*/
interface Message {
event: string;
payload: any;
}Usage Examples:
// Messages sent within 100ms are automatically batched
bridge.send('tree-operation', { type: 'ADD', id: 1 });
bridge.send('tree-operation', { type: 'ADD', id: 2 });
bridge.send('tree-operation', { type: 'ADD', id: 3 });
// All three messages sent as single batched message
// Batching can be bypassed for urgent messages by waiting
bridge.send('urgent-message', { critical: true });
await new Promise(resolve => setTimeout(resolve, 101));
bridge.send('another-message', { data: 'test' });Implementation for Chrome extension communication between background, content script, and injected contexts.
// Background script to content script
const backgroundWall = {
listen: (callback) => {
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
callback(message);
});
},
send: (event, payload) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, { event, payload });
});
}
};
// Content script to injected script
const contentWall = {
listen: (callback) => {
window.addEventListener('message', (event) => {
if (event.source === window && event.data.source === 'react-devtools') {
callback(event.data);
}
});
},
send: (event, payload) => {
window.postMessage({
source: 'react-devtools',
event,
payload
}, '*');
}
};Implementation for standalone DevTools using WebSocket communication.
const websocketWall = {
listen: (callback) => {
this.socket.on('message', (message) => {
const data = JSON.parse(message);
callback(data);
});
},
send: (event, payload) => {
this.socket.send(JSON.stringify({ event, payload }));
}
};Implementation for development environments with hot reloading.
const devServerWall = {
listen: (callback) => {
// Set up Server-Sent Events or WebSocket
this.eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
callback(data);
};
},
send: (event, payload) => {
fetch('/devtools-message', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event, payload })
});
}
};Standard events used throughout the DevTools communication protocol.
// Element inspection events
'inspect-element' // Request element inspection
'element-inspected' // Element inspection result
'select-element' // Select element in tree
'element-selected' // Element selection result
// Tree operations
'tree-operations' // Batch of tree changes
'tree-updated' // Tree update notification
// Profiling events
'start-profiling' // Start performance profiling
'stop-profiling' // Stop performance profiling
'profiling-data' // Profiling results
// State modification events
'override-props' // Modify component props
'override-state' // Modify component state
'override-context' // Modify context values
'override-hooks' // Modify hook state
// DOM inspection events
'start-dom-inspection' // Enter DOM inspection mode
'stop-dom-inspection' // Exit DOM inspection mode
'highlight-element' // Highlight element in DOM
'unhighlight-element' // Remove element highlighting
// Bridge lifecycle events
'bridge-ready' // Bridge initialization complete
'bridge-shutdown' // Bridge shutting downUsage Examples:
// Set up comprehensive event handling
const bridge = new Bridge(wall);
// Element inspection
bridge.on('inspect-element', ({ id, rendererID }) => {
const elementData = inspectElement(id, rendererID);
bridge.send('element-inspected', elementData);
});
// Profiling control
bridge.on('start-profiling', () => {
startProfilingSession();
bridge.send('profiling-started', { timestamp: Date.now() });
});
bridge.on('stop-profiling', () => {
const profilingData = stopProfilingSession();
bridge.send('profiling-data', profilingData);
});
// State modification
bridge.on('override-props', ({ id, path, value, rendererID }) => {
modifyComponentProps(id, path, value, rendererID);
bridge.send('props-overridden', { id, path, value });
});Bridge communication includes error handling and recovery mechanisms.
// Error events
'bridge-error' // Communication error occurred
'message-failed' // Specific message failed to send
'connection-lost' // Connection to other context lost
'connection-restored' // Connection restored
// Recovery events
'retry-message' // Retry failed message
'reconnect-bridge' // Attempt to reconnect
'bridge-reset' // Reset bridge stateUsage Examples:
// Handle bridge errors
bridge.on('bridge-error', (error) => {
console.error('Bridge communication error:', error);
// Attempt recovery
setTimeout(() => {
bridge.send('reconnect-bridge', {});
}, 1000);
});
// Handle connection loss
bridge.on('connection-lost', () => {
console.warn('DevTools connection lost');
// Show disconnected state in UI
showDisconnectedState();
});
bridge.on('connection-restored', () => {
console.log('DevTools connection restored');
// Restore normal operation
hideDisconnectedState();
// Resync data
bridge.send('request-full-sync', {});
});The bridge uses intelligent batching to optimize performance:
// Messages are batched within 100ms windows
// Multiple tree operations become single batch
bridge.send('tree-operation', { type: 'ADD', id: 1, parentID: 0 });
bridge.send('tree-operation', { type: 'ADD', id: 2, parentID: 1 });
bridge.send('tree-operation', { type: 'UPDATE', id: 1, name: 'NewName' });
// Results in single batched message:
// {
// event: 'batched-operations',
// payload: [
// { type: 'ADD', id: 1, parentID: 0 },
// { type: 'ADD', id: 2, parentID: 1 },
// { type: 'UPDATE', id: 1, name: 'NewName' }
// ]
// }Use transferable objects for large data to improve performance:
// Send large profiling data efficiently
const profilingBuffer = new ArrayBuffer(1024 * 1024);
bridge.send('profiling-data', profilingBuffer, [profilingBuffer]);
// Send element tree data
const treeBuffer = serializeElementTree(elementTree);
bridge.send('tree-data', treeBuffer, [treeBuffer]);The bridge includes cleanup mechanisms to prevent memory leaks:
// Clean up bridge resources
bridge.on('shutdown', () => {
// Clear message queues
bridge._messageQueue = [];
// Clear timeouts
if (bridge._timeoutID) {
clearTimeout(bridge._timeoutID);
}
// Remove all listeners
bridge.removeAllListeners();
});