1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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,
};
};
|