# How to Manually Initiate a WebSocket Connection Using the Manual Connection Feature in law-chain-hot/websocket-devtools

> Learn to manually initiate a WebSocket connection with law-chain-hot/websocket-devtools. Open the modal, enter a URL, and establish a socket directly in your page's JavaScript context.

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

---

**Developers can manually initiate a WebSocket connection by opening the Manual Connect modal in the DevTools panel, entering a valid ws:// or wss:// URL, and triggering a four-layer message flow that instantiates the socket directly in the target page's JavaScript context.**

The **manual connection feature** in `law-chain-hot/websocket-devtools` allows developers to create WebSocket connections from the Chrome DevTools panel even when the inspected page does not establish one natively. This feature routes requests through the extension's architecture—from the UI modal through the background script to the content script—where the actual `WebSocket` object is constructed and monitored.

## Understanding the Manual Connection Architecture

The manual connection workflow operates across four distinct layers of the Chrome extension architecture. Each layer handles a specific responsibility in the request chain.

- **UI Layer ([`src/components/ManualConnectModal.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/components/ManualConnectModal.jsx)):** Presents a modal interface where developers input the target WebSocket URL. The component validates the URL scheme (restricting input to `ws://` or `wss://` protocols) and invokes the `onConnect` callback with the sanitized URL.

- **Panel Logic Layer ([`src/devtools/panel.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/panel.jsx)):** Implements the `handleManualConnect` function that creates a Promise-based listener to await the connection outcome. This layer sends the creation request to the background script using `chrome.runtime.sendMessage` with type `"create-manual-websocket"`.

- **Background Script Layer ([`src/background/background.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/background/background.js)):** Receives the panel's request and forwards it to the specific tab's content script via `chrome.tabs.sendMessage`. This layer acts as a message broker between the DevTools panel and the page context.

- **Content Script Layer ([`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js)):** Executes inside the inspected page's JavaScript context. This layer constructs the actual `new window.WebSocket(url)` instance, attaches event listeners, and reports success or failure back to the background script.

## Step-by-Step Connection Flow

### Step 1: Modal Input and Validation

When a developer clicks to open a manual connection, [`src/components/ManualConnectModal.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/components/ManualConnectModal.jsx) renders an input field for the WebSocket URL. The component validates that the URL begins with `ws:` or `wss:` before enabling the Connect button. Upon confirmation, the modal calls `onConnect(wsUrl)`, passing the validated string to the panel's handler.

### Step 2: Panel-Side Request and Promise Creation

In [`src/devtools/panel.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/panel.jsx), the `handleManualConnect` function orchestrates the request lifecycle. The function first establishes a Promise that listens for Chrome runtime messages with types `"manual-connection-created"` or `"manual-connection-error"`, implementing a **10-second timeout** to prevent indefinite waiting.

The handler then dispatches the creation request:

```javascript
const response = await chrome.runtime.sendMessage({
  type: "create-manual-websocket",
  data: { url: wsUrl, tabId: currentTabId },
});

```

The function awaits both the background script's acknowledgment and the content script's completion event before resolving with the `connectionId`.

### Step 3: Background Script Forwarding

The [`src/background/background.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/background/background.js) file processes the `"create-manual-websocket"` message by extracting the `tabId` from the message payload. According to the source code, the background script validates the presence of `tabId` and forwards the request:

```javascript
case "create-manual-websocket": {
  const tabId = message.data.tabId;
  if (tabId) {
    chrome.tabs.sendMessage(tabId, {
      type: "create-manual-websocket",
      url: message.data.url,
    })
    .then(() => sendResponse({ success: true }))
    .catch(err => sendResponse({ success: false, error: err.message }));
  }
  break;
}

```

This bridges the communication gap between the isolated DevTools panel and the target page's content script.

### Step 4: Content Script Instantiation

The [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js) file receives the forwarded message and executes within the page's JavaScript context. The script constructs a native `WebSocket` instance and assigns a unique `connectionId` derived from the internal proxy wrapper:

```javascript
const manualWs = new window.WebSocket(event.data.url);
const connectionId = manualWs._connectionId;

```

The script immediately attaches event listeners for `open`, `error`, and `close` events. Upon successful connection, it dispatches a `"websocket-event"` message with subtype `"manual-connection-created"`:

```javascript
chrome.runtime.sendMessage({
  type: "websocket-event",
  data: {
    type: "manual-connection-created",
    url: event.data.url,
    connectionId,
  },
});

```

If instantiation fails, the script sends `"manual-connection-error"` with the error message. The background script relays these events back to the DevTools panel, where the Promise created in Step 2 resolves and updates the UI.

## Implementation Code Examples

### Panel Handler with Promise-Based Response

The `handleManualConnect` function in [`src/devtools/panel.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/panel.jsx) demonstrates the complete request-response cycle with timeout handling:

```javascript
const handleManualConnect = async (wsUrl) => {
  try {
    const connectionCreatedPromise = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => 
        reject(new Error("Connection creation timeout")), 10000);

      const listener = (msg, _, sendResponse) => {
        if (msg.type === "websocket-event") {
          const d = msg.data;
          if (d.type === "manual-connection-created" && d.url === wsUrl) {
            clearTimeout(timeout);
            chrome.runtime.onMessage.removeListener(listener);
            resolve(d.connectionId);
          } else if (d.type === "manual-connection-error" && d.url === wsUrl) {
            clearTimeout(timeout);
            chrome.runtime.onMessage.removeListener(listener);
            reject(new Error(d.error || "Manual connection failed"));
          }
        }
        if (typeof sendResponse === "function") sendResponse({ received: true });
      };

      chrome.runtime.onMessage.addListener(listener);
    });

    const response = await chrome.runtime.sendMessage({
      type: "create-manual-websocket",
      data: { url: wsUrl, tabId: currentTabId },
    });

    if (!response?.success) throw new Error(response?.error);
    const connectionId = await connectionCreatedPromise;
    return { success: true, connectionId };
  } catch (e) {
    throw e;
  }
};

```

### Content Script Socket Creation

The content script in [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js) handles the actual WebSocket construction and error reporting:

```javascript
case "create-manual-websocket": {
  try {
    const manualWs = new window.WebSocket(event.data.url);
    const connectionId = manualWs._connectionId;

    manualWs.addEventListener('open', () => {
      chrome.runtime.sendMessage({
        type: "websocket-event",
        data: {
          type: "manual-connection-created",
          url: event.data.url,
          connectionId,
        },
      });
    });

    manualWs.addEventListener('error', (e) => {
      chrome.runtime.sendMessage({
        type: "websocket-event",
        data: {
          type: "manual-connection-error",
          url: event.data.url,
          error: e.message || "WebSocket error",
        },
      });
    });
  } catch (err) {
    chrome.runtime.sendMessage({
      type: "websocket-event",
      data: {
        type: "manual-connection-error",
        url: event.data.url,
        error: err.message,
      },
    });
  }
  break;
}

```

## Key Source Files

- **[`src/components/ManualConnectModal.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/components/ManualConnectModal.jsx)**: Renders the UI modal for URL input and triggers the connection callback.

