はじめに

Cordova Android で iframe 内のリンクをシステムデフォルトブラウザで開く方法。


前置きなどは 親 Window で iframe からのデータ受信、イベント検出する方法 の「前置き」と同じ。

以下はとあるプロジェクトの要件など。

  • Cordova で iOS/Android まとめて開発
  • コンテンツ内部は先方が作成
    • コンテンツ内に先方管理外のサイトへのリンクがある
  • ガワはこちらで作成して、コンテンツを iframe で読み込む
  • iframe 内のリンクをシステムデフォルトブラウザで開きたい

iOS は問題なかった(意図通りに動作した)が、Android だけ上手く動かなかった。具体的には、リンクをタップすると iframe 内で開こうとするが、X-Frame-Options の設定により net::ERR_BLOCKED_BY_RESPONSE となる、というもの。

TL;DR

  • iframe 側: postMessage でリンク先 URL を送信
    • 送信後 preventDefault で元イベント(リンク先を iframe 内で開こうとするアクション)を抑止
  • アプリ(Cordova App)側: apache/cordova-plugin-inappbrowser でシステムデフォルトのブラウザを呼び出す
  • 他ページで余計な動作をしないように注意
この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. セットアップ
    2. 結論(実装サンプル)
      1. iframe 側
      2. 親(Cordova)側
  5. まとめ
  6. その他・メモ
  7. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ sw_vers
ProductName: macOS
ProductVersion: 11.1
BuildVersion: 20C69

$ node -v
v12.7.0

$ npm -v
6.14.5

$ cordova -v
10.0.0

$ cordova platforms
6.0.0
Installed platforms:
android 9.0.0
ios 6.1.1

$ npm ls vue vue-router
├── vue@2.6.11
└── vue-router@3.4.3

Android 検証機: Huawei nova lite 2 (Android 9) (公式)

詳細

セットアップ

apache/cordova-plugin-inappbrowser をインストール

1
$ cordova plugin add cordova-plugin-inappbrowser

結論(実装サンプル)

基本的な仕組み自体は前回(親 Window で iframe からのデータ受信、イベント検出する方法)とほぼ同じ。

postMessage でリンク先 URL を送って、アプリ(Cordova App)側で apache/cordova-plugin-inappbrowser を経由してシステムデフォルトのブラウザを呼び出す。

iframe

iframe で読み込まれていたら(= window.parent が存在していたら) postMessage でリンク先を送信して、preventDefault で元イベント(リンク先を iframe 内で開こうとするアクション)を抑止する。

iframe.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<a href="https://google.co.jp">
Link
</a>
<script>
document.querySelectorAll('a').forEach(anchor => {
anchor.addEventListener('click', clickEvent => {
if (!window.parent) {
return;
}

window.parent.postMessage(anchor.href, '*');
clickEvent.preventDefault();
});
});
</script>

親(Cordova)側

Vue + Vue Router + Cordova でいつも実装しているので、サンプルコードも Vue で書く。

iframe.htmlhttps://example.com から読み込む場合、メッセージハンドラ(messageHandlerFromIframe)内で event.originhttps://example.com かどうかをチェック(前回と同じ)。

他ページで余計な動作をしないように、created, beforeDestroy フックでイベントリスナの 追加/削除 を実施。

Cordova 環境の場合のみ apache/cordova-plugin-inappbrowser を利用。

parent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<div>
<iframe id="iframe" :src="iframeUrl"></iframe>
</div>
</template>

<script>
export default {
data() {
return {
iframeUrl: 'https://example.com/iframe.html',
};
},
created() {
this.setMessageReceiverFromIframe();
},
beforeDestroy() {
this.removeMessageReceiverFromIframe();
},
methods: {
openUrl(url, target = '_system') {
if (typeof cordova === 'undefined' || !cordova.InAppBrowser) {
window.open(url);
} else {
cordova.InAppBrowser.open(url, target);
}
},
messageHandlerFromIframe(event) {
if (event.origin !== 'https://example.com' || !event.data) {
return;
}
this.openUrl(event.data, '_system');
},
setMessageReceiverFromIframe() {
window.addEventListener('message', this.messageHandlerFromIframe);
},
removeMessageReceiverFromIframe() {
window.removeEventListener('message', this.messageHandlerFromIframe);
},
},
};
</script>

まとめ

  • iframe 側: postMessage でリンク先 URL を送信
    • 送信後 preventDefault で元イベント(リンク先を iframe 内で開こうとするアクション)を抑止
  • アプリ(Cordova App)側: apache/cordova-plugin-inappbrowser でシステムデフォルトのブラウザを呼び出す
  • 他ページで余計な動作をしないように注意

その他・メモ

以下記事で「config.xml(apache/cordova-plugin-whitelist)の設定で解決した」みたいなことが書かれていたので試したが、回答時から時間が経って環境(バージョン)が変わっているせいか自分の方では上手くいかなかった。

参考文献

関連記事

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list