VisiblePosition RenderReplaced::positionForCoordinates(int x, int y) { InlineBox* box = inlineBoxWrapper(); if (!box) return VisiblePosition(element(), 0, DOWNSTREAM); // FIXME: This code is buggy if the replaced element is relative positioned. RootInlineBox* root = box->root(); int top = root->topOverflow(); int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow(); if (y + yPos() < top) return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); // coordinates are above if (y + yPos() >= bottom) return VisiblePosition(element(), caretMaxOffset(), DOWNSTREAM); // coordinates are below if (element()) { if (x <= width() / 2) return VisiblePosition(element(), 0, DOWNSTREAM); return VisiblePosition(element(), 1, DOWNSTREAM); } return RenderBox::positionForCoordinates(x, y); }
VisiblePosition RenderReplaced::positionForPoint(const LayoutPoint& point, const RenderRegion* region) { // FIXME: This code is buggy if the replaced element is relative positioned. InlineBox* box = inlineBoxWrapper(); const RootInlineBox* rootBox = box ? &box->root() : 0; LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); LayoutUnit blockDirectionPosition = isHorizontalWritingMode() ? point.y() + y() : point.x() + x(); LayoutUnit lineDirectionPosition = isHorizontalWritingMode() ? point.x() + x() : point.y() + y(); if (blockDirectionPosition < top) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above if (blockDirectionPosition >= bottom) return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below if (element()) { if (lineDirectionPosition <= logicalLeft() + (logicalWidth() / 2)) return createVisiblePosition(0, DOWNSTREAM); return createVisiblePosition(1, DOWNSTREAM); } return RenderBox::positionForPoint(point, region); }
void DOMSelection::extend(Node* node, int offset, ExceptionState& exceptionState) { if (!m_frame) return; if (!node) { exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "Node")); return; } if (offset < 0) { exceptionState.throwDOMException(IndexSizeError, String::number(offset) + " is not a valid offset."); return; } if (offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->countChildren())) { exceptionState.throwDOMException(IndexSizeError, String::number(offset) + " is larger than the given node's length."); return; } if (!isValidForPosition(node)) return; // FIXME: Eliminate legacy editing positions m_frame->selection().setExtent(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM)); }
VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point) { InlineBox* box = inlineBoxWrapper(); if (!box) return createVisiblePosition(0, DOWNSTREAM); // FIXME: This code is buggy if the replaced element is relative positioned. RootInlineBox* root = box->root(); int top = root->selectionTop(); int bottom = root->selectionBottom(); int blockDirectionPosition = box->isHorizontal() ? point.y() + y() : point.x() + x(); int lineDirectionPosition = box->isHorizontal() ? point.x() + x() : point.y() + y(); if (blockDirectionPosition < top) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above if (blockDirectionPosition >= bottom) return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below if (node()) { if (lineDirectionPosition <= box->logicalLeft() + (box->logicalWidth() / 2)) return createVisiblePosition(0, DOWNSTREAM); return createVisiblePosition(1, DOWNSTREAM); } return RenderBox::positionForPoint(point); }
PositionWithAffinity LayoutReplaced::positionForPoint(const LayoutPoint& point) { // FIXME: This code is buggy if the replaced element is relative positioned. InlineBox* box = inlineBoxWrapper(); RootInlineBox* rootBox = box ? &box->root() : 0; LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); LayoutUnit blockDirectionPosition = isHorizontalWritingMode() ? point.y() + location().y() : point.x() + location().x(); LayoutUnit lineDirectionPosition = isHorizontalWritingMode() ? point.x() + location().x() : point.y() + location().y(); if (blockDirectionPosition < top) return createPositionWithAffinity(caretMinOffset()); // coordinates are above if (blockDirectionPosition >= bottom) return createPositionWithAffinity(caretMaxOffset()); // coordinates are below if (node()) { if (lineDirectionPosition <= logicalLeft() + (logicalWidth() / 2)) return createPositionWithAffinity(0); return createPositionWithAffinity(1); } return LayoutBox::positionForPoint(point); }
VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point) { InlineBox* box = inlineBoxWrapper(); if (!box) return createVisiblePosition(0, DOWNSTREAM); // FIXME: This code is buggy if the replaced element is relative positioned. RootInlineBox* root = box->root(); int top = root->topOverflow(); int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow(); if (point.y() + y() < top) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above if (point.y() + y() >= bottom) return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below if (node()) { if (point.x() <= width() / 2) return createVisiblePosition(0, DOWNSTREAM); return createVisiblePosition(1, DOWNSTREAM); } return RenderBox::positionForPoint(point); }
static int maxOffsetIncludingCollapsedSpaces(Node* node) { int offset = caretMaxOffset(node); if (node->layoutObject() && node->layoutObject()->isText()) offset += collapsedSpaceLength(toLayoutText(node->layoutObject()), offset); return offset; }
void DOMSelection::extend(Node& node, unsigned offset, ExceptionCode& ec) { if (!m_frame) return; if (offset > (node.offsetInCharacters() ? caretMaxOffset(node) : node.countChildNodes())) { ec = INDEX_SIZE_ERR; return; } if (!isValidForPosition(&node)) return; // FIXME: Eliminate legacy editing positions m_frame->selection().setExtent(createLegacyEditingPosition(&node, offset), DOWNSTREAM); }
void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec) { if (!m_frame) return; if (!node) { ec = TYPE_MISMATCH_ERR; return; } if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) { ec = INDEX_SIZE_ERR; return; } SelectionController* selection = m_frame->selection(); selection->expandUsingGranularity(CharacterGranularity); selection->setExtent(VisiblePosition(node, offset, DOWNSTREAM)); }
PositionWithAffinity RenderReplaced::positionForPoint(const LayoutPoint& point) { // FIXME: This code is buggy if the replaced element is relative positioned. InlineBox* box = inlineBoxWrapper(); RootInlineBox* rootBox = box ? &box->root() : 0; LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); LayoutUnit blockDirectionPosition = point.y() + y(); if (blockDirectionPosition < top) return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); // coordinates are above if (blockDirectionPosition >= bottom) return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); // coordinates are below return RenderBox::positionForPoint(point); }
void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec) { if (!m_frame) return; if (!node) { ec = TYPE_MISMATCH_ERR; return; } if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) { ec = INDEX_SIZE_ERR; return; } if (!isValidForPosition(node)) return; m_frame->selection()->setExtent(VisiblePosition(node, offset, DOWNSTREAM)); }
Position InsertTextCommand::insertTab(const Position& pos) { Position insertPos = VisiblePosition(pos, DOWNSTREAM).deepEquivalent(); Node *node = insertPos.node(); unsigned int offset = insertPos.offset(); // keep tabs coalesced in tab span if (isTabSpanTextNode(node)) { insertTextIntoNode(static_cast<Text *>(node), offset, "\t"); return Position(node, offset + 1); } // create new tab span RefPtr<Element> spanNode = createTabSpanElement(document()); // place it if (!node->isTextNode()) { insertNodeAt(spanNode.get(), insertPos); } else { Text *textNode = static_cast<Text *>(node); if (offset >= textNode->length()) { insertNodeAfter(spanNode.get(), textNode); } else { // split node to make room for the span // NOTE: splitTextNode uses textNode for the // second node in the split, so we need to // insert the span before it. if (offset > 0) splitTextNode(textNode, offset); insertNodeBefore(spanNode.get(), textNode); } } // return the position following the new tab return Position(spanNode->lastChild(), caretMaxOffset(spanNode->lastChild())); }
void InsertParagraphSeparatorCommand::doApply() { if (!endingSelection().isNonOrphanedCaretOrRange()) return; Position insertionPosition = endingSelection().start(); EAffinity affinity = endingSelection().affinity(); // Delete the current selection. if (endingSelection().isRange()) { calculateStyleBeforeInsertion(insertionPosition); deleteSelection(false, true); insertionPosition = endingSelection().start(); affinity = endingSelection().affinity(); } // FIXME: The parentAnchoredEquivalent conversion needs to be moved into enclosingBlock. RefPtr<Element> startBlock = enclosingBlock(insertionPosition.parentAnchoredEquivalent().containerNode()); Position canonicalPos = VisiblePosition(insertionPosition).deepEquivalent(); if (!startBlock || !startBlock->nonShadowBoundaryParentNode() // FIXME: If the node is hidden, we don't have a canonical position so we will do the wrong thing for tables and <hr>. https://bugs.webkit.org/show_bug.cgi?id=40342 || (!canonicalPos.isNull() && isRenderedTableElement(canonicalPos.deprecatedNode()))) { applyCommandToComposite(InsertLineBreakCommand::create(document())); return; } // Use the leftmost candidate. insertionPosition = insertionPosition.upstream(); if (!insertionPosition.isCandidate()) insertionPosition = insertionPosition.downstream(); // Adjust the insertion position after the delete insertionPosition = positionAvoidingSpecialElementBoundary(insertionPosition); VisiblePosition visiblePos(insertionPosition, affinity); calculateStyleBeforeInsertion(insertionPosition); //--------------------------------------------------------------------- // Handle special case of typing return on an empty list item if (breakOutOfEmptyListItem()) return; //--------------------------------------------------------------------- // Prepare for more general cases. bool isFirstInBlock = isStartOfBlock(visiblePos); bool isLastInBlock = isEndOfBlock(visiblePos); bool nestNewBlock = false; // Create block to be inserted. RefPtr<Element> blockToInsert = nullptr; if (startBlock->isRootEditableElement()) { blockToInsert = createDefaultParagraphElement(document()); nestNewBlock = true; } else if (shouldUseDefaultParagraphElement(startBlock.get())) { blockToInsert = createDefaultParagraphElement(document()); } else { blockToInsert = startBlock->cloneElementWithoutChildren(); } //--------------------------------------------------------------------- // Handle case when position is in the last visible position in its block, // including when the block is empty. if (isLastInBlock) { if (nestNewBlock) { if (isFirstInBlock && !lineBreakExistsAtVisiblePosition(visiblePos)) { // The block is empty. Create an empty block to // represent the paragraph that we're leaving. RefPtr<HTMLElement> extraBlock = createDefaultParagraphElement(document()); appendNode(extraBlock, startBlock); } appendNode(blockToInsert, startBlock); } else { // Most of the time we want to stay at the nesting level of the startBlock (e.g., when nesting within lists). However, // for div nodes, this can result in nested div tags that are hard to break out of. Element* siblingElement = startBlock.get(); insertNodeAfter(blockToInsert, siblingElement); } // Recreate the same structure in the new paragraph. Vector<RefPtr<Element> > ancestors; getAncestorsInsideBlock(positionOutsideTabSpan(insertionPosition).deprecatedNode(), startBlock.get(), ancestors); RefPtr<Element> parent = cloneHierarchyUnderNewBlock(ancestors, blockToInsert); setEndingSelection(VisibleSelection(firstPositionInNode(parent.get()), DOWNSTREAM, endingSelection().isDirectional())); return; } //--------------------------------------------------------------------- // Handle case when position is in the first visible position in its block, and // similar case where previous position is in another, presumeably nested, block. if (isFirstInBlock || !inSameBlock(visiblePos, visiblePos.previous())) { Node* refNode = 0; insertionPosition = positionOutsideTabSpan(insertionPosition); if (isFirstInBlock && !nestNewBlock) { refNode = startBlock.get(); } else if (isFirstInBlock && nestNewBlock) { // startBlock should always have children, otherwise isLastInBlock would be true and it's handled above. ASSERT(startBlock->hasChildren()); refNode = startBlock->firstChild(); } else if (insertionPosition.deprecatedNode() == startBlock && nestNewBlock) { refNode = NodeTraversal::childAt(*startBlock, insertionPosition.deprecatedEditingOffset()); ASSERT(refNode); // must be true or we'd be in the end of block case } else refNode = insertionPosition.deprecatedNode(); // find ending selection position easily before inserting the paragraph insertionPosition = insertionPosition.downstream(); if (refNode) insertNodeBefore(blockToInsert, refNode); // Recreate the same structure in the new paragraph. Vector<RefPtr<Element> > ancestors; getAncestorsInsideBlock(positionAvoidingSpecialElementBoundary(positionOutsideTabSpan(insertionPosition)).deprecatedNode(), startBlock.get(), ancestors); // In this case, we need to set the new ending selection. setEndingSelection(VisibleSelection(insertionPosition, DOWNSTREAM, endingSelection().isDirectional())); return; } //--------------------------------------------------------------------- // Handle the (more complicated) general case, // Move downstream. Typing style code will take care of carrying along the // style of the upstream position. insertionPosition = insertionPosition.downstream(); // At this point, the insertionPosition's node could be a container, and we want to make sure we include // all of the correct nodes when building the ancestor list. So this needs to be the deepest representation of the position // before we walk the DOM tree. insertionPosition = positionOutsideTabSpan(VisiblePosition(insertionPosition).deepEquivalent()); // If the returned position lies either at the end or at the start of an element that is ignored by editing // we should move to its upstream or downstream position. if (editingIgnoresContent(insertionPosition.deprecatedNode())) { if (insertionPosition.atLastEditingPositionForNode()) insertionPosition = insertionPosition.downstream(); else if (insertionPosition.atFirstEditingPositionForNode()) insertionPosition = insertionPosition.upstream(); } // Make sure we do not cause a rendered space to become unrendered. // FIXME: We need the affinity for pos, but pos.downstream() does not give it Position leadingWhitespace = leadingWhitespacePosition(insertionPosition, VP_DEFAULT_AFFINITY); // FIXME: leadingWhitespacePosition is returning the position before preserved newlines for positions // after the preserved newline, causing the newline to be turned into a nbsp. if (leadingWhitespace.isNotNull() && leadingWhitespace.deprecatedNode()->isTextNode()) { Text* textNode = toText(leadingWhitespace.deprecatedNode()); ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); replaceTextInNodePreservingMarkers(textNode, leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); } // Split at pos if in the middle of a text node. Position positionAfterSplit; if (insertionPosition.anchorType() == Position::PositionIsOffsetInAnchor && insertionPosition.containerNode()->isTextNode()) { RefPtr<Text> textNode = toText(insertionPosition.containerNode()); bool atEnd = static_cast<unsigned>(insertionPosition.offsetInContainerNode()) >= textNode->length(); if (insertionPosition.deprecatedEditingOffset() > 0 && !atEnd) { splitTextNode(textNode, insertionPosition.offsetInContainerNode()); positionAfterSplit = firstPositionInNode(textNode.get()); insertionPosition.moveToPosition(textNode->previousSibling(), insertionPosition.offsetInContainerNode()); visiblePos = VisiblePosition(insertionPosition); } } // If we got detached due to mutation events, just bail out. if (!startBlock->parentNode()) return; // Put the added block in the tree. if (nestNewBlock) { appendNode(blockToInsert.get(), startBlock); } else { insertNodeAfter(blockToInsert.get(), startBlock); } document().updateLayoutIgnorePendingStylesheets(); // Move the start node and the siblings of the start node. if (VisiblePosition(insertionPosition) != VisiblePosition(positionBeforeNode(blockToInsert.get()))) { Node* n; if (insertionPosition.containerNode() == startBlock) n = insertionPosition.computeNodeAfterPosition(); else { Node* splitTo = insertionPosition.containerNode(); if (splitTo->isTextNode() && insertionPosition.offsetInContainerNode() >= caretMaxOffset(splitTo)) splitTo = NodeTraversal::next(*splitTo, startBlock.get()); ASSERT(splitTo); splitTreeToNode(splitTo, startBlock.get()); for (n = startBlock->firstChild(); n; n = n->nextSibling()) { VisiblePosition beforeNodePosition(positionBeforeNode(n)); if (!beforeNodePosition.isNull() && comparePositions(VisiblePosition(insertionPosition), beforeNodePosition) <= 0) break; } } moveRemainingSiblingsToNewParent(n, blockToInsert.get(), blockToInsert); } // Handle whitespace that occurs after the split if (positionAfterSplit.isNotNull()) { document().updateLayoutIgnorePendingStylesheets(); if (!positionAfterSplit.isRenderedCharacter()) { // Clear out all whitespace and insert one non-breaking space ASSERT(!positionAfterSplit.containerNode()->renderer() || positionAfterSplit.containerNode()->renderer()->style()->collapseWhiteSpace()); deleteInsignificantTextDownstream(positionAfterSplit); if (positionAfterSplit.deprecatedNode()->isTextNode()) insertTextIntoNode(toText(positionAfterSplit.containerNode()), 0, nonBreakingSpaceString()); } } setEndingSelection(VisibleSelection(firstPositionInNode(blockToInsert.get()), DOWNSTREAM, endingSelection().isDirectional())); }