| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2007, 2009 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 |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * | 8 * |
| 9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 if (selection.isNone()) | 168 if (selection.isNone()) |
| 169 return "None"; | 169 return "None"; |
| 170 if (selection.isCaret()) | 170 if (selection.isCaret()) |
| 171 return "Caret"; | 171 return "Caret"; |
| 172 return "Range"; | 172 return "Range"; |
| 173 } | 173 } |
| 174 | 174 |
| 175 int DOMSelection::rangeCount() const { | 175 int DOMSelection::rangeCount() const { |
| 176 if (!isAvailable()) | 176 if (!isAvailable()) |
| 177 return 0; | 177 return 0; |
| 178 if (documentCachedRange()) |
| 179 return 1; |
| 178 return frame()->selection().isNone() ? 0 : 1; | 180 return frame()->selection().isNone() ? 0 : 1; |
| 179 } | 181 } |
| 180 | 182 |
| 181 void DOMSelection::collapse(Node* node, | 183 void DOMSelection::collapse(Node* node, |
| 182 int offset, | 184 int offset, |
| 183 ExceptionState& exceptionState) { | 185 ExceptionState& exceptionState) { |
| 184 if (!isAvailable()) | 186 if (!isAvailable()) |
| 185 return; | 187 return; |
| 186 | 188 |
| 187 if (!node) { | 189 if (!node) { |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 279 return; | 281 return; |
| 280 } | 282 } |
| 281 if (!extentNode) { | 283 if (!extentNode) { |
| 282 UseCounter::count(frame(), UseCounter::SelectionSetBaseAndExtentNull); | 284 UseCounter::count(frame(), UseCounter::SelectionSetBaseAndExtentNull); |
| 283 extentOffset = 0; | 285 extentOffset = 0; |
| 284 } | 286 } |
| 285 | 287 |
| 286 if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) | 288 if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) |
| 287 return; | 289 return; |
| 288 | 290 |
| 291 clearCachedRangeIfSelectionOfDocument(); |
| 292 |
| 289 // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets | 293 // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 290 // needs to be audited. See http://crbug.com/590369 for more details. | 294 // needs to be audited. See http://crbug.com/590369 for more details. |
| 291 // In the long term, we should change FrameSelection::setSelection to take a | 295 // In the long term, we should change FrameSelection::setSelection to take a |
| 292 // parameter that does not require clean layout, so that modifying selection | 296 // parameter that does not require clean layout, so that modifying selection |
| 293 // no longer performs synchronous layout by itself. | 297 // no longer performs synchronous layout by itself. |
| 294 // TODO(editing-dev): Once SVG USE element doesn't modifies DOM tree, we | 298 // TODO(editing-dev): Once SVG USE element doesn't modifies DOM tree, we |
| 295 // should get rid of this update layout call. | 299 // should get rid of this update layout call. |
| 296 // See http://crbug.com/566281 | 300 // See http://crbug.com/566281 |
| 297 // See "svg/text/textpath-reference-crash.html" | 301 // See "svg/text/textpath-reference-crash.html" |
| 298 frame()->document()->updateStyleAndLayoutIgnorePendingStylesheets(); | 302 frame()->document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 376 if (static_cast<unsigned>(offset) > node->lengthOfContents()) { | 380 if (static_cast<unsigned>(offset) > node->lengthOfContents()) { |
| 377 exceptionState.throwDOMException( | 381 exceptionState.throwDOMException( |
| 378 IndexSizeError, | 382 IndexSizeError, |
| 379 String::number(offset) + " is larger than the given node's length."); | 383 String::number(offset) + " is larger than the given node's length."); |
| 380 return; | 384 return; |
| 381 } | 385 } |
| 382 | 386 |
| 383 if (!isValidForPosition(node)) | 387 if (!isValidForPosition(node)) |
| 384 return; | 388 return; |
| 385 | 389 |
| 390 clearCachedRangeIfSelectionOfDocument(); |
| 386 const Position& base = frame()->selection().base(); | 391 const Position& base = frame()->selection().base(); |
| 387 if (base.isNull()) { | 392 if (base.isNull()) { |
| 388 // TODO(editing-dev): We should throw |InvalidStateError| if selection is | 393 // TODO(editing-dev): We should throw |InvalidStateError| if selection is |
| 389 // none to follow the spec. | 394 // none to follow the spec. |
| 390 frame()->selection().setSelection(SelectionInDOMTree::Builder() | 395 frame()->selection().setSelection(SelectionInDOMTree::Builder() |
| 391 .collapse(Position(node, offset)) | 396 .collapse(Position(node, offset)) |
| 392 .setIsDirectional(true) | 397 .setIsDirectional(true) |
| 393 .build()); | 398 .build()); |
| 394 return; | 399 return; |
| 395 } | 400 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 406 | 411 |
| 407 if (index < 0 || index >= rangeCount()) { | 412 if (index < 0 || index >= rangeCount()) { |
| 408 exceptionState.throwDOMException( | 413 exceptionState.throwDOMException( |
| 409 IndexSizeError, String::number(index) + " is not a valid index."); | 414 IndexSizeError, String::number(index) + " is not a valid index."); |
| 410 return nullptr; | 415 return nullptr; |
| 411 } | 416 } |
| 412 | 417 |
| 413 // If you're hitting this, you've added broken multi-range selection support | 418 // If you're hitting this, you've added broken multi-range selection support |
| 414 DCHECK_EQ(rangeCount(), 1); | 419 DCHECK_EQ(rangeCount(), 1); |
| 415 | 420 |
| 421 if (Range* cachedRange = documentCachedRange()) |
| 422 return cachedRange; |
| 423 |
| 424 Range* range = createRangeFromSelectionEditor(); |
| 425 cacheRangeIfSelectionOfDocument(range); |
| 426 return range; |
| 427 } |
| 428 |
| 429 Range* DOMSelection::createRangeFromSelectionEditor() { |
| 416 Position anchor = anchorPosition(visibleSelection()); | 430 Position anchor = anchorPosition(visibleSelection()); |
| 417 if (!anchor.anchorNode()->isInShadowTree()) | 431 if (!anchor.anchorNode()->isInShadowTree()) |
| 418 return frame()->selection().firstRange(); | 432 return frame()->selection().firstRange(); |
| 419 | 433 |
| 420 Node* node = shadowAdjustedNode(anchor); | 434 Node* node = shadowAdjustedNode(anchor); |
| 421 if (!node) // crbug.com/595100 | 435 if (!node) // crbug.com/595100 |
| 422 return nullptr; | 436 return nullptr; |
| 423 if (!visibleSelection().isBaseFirst()) | 437 if (!visibleSelection().isBaseFirst()) |
| 424 return Range::create(*anchor.document(), focusNode(), focusOffset(), node, | 438 return Range::create(*anchor.document(), focusNode(), focusOffset(), node, |
| 425 anchorOffset()); | 439 anchorOffset()); |
| 426 return Range::create(*anchor.document(), node, anchorOffset(), focusNode(), | 440 return Range::create(*anchor.document(), node, anchorOffset(), focusNode(), |
| 427 focusOffset()); | 441 focusOffset()); |
| 428 } | 442 } |
| 429 | 443 |
| 444 bool DOMSelection::isSelectionOfDocument() const { |
| 445 return m_treeScope == m_treeScope->document(); |
| 446 } |
| 447 |
| 448 void DOMSelection::cacheRangeIfSelectionOfDocument(Range* range) { |
| 449 if (!isSelectionOfDocument()) |
| 450 return; |
| 451 frame()->selection().cacheRangeOfDocument(range); |
| 452 } |
| 453 |
| 454 Range* DOMSelection::documentCachedRange() const { |
| 455 if (!isSelectionOfDocument()) |
| 456 return nullptr; |
| 457 return frame()->selection().documentCachedRange(); |
| 458 } |
| 459 |
| 460 void DOMSelection::clearCachedRangeIfSelectionOfDocument() { |
| 461 if (!isSelectionOfDocument()) |
| 462 return; |
| 463 frame()->selection().clearDocumentCachedRange(); |
| 464 } |
| 465 |
| 430 void DOMSelection::removeAllRanges() { | 466 void DOMSelection::removeAllRanges() { |
| 431 if (!isAvailable()) | 467 if (!isAvailable()) |
| 432 return; | 468 return; |
| 433 frame()->selection().clear(); | 469 frame()->selection().clear(); |
| 434 } | 470 } |
| 435 | 471 |
| 436 void DOMSelection::addRange(Range* newRange) { | 472 void DOMSelection::addRange(Range* newRange) { |
| 437 DCHECK(newRange); | 473 DCHECK(newRange); |
| 438 | 474 |
| 439 if (!isAvailable()) | 475 if (!isAvailable()) |
| (...skipping 16 matching lines...) Expand all Loading... |
| 456 | 492 |
| 457 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 493 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 458 // needs to be audited. See http://crbug.com/590369 for more details. | 494 // needs to be audited. See http://crbug.com/590369 for more details. |
| 459 // In the long term, we should change FrameSelection::setSelection to take a | 495 // In the long term, we should change FrameSelection::setSelection to take a |
| 460 // parameter that does not require clean layout, so that modifying selection | 496 // parameter that does not require clean layout, so that modifying selection |
| 461 // no longer performs synchronous layout by itself. | 497 // no longer performs synchronous layout by itself. |
| 462 frame()->document()->updateStyleAndLayoutIgnorePendingStylesheets(); | 498 frame()->document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 463 | 499 |
| 464 if (selection.isNone()) { | 500 if (selection.isNone()) { |
| 465 selection.setSelectedRange(EphemeralRange(newRange), VP_DEFAULT_AFFINITY); | 501 selection.setSelectedRange(EphemeralRange(newRange), VP_DEFAULT_AFFINITY); |
| 502 cacheRangeIfSelectionOfDocument(newRange); |
| 466 return; | 503 return; |
| 467 } | 504 } |
| 468 | 505 |
| 469 Range* originalRange = selection.firstRange(); | 506 Range* originalRange = selection.firstRange(); |
| 470 | 507 |
| 471 if (originalRange->startContainer()->document() != | 508 if (originalRange->startContainer()->document() != |
| 472 newRange->startContainer()->document()) { | 509 newRange->startContainer()->document()) { |
| 473 addConsoleError( | 510 addConsoleError( |
| 474 "The given range does not belong to the current selection's document."); | 511 "The given range does not belong to the current selection's document."); |
| 475 return; | 512 return; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 503 ? originalRange | 540 ? originalRange |
| 504 : newRange; | 541 : newRange; |
| 505 Range* end = originalRange->compareBoundaryPoints(Range::kEndToEnd, newRange, | 542 Range* end = originalRange->compareBoundaryPoints(Range::kEndToEnd, newRange, |
| 506 ASSERT_NO_EXCEPTION) < 0 | 543 ASSERT_NO_EXCEPTION) < 0 |
| 507 ? newRange | 544 ? newRange |
| 508 : originalRange; | 545 : originalRange; |
| 509 const EphemeralRange merged = | 546 const EphemeralRange merged = |
| 510 EphemeralRange(start->startPosition(), end->endPosition()); | 547 EphemeralRange(start->startPosition(), end->endPosition()); |
| 511 TextAffinity affinity = selection.selection().affinity(); | 548 TextAffinity affinity = selection.selection().affinity(); |
| 512 selection.setSelectedRange(merged, affinity); | 549 selection.setSelectedRange(merged, affinity); |
| 550 cacheRangeIfSelectionOfDocument(createRange(merged)); |
| 513 } | 551 } |
| 514 | 552 |
| 515 void DOMSelection::deleteFromDocument() { | 553 void DOMSelection::deleteFromDocument() { |
| 516 if (!isAvailable()) | 554 if (!isAvailable()) |
| 517 return; | 555 return; |
| 518 | 556 |
| 519 FrameSelection& selection = frame()->selection(); | 557 FrameSelection& selection = frame()->selection(); |
| 520 | 558 |
| 521 if (selection.isNone()) | 559 if (selection.isNone()) |
| 522 return; | 560 return; |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 668 m_treeScope->document().addConsoleMessage( | 706 m_treeScope->document().addConsoleMessage( |
| 669 ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message)); | 707 ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message)); |
| 670 } | 708 } |
| 671 | 709 |
| 672 DEFINE_TRACE(DOMSelection) { | 710 DEFINE_TRACE(DOMSelection) { |
| 673 visitor->trace(m_treeScope); | 711 visitor->trace(m_treeScope); |
| 674 ContextClient::trace(visitor); | 712 ContextClient::trace(visitor); |
| 675 } | 713 } |
| 676 | 714 |
| 677 } // namespace blink | 715 } // namespace blink |
| OLD | NEW |