- **[`src/devtools/panel.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/devtools/panel.jsx)**: Contains the `handleManualConnect` function that manages the asynchronous request lifecycle and Promise resolution.

- **[`src/background/background.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/background/background.js)**: Implements the message broker logic for the `"create-manual-websocket"` case, forwarding requests to content scripts.

- **[`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js)**: Executes within the page context to instantiate `window.WebSocket` objects and report connection status.

- **[`src/utils/wsHistoryService.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/utils/wsHistoryService.js)**: Persists recently used manual connection URLs for quick re-access in the modal.

## Summary

- The manual connection feature creates WebSocket instances in the target page's JavaScript context, enabling inspection of connections that the page itself does not initiate.

- The implementation uses a four-layer architecture: UI modal → DevTools panel → Background script → Content script.

- Message types `"create-manual-websocket"`, `"manual-connection-created"`, and `"manual-connection-error"` coordinate the cross-context communication.

- The panel implements a 10-second timeout to handle unresponsive connection attempts gracefully.

- Once established, manual connections are monitored identically to native WebSocket connections through the extension's proxy layer.

## Frequently Asked Questions

### How does the extension handle manual connection errors?

The content script in [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js) catches instantiation failures and WebSocket error events, dispatching `"manual-connection-error"` messages to the background script. The panel's `handleManualConnect` function rejects its Promise upon receiving this event, while a 10-second timeout prevents indefinite waiting if the content script fails to respond.

### Can developers initiate manual connections to any tab from the DevTools panel?

Yes, provided the extension has access to the target tab. The `handleManualConnect` function requires a `currentTabId` parameter that specifies which tab's content script should receive the `"create-manual-websocket"` message. The background script validates this `tabId` before forwarding the request via `chrome.tabs.sendMessage`.

### What WebSocket URL schemes are supported for manual connections?

The extension supports standard WebSocket protocols. The [`ManualConnectModal.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/ManualConnectModal.jsx) component validates that URLs begin with `ws://` or `wss://` before enabling the Connect button, ensuring only secure and unencrypted WebSocket schemes are accepted.

### Are manually created WebSocket connections monitored differently than native ones?

No. Because the content script creates the WebSocket using `new window.WebSocket(url)` inside the page's actual JavaScript context, the extension's proxy layer automatically intercepts all subsequent traffic. Developers can inspect, simulate, or block messages on manual connections using the same tools available for page-native WebSocket connections.