|
Lines 1-5
a/Source/WebCore/page/DOMSelection.cpp_sec1
|
| 1 |
/* |
1 |
/* |
| 2 |
* Copyright (C) 2007, 2009, 2016 Apple Inc. All rights reserved. |
2 |
* Copyright (C) 2007-2020 Apple Inc. All rights reserved. |
| 3 |
* Copyright (C) 2012 Google Inc. All rights reserved. |
3 |
* Copyright (C) 2012 Google Inc. All rights reserved. |
| 4 |
* |
4 |
* |
| 5 |
* Redistribution and use in source and binary forms, with or without |
5 |
* Redistribution and use in source and binary forms, with or without |
|
Lines 39-44
a/Source/WebCore/page/DOMSelection.cpp_sec2
|
| 39 |
|
39 |
|
| 40 |
namespace WebCore { |
40 |
namespace WebCore { |
| 41 |
|
41 |
|
|
|
42 |
static bool shouldUseSelectionAssociatedRange() |
| 43 |
{ |
| 44 |
// FIXME: This will be controlled by a feature flag during development, I guess. |
| 45 |
return false; |
| 46 |
} |
| 47 |
|
| 42 |
static Node* selectionShadowAncestor(Frame& frame) |
48 |
static Node* selectionShadowAncestor(Frame& frame) |
| 43 |
{ |
49 |
{ |
| 44 |
auto* node = frame.selection().selection().base().anchorNode(); |
50 |
auto* node = frame.selection().selection().base().anchorNode(); |
|
Lines 166-240
unsigned DOMSelection::rangeCount() const
a/Source/WebCore/page/DOMSelection.cpp_sec3
|
| 166 |
return !frame || frame->selection().isNone() ? 0 : 1; |
172 |
return !frame || frame->selection().isNone() ? 0 : 1; |
| 167 |
} |
173 |
} |
| 168 |
|
174 |
|
| 169 |
void DOMSelection::collapse(Node* node, unsigned offset) |
175 |
ExceptionOr<void> DOMSelection::collapse(Node* node, unsigned offset) |
| 170 |
{ |
176 |
{ |
| 171 |
if (!isValidForPosition(node)) |
177 |
auto frame = makeRefPtr(this->frame()); |
| 172 |
return; |
178 |
if (!frame) |
| 173 |
|
179 |
return { }; |
| 174 |
Ref<Frame> protectedFrame(*frame()); |
180 |
if (shouldUseSelectionAssociatedRange()) { |
| 175 |
protectedFrame->selection().moveTo(createLegacyEditingPosition(node, offset), Affinity::Downstream); |
181 |
if (!node) { |
|
|
182 |
removeAllRanges(); |
| 183 |
return { }; |
| 184 |
} |
| 185 |
// FIXME: The Selection API specification does not mention this, but Web Platform Tests results expect it. |
| 186 |
if (node->isDocumentTypeNode()) |
| 187 |
return Exception { InvalidNodeTypeError }; |
| 188 |
// FIXME: The Selection API specification requires this exception, but it may be incompatible in practice. |
| 189 |
if (offset > node->length()) |
| 190 |
return Exception { IndexSizeError }; |
| 191 |
if (!frame->document()->contains(*node)) |
| 192 |
return { }; |
| 193 |
} else { |
| 194 |
if (!isValidForPosition(node)) |
| 195 |
return { }; |
| 196 |
} |
| 197 |
auto& selection = frame->selection(); |
| 198 |
selection.disassociateLiveRange(); |
| 199 |
selection.moveTo(createLegacyEditingPosition(node, offset), Affinity::Downstream); |
| 200 |
return { }; |
| 176 |
} |
201 |
} |
| 177 |
|
202 |
|
| 178 |
ExceptionOr<void> DOMSelection::collapseToEnd() |
203 |
ExceptionOr<void> DOMSelection::collapseToEnd() |
| 179 |
{ |
204 |
{ |
| 180 |
auto* frame = this->frame(); |
205 |
auto frame = makeRefPtr(this->frame()); |
| 181 |
if (!frame) |
206 |
if (!frame) |
| 182 |
return { }; |
207 |
return { }; |
| 183 |
auto& selection = frame->selection(); |
208 |
auto& selection = frame->selection(); |
| 184 |
if (selection.isNone()) |
209 |
if (selection.isNone()) |
| 185 |
return Exception { InvalidStateError }; |
210 |
return Exception { InvalidStateError }; |
| 186 |
|
211 |
selection.disassociateLiveRange(); |
| 187 |
Ref<Frame> protector(*frame); |
|
|
| 188 |
selection.moveTo(selection.selection().end(), Affinity::Downstream); |
212 |
selection.moveTo(selection.selection().end(), Affinity::Downstream); |
| 189 |
return { }; |
213 |
return { }; |
| 190 |
} |
214 |
} |
| 191 |
|
215 |
|
| 192 |
ExceptionOr<void> DOMSelection::collapseToStart() |
216 |
ExceptionOr<void> DOMSelection::collapseToStart() |
| 193 |
{ |
217 |
{ |
| 194 |
auto* frame = this->frame(); |
218 |
auto frame = makeRefPtr(this->frame()); |
| 195 |
if (!frame) |
219 |
if (!frame) |
| 196 |
return { }; |
220 |
return { }; |
| 197 |
auto& selection = frame->selection(); |
221 |
auto& selection = frame->selection(); |
| 198 |
if (selection.isNone()) |
222 |
if (selection.isNone()) |
| 199 |
return Exception { InvalidStateError }; |
223 |
return Exception { InvalidStateError }; |
| 200 |
|
224 |
selection.disassociateLiveRange(); |
| 201 |
Ref<Frame> protector(*frame); |
|
|
| 202 |
selection.moveTo(selection.selection().start(), Affinity::Downstream); |
225 |
selection.moveTo(selection.selection().start(), Affinity::Downstream); |
| 203 |
return { }; |
226 |
return { }; |
| 204 |
} |
227 |
} |
| 205 |
|
228 |
|
| 206 |
void DOMSelection::empty() |
229 |
void DOMSelection::empty() |
| 207 |
{ |
230 |
{ |
| 208 |
auto* frame = this->frame(); |
231 |
removeAllRanges(); |
| 209 |
if (!frame) |
|
|
| 210 |
return; |
| 211 |
frame->selection().clear(); |
| 212 |
} |
232 |
} |
| 213 |
|
233 |
|
| 214 |
void DOMSelection::setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset) |
234 |
ExceptionOr<void> DOMSelection::setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset) |
| 215 |
{ |
235 |
{ |
| 216 |
if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) |
236 |
auto frame = makeRefPtr(this->frame()); |
| 217 |
return; |
237 |
if (!frame) |
| 218 |
|
238 |
return { }; |
| 219 |
Ref<Frame> protectedFrame(*frame()); |
239 |
if (shouldUseSelectionAssociatedRange()) { |
| 220 |
protectedFrame->selection().moveTo(createLegacyEditingPosition(baseNode, baseOffset), createLegacyEditingPosition(extentNode, extentOffset), Affinity::Downstream); |
240 |
// FIXME: The Selection API specification requires this exception, but it may be incompatible in practice. |
|
|
241 |
// FIXME: We should do this by making the arguments non-nullable in the IDL file, once shouldUseSelectionAssociatedRange is always true. |
| 242 |
if (!baseNode || !extentNode) |
| 243 |
return Exception { TypeError }; |
| 244 |
// FIXME: The Selection API specification requires this exception, but it may be incompatible in practice. |
| 245 |
if (baseOffset > baseNode->length() || extentOffset > extentNode->length()) |
| 246 |
return Exception { IndexSizeError }; |
| 247 |
auto& document = *frame->document(); |
| 248 |
if (!document.contains(*baseNode) || !document.contains(*extentNode)) |
| 249 |
return { }; |
| 250 |
} else { |
| 251 |
if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) |
| 252 |
return { }; |
| 253 |
} |
| 254 |
auto& selection = frame->selection(); |
| 255 |
selection.disassociateLiveRange(); |
| 256 |
selection.moveTo(createLegacyEditingPosition(baseNode, baseOffset), createLegacyEditingPosition(extentNode, extentOffset), Affinity::Downstream); |
| 257 |
return { }; |
| 221 |
} |
258 |
} |
| 222 |
|
259 |
|
| 223 |
void DOMSelection::setPosition(Node* node, unsigned offset) |
260 |
ExceptionOr<void> DOMSelection::setPosition(Node* node, unsigned offset) |
| 224 |
{ |
261 |
{ |
| 225 |
if (!isValidForPosition(node)) |
262 |
return collapse(node, offset); |
| 226 |
return; |
|
|
| 227 |
|
| 228 |
Ref<Frame> protectedFrame(*frame()); |
| 229 |
protectedFrame->selection().moveTo(createLegacyEditingPosition(node, offset), Affinity::Downstream); |
| 230 |
} |
263 |
} |
| 231 |
|
264 |
|
| 232 |
void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString) |
265 |
void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString) |
| 233 |
{ |
266 |
{ |
| 234 |
auto* frame = this->frame(); |
|
|
| 235 |
if (!frame) |
| 236 |
return; |
| 237 |
|
| 238 |
FrameSelection::EAlteration alter; |
267 |
FrameSelection::EAlteration alter; |
| 239 |
if (equalLettersIgnoringASCIICase(alterString, "extend")) |
268 |
if (equalLettersIgnoringASCIICase(alterString, "extend")) |
| 240 |
alter = FrameSelection::AlterationExtend; |
269 |
alter = FrameSelection::AlterationExtend; |
|
Lines 277-284
void DOMSelection::modify(const String& alterString, const String& directionStri
a/Source/WebCore/page/DOMSelection.cpp_sec4
|
| 277 |
else |
306 |
else |
| 278 |
return; |
307 |
return; |
| 279 |
|
308 |
|
| 280 |
Ref<Frame> protector(*frame); |
309 |
if (auto frame = makeRefPtr(this->frame())) |
| 281 |
frame->selection().modify(alter, direction, granularity); |
310 |
frame->selection().modify(alter, direction, granularity); |
| 282 |
} |
311 |
} |
| 283 |
|
312 |
|
| 284 |
ExceptionOr<void> DOMSelection::extend(Node& node, unsigned offset) |
313 |
ExceptionOr<void> DOMSelection::extend(Node& node, unsigned offset) |
|
Lines 288-296
ExceptionOr<void> DOMSelection::extend(Node& node, unsigned offset)
a/Source/WebCore/page/DOMSelection.cpp_sec5
|
| 288 |
return { }; |
317 |
return { }; |
| 289 |
if (offset > node.length()) |
318 |
if (offset > node.length()) |
| 290 |
return Exception { IndexSizeError }; |
319 |
return Exception { IndexSizeError }; |
| 291 |
if (!isValidForPosition(&node)) |
320 |
if (shouldUseSelectionAssociatedRange()) { |
| 292 |
return { }; |
321 |
if (!frame->document()->contains(node)) |
| 293 |
frame->selection().setExtent(createLegacyEditingPosition(&node, offset), Affinity::Downstream); |
322 |
return { }; |
|
|
323 |
} else { |
| 324 |
if (!isValidForPosition(&node)) |
| 325 |
return { }; |
| 326 |
} |
| 327 |
auto& selection = frame->selection(); |
| 328 |
selection.disassociateLiveRange(); |
| 329 |
selection.setExtent(createLegacyEditingPosition(&node, offset), Affinity::Downstream); |
| 294 |
return { }; |
330 |
return { }; |
| 295 |
} |
331 |
} |
| 296 |
|
332 |
|
|
Lines 299-304
ExceptionOr<Ref<Range>> DOMSelection::getRangeAt(unsigned index)
a/Source/WebCore/page/DOMSelection.cpp_sec6
|
| 299 |
if (index >= rangeCount()) |
335 |
if (index >= rangeCount()) |
| 300 |
return Exception { IndexSizeError }; |
336 |
return Exception { IndexSizeError }; |
| 301 |
auto& frame = *this->frame(); |
337 |
auto& frame = *this->frame(); |
|
|
338 |
if (shouldUseSelectionAssociatedRange()) |
| 339 |
return frame.selection().associatedLiveRange().releaseNonNull(); |
| 302 |
if (auto shadowAncestor = selectionShadowAncestor(frame)) |
340 |
if (auto shadowAncestor = selectionShadowAncestor(frame)) |
| 303 |
return createLiveRange(makeSimpleRange(*makeBoundaryPointBeforeNode(*shadowAncestor))); |
341 |
return createLiveRange(makeSimpleRange(*makeBoundaryPointBeforeNode(*shadowAncestor))); |
| 304 |
return createLiveRange(*frame.selection().selection().firstRange()); |
342 |
return createLiveRange(*frame.selection().selection().firstRange()); |
|
Lines 317-341
void DOMSelection::addRange(Range& liveRange)
a/Source/WebCore/page/DOMSelection.cpp_sec7
|
| 317 |
auto frame = makeRefPtr(this->frame()); |
355 |
auto frame = makeRefPtr(this->frame()); |
| 318 |
if (!frame) |
356 |
if (!frame) |
| 319 |
return; |
357 |
return; |
| 320 |
|
|
|
| 321 |
auto range = makeSimpleRange(liveRange); |
| 322 |
auto& selection = frame->selection(); |
358 |
auto& selection = frame->selection(); |
| 323 |
|
359 |
if (shouldUseSelectionAssociatedRange()) { |
|
|
360 |
if (selection.isNone()) |
| 361 |
selection.associateLiveRange(liveRange); |
| 362 |
return; |
| 363 |
} |
| 364 |
auto range = makeSimpleRange(liveRange); |
| 324 |
if (auto selectedRange = selection.selection().toNormalizedRange()) { |
365 |
if (auto selectedRange = selection.selection().toNormalizedRange()) { |
| 325 |
if (!selectedRange->start.container->containingShadowRoot() && intersects(*selectedRange, range)) |
366 |
if (!selectedRange->start.container->containingShadowRoot() && intersects(*selectedRange, range)) |
| 326 |
selection.setSelection(unionRange(*selectedRange, range)); |
367 |
selection.setSelection(unionRange(*selectedRange, range)); |
| 327 |
return; |
368 |
return; |
| 328 |
} |
369 |
} |
| 329 |
|
|
|
| 330 |
selection.setSelection(range); |
370 |
selection.setSelection(range); |
| 331 |
} |
371 |
} |
| 332 |
|
372 |
|
|
|
373 |
ExceptionOr<void> DOMSelection::removeRange(Range& liveRange) |
| 374 |
{ |
| 375 |
if (&liveRange != frame()->selection().associatedLiveRange()) |
| 376 |
return Exception { NotFoundError }; |
| 377 |
removeAllRanges(); |
| 378 |
return { }; |
| 379 |
} |
| 380 |
|
| 333 |
void DOMSelection::deleteFromDocument() |
381 |
void DOMSelection::deleteFromDocument() |
| 334 |
{ |
382 |
{ |
| 335 |
auto frame = makeRefPtr(this->frame()); |
383 |
auto frame = makeRefPtr(this->frame()); |
| 336 |
if (!frame) |
384 |
if (!frame) |
| 337 |
return; |
385 |
return; |
| 338 |
|
386 |
|
|
|
387 |
if (shouldUseSelectionAssociatedRange()) { |
| 388 |
if (auto associatedRange = frame->selection().associatedLiveRange()) |
| 389 |
associatedRange->deleteContents(); |
| 390 |
return; |
| 391 |
} |
| 392 |
|
| 339 |
auto selectedRange = frame->selection().selection().toNormalizedRange(); |
393 |
auto selectedRange = frame->selection().selection().toNormalizedRange(); |
| 340 |
if (!selectedRange || selectedRange->start.container->containingShadowRoot()) |
394 |
if (!selectedRange || selectedRange->start.container->containingShadowRoot()) |
| 341 |
return; |
395 |
return; |
|
Lines 346-354
void DOMSelection::deleteFromDocument()
a/Source/WebCore/page/DOMSelection.cpp_sec8
|
| 346 |
|
400 |
|
| 347 |
bool DOMSelection::containsNode(Node& node, bool allowPartial) const |
401 |
bool DOMSelection::containsNode(Node& node, bool allowPartial) const |
| 348 |
{ |
402 |
{ |
| 349 |
// FIXME: This behavior does not match what the selection API standard specifies. |
403 |
if (!shouldUseSelectionAssociatedRange()) { |
| 350 |
if (node.isTextNode()) |
404 |
// FIXME: This does not match the Selection API specification. |
| 351 |
allowPartial = true; |
405 |
if (node.isTextNode()) |
|
|
406 |
allowPartial = true; |
| 407 |
} |
| 352 |
|
408 |
|
| 353 |
auto* frame = this->frame(); |
409 |
auto* frame = this->frame(); |
| 354 |
if (!frame) |
410 |
if (!frame) |
|
Lines 363-370
bool DOMSelection::containsNode(Node& node, bool allowPartial) const
a/Source/WebCore/page/DOMSelection.cpp_sec9
|
| 363 |
|
419 |
|
| 364 |
void DOMSelection::selectAllChildren(Node& node) |
420 |
void DOMSelection::selectAllChildren(Node& node) |
| 365 |
{ |
421 |
{ |
| 366 |
// This doesn't (and shouldn't) select text node characters. |
422 |
if (shouldUseSelectionAssociatedRange()) |
| 367 |
setBaseAndExtent(&node, 0, &node, node.countChildNodes()); |
423 |
setBaseAndExtent(&node, 0, &node, node.length()); |
|
|
424 |
else { |
| 425 |
// This doesn't select text node characters. |
| 426 |
setBaseAndExtent(&node, 0, &node, node.countChildNodes()); |
| 427 |
} |
| 368 |
} |
428 |
} |
| 369 |
|
429 |
|
| 370 |
String DOMSelection::toString() |
430 |
String DOMSelection::toString() |
|
Lines 410-415
unsigned DOMSelection::shadowAdjustedOffset(const Position& position) const
a/Source/WebCore/page/DOMSelection.cpp_sec10
|
| 410 |
|
470 |
|
| 411 |
bool DOMSelection::isValidForPosition(Node* node) const |
471 |
bool DOMSelection::isValidForPosition(Node* node) const |
| 412 |
{ |
472 |
{ |
|
|
473 |
ASSERT(!shouldUseSelectionAssociatedRange()); |
| 413 |
auto* frame = this->frame(); |
474 |
auto* frame = this->frame(); |
| 414 |
return frame && (!node || &node->document() == frame->document()); |
475 |
return frame && (!node || &node->document() == frame->document()); |
| 415 |
} |
476 |
} |