123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- // Copyright 2009 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.
- goog.provide('goog.windowTest');
- goog.setTestOnly('goog.windowTest');
- goog.require('goog.Promise');
- goog.require('goog.dom');
- goog.require('goog.dom.TagName');
- goog.require('goog.events');
- goog.require('goog.functions');
- goog.require('goog.html.SafeUrl');
- goog.require('goog.labs.userAgent.browser');
- goog.require('goog.labs.userAgent.engine');
- goog.require('goog.labs.userAgent.platform');
- goog.require('goog.string');
- goog.require('goog.testing.PropertyReplacer');
- goog.require('goog.testing.TestCase');
- goog.require('goog.testing.jsunit');
- goog.require('goog.window');
- var newWin;
- var REDIRECT_URL_PREFIX = 'window_test.html?runTests=';
- var WIN_LOAD_TRY_TIMEOUT = 100;
- var MAX_WIN_LOAD_TRIES = 50; // 50x100ms = 5s waiting for window to load.
- var stubs = new goog.testing.PropertyReplacer();
- function shouldRunTests() {
- // MS Edge has a bunch of flaky test failures around window.open.
- // TODO(joeltine): Remove this when http://b/25455129 is fixed.
- return !goog.labs.userAgent.browser.isEdge();
- }
- function setUpPage() {
- var anchors = goog.dom.getElementsByTagNameAndClass(
- goog.dom.TagName.DIV, 'goog-like-link');
- for (var i = 0; i < anchors.length; i++) {
- goog.events.listen(anchors[i], 'click', function(e) {
- goog.window.open(goog.dom.getTextContent(e.target), {'noreferrer': true});
- });
- }
- goog.testing.TestCase.getActiveTestCase().promiseTimeout = 60000; // 60s
- }
- // To test goog.window.open we open a new window with this file again. Once
- // the new window parses this file it sets this variable to true, indicating
- // that the parent test may check window properties like referrer and location.
- var newWinLoaded = true;
- function setUp() {
- newWin = undefined;
- }
- function tearDown() {
- if (newWin) {
- newWin.close();
- }
- stubs.reset();
- }
- /**
- * Uses setTimeout to poll a new window for the "newWinLoaded" variable, which
- * is set once the JavaScript is evaluated in that window.
- *
- * @param {Window} win
- * @return {!goog.Promise<!Window>} Promise for a window that resolves once the
- * window has loaded.
- */
- function waitForTestWindow(win) {
- return new goog.Promise(function(resolve, reject) {
- var checkWindow = function(numTries) {
- if (!win) {
- fail('Could not open new window. Check if popup blocker is enabled.');
- }
- if (numTries > MAX_WIN_LOAD_TRIES) {
- fail('Window did not load after maximum number of checks.');
- }
- if (win.newWinLoaded) {
- resolve(win);
- } else {
- window.setTimeout(checkWindow, WIN_LOAD_TRY_TIMEOUT);
- }
- };
- checkWindow(0);
- });
- }
- /**
- * Opens a window and then verifies that the new window has the expected
- * properties.
- *
- * @param {boolean} noreferrer Whether to test the noreferrer option.
- * @param {string} urlParam Url param to append to the url being opened.
- * @param {boolean} encodeUrlParam_opt Whether to percent-encode urlParam. This
- * is needed because IE will not encode it automatically like other browsers
- * browser and the Closure test server will 400 on certain characters in
- * the URL (like '<' and '"').
- * @return {!goog.Promise} Promise that resolves once the test is complete.
- */
- function doTestOpenWindow(noreferrer, urlParam, encodeUrlParam_opt) {
- if (encodeUrlParam_opt) {
- urlParam = encodeURIComponent(urlParam);
- }
- // TODO(mlourenco): target is set because goog.window.open() will currently
- // allow it to be undefined, which in IE seems to result in the same window
- // being reused, instead of a new one being created. If goog.window.open()
- // is fixed to use "_blank" by default then target can be removed here.
- newWin = goog.window.open(
- REDIRECT_URL_PREFIX + urlParam,
- {'noreferrer': noreferrer, 'target': '_blank'});
- return waitForTestWindow(newWin).then(function(win) {
- verifyWindow(win, noreferrer, urlParam);
- });
- }
- /**
- * Asserts that a newly created window has the correct parameters.
- *
- * @param {Window} win
- * @param {boolean} noreferrer Whether the noreferrer option is being tested.
- * @param {string} urlParam Url param appended to the url being opened.
- */
- function verifyWindow(win, noreferrer, urlParam) {
- if (noreferrer) {
- assertEquals(
- 'Referrer should have been stripped', '', win.document.referrer);
- }
- var winUrl = decodeURI(win.location);
- var expectedUrlSuffix = decodeURI(urlParam);
- assertTrue(
- 'New window href should have ended with <' + expectedUrlSuffix +
- '> but was <' + winUrl + '>',
- goog.string.endsWith(winUrl, expectedUrlSuffix));
- }
- function testOpenNotEncoded() {
- return doTestOpenWindow(false, 'bogus~');
- }
- function testOpenEncoded() {
- return doTestOpenWindow(false, 'bogus%7E');
- }
- function testOpenEncodedPercent() {
- // Intent of url is to pass %7E to the server, so it was encoded to %257E .
- return doTestOpenWindow(false, 'bogus%257E');
- }
- function testOpenNotEncodedHidingReferrer() {
- return doTestOpenWindow(true, 'bogus~');
- }
- function testOpenEncodedHidingReferrer() {
- return doTestOpenWindow(true, 'bogus%7E');
- }
- function testOpenEncodedPercentHidingReferrer() {
- // Intent of url is to pass %7E to the server, so it was encoded to %257E .
- return doTestOpenWindow(true, 'bogus%257E');
- }
- function testOpenSemicolon() {
- return doTestOpenWindow(true, 'beforesemi;aftersemi');
- }
- function testTwoSemicolons() {
- return doTestOpenWindow(true, 'a;b;c');
- }
- function testOpenAmpersand() {
- return doTestOpenWindow(true, 'this&that');
- }
- function testOpenSingleQuote() {
- return doTestOpenWindow(true, "'");
- }
- function testOpenDoubleQuote() {
- return doTestOpenWindow(true, '"', goog.labs.userAgent.browser.isIE());
- }
- function testOpenTag() {
- return doTestOpenWindow(true, '<', goog.labs.userAgent.browser.isIE());
- }
- function testOpenWindowSanitization() {
- var navigatedUrl;
- var mockWin = {open: function(url) { navigatedUrl = url; }};
- goog.window.open('javascript:evil();', {}, mockWin);
- assertEquals(goog.html.SafeUrl.INNOCUOUS_STRING, navigatedUrl);
- // Try the other code path
- goog.window.open({href: 'javascript:evil();'}, {}, mockWin);
- assertEquals(goog.html.SafeUrl.INNOCUOUS_STRING, navigatedUrl);
- goog.window.open('javascript:\'\'', {}, mockWin);
- assertEquals(goog.html.SafeUrl.INNOCUOUS_STRING, navigatedUrl);
- goog.window.open('about:blank', {}, mockWin);
- assertEquals(goog.html.SafeUrl.INNOCUOUS_STRING, navigatedUrl);
- }
- function testOpenWindowNoSanitization() {
- var navigatedUrl;
- var mockWin = {open: function(url) { navigatedUrl = url; }};
- goog.window.open('', {}, mockWin);
- assertEquals('', navigatedUrl);
- goog.window.open(goog.html.SafeUrl.ABOUT_BLANK, {}, mockWin);
- assertEquals('about:blank', navigatedUrl);
- }
- function testOpenBlank() {
- newWin = goog.window.openBlank('Loading...');
- var urlParam = 'bogus~';
- newWin.location.href = REDIRECT_URL_PREFIX + urlParam;
- return waitForTestWindow(newWin).then(function() {
- verifyWindow(newWin, false, urlParam);
- });
- }
- function testOpenBlankReturnsNullPopupBlocker() {
- var mockWin = {
- // emulate popup-blocker by returning a null window on open().
- open: function() { return null; }
- };
- var win = goog.window.openBlank('', {noreferrer: true}, mockWin);
- assertNull(win);
- }
- function testOpenBlankEscapesSafely() {
- // Opening a window with javascript: and then reading from its document.body
- // is problematic because in some browsers the document.body won't have been
- // updated yet, and in some IE versions the parent window does not have
- // access to document.body in new blank window.
- var navigatedUrl;
- var mockWin = {open: function(url) { navigatedUrl = url; }};
- // Test string determines that all necessary escaping transformations happen,
- // and that they happen in the right order (HTML->JS->URI).
- // - " which would be escaped by HTML escaping and JS string escaping. It
- // should be HTML escaped.
- // - \ which would be escaped by JS string escaping and percent-encoded
- // by encodeURI(). It gets JS string escaped first (to two '\') and then
- // percent-encoded.
- var win = goog.window.openBlank('"\\', {}, mockWin);
- assertEquals('javascript:""%5C%5C"', navigatedUrl);
- }
- function testOpenIosBlank() {
- if (!goog.labs.userAgent.engine.isWebKit() || !window.navigator) {
- // Don't even try this on IE8!
- return;
- }
- var attrs = {};
- var dispatchedEvent = null;
- var element = {
- setAttribute: function(name, value) { attrs[name] = value; },
- dispatchEvent: function(event) { dispatchedEvent = event; }
- };
- stubs.replace(window.document, 'createElement', function(name) {
- if (name == goog.dom.TagName.A) {
- return element;
- }
- return null;
- });
- stubs.set(window.navigator, 'standalone', true);
- stubs.replace(goog.labs.userAgent.platform, 'isIos', goog.functions.TRUE);
- var newWin = goog.window.open('http://google.com', {target: '_blank'});
- // This mode cannot return a new window.
- assertNotNull(newWin);
- assertUndefined(newWin.document);
- // Attributes.
- // element.href is directly set through goog.dom.safe.setAnchorHref, not with
- // element.setAttribute.
- assertEquals('http://google.com', element.href);
- assertEquals('_blank', attrs['target']);
- assertEquals('', attrs['rel'] || '');
- // Click event.
- assertNotNull(dispatchedEvent);
- assertEquals('click', dispatchedEvent.type);
- }
- function testOpenIosBlankNoreferrer() {
- if (!goog.labs.userAgent.engine.isWebKit() || !window.navigator) {
- // Don't even try this on IE8!
- return;
- }
- var attrs = {};
- var dispatchedEvent = null;
- var element = {
- setAttribute: function(name, value) { attrs[name] = value; },
- dispatchEvent: function(event) { dispatchedEvent = event; }
- };
- stubs.replace(window.document, 'createElement', function(name) {
- if (name == goog.dom.TagName.A) {
- return element;
- }
- return null;
- });
- stubs.set(window.navigator, 'standalone', true);
- stubs.replace(goog.labs.userAgent.platform, 'isIos', goog.functions.TRUE);
- var newWin = goog.window.open(
- 'http://google.com', {target: '_blank', noreferrer: true});
- // This mode cannot return a new window.
- assertNotNull(newWin);
- assertUndefined(newWin.document);
- // Attributes.
- // element.href is directly set through goog.dom.safe.setAnchorHref, not with
- // element.setAttribute.
- assertEquals('http://google.com', element.href);
- assertEquals('_blank', attrs['target']);
- assertEquals('noreferrer', attrs['rel']);
- // Click event.
- assertNotNull(dispatchedEvent);
- assertEquals('click', dispatchedEvent.type);
- }
- function testOpenNoReferrerEscapesUrl() {
- var documentWriteHtml;
- var mockNewWin = {};
- mockNewWin.document = {
- write: function(html) { documentWriteHtml = html; },
- close: function() {}
- };
- var mockWin = {open: function() { return mockNewWin; }};
- goog.window.open('https://hello&world', {noreferrer: true}, mockWin);
- assertRegExp(
- 'Does not contain expected HTML-escaped string: ' + documentWriteHtml,
- /hello&world/, documentWriteHtml);
- }
|