-
Notifications
You must be signed in to change notification settings - Fork 141
/
formats.js
145 lines (127 loc) · 3.8 KB
/
formats.js
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
export function isAbsoluteUrl(value) {
try {
// eslint-disable-next-line no-new
new URL(value);
} catch (e) {
// Couldn't parse, invalid.
return false;
}
// Could parse without a base, it's absolute.
return true;
}
function isRelativeUrl(value) {
// A dummy protocol that shouldn't exist.
const protocol = 'asdoiasjdpoaisjd:';
let url;
try {
url = new URL(value, `${protocol}//foo`);
} catch (e) {
// URL is invalid.
return false;
}
// If the URL is relative, then the protocol will stay the same, but host
// could change due to protocol relative. Also check that the URL isn't
// absolute, since then it is using the dummy protocol we defined.
return url.protocol === protocol && !isAbsoluteUrl(value);
}
export function isAnyUrl(value) {
return isAbsoluteUrl(value) || isRelativeUrl(value);
}
export function isStrictRelativeUrl(value) {
return !value.startsWith('//') && isRelativeUrl(value);
}
export function isSecureUrl(value) {
let url;
try {
url = new URL(value);
} catch (e) {
// It's invalid or not absolute.
return false;
}
// URL is absolute, check against secure protocols.
return ['https:', 'wss:'].includes(url.protocol);
}
export function isOrigin(value) {
let url;
try {
url = new URL(value);
} catch {
return false;
}
if (!/^https?:/.test(url.protocol)) {
return false;
}
if (value.includes('*')) {
return false;
}
// url.origin is punycode so a direct check against string won't work.
// url.href appends a slash even if not in the original string, so we
// additionally check that the value does not end with slash.
if (value.endsWith('/') || url.href !== new URL(url.origin).href) {
return false;
}
return true;
}
export function imageDataOrStrictRelativeUrl(value) {
// Do not accept a string which resolves as an absolute URL, or any
// protocol-relative URL, except PNG or JPG data URLs.
return (
value.startsWith('data:image/png;base64,') ||
value.startsWith('data:image/jpeg;base64,') ||
isStrictRelativeUrl(value)
);
}
export const isUnresolvedRelativeUrl = isStrictRelativeUrl;
export function manifestShortcutKey(value) {
// Partially taken from Firefox directly via
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/Schemas.jsm#987
// Please make sure to always update this function when doing a schema update
// to pull in the most recent implementation to stay up-to-date with upstream.
const MEDIA_KEYS =
/^(MediaNextTrack|MediaPlayPause|MediaPrevTrack|MediaStop)$/;
const BASIC_KEYS =
/^([A-Z0-9]|Comma|Period|Home|End|PageUp|PageDown|Space|Insert|Delete|Up|Down|Left|Right)$/;
const FUNCTION_KEYS = /^(F[1-9]|F1[0-2])$/;
if (MEDIA_KEYS.test(value.trim())) {
return true;
}
const modifiers = value.split('+').map((s) => s.trim());
const key = modifiers.pop();
if (!BASIC_KEYS.test(key) && !FUNCTION_KEYS.test(key)) {
return false;
}
const chromeModifierKeyMap = {
Alt: 'alt',
Command: 'accel',
Ctrl: 'accel',
MacCtrl: 'control',
Shift: 'shift',
};
const chromeModifiers = modifiers.map((m) => chromeModifierKeyMap[m]);
// If the modifier wasn't found it will be undefined.
if (chromeModifiers.some((modifier) => !modifier)) {
return false;
}
switch (modifiers.length) {
case 0:
// A lack of modifiers is only allowed with function keys.
if (!FUNCTION_KEYS.test(key)) {
return false;
}
break;
case 1:
// Shift is only allowed on its own with function keys.
if (chromeModifiers[0] === 'shift' && !FUNCTION_KEYS.test(key)) {
return false;
}
break;
case 2:
if (chromeModifiers[0] === chromeModifiers[1]) {
return false;
}
break;
default:
return false;
}
return true;
}