iframe.ts 3.9 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

/**
 * Represents a window in a possible chain of iframes
 */
export interface IWindowChainElement {
	/**
	 * The window object for it
	 */
	window: Window;
	/**
	 * The iframe element inside the window.parent corresponding to window
	 */
	iframeElement: HTMLIFrameElement;
}

B
Benjamin Pasero 已提交
21 22
let hasDifferentOriginAncestorFlag: boolean = false;
let sameOriginWindowChainCache: IWindowChainElement[] = null;
E
Erich Gamma 已提交
23

B
Benjamin Pasero 已提交
24
function getParentWindowIfSameOrigin(w: Window): Window {
E
Erich Gamma 已提交
25 26 27
	if (!w.parent || w.parent === w) {
		return null;
	}
J
Joao Moreno 已提交
28

E
Erich Gamma 已提交
29 30
	// Cannot really tell if we have access to the parent window unless we try to access something in it
	try {
B
Benjamin Pasero 已提交
31 32
		let location = w.location;
		let parentLocation = w.parent.location;
E
Erich Gamma 已提交
33 34 35 36 37 38 39 40
		if (location.protocol !== parentLocation.protocol || location.hostname !== parentLocation.hostname || location.port !== parentLocation.port) {
			hasDifferentOriginAncestorFlag = true;
			return null;
		}
	} catch (e) {
		hasDifferentOriginAncestorFlag = true;
		return null;
	}
J
Joao Moreno 已提交
41

E
Erich Gamma 已提交
42 43 44
	return w.parent;
}

B
Benjamin Pasero 已提交
45 46 47 48
function findIframeElementInParentWindow(parentWindow: Window, childWindow: Window): HTMLIFrameElement {
	let parentWindowIframes = parentWindow.document.getElementsByTagName('iframe');
	let iframe: HTMLIFrameElement;
	for (let i = 0, len = parentWindowIframes.length; i < len; i++) {
E
Erich Gamma 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
		iframe = parentWindowIframes[i];
		if (iframe.contentWindow === childWindow) {
			return iframe;
		}
	}
	return null;
}

/**
 * Returns a chain of embedded windows with the same origin (which can be accessed programmatically).
 * Having a chain of length 1 might mean that the current execution environment is running outside of an iframe or inside an iframe embedded in a window with a different origin.
 * To distinguish if at one point the current execution environment is running inside a window with a different origin, see hasDifferentOriginAncestor()
 */
export function getSameOriginWindowChain(): IWindowChainElement[] {
	if (!sameOriginWindowChainCache) {
		sameOriginWindowChainCache = [];
B
Benjamin Pasero 已提交
65
		let w = window, parent: Window;
E
Erich Gamma 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
		do {
			parent = getParentWindowIfSameOrigin(w);
			if (parent) {
				sameOriginWindowChainCache.push({
					window: w,
					iframeElement: findIframeElementInParentWindow(parent, w)
				});
			} else {
				sameOriginWindowChainCache.push({
					window: w,
					iframeElement: null
				});
			}
			w = parent;
		} while (w);
	}
	return sameOriginWindowChainCache.slice(0);
}

/**
 * Returns true if the current execution environment is chained in a list of iframes which at one point ends in a window with a different origin.
 * Returns false if the current execution environment is not running inside an iframe or if the entire chain of iframes have the same origin.
 */
export function hasDifferentOriginAncestor(): boolean {
	if (!sameOriginWindowChainCache) {
		getSameOriginWindowChain();
	}
	return hasDifferentOriginAncestorFlag;
}

/**
 * Returns the position of `childWindow` relative to `ancestorWindow`
 */
B
Benjamin Pasero 已提交
99
export function getPositionOfChildWindowRelativeToAncestorWindow(childWindow: Window, ancestorWindow: any) {
J
Joao Moreno 已提交
100

E
Erich Gamma 已提交
101 102 103 104 105 106
	if (!ancestorWindow || childWindow === ancestorWindow) {
		return {
			top: 0,
			left: 0
		};
	}
J
Joao Moreno 已提交
107

B
Benjamin Pasero 已提交
108
	let top = 0, left = 0;
J
Joao Moreno 已提交
109

B
Benjamin Pasero 已提交
110
	let windowChain = getSameOriginWindowChain();
J
Joao Moreno 已提交
111

B
Benjamin Pasero 已提交
112 113
	for (let i = 0; i < windowChain.length; i++) {
		let windowChainEl = windowChain[i];
J
Joao Moreno 已提交
114

E
Erich Gamma 已提交
115 116 117
		if (windowChainEl.window === ancestorWindow) {
			break;
		}
J
Joao Moreno 已提交
118

E
Erich Gamma 已提交
119 120 121
		if (!windowChainEl.iframeElement) {
			break;
		}
J
Joao Moreno 已提交
122

B
Benjamin Pasero 已提交
123
		let boundingRect = windowChainEl.iframeElement.getBoundingClientRect();
E
Erich Gamma 已提交
124 125 126
		top += boundingRect.top;
		left += boundingRect.left;
	}
J
Joao Moreno 已提交
127

E
Erich Gamma 已提交
128 129 130 131
	return {
		top: top,
		left: left
	};
B
Benjamin Pasero 已提交
132
}