嵌入模式识别
This commit is contained in:
147
src/_export.js
147
src/_export.js
@@ -1,20 +1,19 @@
|
||||
import { isReadyCalled } from "./_core";
|
||||
import { getConfig } from "./services/config";
|
||||
import {
|
||||
readWxLikeEnvFromWindow,
|
||||
setParentWxEnvReport,
|
||||
getParentWxEnvReport,
|
||||
} from "./services/embedEnvProbe";
|
||||
import {
|
||||
hasDistinctParentWindow,
|
||||
resolveUseParentProxy,
|
||||
} from "./services/embedProxy";
|
||||
import { createUUID } from "./utils/uuid";
|
||||
|
||||
const EMBED_SOURCE = "IScanEmbed";
|
||||
const EMBED_V = 1;
|
||||
|
||||
function isEmbedded() {
|
||||
if (typeof window === "undefined") {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function isEmbedMessage(data) {
|
||||
return data && data.source === EMBED_SOURCE && data.v === EMBED_V;
|
||||
}
|
||||
@@ -120,11 +119,62 @@ function deserializeEmbedInvokeResult(methodKey, raw) {
|
||||
return raw;
|
||||
}
|
||||
|
||||
let embedWxProbeScheduled = false;
|
||||
let embedWxProbeAttempts = 0;
|
||||
const EMBED_WX_PROBE_MAX = 4;
|
||||
|
||||
function shouldScheduleParentWxProbe() {
|
||||
if (typeof window === "undefined" || !hasDistinctParentWindow()) {
|
||||
return false;
|
||||
}
|
||||
const mode = getConfig("embedProxyMode");
|
||||
if (mode === false || mode === "local" || mode === "off") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 向父页询问是否微信环境(子 iframe 跨域时本地 UA 可能不可靠) */
|
||||
function scheduleEmbedWxEnvProbeIfNeeded() {
|
||||
if (!shouldScheduleParentWxProbe()) {
|
||||
return;
|
||||
}
|
||||
if (embedWxProbeScheduled) {
|
||||
return;
|
||||
}
|
||||
if (embedWxProbeAttempts >= EMBED_WX_PROBE_MAX) {
|
||||
return;
|
||||
}
|
||||
embedWxProbeScheduled = true;
|
||||
embedWxProbeAttempts++;
|
||||
ensureEmbedChildListener();
|
||||
window.parent.postMessage(
|
||||
{
|
||||
source: EMBED_SOURCE,
|
||||
v: EMBED_V,
|
||||
kind: "probeWxEnv",
|
||||
id: createUUID(),
|
||||
},
|
||||
"*"
|
||||
);
|
||||
window.setTimeout(() => {
|
||||
embedWxProbeScheduled = false;
|
||||
if (getParentWxEnvReport() !== null || !shouldScheduleParentWxProbe()) {
|
||||
return;
|
||||
}
|
||||
scheduleEmbedWxEnvProbeIfNeeded();
|
||||
}, 600);
|
||||
}
|
||||
|
||||
function embedChildOnMessage(ev) {
|
||||
const data = ev.data;
|
||||
if (!isEmbedMessage(data)) {
|
||||
return;
|
||||
}
|
||||
if (data.kind === "probeWxEnvResult") {
|
||||
setParentWxEnvReport(!!data.wx);
|
||||
return;
|
||||
}
|
||||
if (data.kind === "invokeResult") {
|
||||
const pending = pendingInvokes[data.id];
|
||||
if (!pending) {
|
||||
@@ -147,7 +197,7 @@ function embedChildOnMessage(ev) {
|
||||
}
|
||||
|
||||
function ensureEmbedChildListener() {
|
||||
if (embedChildInstalled || typeof window === "undefined" || !isEmbedded()) {
|
||||
if (embedChildInstalled || typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
embedChildInstalled = true;
|
||||
@@ -156,6 +206,7 @@ function ensureEmbedChildListener() {
|
||||
|
||||
function embedInvoke(methodKey, params) {
|
||||
ensureEmbedChildListener();
|
||||
scheduleEmbedWxEnvProbeIfNeeded();
|
||||
const id = createUUID();
|
||||
const { serialized, registry } = serializeEmbedParams(params);
|
||||
Object.keys(registry).forEach((cbId) => {
|
||||
@@ -235,7 +286,27 @@ export function installEmbedHost(lib) {
|
||||
embedHostInstalled = true;
|
||||
window.addEventListener("message", (ev) => {
|
||||
const data = ev.data;
|
||||
if (!isEmbedMessage(data) || data.kind !== "invoke") {
|
||||
if (!isEmbedMessage(data)) {
|
||||
return;
|
||||
}
|
||||
if (data.kind === "probeWxEnv") {
|
||||
if (!ev.source || ev.source === window) {
|
||||
return;
|
||||
}
|
||||
const wx = readWxLikeEnvFromWindow(window);
|
||||
ev.source.postMessage(
|
||||
{
|
||||
source: EMBED_SOURCE,
|
||||
v: EMBED_V,
|
||||
kind: "probeWxEnvResult",
|
||||
id: data.id,
|
||||
wx,
|
||||
},
|
||||
ev.origin
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (data.kind !== "invoke") {
|
||||
return;
|
||||
}
|
||||
if (!data.id || !data.methodKey) {
|
||||
@@ -300,6 +371,42 @@ function freezeObj(obj) {
|
||||
Object.freeze(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一调用代理:根据 embedProxyMode + 环境决定走父页面转发或本地 _exec。
|
||||
*/
|
||||
function createInvokeTransport(lib, method, methodName, initNames) {
|
||||
return function IScanInvokeProxy(...params) {
|
||||
if (resolveUseParentProxy()) {
|
||||
if (methodName === "onScanListener") {
|
||||
const listener = params[0];
|
||||
const key = params[1];
|
||||
if (!key || typeof key !== "string" || typeof listener !== "function") {
|
||||
return;
|
||||
}
|
||||
embedInvoke(methodName, params).catch(() => {});
|
||||
return {
|
||||
key,
|
||||
cancel: () => embedInvoke("offScanListener", [key]),
|
||||
};
|
||||
}
|
||||
return embedInvoke(methodName, params);
|
||||
}
|
||||
if (!isReadyCalled() && initNames && initNames.indexOf(method) < 0) {
|
||||
throw `[IScan]:Can't call the "IScan.${method}" method, because "IScan" not ready, please confirm that "IScan.ready()" has been called. params: ${JSON.stringify(params)}`
|
||||
}
|
||||
return _exec(lib, methodName, ...params);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试:当前解析到的嵌入转发开关(每次读取最新 config)。
|
||||
*/
|
||||
export function getEmbedProxyResolved() {
|
||||
return resolveUseParentProxy();
|
||||
}
|
||||
|
||||
export { resolveUseParentProxy } from "./services/embedProxy";
|
||||
|
||||
export function exportSDK(lib, funcs, ...initNames) {
|
||||
let methods = {};
|
||||
if (funcs && typeof funcs === 'object') {
|
||||
@@ -311,19 +418,9 @@ export function exportSDK(lib, funcs, ...initNames) {
|
||||
Object.keys(methods).forEach(method => {
|
||||
let methodItem = methods[method];
|
||||
let methodName = methodItem && methodItem.method || method;
|
||||
hook(library, method, (...params) => {
|
||||
if (isEmbedded()) {
|
||||
return embedInvoke(methodName, params);
|
||||
}
|
||||
if (!isReadyCalled() && initNames && initNames.indexOf(method) < 0) {
|
||||
throw `[IScan]:Can't call the "IScan.${method}" method, because "IScan" not ready, please confirm that "IScan.ready()" has been called. params: ${JSON.stringify(params)}`
|
||||
}
|
||||
return _exec(lib, methodName, ...params);
|
||||
});
|
||||
hook(library, method, createInvokeTransport(lib, method, methodName, initNames));
|
||||
});
|
||||
if (typeof window !== "undefined" && isEmbedded()) {
|
||||
ensureEmbedChildListener();
|
||||
}
|
||||
scheduleEmbedWxEnvProbeIfNeeded();
|
||||
freezeObj(library);
|
||||
return library;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user