Skip to content

Commit 77bc4de

Browse files
author
Daniel Brain
committed
Separate out bridge code into parent/child/bridge/common
1 parent 0a8134e commit 77bc4de

File tree

5 files changed

+497
-467
lines changed

5 files changed

+497
-467
lines changed

src/bridge/bridge.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
import { CONSTANTS } from '../conf';
3+
import { getParent, isWindowClosed } from '../lib';
4+
import { global } from '../global';
5+
import { send } from '../interface';
6+
7+
global.openTunnelToParent = function openTunnelToParent({ name, source, canary, sendMessage }) {
8+
9+
let remoteWindow = getParent(window);
10+
11+
if (!remoteWindow) {
12+
throw new Error(`No parent window found to open tunnel to`);
13+
}
14+
15+
return send(remoteWindow, CONSTANTS.POST_MESSAGE_NAMES.OPEN_TUNNEL, {
16+
name,
17+
sendMessage() {
18+
19+
if (isWindowClosed(source)) {
20+
return;
21+
}
22+
23+
try {
24+
canary();
25+
} catch (err) {
26+
return;
27+
}
28+
29+
sendMessage.apply(this, arguments);
30+
}
31+
}, { domain: '*' });
32+
};
33+

src/bridge/child.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
2+
import { SyncPromise as Promise } from 'sync-browser-mocks/src/promise';
3+
import { CONSTANTS } from '../conf';
4+
import { isSameDomain, getOpener, getFrames, util, getFrameByName } from '../lib';
5+
import { receiveMessage } from '../drivers';
6+
7+
import { needsBridge, registerRemoteWindow, rejectRemoteSendMessage, registerRemoteSendMessage, getBridgeName } from './common';
8+
9+
function getRemoteBridgeForWindow(win) {
10+
return Promise.try(() => {
11+
for (let frame of getFrames(win)) {
12+
try {
13+
if (frame && frame !== window && isSameDomain(frame) && frame[CONSTANTS.WINDOW_PROPS.POSTROBOT]) {
14+
return frame;
15+
}
16+
17+
} catch (err) {
18+
continue;
19+
}
20+
}
21+
22+
try {
23+
let frame = getFrameByName(win, getBridgeName(util.getDomain()));
24+
25+
if (!frame) {
26+
return;
27+
}
28+
29+
if (isSameDomain(frame) && frame[CONSTANTS.WINDOW_PROPS.POSTROBOT]) {
30+
return frame;
31+
}
32+
33+
return new Promise(resolve => {
34+
35+
let interval;
36+
let timeout;
37+
38+
interval = setInterval(() => {
39+
if (isSameDomain(frame) && frame[CONSTANTS.WINDOW_PROPS.POSTROBOT]) {
40+
clearInterval(interval);
41+
clearTimeout(timeout);
42+
return resolve(frame);
43+
}
44+
45+
setTimeout(() => {
46+
clearInterval(interval);
47+
return resolve();
48+
}, 2000);
49+
}, 100);
50+
});
51+
52+
} catch (err) {
53+
return;
54+
}
55+
});
56+
}
57+
58+
export function openTunnelToOpener() {
59+
return Promise.try(() => {
60+
61+
let opener = getOpener(window);
62+
63+
if (!opener) {
64+
return;
65+
}
66+
67+
if (!needsBridge({ win: opener })) {
68+
return;
69+
}
70+
71+
registerRemoteWindow(opener);
72+
73+
return getRemoteBridgeForWindow(opener).then(bridge => {
74+
75+
if (!bridge) {
76+
return rejectRemoteSendMessage(opener, new Error(`Can not register with opener: no bridge found in opener`));
77+
}
78+
79+
if (!window.name) {
80+
return rejectRemoteSendMessage(opener, new Error(`Can not register with opener: window does not have a name`));
81+
}
82+
83+
return bridge[CONSTANTS.WINDOW_PROPS.POSTROBOT].openTunnelToParent({
84+
85+
name: window.name,
86+
87+
source: window,
88+
89+
canary() {
90+
// pass
91+
},
92+
93+
sendMessage(message) {
94+
95+
if (!window || window.closed) {
96+
return;
97+
}
98+
99+
receiveMessage({
100+
data: message,
101+
origin: this.origin,
102+
source: this.source
103+
});
104+
}
105+
106+
}).then(({ source, origin, data }) => {
107+
108+
if (source !== opener) {
109+
throw new Error(`Source does not match opener`);
110+
}
111+
112+
registerRemoteSendMessage(source, origin, data.sendMessage);
113+
114+
}).catch(err => {
115+
116+
rejectRemoteSendMessage(opener, err);
117+
throw err;
118+
});
119+
});
120+
});
121+
}

