-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Expand file tree
/
Copy pathHighlightRegistry-highlightsFromPoint.html
More file actions
255 lines (209 loc) · 13.5 KB
/
HighlightRegistry-highlightsFromPoint.html
File metadata and controls
255 lines (209 loc) · 13.5 KB
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
<!doctype html>
<meta name="author" title="Fernando Fiori" href="mailto:ffiori@microsoft.com">
<meta name="assert" content="HighlightRegistry.highlightsFromPoint returns the Highlights present at the coordinates provided as argument in the right order.">
<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/highlight/HighlightsFromPointsExplainer.md">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
::highlight(example-highlight) {
background-color: rgba(0, 255, 255, 0.5);
}
::highlight(example-highlight-2) {
background-color: rgba(255, 255, 0, 0.5);
}
body {
font-family: monospace;
}
</style>
<span id="example-span">0123456789</span>
<script>
test(() => {
assert_throws_js(TypeError, () => { CSS.highlights.highlightsFromPoint("asdf", 10); });
assert_throws_js(TypeError, () => { CSS.highlights.highlightsFromPoint(10); });
assert_throws_js(TypeError, () => { CSS.highlights.highlightsFromPoint(); });
assert_throws_js(TypeError, () => { CSS.highlights.highlightsFromPoint(10, 10, "asdf"); });
}, 'CSS.highlights.highlightsFromPoint() should throw when called with incorrect parameters.');
test(() => {
assert_equals(CSS.highlights.highlightsFromPoint(-1,-1).length, 0);
}, 'CSS.highlights.highlightsFromPoint() should return an empty array when called with a point outside the document.');
test(() => {
// Set two Highlights in this way: 01[234[56789]]
const textNode = document.querySelector("#example-span");
let range1 = new Range();
range1.setStart(textNode.childNodes[0], 2);
range1.setEnd(textNode.childNodes[0], 10);
let range2 = new Range();
range2.setStart(textNode.childNodes[0], 5);
range2.setEnd(textNode.childNodes[0], 10);
let highlight1 = new Highlight(range1);
CSS.highlights.set("example-highlight", highlight1);
let highlight2 = new Highlight(range2);
CSS.highlights.set("example-highlight-2", highlight2);
const rect = textNode.getBoundingClientRect();
const characterWidth = rect.width / textNode.textContent.length;
// No Highlights outside of text contents.
let x = rect.left - 1;
let y = rect.top - 1;
let highlight_hit_results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(highlight_hit_results.length, 0, 'CSS.highlights.highlightsFromPoint() returns an empty array when the coordinates provided are outside of the text contents');
// Get x and y coordinates between '0' and '1'.
x = rect.left + characterWidth;
y = rect.top + rect.height / 2;
highlights = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(highlight_hit_results.length, 0, 'CSS.highlights.highlightsFromPoint() returns an empty array when the coordinates provided point at no Highlights');
// Get x and y coordinates between '2' and '3'.
x = rect.left + 3 * characterWidth;
highlight_hit_results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(highlight_hit_results.length, 1, 'CSS.highlights.highlightsFromPoint() returns exactly one HighlightHitResult when the coordinates provided point at one Highlight');
assert_equals(highlight_hit_results[0].highlight, highlight1, 'CSS.highlights.highlightsFromPoint() returns a HighlightHitResult with the Highlight present at the coordinates provided');
assert_array_equals(highlight_hit_results[0].ranges, [range1], 'CSS.highlights.highlightsFromPoint() returns a HighlightHitResult with the ranges of the Highlight present at the coordinates provided');
// Get x and y coordinates between '6' and '7'.
// Same priority for the Highlights, break tie by order of registration.
x = rect.left + 7 * characterWidth;
highlight_hit_results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(highlight_hit_results.length, 2, 'CSS.highlights.highlightsFromPoint() returns exactly two HighlightHitResults when the coordinates provided point at two overlapping Highlights');
assert_equals(highlight_hit_results[0].highlight, highlight2, 'CSS.highlights.highlightsFromPoint() returns first a HighlightHitResult with the Highlight registered last when both Highlights present at the point provided have the same priority');
assert_equals(highlight_hit_results[1].highlight, highlight1, 'CSS.highlights.highlightsFromPoint() returns last a HighlightHitResult with the Highlight registered first when both Highlights present at the point provided have the same priority');
assert_array_equals(highlight_hit_results[0].ranges, [range2], 'CSS.highlights.highlightsFromPoint() returns first a HighlightHitResult with the ranges of the Highlight present on top at the coordinates provided');
assert_array_equals(highlight_hit_results[1].ranges, [range1], 'CSS.highlights.highlightsFromPoint() returns last a HighlightHitResult with the ranges of the Highlight present at the bottom at the coordinates provided');
// Now highlight1 should be first because it's got higher priority.
highlight1.priority = 2;
highlight2.priority = 1;
highlight_hit_results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(highlight_hit_results.length, 2, 'CSS.highlights.highlightsFromPoint() returns exactly two HighlightHitResults when the coordinates provided point at two overlapping Highlights');
assert_equals(highlight_hit_results[0].highlight, highlight1, 'CSS.highlights.highlightsFromPoint() returns first a HighlightHitResult with the Highlight with higher priority when there are two Highlights present at the point provided');
assert_equals(highlight_hit_results[1].highlight, highlight2, 'CSS.highlights.highlightsFromPoint() returns last a HighlightHitResult with the Highlight with lower priority when there are two Highlights present at the point provided');
}, 'CSS.highlights.highlightsFromPoint() returns the Highlights with their corresponding ranges present at the given point in the right order.');
test(() => {
const iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
const iframeWindow = iframe.contentWindow;
const iframeDoc = iframeWindow.document;
const span = iframeDoc.createElement("span");
span.textContent = "0123456789";
iframeDoc.body.appendChild(span);
const range = iframeDoc.createRange();
range.setStart(span.childNodes[0], 0);
range.setEnd(span.childNodes[0], 10);
const highlight = new iframeWindow.Highlight(range);
iframeWindow.CSS.highlights.set("example-highlight", highlight);
const results = iframeWindow.CSS.highlights.highlightsFromPoint(5, 5);
assert_equals(results.length, 0,
'highlightsFromPoint() returns empty array on a display:none iframe');
document.body.removeChild(iframe);
}, 'CSS.highlights.highlightsFromPoint() returns empty array when called on a display:none iframe document.');
test(() => {
CSS.highlights.clear();
const textNode = document.querySelector("#example-span");
const range = new Range();
range.setStart(textNode.childNodes[0], 0);
range.setEnd(textNode.childNodes[0], 10);
const highlight = new Highlight(range);
CSS.highlights.set("example-highlight", highlight);
const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight;
// Query a point just outside the right edge of the viewport.
let results = CSS.highlights.highlightsFromPoint(viewportWidth + 1, 5);
assert_equals(results.length, 0,
'highlightsFromPoint() returns empty for x beyond viewport width');
// Query a point just outside the bottom edge of the viewport.
results = CSS.highlights.highlightsFromPoint(5, viewportHeight + 1);
assert_equals(results.length, 0,
'highlightsFromPoint() returns empty for y beyond viewport height');
// Verify the highlight is actually found at a valid point inside the viewport.
const rect = textNode.getBoundingClientRect();
const characterWidth = rect.width / textNode.textContent.length;
const x = rect.left + 3 * characterWidth;
const y = rect.top + rect.height / 2;
results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(results.length, 1,
'highlightsFromPoint() returns the highlight at valid coordinates inside the viewport');
}, 'CSS.highlights.highlightsFromPoint() returns empty array for coordinates outside the viewport.');
test(() => {
CSS.highlights.clear();
const textNode = document.querySelector("#example-span");
const range = new Range();
range.setStart(textNode.childNodes[0], 5);
range.setEnd(textNode.childNodes[0], 5);
assert_true(range.collapsed, 'range should be collapsed');
const highlight = new Highlight(range);
CSS.highlights.set("example-highlight", highlight);
const rect = textNode.getBoundingClientRect();
const characterWidth = rect.width / textNode.textContent.length;
const x = rect.left + 5 * characterWidth;
const y = rect.top + rect.height / 2;
const results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(results.length, 0,
'highlightsFromPoint() does not return a highlight whose only range is collapsed');
}, 'CSS.highlights.highlightsFromPoint() does not return highlights with only collapsed ranges.');
test(() => {
CSS.highlights.clear();
const textNode = document.querySelector("#example-span");
// Create a valid StaticRange, add it to a highlight, then invalidate it
// by removing the start container from the document.
const tempSpan = document.createElement("span");
tempSpan.textContent = "temporary";
document.body.appendChild(tempSpan);
const staticRange = new StaticRange({
startContainer: tempSpan.childNodes[0],
startOffset: 0,
endContainer: tempSpan.childNodes[0],
endOffset: 9
});
const validRange = new Range();
validRange.setStart(textNode.childNodes[0], 0);
validRange.setEnd(textNode.childNodes[0], 10);
const highlight = new Highlight(staticRange, validRange);
CSS.highlights.set("example-highlight", highlight);
const rect = tempSpan.getBoundingClientRect();
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;
// Verify the highlight is returned before invalidation.
let results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(results.length, 1,
'highlightsFromPoint() returns highlight before StaticRange is invalidated');
// Invalidate the StaticRange by removing its start container.
document.body.removeChild(tempSpan);
// The point now has no content, so check at the valid range's position.
const validRect = textNode.getBoundingClientRect();
const characterWidth = validRect.width / textNode.textContent.length;
const x2 = validRect.left + 3 * characterWidth;
const y2 = validRect.top + validRect.height / 2;
results = CSS.highlights.highlightsFromPoint(x2, y2);
assert_equals(results.length, 1,
'highlightsFromPoint() still returns the highlight via the valid range');
assert_array_equals(results[0].ranges, [validRange],
'the invalid StaticRange is not included in the returned ranges');
}, 'CSS.highlights.highlightsFromPoint() skips invalid StaticRanges.');
test(() => {
CSS.highlights.clear();
const textNode = document.querySelector("#example-span");
// Create two overlapping ranges: [0123456789] and [012345]6789
const range1 = new Range();
range1.setStart(textNode.childNodes[0], 0);
range1.setEnd(textNode.childNodes[0], 10);
const range2 = new Range();
range2.setStart(textNode.childNodes[0], 0);
range2.setEnd(textNode.childNodes[0], 6);
const highlight = new Highlight(range1, range2);
CSS.highlights.set("example-highlight", highlight);
const rect = textNode.getBoundingClientRect();
const characterWidth = rect.width / textNode.textContent.length;
// Query at character '3' -- both ranges cover this point.
let x = rect.left + 3 * characterWidth;
let y = rect.top + rect.height / 2;
let results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(results.length, 1,
'highlightsFromPoint() returns exactly one HighlightHitResult for a single highlight');
assert_array_equals(results[0].ranges, [range1, range2],
'both overlapping ranges are returned when both cover the queried point');
// Query at character '8' -- only range1 covers this point.
x = rect.left + 8 * characterWidth;
results = CSS.highlights.highlightsFromPoint(x, y);
assert_equals(results.length, 1,
'highlightsFromPoint() returns the highlight');
assert_array_equals(results[0].ranges, [range1],
'only the range that covers the queried point is returned');
}, 'CSS.highlights.highlightsFromPoint() returns all overlapping ranges within the same highlight.');
</script>