| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| 'use strict'; |
| |
| /** |
| * Creates a new Navigator for navigating to links inside or outside the PDF. |
| * @param {string} originalUrl The original page URL. |
| * @param {Object} viewport The viewport info of the page. |
| * @param {Object} paramsParser The object for URL parsing. |
| * @param {Function} navigateInCurrentTabCallback The Callback function that |
| * gets called when navigation happens in the current tab. |
| * @param {Function} navigateInNewTabCallback The Callback function that gets |
| * called when navigation happens in the new tab. |
| */ |
| function Navigator(originalUrl, |
| viewport, |
| paramsParser, |
| navigateInCurrentTabCallback, |
| navigateInNewTabCallback) { |
| this.originalUrl_ = originalUrl; |
| this.viewport_ = viewport; |
| this.paramsParser_ = paramsParser; |
| this.navigateInCurrentTabCallback_ = navigateInCurrentTabCallback; |
| this.navigateInNewTabCallback_ = navigateInNewTabCallback; |
| } |
| |
| Navigator.prototype = { |
| /** |
| * @private |
| * Function to navigate to the given URL. This might involve navigating |
| * within the PDF page or opening a new url (in the same tab or a new tab). |
| * @param {string} url The URL to navigate to. |
| * @param {boolean} newTab Whether to perform the navigation in a new tab or |
| * in the current tab. |
| */ |
| navigate: function(url, newTab) { |
| if (url.length == 0) |
| return; |
| |
| // If |urlFragment| starts with '#', then it's for the same URL with a |
| // different URL fragment. |
| if (url.charAt(0) == '#') { |
| // if '#' is already present in |originalUrl| then remove old fragment |
| // and add new url fragment. |
| var hashIndex = this.originalUrl_.search('#'); |
| if (hashIndex != -1) |
| url = this.originalUrl_.substring(0, hashIndex) + url; |
| else |
| url = this.originalUrl_ + url; |
| } |
| |
| // If there's no scheme, then take a guess at the scheme. |
| if (url.indexOf('://') == -1 && url.indexOf('mailto:') == -1) |
| url = this.guessUrlWithoutScheme_(url); |
| |
| if (!this.isValidUrl_(url)) |
| return; |
| |
| if (newTab) { |
| this.navigateInNewTabCallback_(url); |
| } else { |
| this.paramsParser_.getViewportFromUrlParams( |
| url, this.onViewportReceived_.bind(this)); |
| } |
| }, |
| |
| /** |
| * @private |
| * Called when the viewport position is received. |
| * @param {Object} viewportPosition Dictionary containing the viewport |
| * position. |
| */ |
| onViewportReceived_: function(viewportPosition) { |
| var originalUrl = this.originalUrl_; |
| var hashIndex = originalUrl.search('#'); |
| if (hashIndex != -1) |
| originalUrl = originalUrl.substring(0, hashIndex); |
| |
| var newUrl = viewportPosition.url; |
| hashIndex = newUrl.search('#'); |
| if (hashIndex != -1) |
| newUrl = newUrl.substring(0, hashIndex); |
| |
| var pageNumber = viewportPosition.page; |
| if (pageNumber != undefined && originalUrl == newUrl) |
| this.viewport_.goToPage(pageNumber); |
| else |
| this.navigateInCurrentTabCallback_(viewportPosition.url); |
| }, |
| |
| /** |
| * @private |
| * Checks if the URL starts with a scheme and s not just a scheme. |
| * @param {string} The input URL |
| * @return {boolean} Whether the url is valid. |
| */ |
| isValidUrl_: function(url) { |
| // Make sure |url| starts with a valid scheme. |
| if (url.indexOf('http://') != 0 && |
| url.indexOf('https://') != 0 && |
| url.indexOf('ftp://') != 0 && |
| url.indexOf('file://') != 0 && |
| url.indexOf('mailto:') != 0) { |
| return false; |
| } |
| |
| // Make sure |url| is not only a scheme. |
| if (url == 'http://' || |
| url == 'https://' || |
| url == 'ftp://' || |
| url == 'file://' || |
| url == 'mailto:') { |
| return false; |
| } |
| |
| return true; |
| }, |
| |
| /** |
| * @private |
| * Attempt to figure out what a URL is when there is no scheme. |
| * @param {string} The input URL |
| * @return {string} The URL with a scheme or the original URL if it is not |
| * possible to determine the scheme. |
| */ |
| guessUrlWithoutScheme_: function(url) { |
| // If the original URL is mailto:, that does not make sense to start with, |
| // and neither does adding |url| to it. |
| // If the original URL is not a valid URL, this cannot make a valid URL. |
| // In both cases, just bail out. |
| if (this.originalUrl_.startsWith('mailto:') || |
| !this.isValidUrl_(this.originalUrl_)) { |
| return url; |
| } |
| |
| // Check for absolute paths. |
| if (url.startsWith('/')) { |
| var schemeEndIndex = this.originalUrl_.indexOf('://'); |
| var firstSlash = this.originalUrl_.indexOf('/', schemeEndIndex + 3); |
| // e.g. http://www.foo.com/bar -> http://www.foo.com |
| var domain = firstSlash != -1 ? |
| this.originalUrl_.substr(0, firstSlash) : this.originalUrl_; |
| return domain + url; |
| } |
| |
| // Check for obvious relative paths. |
| var isRelative = false; |
| if (url.startsWith('.') || url.startsWith('\\')) |
| isRelative = true; |
| |
| // In Adobe Acrobat Reader XI, it looks as though links with less than |
| // 2 dot separators in the domain are considered relative links, and |
| // those with 2 of more are considered http URLs. e.g. |
| // |
| // www.foo.com/bar -> http |
| // foo.com/bar -> relative link |
| if (!isRelative) { |
| var domainSeparatorIndex = url.indexOf('/'); |
| var domainName = domainSeparatorIndex == -1 ? |
| url : url.substr(0, domainSeparatorIndex); |
| var domainDotCount = (domainName.match(/\./g) || []).length; |
| if (domainDotCount < 2) |
| isRelative = true; |
| } |
| |
| if (isRelative) { |
| var slashIndex = this.originalUrl_.lastIndexOf('/'); |
| var path = slashIndex != -1 ? |
| this.originalUrl_.substr(0, slashIndex) : this.originalUrl_; |
| return path + '/' + url; |
| } |
| |
| return 'http://' + url; |
| } |
| }; |