Electron – チュートリアルその5 プリロードスクリプトの使い方

目次

概要

Electron アプリ開発における、プレロードスクリプトの使い方について解説します。

メインプロセスとレンダラープロセス

Chrome では、アプリケーション全体を管理する単一のプロセスと、タブごとにページの描画を担当する複数のプロセスに分かれています。Electron でも同様の設計となっており、ウィンドウを管理する単一のプロセスをメインプロセス (main process)、ウィンドウごとに表示するページの描画を担当するプロセスをレンダラープロセス (renderer process) といいます。

  • メインプロセスでは、BrowserWindow() によりウィンドウを作成し、管理を行います。Node.js の API を使用することができます。
  • レンダラープロセス: BrowserWindow() でウィンドウを作成すると、ウィンドウごとにレンダラープロセスが作成され、ウィンドウに表示するページの描画が行われます。DOM にアクセスできる一方、セキュリティ上の理由からデフォルトでは、Node.js の API にはアクセスできないようになっています。もし、レンダラープロセスから OS に対する操作を可能にする Node.js の API へのアクセスを許可すると、XSS などの脆弱性の危険があるためです。

メインプロセスとレンダラープロセス間でやり取りを行いたい場合、プロセス間通信を行う必要があります。 例えば、レンダラープロセスで画面のボタンをクリックしたら、プロセス間通信によりそのイベントをメインプロセス側に通知し、メインプロセス側でウィンドウを開く処理を実行します。

メインプロセスとレンダラープロセス

プロセス間通信

プロセス間通信 (Inter-Process Communication, IPC) とは、複数のプロセス間でデータをやり取りする仕組みです。Electron では、メインプロセスとレンダラープロセスでデータをやり取りする際に使用されます。

プリロードスクリプト

プリロード (preload) スクリプトは、レンダラープロセスでページの描画の前に実行されるスクリプトです。 プリロードスクリプト内では、Node.js や Electron の API の一部にアクセスできます。レンダラープロセスと Window インタフェースを共有しているので、レンダラープロセスから直接はアクセスできない関数を使用した API をプリロードスクリプト内に定義し、レンダラープロセスに公開できます。 この仕組みにより、Node.js や Electron の API を利用する機能を安全にレンダラープロセスに追加できます。 ただし、デフォルトでは、プリロードスクリプトとレンダラープロセスでは、Javascript コンテキストを共有しないようになっています。そのため、グローバル変数 window に対して、window.myAPI = 関数 のように直接 API を追加することはできず、contextBridge モジュールを通して安全に行います。

プリロードスクリプトを読み込むようにするには、BrowserWindow() のウィンドウ作成時に以下のように、プリロードスクリプトのパスを指定します。

const win = new BrowserWindow({
  webPreferences: {
    preload: path.join(__dirname, "preload.js"),
  },
});

nodeIntegration

レンダラープロセスで Node.js を利用できるようにするかどうかを指定する BrowserWindow() の引数です。レンダラープロセスから直接 Node.js を利用できるのは危険であるため、デフォルトでは、nodeIntegration = false になっています。

const win = new BrowserWindow({
  webPreferences: {
    nodeIntegration: true, // 危険
  },
});

contextIsolation

プリロードスクリプトとレンダラープロセスで、Javascript コンテキストを共有するかどうかを指定する BrowserWindow() の引数です。デフォルトでは、contextIsolation = true になっています。

const win = new BrowserWindow({
  webPreferences: {
    contextIsolation: false, // 危険
  },
});

プリロードスクリプトの使い方

コード

コード全体は以下を参照ください。

electron-examples/05_electron-tutorial-use-preload-script

1. プリロードスクリプトを作成する

preload.ts を作成します。

preload.ts

import { contextBridge } from "electron";

contextBridge.exposeInMainWorld("versions", {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
});

contextBridge.exposeInMainWorld(API名, API の一覧) とすると、レンダラープロセス内で、window.API名 として、第2引数の API の一覧が公開されます。 今回の例では、window.versions.node()window.versions.chrome()window.versions.electron() という形で、レンダラープロセスで利用できるようになります。(window オブジェクトなので、省略して、versions.node()versions.chrome()versions.electron() でも可)

このとき、ディレクトリ構成は以下のようになっています。

electron-examples
├── build <-- ビルドすると生成
|  ├── main.js
|  └── preload.js
├── index.html
├── main.ts
├── node_modules
├── package-lock.json
├── package.json
├── preload.ts
└── tsconfig.json

2. メインプロセスでプリロードスクリプトを指定する

メインプロセスでは、BrowserWindow() でウィンドウを作成する際に、読み込むプリロードスクリプトを追加します。今回は Typescript であるため、プリロードスクリプトのパスは preload.ts ではなく、ビルド後の ./build/preload.js のパスを指定します。

main.ts

import { app, BrowserWindow } from "electron";
import * as path from "path";

const createWindow = () => {
  const win = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });
  win.webContents.openDevTools();
  win.loadFile("index.html");
};

app.whenReady().then(createWindow);

3. レンダラープロセスで公開した API を利用する

contextBridge で公開した API を利用する例を記載します。window オブジェクトに versions という名前で、プリロードスクリプトで定義した API が追加されています。

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
  </head>
  <body>
    <h1>Hello World</h1>
    <p id="info"></p>
  </body>
</html>
<script>
  const info = document.getElementById("info");
  info.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`;
</script>

これを実行すると、画面上に Node.js、Chrome、Electron のバージョンが表示されます。

コメント

コメントする

目次