You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
6.1 KiB
181 lines
6.1 KiB
|
4 days ago
|
(function (global) {
|
||
|
|
'use strict';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 壳侧桥:与游戏 iframe 内 Agent 通信(postMessage;同域可走 contentWindow 快路径探测)。
|
||
|
|
* @constructor
|
||
|
|
*/
|
||
|
|
function WorkbenchBridge() {
|
||
|
|
this.iframe = null;
|
||
|
|
this.ready = false;
|
||
|
|
this.hibernating = false;
|
||
|
|
this._reqId = 1;
|
||
|
|
this._pending = {};
|
||
|
|
this._listeners = {};
|
||
|
|
this._targetOrigin = '*';
|
||
|
|
this._boundMessage = this._onMessage.bind(this);
|
||
|
|
this._crossOrigin = false;
|
||
|
|
this._fallbackTried = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {HTMLIFrameElement} iframe
|
||
|
|
* @param {{crossOrigin?:boolean}} opts
|
||
|
|
*/
|
||
|
|
WorkbenchBridge.prototype.attach = function (iframe, opts) {
|
||
|
|
this.iframe = iframe;
|
||
|
|
this._crossOrigin = !!(opts && opts.crossOrigin);
|
||
|
|
window.addEventListener('message', this._boundMessage);
|
||
|
|
var self = this;
|
||
|
|
iframe.addEventListener('load', function () {
|
||
|
|
self.call('ping').then(function () {
|
||
|
|
self.ready = true;
|
||
|
|
}).catch(function () { /* wait agent.ready event */ });
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.prototype.detach = function () {
|
||
|
|
window.removeEventListener('message', this._boundMessage);
|
||
|
|
this.iframe = null;
|
||
|
|
this.ready = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {string} name
|
||
|
|
* @param {function(*):void} cb
|
||
|
|
*/
|
||
|
|
WorkbenchBridge.prototype.on = function (name, cb) {
|
||
|
|
if (!this._listeners[name]) this._listeners[name] = [];
|
||
|
|
this._listeners[name].push(cb);
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.prototype.off = function (name, cb) {
|
||
|
|
var list = this._listeners[name];
|
||
|
|
if (!list) return;
|
||
|
|
var i = list.indexOf(cb);
|
||
|
|
if (i >= 0) list.splice(i, 1);
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.prototype._emit = function (name, data) {
|
||
|
|
var list = this._listeners[name] || [];
|
||
|
|
for (var i = 0; i < list.length; i++) {
|
||
|
|
try { list[i](data); } catch (e) { console.warn('[Bridge] listener', name, e); }
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.prototype._onMessage = function (ev) {
|
||
|
|
var msg = ev.data;
|
||
|
|
if (!msg || msg.source !== 'pw-agent') return;
|
||
|
|
if (msg.type === 'rpc-result' && msg.id != null && this._pending[msg.id]) {
|
||
|
|
var p = this._pending[msg.id];
|
||
|
|
delete this._pending[msg.id];
|
||
|
|
if (msg.ok) p.resolve(msg.result);
|
||
|
|
else p.reject(new Error(msg.error || 'rpc failed'));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (msg.type === 'event') {
|
||
|
|
if (msg.name === 'agent.ready') {
|
||
|
|
this.ready = !!(msg.data && msg.data.ok);
|
||
|
|
}
|
||
|
|
if (msg.name === 'agent.hibernate') {
|
||
|
|
this.hibernating = !!(msg.data && msg.data.on);
|
||
|
|
}
|
||
|
|
this._emit(msg.name, msg.data);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {string} method
|
||
|
|
* @param {object=} params
|
||
|
|
* @returns {Promise<*>}
|
||
|
|
*/
|
||
|
|
WorkbenchBridge.prototype.call = function (method, params) {
|
||
|
|
var self = this;
|
||
|
|
return new Promise(function (resolve, reject) {
|
||
|
|
if (!self.iframe || !self.iframe.contentWindow) {
|
||
|
|
reject(new Error('iframe missing'));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// 同域快路径
|
||
|
|
if (!self._crossOrigin) {
|
||
|
|
try {
|
||
|
|
var agent = self.iframe.contentWindow.__PW_Agent;
|
||
|
|
if (agent && typeof agent._dispatch === 'function') {
|
||
|
|
var direct = agent._dispatch(method, params || {});
|
||
|
|
resolve(direct);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
} catch (e) { /* fall through postMessage */ }
|
||
|
|
}
|
||
|
|
var id = self._reqId++;
|
||
|
|
self._pending[id] = { resolve: resolve, reject: reject };
|
||
|
|
var target = self._crossOrigin ? self._targetOrigin : '*';
|
||
|
|
try {
|
||
|
|
if (self.iframe.src) {
|
||
|
|
var u = new URL(self.iframe.src, location.href);
|
||
|
|
target = u.origin;
|
||
|
|
self._targetOrigin = target;
|
||
|
|
}
|
||
|
|
} catch (e2) { /* */ }
|
||
|
|
self.iframe.contentWindow.postMessage({
|
||
|
|
source: 'pw-bridge',
|
||
|
|
id: id,
|
||
|
|
method: method,
|
||
|
|
params: params || {},
|
||
|
|
}, target);
|
||
|
|
setTimeout(function () {
|
||
|
|
if (self._pending[id]) {
|
||
|
|
delete self._pending[id];
|
||
|
|
reject(new Error('rpc timeout: ' + method));
|
||
|
|
}
|
||
|
|
}, 8000);
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.prototype.subscribe = function (channel) {
|
||
|
|
return this.call('subscribe', { channel: channel });
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.prototype.unsubscribe = function (channel) {
|
||
|
|
return this.call('unsubscribe', { channel: channel });
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.prototype.hibernate = function () {
|
||
|
|
return this.call('hibernate');
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.prototype.wake = function () {
|
||
|
|
return this.call('wake');
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 构建游戏页 URL;优先 localhost↔127.0.0.1 跨域,失败标记后同域降级。
|
||
|
|
* @param {boolean=} forceSameOrigin
|
||
|
|
* @returns {{url:string, crossOrigin:boolean}}
|
||
|
|
*/
|
||
|
|
WorkbenchBridge.buildGameUrl = function (forceSameOrigin) {
|
||
|
|
var u = new URL(location.href);
|
||
|
|
u.searchParams.set('pw', 'game');
|
||
|
|
var crossFail = false;
|
||
|
|
try { crossFail = sessionStorage.getItem('pw-cross-fail') === '1'; } catch (e) { /* */ }
|
||
|
|
var crossOrigin = false;
|
||
|
|
if (!forceSameOrigin && !crossFail) {
|
||
|
|
var host = location.hostname;
|
||
|
|
if (host === 'localhost') {
|
||
|
|
u.hostname = '127.0.0.1';
|
||
|
|
crossOrigin = true;
|
||
|
|
} else if (host === '127.0.0.1') {
|
||
|
|
u.hostname = 'localhost';
|
||
|
|
crossOrigin = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return { url: u.toString(), crossOrigin: crossOrigin };
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchBridge.markCrossOriginFailed = function () {
|
||
|
|
try { sessionStorage.setItem('pw-cross-fail', '1'); } catch (e) { /* */ }
|
||
|
|
};
|
||
|
|
|
||
|
|
global.PWBridge = WorkbenchBridge;
|
||
|
|
})(window);
|