diff options
Diffstat (limited to 'testing/marionette/navigate.js')
-rw-r--r-- | testing/marionette/navigate.js | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/testing/marionette/navigate.js b/testing/marionette/navigate.js new file mode 100644 index 000000000..edbfa552a --- /dev/null +++ b/testing/marionette/navigate.js @@ -0,0 +1,119 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.importGlobalProperties(["URL"]); + +this.EXPORTED_SYMBOLS = ["navigate"]; + +this.navigate = {}; + +/** + * Determines if we expect to get a DOM load event (DOMContentLoaded) + * on navigating to the |future| URL. + * + * @param {string} current + * URL the browser is currently visiting. + * @param {string=} future + * Destination URL, if known. + * + * @return {boolean} + * Full page load would be expected if future is followed. + * + * @throws TypeError + * If |current| is not defined, or any of |current| or |future| + * are invalid URLs. + */ +navigate.isLoadEventExpected = function (current, future = undefined) { + if (typeof current == "undefined") { + throw TypeError("Expected at least one URL"); + } + + // assume we will go somewhere exciting + if (typeof future == "undefined") { + return true; + } + + let cur = new navigate.IdempotentURL(current); + let fut = new navigate.IdempotentURL(future); + + // assume javascript:<whatever> will modify current document + // but this is not an entirely safe assumption to make, + // considering it could be used to set window.location + if (fut.protocol == "javascript:") { + return false; + } + + // navigating to same url, but with any hash + if (cur.origin == fut.origin && + cur.pathname == fut.pathname && + fut.hash != "") { + return false; + } + + return true; +}; + +/** + * Sane URL implementation that normalises URL fragments (hashes) and + * path names for "data:" URLs, and makes them idempotent. + * + * At the time of writing this, the web is approximately 10 000 days (or + * ~27.39 years) old. One should think that by this point we would have + * solved URLs. The following code is prudent example that we have not. + * + * When a URL with a fragment identifier but no explicit name for the + * fragment is given, i.e. "#", the {@code hash} property a {@code URL} + * object computes is an empty string. This is incidentally the same as + * the default value of URLs without fragments, causing a lot of confusion. + * + * This means that the URL "http://a/#b" produces a hash of "#b", but that + * "http://a/#" produces "". This implementation rectifies this behaviour + * by returning the actual full fragment, which is "#". + * + * "data:" URLs that contain fragments, which if they have the same origin + * and path name are not meant to cause a page reload on navigation, + * confusingly adds the fragment to the {@code pathname} property. + * This implementation remedies this behaviour by trimming it off. + * + * The practical result of this is that while {@code URL} objects are + * not idempotent, the returned URL elements from this implementation + * guarantees that |url.hash == url.hash|. + * + * @param {string|URL} o + * Object to make an URL of. + * + * @return {navigate.IdempotentURL} + * Considered by some to be a somewhat saner URL. + * + * @throws TypeError + * If |o| is not a valid type or if is a string that cannot be parsed + * as a URL. + */ +navigate.IdempotentURL = function (o) { + let url = new URL(o); + + let hash = url.hash; + if (hash == "" && url.href[url.href.length - 1] == "#") { + hash = "#"; + } + + return { + hash: hash, + host: url.host, + hostname: url.hostname, + href: url.href, + origin: url.origin, + password: url.password, + pathname: url.pathname, + port: url.port, + protocol: url.protocol, + search: url.search, + searchParams: url.searchParams, + username: url.username, + }; +}; |