gecko-dev/testing/marionette/navigate.js
Andreas Tolfsen 175975b04b Bug 1280300 - Support navigation by fragment; r=automatedtester
Adds support for navigating to a fragment on the currenty visible document
without waiting for a DOM event that the document has been fully loaded.

This addresses https://github.com/mozilla/geckodriver/issues/150.

MozReview-Commit-ID: 7uiPT5cjGQE

--HG--
extra : rebase_source : f9152a6623a25c17e10dc3bc6552b8e635c21317
2016-07-19 18:47:33 +01:00

125 lines
3.8 KiB
JavaScript

/* 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 = "#";
}
let pathname = url.pathname;
if (url.protocol == "data:" && hash != "") {
pathname = pathname.substring(0, pathname.length - hash.length);
}
return {
hash: hash,
host: url.host,
hostname: url.hostname,
href: url.href,
origin: url.origin,
password: url.password,
pathname: pathname,
port: url.port,
protocol: url.protocol,
search: url.search,
searchParams: url.searchParams,
username: url.username,
};
};