src/bridge/common.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
2+
import { CONFIG, CONSTANTS } from '../conf';
3+
import { util, promise, isSameDomain, isOpener, isSameTopWindow, getUserAgent } from '../lib';
4+
import { global } from '../global';
5+
import { receiveMessage } from '../drivers';
6+
7+
export function needsBridgeForBrowser() {
8+
9+
if (getUserAgent(window).match(/MSIE|trident|edge/i)) {
10+
return true;
11+
}
12+
13+
if (!CONFIG.ALLOW_POSTMESSAGE_POPUP) {
14+
return true;
15+
}
16+
17+
return false;
18+
}
19+
20+
export function needsBridgeForWin(win) {
21+
22+
if (win && isSameTopWindow(window, win)) {
23+
return false;
24+
}
25+
26+
if (win && isSameDomain(win)) {
27+
return false;
28+
}
29+
30+
return true;
31+
}
32+
33+
export function needsBridgeForDomain(domain) {
34+
35+
if (domain && util.getDomain() === util.getDomainFromUrl(domain)) {
36+
return false;
37+
}
38+
39+
return true;
40+
}
41+
42+
export function needsBridge({ win, domain }) {
43+
return needsBridgeForBrowser() && needsBridgeForWin(win) && needsBridgeForDomain(domain);
44+
}
45+
46+
export function getBridgeName(domain) {
47+
48+
domain = domain || util.getDomainFromUrl(domain);
49+
50+
let sanitizedDomain = domain.replace(/[^a-zA-Z0-9]+/g, '_');
51+
52+
let id = `${CONSTANTS.BRIDGE_NAME_PREFIX}_${sanitizedDomain}`;
53+
54+
return id;
55+
}
56+
57+
export function isBridge() {
58+
return window.name && window.name === getBridgeName(util.getDomain());
59+
}
60+
61+
export let documentBodyReady = new promise.Promise(resolve => {
62+
63+
if (window.document && window.document.body) {
64+
return resolve(window.document.body);
65+
}
66+
67+
let interval = setInterval(() => {
68+
if (window.document && window.document.body) {
69+
clearInterval(interval);
70+
return resolve(window.document.body);
71+
}
72+
}, 10);
73+
});
74+
75+
global.remoteWindows = global.remoteWindows || [];
76+
77+
export function registerRemoteWindow(win, timeout = CONFIG.BRIDGE_TIMEOUT) {
78+
let sendMessagePromise = new promise.Promise();
79+
global.clean.push(global.remoteWindows, { win, sendMessagePromise });
80+
}
81+
82+
export function findRemoteWindow(win) {
83+
for (let i = 0; i < global.remoteWindows.length; i++) {
84+
if (global.remoteWindows[i].win === win) {
85+
return global.remoteWindows[i];
86+
}
87+
}
88+
}
89+
90+
export function registerRemoteSendMessage(win, domain, sendMessage) {
91+
92+
let remoteWindow = findRemoteWindow(win);
93+
94+
if (!remoteWindow) {
95+
throw new Error(`Window not found to register sendMessage to`);
96+
}
97+
98+
let sendMessageWrapper = (remoteWin, message, remoteDomain) => {
99+
100+
if (remoteWin !== win) {
101+
throw new Error(`Remote window does not match window`);
102+
}
103+
104+
if (remoteDomain !== `*` && remoteDomain !== domain) {
105+
throw new Error(`Remote domain ${remoteDomain} does not match domain ${domain}`);
106+
}
107+
108+
sendMessage(message);
109+
};
110+
111+
remoteWindow.sendMessagePromise.resolve(sendMessageWrapper);
112+
remoteWindow.sendMessagePromise = promise.Promise.resolve(sendMessageWrapper);
113+
}
114+
115+
export function rejectRemoteSendMessage(win, err) {
116+
117+
let remoteWindow = findRemoteWindow(win);
118+
119+
if (!remoteWindow) {
120+
throw new Error(`Window not found on which to reject sendMessage`);
121+
}
122+
123+
return remoteWindow.sendMessagePromise.asyncReject(err);
124+
}
125+
126+
export function sendBridgeMessage(win, message, domain) {
127+
128+
let messagingChild = isOpener(window, win);
129+
let messagingParent = isOpener(win, window);
130+
131+
if (!messagingChild && !messagingParent) {
132+
throw new Error(`Can only send messages to and from parent and popup windows`);
133+
}
134+
135+
let remoteWindow = findRemoteWindow(win);
136+
137+
if (!remoteWindow) {
138+
throw new Error(`Window not found to send message to`);
139+
}
140+
141+
return remoteWindow.sendMessagePromise.then(sendMessage => {
142+
return sendMessage(win, message, domain);
143+
});
144+
}
145+
146+
global.receiveMessage = function(event) {
147+
return receiveMessage(event);
148+
};

0 commit comments

Comments
 (0)