Draw.io Desktop electron.js Main Process File Structure and IPC Handlers Explained

The src/main/electron.js file serves as the entry point for the Draw.io Desktop Electron application, organizing imports, security policies, window management, and inter-process communication through a structured series of IPC handlers that bridge the renderer process and native OS capabilities.

The jgraph/drawio-desktop repository bundles the popular draw.io diagram editor as a cross-platform desktop application. Understanding the electron.js main process file structure is essential for developers extending the application or debugging its native behavior, as this file orchestrates everything from window creation to export pipelines and auto-updates according to the source code in the dev branch.

Overview of the Main Process Architecture

The main process file follows a layered architecture that prioritizes security and modularity. At approximately 1,500+ lines, src/main/electron.js (viewable on GitHub at [src/main/electron.js](https://github.com/jgraph/drawio-desktop/blob/dev/src/main/electron.js)) divides functionality into distinct regions: global constants and imports, security helpers, window factory functions, IPC handler registrations, the export pipeline, and application lifecycle management.

File Structure and Organization

Imports and Global Configuration

The first 21 lines establish the runtime environment by loading Node.js modules, Electron APIs (app, BrowserWindow, ipcMain, session), and third-party dependencies including electron-log, commander, electron-updater, @cantoo/pdf-lib, and electron-store (lines 1-21). This section defines critical path constants such as __dirname, codeDir, and appBaseDir that anchor the application relative to the bundled draw.io submodule.

Following imports, the code initializes an electron-store instance and evaluates update flags including disableUpdate and silentUpdate, which determine whether the auto-updater runs silently or prompts the user (lines 22-41).

Security Helpers and Window Factory

Three essential functions located in the helper section (lines 36-66) enforce security and window integrity:

  • validateSender(frame) – Guarantees that IPC messages originate exclusively from the bundled draw.io code by verifying the sender URL against codeUrl.
  • isWithinDisplayBounds(pos) – Validates that a saved window position actually exists on a connected display before restoration.
  • createWindow(opt) – Builds a BrowserWindow with hardened security flags, restores previous size/position from the store, injects the preload script at src/main/electron-preload.js, and registers per-window IPC listeners.

Key IPC Handlers in electron.js

The file registers two categories of IPC channels: per-window handlers created inside createWindow, and global handlers attached directly to ipcMain outside any window context.

Per-Window IPC Channels

Inside createWindow() (around lines 44-53), the main process attaches listeners specific to each browser instance:

  • openDevTools – Opens Chrome DevTools on demand when the renderer sends a debug request.
  • isModified-result – Receives the answer to a "has the document changed?" query from the renderer. The handler shows a save modal if needed, deletes draft files, or forces window closure based on the isModified boolean and draftPath provided.

Global Application IPC Channels

Outside the window factory, the file registers global channels (approximately lines 106-120) that respond to application-wide shortcuts and menu actions:

  • newfile – Creates a brand-new window with optional dimension parameters.
  • toggleSpellCheck, toggleStoreBkp, toggleGoogleFonts, toggleFullscreen – Toggle boolean user settings persisted in electron-store.
  • checkForUpdates – Manually triggers autoUpdater.checkForUpdates().
  • zoomIn, zoomOut, resetZoom – UI-level zoom controls wired to the application menu.

The Export Pipeline (exportDiagram)

The most complex IPC handler is the export pipeline (starting at line 378 and spanning to line 754). When the renderer sends an export event, exportDiagram executes the following workflow:

  1. Creates an off-screen BrowserWindow invisible to the user.
  2. Loads export3.html from the bundled drawio submodule (drawio/src/main/webapp/export3.html).
  3. Listens for render-finished from the off-screen renderer, indicating the diagram has rasterized.
  4. Sends render to the off-screen window with export arguments (format, width, height, XML data).
  5. Handles callbacks export-success, export-error, svg-data, xml-data, and export-finalize to coordinate PDF merging (via mergePdfs using @cantoo/pdf-lib), PNG metadata injection (via writePngWithText), DPI settings, and base64 encoding.

Security Implementation

Security hardening begins before any window loads. The app.whenReady() block (lines 671-752) installs a strict Content Security Policy via session.defaultSession.webRequest.onHeadersReceived and registers a file:// request filter.

The navigation guard (lines 724-796) blocks all will-navigate events and disallows external webview creation. The setWindowOpenHandler only permits about:blank or external URLs opened via shell.openExternal, preventing unwanted redirects from the draw.io web codebase.

Code Examples for Renderer Communication

Below are minimal snippets that a renderer process uses to interact with the main-process IPC channels defined in electron.js.

Open DevTools

// In a renderer script (via the contextBridge API)
window.electronAPI.request('openDevTools');

Handled in createWindow() → ipcMain.on('openDevTools', …) (lines 44-53).

Check Modified Document Status

const uid = crypto.randomUUID();
ipcRenderer.send('isModified-result', { 
    uniqueId: uid, 
    isModified: true, 
    draftPath: '/tmp/foo.dtmp' 
});

The main process validates the sender via validateSender(frame) before processing the modification state.

Export Diagram to PNG (Base64)

ipcRenderer.send('export', {
    format: 'png',
    w: 800,
    h: 600,
    base64: '1',
    embedXml: '1',
    xml: '<mxGraphModel>…</mxGraphModel>'
});

ipcRenderer.once('export-success', (e, data) => {
    // data is a base64 PNG string
    const img = document.createElement('img');
    img.src = `data:image/png;base64,${data}`;
    document.body.append(img);
});

The exportDiagram function (line 378) coordinates this operation through the off-screen window pipeline.

Toggle Spell Check Setting

ipcRenderer.send('toggleSpellCheck');

This triggers the handler at approximately line 885, which persists the new boolean value to electron-store.

Manual Update Check

ipcRenderer.send('checkForUpdates');

The handler at line 1002 invokes autoUpdater.checkForUpdates(), displaying a progress bar via electron-progressbar unless running in silentUpdate mode (lines 818-874).

Summary

  • src/main/electron.js is the single entry point that bootstraps the Draw.io Desktop application, initializes security policies, and manages the window lifecycle.
  • Security-first design mandates that all IPC messages pass through validateSender() and that navigation is blocked via webRequest filters and setWindowOpenHandler.
  • Two-tier IPC architecture separates per-window channels (like openDevTools) from global channels (like newfile and toggleSpellCheck).
  • Export pipeline uses an off-screen BrowserWindow loading export3.html to rasterize diagrams, handling PNG metadata injection and PDF merging through dedicated helper functions (writePngWithText, mergePdfs) at lines 1445-1527.
  • Auto-update logic respects the disableUpdate and silentUpdate flags established early in the file (lines 22-41) before wiring the checkForUpdates IPC handler.

Frequently Asked Questions

How does electron.js verify that IPC messages come from legitimate renderer processes?

The file implements a validateSender(frame) helper function that checks the senderFrame URL against the codeUrl constant (pointing to the bundled draw.io source). If the message originates from any other origin, the handler returns early, preventing malicious scripts from invoking main-process capabilities. This validation runs before processing sensitive channels like isModified-result or export.

What is the purpose of the exportDiagram function and how does it handle different file formats?

exportDiagram (starting at line 378) creates a hidden BrowserWindow, loads export3.html, and establishes a coordination protocol with the off-screen renderer. It sends a render event with export parameters, then waits for render-finished. Depending on the requested format, it processes svg-data, xml-data, or binary buffers, injects DPI metadata into PNGs via writePngWithText, and merges multi-page PDFs via mergePdfs using @cantoo/pdf-lib.

Where are user preferences like spell check and backup settings stored?

Toggle handlers such as toggleSpellCheck, toggleStoreBkp, and toggleGoogleFonts modify an electron-store instance initialized near line 22. These settings persist across application restarts and are accessed by both the main process (to configure window behavior) and indirectly by the renderer through IPC queries.

How does the application prevent navigation to external URLs or unauthorized content?

The security block (lines 724-796) registers session.defaultSession.webRequest.onHeadersReceived to inject a strict CSP, blocks all will-navigate events on web-contents-created, and sets a setWindowOpenHandler that only allows about:blank or explicitly allowed URLs opened via shell.openExternal. This ensures the draw.io web app cannot be redirected to malicious sites or open unapproved windows.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →