Skip to content

Instantly share code, notes, and snippets.

@furyutei
Last active February 16, 2024 10:23
Show Gist options
  • Save furyutei/73cab3e8149b0d40e8459de3e500869c to your computer and use it in GitHub Desktop.
Save furyutei/73cab3e8149b0d40e8459de3e500869c to your computer and use it in GitHub Desktop.
window.postMessageを使用した異なるドメイン間の通信サンプル

[JavaScript] window.postMessageを使用した異なるドメイン間の通信サンプル

ポップアップウィンドウで異なるドメインのページを開き、元のページと通信するユーザースクリプトのサンプル。

サンプルユーザースクリプト

  1. test-postMessage.user.js

お試し方法

Tampermonkeyで上記のユーザースクリプトをインストールし、
テスト用ページ にアクセス。
ポップアップウィンドウを開き、一定時間通信した後、閉じる。
※ログはデベロッパーツールのコンソールを参照。

// ==UserScript==
// @name test-postMessage
// @namespace http://furyu.hatenablog.com/
// @author furyu
// @version 0.1
// @include https://twitter.com/furyutei/status/2688944309
// @include https://nazo.furyutei.work/test/postMessage
// @description test-postMessage
// ==/UserScript==
( function () {
var POPUP_NAME = 'test-postMessage',
POPUP_URL = 'https://nazo.furyutei.work/test/postMessage';
function get_popup_info() {
if ( ( ! window.opener ) || ( ! window.name ) || ( ! window.name.match( /^([^.]+)\.(.*)$/ ) ) ) {
return null;
}
else {
var popup_info = {
name : RegExp.$1,
parent_origin : RegExp.$2
};
if ( popup_info.name != POPUP_NAME ) {
return null;
}
return popup_info;
}
} // end of get_popup_info()
function get_origin( url ) {
return new URL( url ).origin;
} // end of get_origin()
function message_handler( event ) {
} // end of message_handler()
function parent_main() {
var parent_origin = get_origin( location.href ),
popup_origin = get_origin( POPUP_URL ),
popup_window = window.open( POPUP_URL, POPUP_NAME + '.' + parent_origin, 'width=640,height=480,top=' + ( window.screenY + 64 ) + ',left=' + (window.screenX + 48 ) + ',toolbar=0,scrollbars=1,status=1,resizable=1,location=1,menuBar=0' ),
timeout_timer_id = setTimeout( function () {
console.error( new Date().toISOString(), 'connection timeout' );
finish();
}, 30000 ),
healthcheck_timer_id = null,
healthcheck_counter = 0,
last_healthcheck_counter = 0,
message_handler = function ( event ) {
if ( event.origin != popup_origin ) {
console.error( new Date().toISOString(), 'origin error:', event.origin );
return;
}
var message = event.data;
switch ( message.status ) {
case 'START' :
if ( ! timeout_timer_id ) {
break;
}
clearTimeout( timeout_timer_id );
timeout_timer_id = null;
healthcheck_timer_id = setInterval( function () {
console.log( new Date().toISOString(), 'healthcheck_counter=', healthcheck_counter, last_healthcheck_counter );
try {
console.log( 'popup_window.closed =', popup_window.closed );
}
catch ( error ) {
}
if ( healthcheck_counter == last_healthcheck_counter ) {
console.error( new Date().toISOString(), 'healthcheck timeout' );
finish();
return;
}
last_healthcheck_counter = healthcheck_counter;
}, 3000 );
break;
case 'ALIVE' :
healthcheck_counter ++;
break;
case 'END' :
console.log( new Date().toISOString(), 'send close request' );
popup_window.postMessage( {
command : 'CLOSE_REQUEST'
}, popup_origin );
finish();
break;
default :
console.error( new Date().toISOString(), 'message error:', message );
return;
}
console.log( new Date().toISOString(), 'popup status:', message.status, healthcheck_counter, message );
},
finish = function () {
if ( healthcheck_timer_id ) {
clearTimeout( healthcheck_timer_id );
}
window.removeEventListener( 'message', message_handler );
};
window.addEventListener( 'message', message_handler );
console.log( new Date().toISOString(), 'ready' );
} // end of parent_main()
function popup_main( popup_info ) {
var parent_window = window.opener,
parent_origin = popup_info.parent_origin,
healthcheck_counter = 0,
healthcheck_timer_id = setInterval( function () {
healthcheck_counter ++;
console.log( new Date().toISOString(), 'alive', healthcheck_counter );
parent_window.postMessage( {
status : 'ALIVE',
counter : healthcheck_counter
}, parent_origin );
}, 500 ),
end_timer_id = setTimeout( function () {
console.log( new Date().toISOString(), 'end' );
parent_window.postMessage( {
status : 'END'
}, parent_origin );
}, 10000 );
window.addEventListener( 'message', function ( event ) {
if ( event.origin != parent_origin ) {
console.error( new Date().toISOString(), 'origin error:', event.origin );
return;
}
var message = event.data;
switch ( message.command ) {
case 'CLOSE_REQUEST' :
window.open( '', '_self', '' );
window.close();
break;
default :
console.error( new Date().toISOString(), 'message error:', message );
return;
}
console.log( new Date().toISOString(), 'parent command:', message.command, message );
} );
console.log( new Date().toISOString(), 'start' );
parent_window.postMessage( {
status : 'START'
}, parent_origin );
} // end of popup_main()
var popup_info = get_popup_info();
if ( popup_info ) {
popup_main( popup_info );
}
else {
parent_main();
}
} )();
@furyutei
Copy link
Author

覚え書き

ポップアップウィンドウで異なるドメインのページを開き、元のページと通信するユーザースクリプトのサンプル。

  • ポップアップ側のロード完了タイミングが元ページではわからないため、元ページ→ポップアップページの最初の情報受け渡しは window.name を使用
  • ポップアップウィンドウからの最初のメッセージが一定時間届かなければ、タイムアウトとみなす
  • ポップアップウィンドウ→元ページへは定期的にメッセージを送信(ヘルスチェック)
    元ページは、一定時間メッセージが来ない場合、タイムアウトとみなす
  • ポップアップウィンドウを閉じるために、元ページからポップアップウィンドウに指示を出す(CLOSE_REQUEST)

テスト方法

Tampermonkeyで上記のユーザースクリプトをインストールし、
https://twitter.com/furyutei/status/2688944309
にアクセス。
ポップアップウィンドウを開き、一定時間通信した後、閉じる。
※ログはデベロッパーツールのコンソールを参照。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment