Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/window/window.js
4049 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities for window manipulation.
9
*/
10
11
12
goog.provide('goog.window');
13
14
goog.require('goog.dom');
15
goog.require('goog.dom.TagName');
16
goog.require('goog.dom.safe');
17
goog.require('goog.html.SafeUrl');
18
goog.require('goog.html.uncheckedconversions');
19
goog.require('goog.labs.userAgent.platform');
20
goog.require('goog.string');
21
goog.require('goog.string.Const');
22
goog.require('goog.userAgent');
23
goog.requireType('goog.string.TypedString');
24
25
26
/**
27
* Default height for popup windows
28
* @type {number}
29
*/
30
goog.window.DEFAULT_POPUP_HEIGHT = 500;
31
32
33
/**
34
* Default width for popup windows
35
* @type {number}
36
*/
37
goog.window.DEFAULT_POPUP_WIDTH = 690;
38
39
40
/**
41
* Default target for popup windows
42
* @type {string}
43
*/
44
goog.window.DEFAULT_POPUP_TARGET = 'google_popup';
45
46
47
/**
48
* @return {!Window}
49
* @suppress {checkTypes}
50
* @private
51
*/
52
goog.window.createFakeWindow_ = function() {
53
'use strict';
54
return /** @type {!Window} */ ({});
55
};
56
57
/**
58
* Opens a new window.
59
*
60
* @param {!goog.html.SafeUrl|string|!Object|null} linkRef If an Object with an
61
* 'href' attribute (such as HTMLAnchorElement) is passed then the value of
62
* 'href' is used, otherwise its toString method is called. Note that if a
63
* string|Object is used, it will be sanitized with SafeUrl.sanitize().
64
*
65
* @param {?Object=} opt_options supports the following options:
66
* 'target': (string) target (window name). If null, linkRef.target will
67
* be used.
68
* 'width': (number) window width.
69
* 'height': (number) window height.
70
* 'top': (number) distance from top of screen
71
* 'left': (number) distance from left of screen
72
* 'toolbar': (boolean) show toolbar
73
* 'scrollbars': (boolean) show scrollbars
74
* 'location': (boolean) show location
75
* 'statusbar': (boolean) show statusbar
76
* 'menubar': (boolean) show menubar
77
* 'resizable': (boolean) resizable
78
* 'noreferrer': (boolean) whether to attempt to remove the referrer header
79
* from the request headers. Does this by opening a blank window that
80
* then redirects to the target url, so users may see some flickering.
81
* 'noopener': (boolean) whether to remove the `opener` property from the
82
* window object of the newly created window. The property contains a
83
* reference to the original window, and can be used to launch a
84
* reverse tabnabbing attack.
85
*
86
* @param {?Window=} opt_parentWin Parent window that should be used to open the
87
* new window.
88
*
89
* @return {?Window} Returns the window object that was opened. This returns
90
* null if a popup blocker prevented the window from being
91
* opened. In case when a new window is opened in a different
92
* browser sandbox (such as iOS standalone mode), the returned
93
* object is a emulated Window object that functions as if
94
* a cross-origin window has been opened.
95
*/
96
goog.window.open = function(linkRef, opt_options, opt_parentWin) {
97
'use strict';
98
if (!opt_options) {
99
opt_options = {};
100
}
101
var parentWin = opt_parentWin || window;
102
103
/** @type {!goog.html.SafeUrl} */
104
var safeLinkRef;
105
106
if (linkRef instanceof goog.html.SafeUrl) {
107
safeLinkRef = linkRef;
108
} else {
109
// HTMLAnchorElement has a toString() method with the same behavior as
110
// goog.Uri in all browsers except for Safari, which returns
111
// '[object HTMLAnchorElement]'. We check for the href first, then
112
// assume that it's a goog.Uri or String otherwise.
113
/**
114
* @type {string|!goog.string.TypedString}
115
* @suppress {missingProperties}
116
*/
117
var url =
118
typeof linkRef.href != 'undefined' ? linkRef.href : String(linkRef);
119
safeLinkRef = goog.html.SafeUrl.sanitize(url);
120
}
121
122
/** @suppress {strictMissingProperties} */
123
const browserSupportsCoop = self.crossOriginIsolated !== undefined;
124
let referrerPolicy = 'strict-origin-when-cross-origin';
125
if (window.Request) {
126
/** @suppress {missingProperties} */
127
referrerPolicy = new Request('/').referrerPolicy;
128
}
129
const pageSetsUnsafeReferrerPolicy = referrerPolicy === 'unsafe-url';
130
131
// Opening popups with `noreferrer` and a COOP policy of
132
// `same-origin-allow-popups` doesn't work. The below is a workaround
133
// for this browser limitation.
134
let noReferrerOption = opt_options['noreferrer'];
135
if (browserSupportsCoop && noReferrerOption) {
136
if (pageSetsUnsafeReferrerPolicy) {
137
// If the browser supports COOP, and the page explicitly sets a
138
// referrer-policy of `unsafe-url`, and the caller requests that the
139
// referrer is hidden, then things may break. We can't support the
140
// noreferrer option in this case, but ignoring it is potentially unsafe
141
// since the page is configured to expose the full URL. We just throw in
142
// this case so that callers can make a decision as to what they want to
143
// do here.
144
throw new Error(
145
'Cannot use the noreferrer option on a page that sets a referrer-policy of `unsafe-url` in modern browsers!');
146
}
147
// Any browser that supports COOP defaults to a referrer policy that hides
148
// the full URL. So we don't need to explicitly hide the referrer ourselves
149
// and can instead rely on the browser's default referrer policy to hide the
150
// referrer.
151
noReferrerOption = false;
152
}
153
154
/** @suppress {missingProperties} loose references to 'target' */
155
/** @suppress {strictMissingProperties} */
156
var target = opt_options.target || linkRef.target;
157
158
var sb = [];
159
for (var option in opt_options) {
160
switch (option) {
161
case 'width':
162
case 'height':
163
case 'top':
164
case 'left':
165
sb.push(option + '=' + opt_options[option]);
166
break;
167
case 'target':
168
case 'noopener':
169
case 'noreferrer':
170
break;
171
default:
172
sb.push(option + '=' + (opt_options[option] ? 1 : 0));
173
}
174
}
175
var optionString = sb.join(',');
176
177
var newWin;
178
if (goog.labs.userAgent.platform.isIos() && parentWin.navigator &&
179
parentWin.navigator['standalone'] && target && target != '_self') {
180
// iOS in standalone mode disregards "target" in window.open and always
181
// opens new URL in the same window. The workaround is to create an "A"
182
// element and send a click event to it.
183
// Notice that the "A" tag does NOT have to be added to the DOM.
184
185
var a = goog.dom.createElement(goog.dom.TagName.A);
186
goog.dom.safe.setAnchorHref(a, safeLinkRef);
187
188
a.target = target;
189
if (noReferrerOption) {
190
a.rel = 'noreferrer';
191
}
192
193
var click = /** @type {!MouseEvent} */ (document.createEvent('MouseEvent'));
194
click.initMouseEvent(
195
'click',
196
true, // canBubble
197
true, // cancelable
198
parentWin,
199
1); // detail = mousebutton
200
a.dispatchEvent(click);
201
// New window is not available in this case. Instead, a fake Window object
202
// is returned. In particular, it will have window.document undefined. In
203
// general, it will appear to most of clients as a Window for a different
204
// origin. Since iOS standalone web apps are run in their own sandbox, this
205
// is the most appropriate return value.
206
newWin = goog.window.createFakeWindow_();
207
} else if (noReferrerOption) {
208
// This code used to use meta-refresh to stop the referrer from being
209
// included in the request headers. This was the only cross-browser way
210
// to remove the referrer circa 2009. However, this never worked in Chrome,
211
// and, instead newWin.opener had to be set to null on this browser. This
212
// behavior is slated to be removed in Chrome and should not be relied
213
// upon. Referrer Policy is the only spec'd and supported way of stripping
214
// referrers and works across all current browsers. This is used in
215
// addition to the aforementioned tricks.
216
//
217
// We also set the opener to be set to null in the new window, thus
218
// disallowing the opened window from navigating its opener.
219
//
220
// Detecting user agent and then using a different strategy per browser
221
// would allow the referrer to leak in case of an incorrect/missing user
222
// agent.
223
newWin = goog.dom.safe.openInWindow('', parentWin, target, optionString);
224
225
var sanitizedLinkRef = goog.html.SafeUrl.unwrap(safeLinkRef);
226
if (newWin) {
227
if (goog.userAgent.EDGE_OR_IE) {
228
// IE/EDGE can't parse the content attribute if the url contains
229
// a semicolon. We can fix this by adding quotes around the url, but
230
// then we can't parse quotes in the URL correctly. We take a
231
// best-effort approach.
232
//
233
// If the URL has semicolons, wrap it in single quotes to protect
234
// the semicolons.
235
// If the URL has semicolons and single quotes, url-encode the single
236
// quotes as well.
237
//
238
// This is imperfect. Notice that both ' and ; are reserved characters
239
// in URIs, so this could do the wrong thing, but at least it will
240
// do the wrong thing in only rare cases.
241
// ugh.
242
if (goog.string.contains(sanitizedLinkRef, ';')) {
243
sanitizedLinkRef =
244
'\'' + sanitizedLinkRef.replace(/'/g, '%27') + '\'';
245
}
246
}
247
newWin.opener = null;
248
// Using a blank value for the URL causes the new window to load
249
// this window's location. Instead, using this javascript URL causes an
250
// error to be thrown in the blank document and abort the loading of the
251
// page location. The window's location does update, but the content is
252
// never requested/loaded.
253
// Other values tried here include:
254
// - an empty string or no value at all (page load succeeds)
255
// - 'about:blank' (causes security exceptions if users
256
// later try and assign to the window's location, as about:blank is now
257
// cross-origin from this window).
258
if (sanitizedLinkRef === '') {
259
sanitizedLinkRef = 'javascript:\'\'';
260
}
261
// TODO(rjamet): Building proper SafeHtml with SafeHtml.createMetaRefresh
262
// pulls in a lot of compiled code, which is composed of various unneeded
263
// goog.html parts such as SafeStyle.create among others. So, for now,
264
// keep the unchecked conversion until we figure out how to make the
265
// dependencies of createSafeHtmlTagSecurityPrivateDoNotAccessOrElse less
266
// heavy.
267
var safeHtml =
268
goog.html.uncheckedconversions
269
.safeHtmlFromStringKnownToSatisfyTypeContract(
270
goog.string.Const.from(
271
'b/12014412, meta tag with sanitized URL'),
272
'<meta name="referrer" content="no-referrer">' +
273
'<meta http-equiv="refresh" content="0; url=' +
274
goog.string.htmlEscape(sanitizedLinkRef) + '">');
275
276
// During window loading `newWin.document` may be unset in some browsers.
277
// Storing and checking a reference to the document prevents NPEs.
278
var newDoc = newWin.document;
279
if (newDoc && newDoc.write) {
280
goog.dom.safe.documentWrite(newDoc, safeHtml);
281
newDoc.close();
282
}
283
}
284
} else {
285
newWin = goog.dom.safe.openInWindow(
286
safeLinkRef, parentWin, target, optionString);
287
// Passing in 'noopener' into the 'windowFeatures' param of window.open(...)
288
// will yield a feature-deprived browser. This is an known issue, tracked
289
// here: https://github.com/whatwg/html/issues/1902
290
if (newWin && opt_options['noopener']) {
291
newWin.opener = null;
292
}
293
// If the caller specified noreferrer and we hit this branch, it means that
294
// we're already running on a modern enough browser that the referrer is
295
// hidden by default. But setting noreferrer implies noopener too, so we
296
// also have to clear the opener here.
297
if (newWin && opt_options['noreferrer']) {
298
newWin.opener = null;
299
}
300
}
301
// newWin is null if a popup blocker prevented the window open.
302
return newWin;
303
};
304
305
306
/**
307
* Opens a new window without any real content in it.
308
*
309
* This can be used to get around popup blockers if you need to open a window
310
* in response to a user event, but need to do asynchronous work to determine
311
* the URL to open, and then set the URL later.
312
*
313
* Example usage:
314
*
315
* var newWin = goog.window.openBlank('Loading...');
316
* setTimeout(
317
* function() {
318
* newWin.location.href = 'http://www.google.com';
319
* }, 100);
320
*
321
* @param {string=} opt_message String to show in the new window. This string
322
* will be HTML-escaped to avoid XSS issues.
323
* @param {?Object=} opt_options Options to open window with.
324
* {@see goog.window.open for exact option semantics}.
325
* @param {?Window=} opt_parentWin Parent window that should be used to open the
326
* new window.
327
* @return {?Window} Returns the window object that was opened. This returns
328
* null if a popup blocker prevented the window from being
329
* opened.
330
*/
331
goog.window.openBlank = function(opt_message, opt_options, opt_parentWin) {
332
'use strict';
333
const win =
334
/** @type {?Window} */ (goog.window.open('', opt_options, opt_parentWin));
335
if (win && opt_message) {
336
const body = win.document.body;
337
if (body) {
338
// The body can be undefined in IE, where for some reason the created
339
// document doesn't have a body.
340
body.textContent = opt_message;
341
}
342
}
343
return win;
344
};
345
346
347
/**
348
* Raise a help popup window, defaulting to "Google standard" size and name.
349
*
350
* (If your project is using GXPs, consider using {@link PopUpLink.gxp}.)
351
*
352
* @param {?goog.html.SafeUrl|string|?Object} linkRef If an Object with an
353
* 'href' attribute (such as HTMLAnchorElement) is passed then the value of
354
* 'href' is used, otherwise otherwise its toString method is called. Note
355
* that if a string|Object is used, it will be sanitized with
356
* SafeUrl.sanitize().
357
*
358
* @param {?Object=} opt_options Options to open window with.
359
* {@see goog.window.open for exact option semantics}
360
* Additional wrinkles to the options:
361
* - if 'target' field is null, linkRef.target will be used. If *that's*
362
* null, the default is "google_popup".
363
* - if 'width' field is not specified, the default is 690.
364
* - if 'height' field is not specified, the default is 500.
365
*
366
* @return {boolean} true if the window was not popped up, false if it was.
367
*/
368
goog.window.popup = function(linkRef, opt_options) {
369
'use strict';
370
if (!opt_options) {
371
opt_options = {};
372
}
373
374
// set default properties
375
opt_options['target'] = opt_options['target'] || linkRef['target'] ||
376
goog.window.DEFAULT_POPUP_TARGET;
377
opt_options['width'] =
378
opt_options['width'] || goog.window.DEFAULT_POPUP_WIDTH;
379
opt_options['height'] =
380
opt_options['height'] || goog.window.DEFAULT_POPUP_HEIGHT;
381
382
var newWin = goog.window.open(linkRef, opt_options);
383
if (!newWin) {
384
return true;
385
}
386
newWin.focus();
387
388
return false;
389
};
390
391