123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- // Copyright 2006 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview Utilities for window manipulation.
- */
- goog.provide('goog.window');
- goog.require('goog.dom.TagName');
- goog.require('goog.dom.safe');
- goog.require('goog.html.SafeUrl');
- goog.require('goog.html.uncheckedconversions');
- goog.require('goog.labs.userAgent.platform');
- goog.require('goog.string');
- goog.require('goog.string.Const');
- goog.require('goog.userAgent');
- /**
- * Default height for popup windows
- * @type {number}
- */
- goog.window.DEFAULT_POPUP_HEIGHT = 500;
- /**
- * Default width for popup windows
- * @type {number}
- */
- goog.window.DEFAULT_POPUP_WIDTH = 690;
- /**
- * Default target for popup windows
- * @type {string}
- */
- goog.window.DEFAULT_POPUP_TARGET = 'google_popup';
- /**
- * @return {!Window}
- * @suppress {checkTypes}
- * @private
- */
- goog.window.createFakeWindow_ = function() {
- return /** @type {!Window} */ ({});
- };
- /**
- * Opens a new window.
- *
- * @param {?goog.html.SafeUrl|string|?Object} linkRef If an Object with an 'href'
- * attribute (such as HTMLAnchorElement) is passed then the value of 'href'
- * is used, otherwise its toString method is called. Note that if a
- * string|Object is used, it will be sanitized with SafeUrl.sanitize().
- *
- * @param {?Object=} opt_options supports the following options:
- * 'target': (string) target (window name). If null, linkRef.target will
- * be used.
- * 'width': (number) window width.
- * 'height': (number) window height.
- * 'top': (number) distance from top of screen
- * 'left': (number) distance from left of screen
- * 'toolbar': (boolean) show toolbar
- * 'scrollbars': (boolean) show scrollbars
- * 'location': (boolean) show location
- * 'statusbar': (boolean) show statusbar
- * 'menubar': (boolean) show menubar
- * 'resizable': (boolean) resizable
- * 'noreferrer': (boolean) whether to attempt to remove the referrer header
- * from the request headers. Does this by opening a blank window that
- * then redirects to the target url, so users may see some flickering.
- *
- * @param {?Window=} opt_parentWin Parent window that should be used to open the
- * new window.
- *
- * @return {?Window} Returns the window object that was opened. This returns
- * null if a popup blocker prevented the window from being
- * opened. In case when a new window is opened in a different
- * browser sandbox (such as iOS standalone mode), the returned
- * object is a emulated Window object that functions as if
- * a cross-origin window has been opened.
- */
- goog.window.open = function(linkRef, opt_options, opt_parentWin) {
- if (!opt_options) {
- opt_options = {};
- }
- var parentWin = opt_parentWin || window;
- /** @type {!goog.html.SafeUrl} */
- var safeLinkRef;
- if (linkRef instanceof goog.html.SafeUrl) {
- safeLinkRef = linkRef;
- } else {
- // HTMLAnchorElement has a toString() method with the same behavior as
- // goog.Uri in all browsers except for Safari, which returns
- // '[object HTMLAnchorElement]'. We check for the href first, then
- // assume that it's a goog.Uri or String otherwise.
- var url =
- typeof linkRef.href != 'undefined' ? linkRef.href : String(linkRef);
- safeLinkRef = goog.html.SafeUrl.sanitize(url);
- }
- var target = opt_options.target || linkRef.target;
- var sb = [];
- for (var option in opt_options) {
- switch (option) {
- case 'width':
- case 'height':
- case 'top':
- case 'left':
- sb.push(option + '=' + opt_options[option]);
- break;
- case 'target':
- case 'noreferrer':
- break;
- default:
- sb.push(option + '=' + (opt_options[option] ? 1 : 0));
- }
- }
- var optionString = sb.join(',');
- var newWin;
- if (goog.labs.userAgent.platform.isIos() && parentWin.navigator &&
- parentWin.navigator['standalone'] && target && target != '_self') {
- // iOS in standalone mode disregards "target" in window.open and always
- // opens new URL in the same window. The workout around is to create an "A"
- // element and send a click event to it.
- // Notice that the "A" tag does NOT have to be added to the DOM.
- var a = /** @type {!HTMLAnchorElement} */
- (parentWin.document.createElement(String(goog.dom.TagName.A)));
- goog.dom.safe.setAnchorHref(a, safeLinkRef);
- a.setAttribute('target', target);
- if (opt_options['noreferrer']) {
- a.setAttribute('rel', 'noreferrer');
- }
- var click = document.createEvent('MouseEvent');
- click.initMouseEvent(
- 'click',
- true, // canBubble
- true, // cancelable
- parentWin,
- 1); // detail = mousebutton
- a.dispatchEvent(click);
- // New window is not available in this case. Instead, a fake Window object
- // is returned. In particular, it will have window.document undefined. In
- // general, it will appear to most of clients as a Window for a different
- // origin. Since iOS standalone web apps are run in their own sandbox, this
- // is the most appropriate return value.
- newWin = goog.window.createFakeWindow_();
- } else if (opt_options['noreferrer']) {
- // Use a meta-refresh to stop the referrer from being included in the
- // request headers. This seems to be the only cross-browser way to
- // remove the referrer. It also allows for the opener to be set to null
- // in the new window, thus disallowing the opened window from navigating
- // its opener.
- //
- // Detecting user agent and then using a different strategy per browser
- // would allow the referrer to leak in case of an incorrect/missing user
- // agent.
- //
- // Also note that we can't use goog.dom.safe.openInWindow here, as it
- // requires a goog.string.Const 'name' parameter, while we're using plain
- // strings here for target.
- newWin = parentWin.open('', target, optionString);
- var sanitizedLinkRef = goog.html.SafeUrl.unwrap(safeLinkRef);
- if (newWin) {
- if (goog.userAgent.EDGE_OR_IE) {
- // IE/EDGE can't parse the content attribute if the url contains
- // a semicolon. We can fix this by adding quotes around the url, but
- // then we can't parse quotes in the URL correctly. We take a
- // best-effort approach.
- //
- // If the URL has semicolons, wrap it in single quotes to protect
- // the semicolons.
- // If the URL has semicolons and single quotes, url-encode the single
- // quotes as well.
- //
- // This is imperfect. Notice that both ' and ; are reserved characters
- // in URIs, so this could do the wrong thing, but at least it will
- // do the wrong thing in only rare cases.
- // ugh.
- if (goog.string.contains(sanitizedLinkRef, ';')) {
- sanitizedLinkRef = "'" + sanitizedLinkRef.replace(/'/g, '%27') + "'";
- }
- }
- newWin.opener = null;
- // TODO(rjamet): Building proper SafeHtml with SafeHtml.createMetaRefresh
- // pulls in a lot of compiled code, which is composed of various unneeded
- // goog.html parts such as SafeStyle.create among others. So, for now,
- // keep the unchecked conversion until we figure out how to make the
- // dependencies of createSafeHtmlTagSecurityPrivateDoNotAccessOrElse less
- // heavy.
- var safeHtml =
- goog.html.uncheckedconversions
- .safeHtmlFromStringKnownToSatisfyTypeContract(
- goog.string.Const.from(
- 'b/12014412, meta tag with sanitized URL'),
- '<META HTTP-EQUIV="refresh" content="0; url=' +
- goog.string.htmlEscape(sanitizedLinkRef) + '">');
- goog.dom.safe.documentWrite(newWin.document, safeHtml);
- newWin.document.close();
- }
- } else {
- newWin = parentWin.open(
- goog.html.SafeUrl.unwrap(safeLinkRef), target, optionString);
- }
- // newWin is null if a popup blocker prevented the window open.
- return newWin;
- };
- /**
- * Opens a new window without any real content in it.
- *
- * This can be used to get around popup blockers if you need to open a window
- * in response to a user event, but need to do asynchronous work to determine
- * the URL to open, and then set the URL later.
- *
- * Example usage:
- *
- * var newWin = goog.window.openBlank('Loading...');
- * setTimeout(
- * function() {
- * newWin.location.href = 'http://www.google.com';
- * }, 100);
- *
- * @param {string=} opt_message String to show in the new window. This string
- * will be HTML-escaped to avoid XSS issues.
- * @param {?Object=} opt_options Options to open window with.
- * {@see goog.window.open for exact option semantics}.
- * @param {?Window=} opt_parentWin Parent window that should be used to open the
- * new window.
- * @return {?Window} Returns the window object that was opened. This returns
- * null if a popup blocker prevented the window from being
- * opened.
- */
- goog.window.openBlank = function(opt_message, opt_options, opt_parentWin) {
- // Open up a window with the loading message and nothing else.
- // This will be interpreted as HTML content type with a missing doctype
- // and html/body tags, but is otherwise acceptable.
- //
- // IMPORTANT: The order of escaping is crucial here in order to avoid XSS.
- // First, HTML-escaping is needed because the result of the JS expression
- // is evaluated as HTML. Second, JS-string escaping is needed; this avoids
- // \u escaping from inserting HTML tags and \ from escaping the final ".
- // Finally, URL percent-encoding is done with encodeURI(); this
- // avoids percent-encoding from bypassing HTML and JS escaping.
- //
- // Note: There are other ways the same result could be achieved but the
- // current behavior was preserved when this code was refactored to use
- // SafeUrl, in order to avoid breakage.
- var loadingMessage;
- if (!opt_message) {
- loadingMessage = '';
- } else {
- loadingMessage =
- goog.string.escapeString(goog.string.htmlEscape(opt_message));
- }
- var url = goog.html.uncheckedconversions
- .safeUrlFromStringKnownToSatisfyTypeContract(
- goog.string.Const.from(
- 'b/12014412, encoded string in javascript: URL'),
- 'javascript:"' + encodeURI(loadingMessage) + '"');
- return /** @type {?Window} */ (
- goog.window.open(url, opt_options, opt_parentWin));
- };
- /**
- * Raise a help popup window, defaulting to "Google standard" size and name.
- *
- * (If your project is using GXPs, consider using {@link PopUpLink.gxp}.)
- *
- * @param {?goog.html.SafeUrl|string|?Object} linkRef If an Object with an 'href'
- * attribute (such as HTMLAnchorElement) is passed then the value of 'href'
- * is used, otherwise otherwise its toString method is called. Note that
- * if a string|Object is used, it will be sanitized with SafeUrl.sanitize().
- *
- * @param {?Object=} opt_options Options to open window with.
- * {@see goog.window.open for exact option semantics}
- * Additional wrinkles to the options:
- * - if 'target' field is null, linkRef.target will be used. If *that's*
- * null, the default is "google_popup".
- * - if 'width' field is not specified, the default is 690.
- * - if 'height' field is not specified, the default is 500.
- *
- * @return {boolean} true if the window was not popped up, false if it was.
- */
- goog.window.popup = function(linkRef, opt_options) {
- if (!opt_options) {
- opt_options = {};
- }
- // set default properties
- opt_options['target'] = opt_options['target'] || linkRef['target'] ||
- goog.window.DEFAULT_POPUP_TARGET;
- opt_options['width'] =
- opt_options['width'] || goog.window.DEFAULT_POPUP_WIDTH;
- opt_options['height'] =
- opt_options['height'] || goog.window.DEFAULT_POPUP_HEIGHT;
- var newWin = goog.window.open(linkRef, opt_options);
- if (!newWin) {
- return true;
- }
- newWin.focus();
- return false;
- };
|