# How law-chain-hot/websocket-devtools Handles Page Refreshes and Manages WebSocket Connections

> Discover how law-chain-hot/websocket-devtools handles page refreshes and manages WebSocket connections. Learn about its seamless reconnection and monitoring abilities.

- Repository: [Brian 阿布/websocket-devtools](https://github.com/law-chain-hot/websocket-devtools)
- Tags: internals
- Published: 2026-03-05

---

**The extension uses a Chrome background service worker to detect navigation events, clears all WebSocket connection records on full page reloads while preserving them during hash or query string changes, and re-injects a proxied WebSocket constructor into the refreshed page to maintain continuous monitoring.**

The `law-chain-hot/websocket-devtools` repository provides a Chrome DevTools extension that intercepts WebSocket traffic by injecting a proxy into every monitored page. Understanding its behavior during page refreshes requires examining how the background worker coordinates with content scripts to manage connection state and lifecycle events.

## Detecting Page Refreshes via the Background Service Worker

The extension’s background script ([`src/background/background.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/background/background.js)) listens for browser navigation events to determine when a user reloads a page or navigates to a new URL.

### Identifying Real Navigation Events

The script registers a listener on `chrome.tabs.onUpdated` and filters for events where `changeInfo.status === "loading"` **and** a `changeInfo.url` is present. It extracts the **base URL**—defined as the combination of protocol, host, and pathname—from the new location and compares it against the previously stored base URL for the same `tabId`.

If the base URL differs from the previous value, the extension treats the event as a **real navigation** or full page refresh. This distinction prevents the extension from clearing state during minor URL updates.

### Clearing Connection Records on Refresh

When a real navigation is detected, the background script performs two critical actions:

- It filters the global `websocketData.connections` array, removing all WebSocket connection records associated with that specific `tabId`.
- It broadcasts a `page-refresh` message to every DevTools panel attached to the tab, including a count of the removed connections so the UI can reset its state accordingly.

## Preserving WebSocket Connections During URL Updates

If the base URL remains identical but only the query string or hash fragment changes, the extension classifies this as a **URL update** rather than a navigation event. In this scenario:

- Existing connection records in `websocketData.connections` are **preserved**.
- The background script sends a `url-update` message to the DevTools panel containing both the `previousUrl` and `newUrl`, allowing the UI to update its display without discarding active connection histories.

## Re-injecting the WebSocket Proxy After Page Reload

When a page refreshes, the content script ([`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js)) executes again and immediately replaces the native `window.WebSocket` constructor with a custom `ProxiedWebSocket` class.

### Connection Initialization and Unique Identification

Each new WebSocket instance created after a refresh receives a unique identifier generated by the `generateConnectionId()` function. The proxy maintains a per-connection record in a JavaScript `Map` named `connections`, tracking metadata and message history for that specific socket.

### Intercepting Messages and Events

The `ProxiedWebSocket` implementation wraps the native WebSocket to provide comprehensive monitoring:

- **Lifecycle events**: On construction, it immediately emits a `connection` event via `sendEvent`. It registers listeners for native `open`, `close`, and `error` events to update the `connectionInfo.status` field in real time.
- **Incoming traffic**: A capturing `message` listener (`ourMessageListener`) intercepts all incoming data, optionally blocks messages, decodes binary payloads, and forwards the information to the DevTools panel.
- **Outgoing traffic**: The `ws.send` method is wrapped to log outgoing messages, apply blocking rules, and emit corresponding events before passing data to the native socket.

### Developer Control Interface

Every proxied WebSocket instance exposes a `_proxyControl` object that provides programmatic access to the interception layer:

```javascript
// Access blocked messages for a specific WebSocket instance
const blocked = ws._proxyControl.getBlockedMessages();
console.log('Blocked queue:', blocked);

// Clear the blocked message history
ws._proxyControl.clearBlockedMessages();

```

## Coordinating State Between Background and DevTools Panels

Communication between the injected proxy and the DevTools UI flows through multiple message channels managed by the background worker.

### Event Forwarding and Keep-Alive

The injected script transmits events to the content script using `window.postMessage`, which the background script receives as type `websocket-event` or `websocket-event-batch`. The background script maintains a `devtoolsPorts` map to route these events to the correct DevTools panel via the `forwardToDevTools` function.

To prevent message channel timeouts during long debugging sessions, the background script implements a **keep-alive** timer that periodically pings connected panels, ensuring the communication port remains active even during idle periods.

### Refresh Behavior Summary

| Situation | Connection State | Message to DevTools |
|-----------|------------------|---------------------|
| Full page reload or navigation to different base URL | All tab connections **cleared** from `websocketData.connections` | `page-refresh` (includes removed count) |
| Hash or query string change only | Connections **preserved** | `url-update` (with previous and new URLs) |
| Same-page reload via cache | Connections **cleared** (base URL change detected) | `page-refresh` |

## Handling Refresh Events in DevTools Panels

Developers building extensions on top of this tool can listen for refresh notifications to synchronize UI state:

```javascript
chrome.runtime.onConnect.addListener(port => {
  if (port.name !== 'devtools') return;

  port.onMessage.addListener(msg => {
    if (msg.type === 'page-refresh') {
      console.log('Page refreshed, resetting connection list');
      // Clear UI state and discard stale connections
    }
    if (msg.type === 'url-update') {
      console.log(`URL updated: ${msg.data.previousUrl} → ${msg.data.newUrl}`);
    }
  });
});

```

## Summary

- **Page refresh detection** relies on `chrome.tabs.onUpdated` monitoring base URL changes in [`src/background/background.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/background/background.js).
- **Connection cleanup** occurs immediately on full refreshes, removing all entries from the global `websocketData.connections` array for the affected tab.
- **State preservation** applies only to hash or query changes, triggering a `url-update` event instead of clearing data.
- **Proxy reinjection** happens via [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js) redefining `window.WebSocket` with `ProxiedWebSocket`, generating fresh connection IDs via `generateConnectionId()`.
- **Message interception** uses wrapped `send` methods and capturing listeners, with control exposed through the `_proxyControl` object.
- **Background coordination** forwards events through `forwardToDevTools` and maintains channels using a keep-alive mechanism.

## Frequently Asked Questions

### Does WebSocket DevTools lose connection history on a hard refresh?

Yes. When a full page reload occurs or the user navigates to a different base URL, the extension removes all WebSocket connection records associated with that tab from the `websocketData.connections` array and emits a `page-refresh` event to clear the DevTools panel. Connection history is only preserved if the URL change is limited to the hash or query string.

### How does the extension distinguish between a page reload and a hash change?

The background script compares the **base URL** (protocol + host + pathname) of the new location against the previous value stored for the `tabId`. If the base URL matches and only the hash or query differs, it treats the change as a URL update; otherwise, it triggers a full refresh cleanup.

### Can I access the proxied WebSocket control object from the browser console?

Yes. Every WebSocket instance created while the extension is active exposes a `_proxyControl` object that allows you to retrieve blocked messages, clear the blocked queue, and access connection metadata. This interface is injected by the `ProxiedWebSocket` class defined in [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js).

### What message types does the DevTools panel receive during navigation?

The panel receives two primary messages from the background script: `page-refresh` when the base URL changes (triggering UI reset) and `url-update` when only the hash or query changes (allowing the panel to update the URL display while preserving connection data). Both messages are sent via the `devtoolsPorts` communication channel.