Help us understand the problem. What is going on with this article?

ElectronのWebViewで遊ぶ

More than 1 year has passed since last update.

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?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした