webView
Electron

ElectronのWebViewで遊ぶ

ElectronのWebviewで、特定のwebコンテンツを扱いたい。専ブラっぽいものの原型を調べる。

webview

  • electronの標準ライブラリ
  • ちょっとiframeっぽい
    • jsの実行空間がElectronレンダラーと別(あたりまえか)
    • iframeはwindow.parent.document.hogeなどでやり取りができるけど、webviewはプロセス間通信が必要(後述)
    • プロセス間通信はwebworkerっぽい感じ。arg/returnではなく、メッセージのやり取りでオブジェクトを運ぶ。
  • Cookieが使えたり色々便利

使い方

基本

  • htmlで<webview>を書くだけ。
index.html
<webview
    id="wv"
    src="https://electron.atom.io/">
</webview>

これだけ。

webview内のdocumentでjsを実行

  • ChromeExtensionなどのようにロードしたドキュメント上でJSが実行できる。
  • 外からJSをできるので、ちょっとXSSっぽい感じがする

preload属性に実行したいjsを渡しておくと、

index.html
<webview
    id="wv"
    preload="inject.js"
    src="https://electron.atom.io/">
</webview>

実行される。

inject.js
var body = document.querySelector(body);
console.log(body);

これでindex.htmlのconsoleを開いてみると、なにも出力されていないはず。これはmain > window > webviewの階層で別プロセス(別のJS空間)だから。index.htmlに以下を追記するとwebviewの中のdeveloperToolが開いて、console.logがへの出力が観察できます。

index.html
<script>
  var webview = document.getElementById('wv');
  webview.openDevTools();
</script>

webview内とelectronのwindowでやりとりする

上の例だとjsを埋め込んだだけになってしまうので、webViewから情報を取得してwindowに渡してみる。webviewの素晴らしいところは、埋め込んだjsinject.jsでnode_moduleが使えるところ。ElectronのipcRendererというメッセージAPIを使うことで、windowとwebviewでお話ができます。このへんのメッセージ授受方式がwebworkerっぽい。細かいことはソースに。

window側にメッセージを送るjsを用意します

index.html
<script src="ipc-host.js"></script>
<webview
    id="wv"
    preload="inject.js"
    src="https://electron.atom.io/">
</webview>
ipc-host.js
var webview = document.getElementById('wv');

// ipcメッセージのイベントがwebviewにある
webview.addEventListener('ipc-message', function(event) {
 switch(event.channel){
   case "getContent":
    console.log(event.arg[0]);  // content.innerTextが出力される
     });
     break;
 }
});

// webviewのロード完了イベント。onloadみたいな感じ。
webview.addEventListener("did-finish-load", function(){
  webview.send("getContent");
});

webview側でメッセージオブジェクトを用意して、on('channel_name', function)で受信時の動作を書きます。この中でsendToHostを呼び出すことで、返信ができます。

inject.js
// node api
const {ipcRenderer} = require('electron')

ipcRenderer.on('getContent', function(){
  var content = document.getElementsById("content");
  ipcRenderer.sendToHost('getContent', content.innerText);
});

便利なところ、工夫が必要なところ

  • webviewでアクセスしたサイトのクッキーを維持できる。Electronを閉じてもログイン状態などが保持された。
  • webview自体のAPIが豊富なのでiframeなどよりも楽に作れる。痒いところにも手が届きやすい感じ
  • 画面遷移時に一瞬だけ画面が空白になる。
    • クライアントアプリっぽさを出すには通信時のUIは必須
    • iOS appはロード時にインジケータやローディングのUIを表示することをアプリ審査の基準としてほぼ義務化している

まだ調べていないこと、よくわからないこと

  • webviewのUAやリファラを指定する属性について。
    • UAを指定していない場合はChromeのUA?
    • リファラを指定していない場合に画面遷移したらリファラはつく?
  • webviewにpreloadで渡したjsはDOM操作できない?
    • HTMLを足したり消したりしようとしてみたがうまくいかず。
    • XSS対策のため、許されていない?
    • webworkerだとwindowオブジェクトに触れないという制約があるので、似たような機構があるのかも
    • もしくはreadonly?