TEST_F(StaticRangeTest, InvalidToRange) { document().body()->setInnerHTML("1234", ASSERT_NO_EXCEPTION); Text* oldText = toText(document().body()->firstChild()); StaticRange* staticRange04 = StaticRange::create(document(), oldText, 0, oldText, 4); // Valid StaticRange. staticRange04->toRange(ASSERT_NO_EXCEPTION); oldText->splitText(2, ASSERT_NO_EXCEPTION); // StaticRange shouldn't mutate, endOffset() become invalid after splitText(). EXPECT_EQ(oldText, staticRange04->startContainer()); EXPECT_EQ(0, staticRange04->startOffset()); EXPECT_EQ(oldText, staticRange04->endContainer()); EXPECT_EQ(4, staticRange04->endOffset()); // Invalid StaticRange. TrackExceptionState exceptionState; staticRange04->toRange(exceptionState); EXPECT_TRUE(exceptionState.hadException()); }
unsigned CharacterData::parserAppendData(const String& string, unsigned offset, unsigned lengthLimit) { unsigned oldLength = m_data.length(); ASSERT(lengthLimit >= oldLength); unsigned characterLength = string.length() - offset; unsigned characterLengthLimit = min(characterLength, lengthLimit - oldLength); // Check that we are not on an unbreakable boundary. // Some text break iterator implementations work best if the passed buffer is as small as possible, // see <https://bugs.webkit.org/show_bug.cgi?id=29092>. // We need at least two characters look-ahead to account for UTF-16 surrogates. if (characterLengthLimit < characterLength) { NonSharedCharacterBreakIterator it(string.characters() + offset, (characterLengthLimit + 2 > characterLength) ? characterLength : characterLengthLimit + 2); if (!isTextBreak(it, characterLengthLimit)) characterLengthLimit = textBreakPreceding(it, characterLengthLimit); } if (!characterLengthLimit) return 0; if (string.is8Bit()) m_data.append(string.characters8() + offset, characterLengthLimit); else m_data.append(string.characters16() + offset, characterLengthLimit); ASSERT(!renderer() || isTextNode()); if (isTextNode()) toText(this)->updateTextRenderer(oldLength, 0); document()->incDOMTreeVersion(); // We don't call dispatchModifiedEvent here because we don't want the // parser to dispatch DOM mutation events. if (parentNode()) parentNode()->childrenChanged(); return characterLengthLimit; }
void replaceChildrenWithText(ContainerNode* container, const String& text, ExceptionState& exceptionState) { DCHECK(container); ContainerNode* containerNode(container); ChildListMutationScope mutation(*containerNode); // FIXME: This is wrong if containerNode->firstChild() has more than one ref! // Example: // <div>foo</div> // <script> // var oldText = div.firstChild; // console.log(oldText.data); // foo // div.innerText = "bar"; // console.log(oldText.data); // bar!?! // </script> // I believe this is an intentional benchmark cheat from years ago. // We should re-visit if we actually want this still. if (containerNode->hasOneTextChild()) { toText(containerNode->firstChild())->setData(text); return; } // NOTE: This method currently always creates a text node, even if that text // node will be empty. Text* textNode = Text::create(containerNode->document(), text); // FIXME: No need to replace the child it is a text node and its contents are // already == text. if (containerNode->hasOneChild()) { containerNode->replaceChild(textNode, containerNode->firstChild(), exceptionState); return; } containerNode->removeChildren(); containerNode->appendChild(textNode, exceptionState); }
unsigned HTMLTextFormControlElement::indexForPosition(const Position& passedPosition) const { TextControlInnerTextElement* innerText = innerTextElement(); if (!innerText || !innerText->contains(passedPosition.anchorNode()) || passedPosition.isNull()) return 0; if (positionBeforeNode(innerText) == passedPosition) return 0; unsigned index = 0; Node* startNode = passedPosition.computeNodeBeforePosition(); if (!startNode) startNode = passedPosition.containerNode(); ASSERT(startNode); ASSERT(innerText->contains(startNode)); for (Node* node = startNode; node; node = NodeTraversal::previous(node, innerText)) { if (node->isTextNode()) { unsigned length = toText(*node).length(); if (node == passedPosition.containerNode()) index += std::min<unsigned>(length, passedPosition.offsetInContainerNode()); else index += length; } else if (node->hasTagName(brTag)) index++; } unsigned length = innerTextValue().length(); index = std::min(index, length); // FIXME: We shouldn't have to call innerTextValue() just to ignore the last LF. See finishText. #ifndef ASSERT_DISABLED VisiblePosition visiblePosition = passedPosition; unsigned indexComputedByVisiblePosition = 0; if (visiblePosition.isNotNull()) indexComputedByVisiblePosition = WebCore::indexForVisiblePosition(innerText, visiblePosition, false /* forSelectionPreservation */); ASSERT(index == indexComputedByVisiblePosition); #endif return index; }
void InputMethodController::setCompositionFromExistingText(const Vector<CompositionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd) { Element* editable = m_frame.selection().rootEditableElement(); Position base = m_frame.selection().base().downstream(); Node* baseNode = base.anchorNode(); if (editable->firstChild() == baseNode && editable->lastChild() == baseNode && baseNode->isTextNode()) { m_compositionNode = nullptr; m_customCompositionUnderlines.clear(); if (base.anchorType() != Position::PositionIsOffsetInAnchor) return; if (!baseNode || baseNode != m_frame.selection().extent().anchorNode()) return; m_compositionNode = toText(baseNode); RefPtr<Range> range = PlainTextRange(compositionStart, compositionEnd).createRange(*editable); m_compositionStart = range->startOffset(); m_compositionEnd = range->endOffset(); m_customCompositionUnderlines = underlines; size_t numUnderlines = m_customCompositionUnderlines.size(); for (size_t i = 0; i < numUnderlines; ++i) { m_customCompositionUnderlines[i].startOffset += m_compositionStart; m_customCompositionUnderlines[i].endOffset += m_compositionStart; } // TODO(ojan): What was this for? Do we need it in sky since we // don't need to support legacy IMEs? if (baseNode->renderer()) baseNode->document().scheduleVisualUpdate(); return; } Editor::RevealSelectionScope revealSelectionScope(&editor()); SelectionOffsetsScope selectionOffsetsScope(this); setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd)); setComposition(m_frame.selectedText(), underlines, 0, 0); }
void MarkupAccumulator::appendStartMarkup(StringBuilder& result, const Node& node, Namespaces* namespaces) { if (namespaces) namespaces->checkConsistency(); switch (node.nodeType()) { case Node::TEXT_NODE: appendText(result, toText(node)); break; case Node::COMMENT_NODE: appendComment(result, toComment(node).data()); break; case Node::DOCUMENT_NODE: appendXMLDeclaration(result, toDocument(node)); break; case Node::DOCUMENT_FRAGMENT_NODE: break; case Node::DOCUMENT_TYPE_NODE: appendDocumentType(result, toDocumentType(node)); break; case Node::PROCESSING_INSTRUCTION_NODE: appendProcessingInstruction(result, toProcessingInstruction(node).target(), toProcessingInstruction(node).data()); break; case Node::ELEMENT_NODE: appendElement(result, toElement(node), namespaces); break; case Node::CDATA_SECTION_NODE: appendCDATASection(result, toCDATASection(node).data()); break; case Node::ATTRIBUTE_NODE: case Node::ENTITY_NODE: case Node::ENTITY_REFERENCE_NODE: case Node::NOTATION_NODE: case Node::XPATH_NAMESPACE_NODE: ASSERT_NOT_REACHED(); break; } }
int main(int argc,char *argv[]) { const CniWrapper* cniWrapper = startCniWrapper(); if (cniWrapper) { // Test the Dice class. printf("\nTesting the execution of the Dice class:\n\n"); int i; for (i = 0;i < 12;i++) { Dice* theDice = rollDice(); if (theDice) { // printf("White: %d Colored: %d Combined: %2d\n", // whiteDieValue(theDice), // coloredDieValue(theDice), // combinedResult(theDice)); printf("%s\n",toText(theDice)); deleteDice(theDice); } else printf("New Dice object generation failed\n"); } stopCniWrapper(); } else printf("New CniWrapper object generation failed\n"); return 0; }
void NodeRenderingContext::createRendererForTextIfNeeded() { ASSERT(!m_node->renderer()); Text* textNode = toText(m_node); if (!shouldCreateRenderer()) return; RenderObject* parentRenderer = this->parentRenderer(); ASSERT(parentRenderer); Document* document = textNode->document(); if (resetStyleInheritance()) m_style = document->styleResolver()->defaultStyleForElement(); else m_style = parentRenderer->style(); if (!textNode->textRendererIsNeeded(*this)) return; RenderText* newRenderer = textNode->createTextRenderer(m_style.get()); if (!newRenderer) return; if (!parentRenderer->isChildAllowed(newRenderer, m_style.get())) { newRenderer->destroy(); return; } // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail. newRenderer->setFlowThreadState(parentRenderer->flowThreadState()); RenderObject* nextRenderer = this->nextRenderer(); textNode->setRenderer(newRenderer); // Parent takes care of the animations, no need to call setAnimatableStyle. newRenderer->setStyle(m_style.release()); parentRenderer->addChild(newRenderer, nextRenderer); }
// If this is the layoutObject for a first-letter pseudoNode then we have to look // at the node for the remaining text to find our content. Text* LayoutTextFragment::associatedTextNode() const { Node* node = this->firstLetterPseudoElement(); if (m_isRemainingTextLayoutObject || !node) { // If we don't have a node, then we aren't part of a first-letter pseudo // element, so use the actual node. Likewise, if we have a node, but // we're the remainingTextLayoutObject for a pseudo element use the real // text node. node = this->node(); } if (!node) return nullptr; if (node->isFirstLetterPseudoElement()) { FirstLetterPseudoElement* pseudo = toFirstLetterPseudoElement(node); LayoutObject* nextLayoutObject = FirstLetterPseudoElement::firstLetterTextLayoutObject(*pseudo); if (!nextLayoutObject) return nullptr; node = nextLayoutObject->node(); } return (node && node->isTextNode()) ? toText(node) : nullptr; }
void InputMethodController::setCompositionFromExistingText(const Vector<CompositionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd) { Element* editable = frame().selection().rootEditableElement(); Position base = frame().selection().base().downstream(); Node* baseNode = base.anchorNode(); if (baseNode && editable->firstChild() == baseNode && editable->lastChild() == baseNode && baseNode->isTextNode()) { m_compositionNode = nullptr; m_customCompositionUnderlines.clear(); if (base.anchorType() != Position::PositionIsOffsetInAnchor) return; if (baseNode != frame().selection().extent().anchorNode()) return; m_compositionNode = toText(baseNode); RefPtrWillBeRawPtr<Range> range = PlainTextRange(compositionStart, compositionEnd).createRange(*editable); if (!range) return; m_compositionStart = range->startOffset(); m_compositionEnd = range->endOffset(); m_customCompositionUnderlines = underlines; size_t numUnderlines = m_customCompositionUnderlines.size(); for (size_t i = 0; i < numUnderlines; ++i) { m_customCompositionUnderlines[i].startOffset += m_compositionStart; m_customCompositionUnderlines[i].endOffset += m_compositionStart; } if (baseNode->layoutObject()) baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); return; } Editor::RevealSelectionScope revealSelectionScope(&editor()); SelectionOffsetsScope selectionOffsetsScope(this); setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd)); setComposition(frame().selectedText(), underlines, 0, 0); }
static void createTextRenderersForSiblingsAfterAttachIfNeeded(Node* sibling) { ASSERT(sibling->previousSibling()); ASSERT(sibling->previousSibling()->renderer()); ASSERT(!sibling->renderer()); ASSERT(sibling->attached()); // If this node got a renderer it may be the previousRenderer() of sibling text nodes and thus affect the // result of Text::textRendererIsNeeded() for those nodes. for (; sibling; sibling = sibling->nextSibling()) { if (sibling->renderer()) break; if (!sibling->attached()) break; // Assume this means none of the following siblings are attached. if (!sibling->isTextNode()) continue; ASSERT(!sibling->renderer()); attachTextRenderer(*toText(sibling)); // If we again decided not to create a renderer for next, we can bail out the loop, // because it won't affect the result of Text::textRendererIsNeeded() for the rest // of sibling nodes. if (!sibling->renderer()) break; } }
TextPtr Node::toTextChecked() const throw(Exceptions::CheckFailed) { TextPtr result = toText(); ZS_THROW_CUSTOM_IF(Exceptions::CheckFailed, !result) return result; }
void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range> >& rangeObjects, const RenderRegion* region) const { LayoutUnit logicalTopForRegion; LayoutUnit logicalBottomForRegion; // extend the first region top to contain everything up to its logical height if (region->isFirstRegion()) logicalTopForRegion = LayoutUnit::min(); else logicalTopForRegion = region->logicalTopForFlowThreadContent(); // extend the last region to contain everything above its y() if (region->isLastRegion()) logicalBottomForRegion = LayoutUnit::max(); else logicalBottomForRegion = region->logicalBottomForFlowThreadContent(); Vector<Node*> nodes; // eliminate the contentNodes that are descendants of other contentNodes for (NamedFlowContentNodes::const_iterator it = contentNodes().begin(); it != contentNodes().end(); ++it) { Node* node = *it; if (!isContainedInNodes(nodes, node)) nodes.append(node); } for (size_t i = 0; i < nodes.size(); i++) { Node* contentNode = nodes.at(i); if (!contentNode->renderer()) continue; RefPtr<Range> range = Range::create(contentNode->document()); bool foundStartPosition = false; bool startsAboveRegion = true; bool endsBelowRegion = true; bool skipOverOutsideNodes = false; Node* lastEndNode = 0; for (Node* node = contentNode; node; node = nextNodeInsideContentNode(node, contentNode)) { RenderObject* renderer = node->renderer(); if (!renderer) continue; LayoutRect boundingBox; if (renderer->isRenderInline()) boundingBox = toRenderInline(renderer)->linesBoundingBox(); else if (renderer->isText()) boundingBox = toRenderText(renderer)->linesBoundingBox(); else { boundingBox = toRenderBox(renderer)->frameRect(); if (toRenderBox(renderer)->isRelPositioned()) boundingBox.move(toRenderBox(renderer)->relativePositionLogicalOffset()); } LayoutUnit offsetTop = renderer->containingBlock()->offsetFromLogicalTopOfFirstPage(); const LayoutPoint logicalOffsetFromTop(isHorizontalWritingMode() ? LayoutUnit() : offsetTop, isHorizontalWritingMode() ? offsetTop : LayoutUnit()); boundingBox.moveBy(logicalOffsetFromTop); LayoutUnit logicalTopForRenderer = region->logicalTopOfFlowThreadContentRect(boundingBox); LayoutUnit logicalBottomForRenderer = region->logicalBottomOfFlowThreadContentRect(boundingBox); // if the bounding box of the current element doesn't intersect the region box // close the current range only if the start element began inside the region, // otherwise just move the start position after this node and keep skipping them until we found a proper start position. if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) { if (foundStartPosition) { if (!startsAboveRegion) { if (range->intersectsNode(node, IGNORE_EXCEPTION)) range->setEndBefore(node, IGNORE_EXCEPTION); rangeObjects.append(range->cloneRange(IGNORE_EXCEPTION)); range = Range::create(contentNode->document()); startsAboveRegion = true; } else skipOverOutsideNodes = true; } if (skipOverOutsideNodes) range->setStartAfter(node, IGNORE_EXCEPTION); foundStartPosition = false; continue; } // start position if (logicalTopForRenderer < logicalTopForRegion && startsAboveRegion) { if (renderer->isText()) { // Text crosses region top // for Text elements, just find the last textbox that is contained inside the region and use its start() offset as start position RenderText* textRenderer = toRenderText(renderer); for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (offsetTop + box->logicalBottom() < logicalTopForRegion) continue; range->setStart(Position(toText(node), box->start())); startsAboveRegion = false; break; } } else { // node crosses region top // for all elements, except Text, just set the start position to be before their children startsAboveRegion = true; range->setStart(Position(node, Position::PositionIsBeforeChildren)); } } else { // node starts inside region // for elements that start inside the region, set the start position to be before them. If we found one, we will just skip the others until // the range is closed. if (startsAboveRegion) { startsAboveRegion = false; range->setStartBefore(node, IGNORE_EXCEPTION); } } skipOverOutsideNodes = false; foundStartPosition = true; // end position if (logicalBottomForRegion < logicalBottomForRenderer && (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode)))) { // for Text elements, just find just find the last textbox that is contained inside the region and use its start()+len() offset as end position if (renderer->isText()) { // Text crosses region bottom RenderText* textRenderer = toRenderText(renderer); InlineTextBox* lastBox = 0; for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if ((offsetTop + box->logicalTop()) < logicalBottomForRegion) { lastBox = box; continue; } ASSERT(lastBox); if (lastBox) range->setEnd(Position(toText(node), lastBox->start() + lastBox->len())); break; } endsBelowRegion = false; lastEndNode = node; } else { // node crosses region bottom // for all elements, except Text, just set the start position to be after their children range->setEnd(Position(node, Position::PositionIsAfterChildren)); endsBelowRegion = true; lastEndNode = node; } } else { // node ends inside region // for elements that ends inside the region, set the end position to be after them // allow this end position to be changed only by other elements that are not descendants of the current end node if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) { range->setEndAfter(node, IGNORE_EXCEPTION); endsBelowRegion = false; lastEndNode = node; } } } if (foundStartPosition || skipOverOutsideNodes) rangeObjects.append(range); } }
void resolveTree(Element* current, Change change) { ASSERT(change != Detach); if (current->hasCustomStyleResolveCallbacks()) { if (!current->willRecalcStyle(change)) return; } bool hasParentStyle = current->parentNodeForRenderingAndStyle() && current->parentNodeForRenderingAndStyle()->renderStyle(); bool hasDirectAdjacentRules = current->childrenAffectedByDirectAdjacentRules(); bool hasIndirectAdjacentRules = current->childrenAffectedByForwardPositionalRules(); if (change > NoChange || current->needsStyleRecalc()) current->resetComputedStyle(); if (hasParentStyle && (change >= Inherit || current->needsStyleRecalc())) change = resolveLocal(current, change); if (change != Detach) { StyleResolverParentPusher parentPusher(current); RenderStyle* currentStyle = current->renderStyle(); if (ShadowRoot* shadowRoot = current->shadowRoot()) { if (change >= Inherit || shadowRoot->childNeedsStyleRecalc() || shadowRoot->needsStyleRecalc()) { parentPusher.push(); resolveShadowTree(shadowRoot, currentStyle, change); } } current->updateBeforePseudoElement(change); // FIXME: This check is good enough for :hover + foo, but it is not good enough for :hover + foo + bar. // For now we will just worry about the common case, since it's a lot trickier to get the second case right // without doing way too much re-resolution. bool forceCheckOfNextElementSibling = false; bool forceCheckOfAnyElementSibling = false; for (Node* child = current->firstChild(); child; child = child->nextSibling()) { if (child->isTextNode()) { updateTextStyle(toText(child), currentStyle, change); continue; } if (!child->isElementNode()) continue; Element* childElement = toElement(child); bool childRulesChanged = childElement->needsStyleRecalc() && childElement->styleChangeType() == FullStyleChange; if ((forceCheckOfNextElementSibling || forceCheckOfAnyElementSibling)) childElement->setNeedsStyleRecalc(); if (change >= Inherit || childElement->childNeedsStyleRecalc() || childElement->needsStyleRecalc()) { parentPusher.push(); resolveTree(childElement, change); } forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules; forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules); } current->updateAfterPseudoElement(change); } current->clearNeedsStyleRecalc(); current->clearChildNeedsStyleRecalc(); if (current->hasCustomStyleResolveCallbacks()) current->didRecalcStyle(change); }
/** * @brief provides the representation for the text to be displayed * @param value of the item * @param locale -- not used * @return the text in the respresentation to be displayed * * This method is re-implemented */ QString TextPanelItemDelegate::displayText(const QVariant &value, const QLocale &locale) const { Q_UNUSED(locale); return toText(value); }
/** * @brief sets the data from the model into the editor field * @param editor that is used * @param index of the item * * This method is re-implemented */ void TextPanelItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QLineEdit *lineeditor = qobject_cast<QLineEdit *>(editor); lineeditor->setText(toText(index.model()->data(index, Qt::EditRole))); }
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())); }
TEST_F(StaticRangeTest, SplitTextNodeRangeOutsideText) { document().body()->setInnerHTML("<span id=\"outer\">0<span id=\"inner-left\">1</span>SPLITME<span id=\"inner-right\">2</span>3</span>", ASSERT_NO_EXCEPTION); Element* outer = document().getElementById(AtomicString::fromUTF8("outer")); Element* innerLeft = document().getElementById(AtomicString::fromUTF8("inner-left")); Element* innerRight = document().getElementById(AtomicString::fromUTF8("inner-right")); Text* oldText = toText(outer->childNodes()->item(2)); StaticRange* staticRangeOuterOutside = StaticRange::create(document(), outer, 0, outer, 5); StaticRange* staticRangeOuterInside = StaticRange::create(document(), outer, 1, outer, 4); StaticRange* staticRangeOuterSurroundingText = StaticRange::create(document(), outer, 2, outer, 3); StaticRange* staticRangeInnerLeft = StaticRange::create(document(), innerLeft, 0, innerLeft, 1); StaticRange* staticRangeInnerRight = StaticRange::create(document(), innerRight, 0, innerRight, 1); StaticRange* staticRangeFromTextToMiddleOfElement = StaticRange::create(document(), oldText, 6, outer, 3); Range* rangeOuterOutside = staticRangeOuterOutside->toRange(ASSERT_NO_EXCEPTION); Range* rangeOuterInside = staticRangeOuterInside->toRange(ASSERT_NO_EXCEPTION); Range* rangeOuterSurroundingText = staticRangeOuterSurroundingText->toRange(ASSERT_NO_EXCEPTION); Range* rangeInnerLeft = staticRangeInnerLeft->toRange(ASSERT_NO_EXCEPTION); Range* rangeInnerRight = staticRangeInnerRight->toRange(ASSERT_NO_EXCEPTION); Range* rangeFromTextToMiddleOfElement = staticRangeFromTextToMiddleOfElement->toRange(ASSERT_NO_EXCEPTION); oldText->splitText(3, ASSERT_NO_EXCEPTION); Text* newText = toText(oldText->nextSibling()); // Range should mutate. EXPECT_TRUE(rangeOuterOutside->boundaryPointsValid()); EXPECT_EQ(outer, rangeOuterOutside->startContainer()); EXPECT_EQ(0, rangeOuterOutside->startOffset()); EXPECT_EQ(outer, rangeOuterOutside->endContainer()); EXPECT_EQ(6, rangeOuterOutside->endOffset()); // Increased by 1 since a new node is inserted. EXPECT_TRUE(rangeOuterInside->boundaryPointsValid()); EXPECT_EQ(outer, rangeOuterInside->startContainer()); EXPECT_EQ(1, rangeOuterInside->startOffset()); EXPECT_EQ(outer, rangeOuterInside->endContainer()); EXPECT_EQ(5, rangeOuterInside->endOffset()); EXPECT_TRUE(rangeOuterSurroundingText->boundaryPointsValid()); EXPECT_EQ(outer, rangeOuterSurroundingText->startContainer()); EXPECT_EQ(2, rangeOuterSurroundingText->startOffset()); EXPECT_EQ(outer, rangeOuterSurroundingText->endContainer()); EXPECT_EQ(4, rangeOuterSurroundingText->endOffset()); EXPECT_TRUE(rangeInnerLeft->boundaryPointsValid()); EXPECT_EQ(innerLeft, rangeInnerLeft->startContainer()); EXPECT_EQ(0, rangeInnerLeft->startOffset()); EXPECT_EQ(innerLeft, rangeInnerLeft->endContainer()); EXPECT_EQ(1, rangeInnerLeft->endOffset()); EXPECT_TRUE(rangeInnerRight->boundaryPointsValid()); EXPECT_EQ(innerRight, rangeInnerRight->startContainer()); EXPECT_EQ(0, rangeInnerRight->startOffset()); EXPECT_EQ(innerRight, rangeInnerRight->endContainer()); EXPECT_EQ(1, rangeInnerRight->endOffset()); EXPECT_TRUE(rangeFromTextToMiddleOfElement->boundaryPointsValid()); EXPECT_EQ(newText, rangeFromTextToMiddleOfElement->startContainer()); EXPECT_EQ(3, rangeFromTextToMiddleOfElement->startOffset()); EXPECT_EQ(outer, rangeFromTextToMiddleOfElement->endContainer()); EXPECT_EQ(4, rangeFromTextToMiddleOfElement->endOffset()); // StaticRange shouldn't mutate. EXPECT_EQ(outer, staticRangeOuterOutside->startContainer()); EXPECT_EQ(0, staticRangeOuterOutside->startOffset()); EXPECT_EQ(outer, staticRangeOuterOutside->endContainer()); EXPECT_EQ(5, staticRangeOuterOutside->endOffset()); EXPECT_EQ(outer, staticRangeOuterInside->startContainer()); EXPECT_EQ(1, staticRangeOuterInside->startOffset()); EXPECT_EQ(outer, staticRangeOuterInside->endContainer()); EXPECT_EQ(4, staticRangeOuterInside->endOffset()); EXPECT_EQ(outer, staticRangeOuterSurroundingText->startContainer()); EXPECT_EQ(2, staticRangeOuterSurroundingText->startOffset()); EXPECT_EQ(outer, staticRangeOuterSurroundingText->endContainer()); EXPECT_EQ(3, staticRangeOuterSurroundingText->endOffset()); EXPECT_EQ(innerLeft, staticRangeInnerLeft->startContainer()); EXPECT_EQ(0, staticRangeInnerLeft->startOffset()); EXPECT_EQ(innerLeft, staticRangeInnerLeft->endContainer()); EXPECT_EQ(1, staticRangeInnerLeft->endOffset()); EXPECT_EQ(innerRight, staticRangeInnerRight->startContainer()); EXPECT_EQ(0, staticRangeInnerRight->startOffset()); EXPECT_EQ(innerRight, staticRangeInnerRight->endContainer()); EXPECT_EQ(1, staticRangeInnerRight->endOffset()); EXPECT_EQ(oldText, staticRangeFromTextToMiddleOfElement->startContainer()); EXPECT_EQ(6, staticRangeFromTextToMiddleOfElement->startOffset()); EXPECT_EQ(outer, staticRangeFromTextToMiddleOfElement->endContainer()); EXPECT_EQ(3, staticRangeFromTextToMiddleOfElement->endOffset()); }
TEST_F(StaticRangeTest, SplitTextNodeRangeWithinText) { document().body()->setInnerHTML("1234", ASSERT_NO_EXCEPTION); Text* oldText = toText(document().body()->firstChild()); StaticRange* staticRange04 = StaticRange::create(document(), oldText, 0, oldText, 4); StaticRange* staticRange02 = StaticRange::create(document(), oldText, 0, oldText, 2); StaticRange* staticRange22 = StaticRange::create(document(), oldText, 2, oldText, 2); StaticRange* staticRange24 = StaticRange::create(document(), oldText, 2, oldText, 4); Range* range04 = staticRange04->toRange(ASSERT_NO_EXCEPTION); Range* range02 = staticRange02->toRange(ASSERT_NO_EXCEPTION); Range* range22 = staticRange22->toRange(ASSERT_NO_EXCEPTION); Range* range24 = staticRange24->toRange(ASSERT_NO_EXCEPTION); oldText->splitText(2, ASSERT_NO_EXCEPTION); Text* newText = toText(oldText->nextSibling()); // Range should mutate. EXPECT_TRUE(range04->boundaryPointsValid()); EXPECT_EQ(oldText, range04->startContainer()); EXPECT_EQ(0, range04->startOffset()); EXPECT_EQ(newText, range04->endContainer()); EXPECT_EQ(2, range04->endOffset()); EXPECT_TRUE(range02->boundaryPointsValid()); EXPECT_EQ(oldText, range02->startContainer()); EXPECT_EQ(0, range02->startOffset()); EXPECT_EQ(oldText, range02->endContainer()); EXPECT_EQ(2, range02->endOffset()); // Our implementation always moves the boundary point at the separation point to the end of the original text node. EXPECT_TRUE(range22->boundaryPointsValid()); EXPECT_EQ(oldText, range22->startContainer()); EXPECT_EQ(2, range22->startOffset()); EXPECT_EQ(oldText, range22->endContainer()); EXPECT_EQ(2, range22->endOffset()); EXPECT_TRUE(range24->boundaryPointsValid()); EXPECT_EQ(oldText, range24->startContainer()); EXPECT_EQ(2, range24->startOffset()); EXPECT_EQ(newText, range24->endContainer()); EXPECT_EQ(2, range24->endOffset()); // StaticRange shouldn't mutate. EXPECT_EQ(oldText, staticRange04->startContainer()); EXPECT_EQ(0, staticRange04->startOffset()); EXPECT_EQ(oldText, staticRange04->endContainer()); EXPECT_EQ(4, staticRange04->endOffset()); EXPECT_EQ(oldText, staticRange02->startContainer()); EXPECT_EQ(0, staticRange02->startOffset()); EXPECT_EQ(oldText, staticRange02->endContainer()); EXPECT_EQ(2, staticRange02->endOffset()); EXPECT_EQ(oldText, staticRange22->startContainer()); EXPECT_EQ(2, staticRange22->startOffset()); EXPECT_EQ(oldText, staticRange22->endContainer()); EXPECT_EQ(2, staticRange22->endOffset()); EXPECT_EQ(oldText, staticRange24->startContainer()); EXPECT_EQ(2, staticRange24->startOffset()); EXPECT_EQ(oldText, staticRange24->endContainer()); EXPECT_EQ(4, staticRange24->endOffset()); }
string Geometry::toString() const { return toText(); }
void Matrix4x4::print() const { char buffer[256]; toText(buffer, 256); puts(buffer); }
void InputMethodController::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd) { UserTypingGestureIndicator typingGestureIndicator(m_frame); Editor::RevealSelectionScope revealSelectionScope(&editor()); // Updates styles before setting selection for composition to prevent // inserting the previous composition text into text nodes oddly. // See https://bugs.webkit.org/show_bug.cgi?id=46868 m_frame->document()->updateStyleIfNeeded(); selectComposition(); if (m_frame->selection().isNone()) return; if (Element* target = m_frame->document()->focusedElement()) { // Dispatch an appropriate composition event to the focused node. // We check the composition status and choose an appropriate composition event since this // function is used for three purposes: // 1. Starting a new composition. // Send a compositionstart and a compositionupdate event when this function creates // a new composition node, i.e. // m_compositionNode == 0 && !text.isEmpty(). // Sending a compositionupdate event at this time ensures that at least one // compositionupdate event is dispatched. // 2. Updating the existing composition node. // Send a compositionupdate event when this function updates the existing composition // node, i.e. m_compositionNode != 0 && !text.isEmpty(). // 3. Canceling the ongoing composition. // Send a compositionend event when function deletes the existing composition node, i.e. // m_compositionNode != 0 && test.isEmpty(). RefPtr<CompositionEvent> event; if (!m_compositionNode) { // We should send a compositionstart event only when the given text is not empty because this // function doesn't create a composition node when the text is empty. if (!text.isEmpty()) { target->dispatchEvent(CompositionEvent::create(eventNames().compositionstartEvent, m_frame->domWindow(), m_frame->selectedText())); event = CompositionEvent::create(eventNames().compositionupdateEvent, m_frame->domWindow(), text); } } else { if (!text.isEmpty()) event = CompositionEvent::create(eventNames().compositionupdateEvent, m_frame->domWindow(), text); else event = CompositionEvent::create(eventNames().compositionendEvent, m_frame->domWindow(), text); } if (event.get()) target->dispatchEvent(event, IGNORE_EXCEPTION); } // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input // will delete the old composition with an optimized replace operation. if (text.isEmpty()) { ASSERT(m_frame->document()); TypingCommand::deleteSelection(*m_frame->document(), TypingCommand::PreventSpellChecking); } m_compositionNode = 0; m_customCompositionUnderlines.clear(); if (!text.isEmpty()) { ASSERT(m_frame->document()); TypingCommand::insertText(*m_frame->document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate); // Find out what node has the composition now. Position base = m_frame->selection().base().downstream(); Position extent = m_frame->selection().extent(); Node* baseNode = base.deprecatedNode(); unsigned baseOffset = base.deprecatedEditingOffset(); Node* extentNode = extent.deprecatedNode(); unsigned extentOffset = extent.deprecatedEditingOffset(); if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) { m_compositionNode = toText(baseNode); m_compositionStart = baseOffset; m_compositionEnd = extentOffset; m_customCompositionUnderlines = underlines; size_t numUnderlines = m_customCompositionUnderlines.size(); for (size_t i = 0; i < numUnderlines; ++i) { m_customCompositionUnderlines[i].startOffset += baseOffset; m_customCompositionUnderlines[i].endOffset += baseOffset; } if (baseNode->renderer()) baseNode->renderer()->repaint(); unsigned start = std::min(baseOffset + selectionStart, extentOffset); unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOffset); RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end); m_frame->selection().setSelectedRange(selectedRange.get(), DOWNSTREAM, false); } } }
int main(int argc, char *argv[]) { ATerm bottomOfStack; ATerm at_tree; char *ATlibArgv[] = {"pandora", "-at-termtable", "21"}; PT_ParseTree tree; PT_Tree ptText; BOX_Start box; char *input = "-"; char *output = "-"; ATbool boxOutput = ATfalse; ATbool bytesOutput = ATfalse; int c; int i; ATbool useToolbus = ATfalse; ATinit(3, ATlibArgv, &bottomOfStack); ASC_initRunTime(INITIAL_TABLE_SIZE); PT_initMEPTApi(); PTPT_initPTMEPTApi(); BOX_initBoxApi(); ERR_initErrorManager("pandora", "all"); register_Box_to_Text(); resolve_Box_to_Text(); init_Box_to_Text(); for (i=1; !useToolbus && i < argc; i++) { useToolbus = !strcmp(argv[i], "-TB_TOOL_NAME"); } if (useToolbus) { int cid; ATBinit(argc, argv, &bottomOfStack); cid = ATBconnect(NULL, NULL, -1, pandora_handler); ATBeventloop(); } else { while ((c = getopt(argc, argv, myarguments)) != -1) { switch (c) { case 'b': boxOutput=ATtrue; break; case 'B': bytesOutput=ATtrue; break; case 'i': input=optarg; break; case 'o': output=optarg; break; case 'v': run_verbose = ATtrue; break; case 'V': version(); exit(0); case 'h': usage(); exit(0); default: usage(); exit(1); } } at_tree = ATreadFromNamedFile(input); if (at_tree != NULL) { tree = PT_ParseTreeFromTerm(at_tree); if (run_verbose) { ATwarning("pandora: mapping parse trees to " "BOX using default rules\n"); } box = pandora(tree); if (!boxOutput) { if (run_verbose) { ATwarning("pandora: rendering BOX to list of characters\n"); } ptText = toText(PT_ParseTreeFromTerm(BOX_StartToTerm(box))); if (run_verbose) { ATwarning("pandora: yielding characters to output\n"); } if (!strcmp(output, "-")) { if (!bytesOutput) { PT_yieldTreeToFile(ptText, stdout, ATfalse); } else { ATwriteToNamedBinaryFile((ATerm) PT_makeValidParseTreeFromTree(ptText), "-"); } } else { if (!bytesOutput) { FILE *fp = fopen(output, "wb"); if (fp != NULL) { PT_yieldTreeToFile(ptText, fp, ATfalse); } else { ATerror("Could not open %s for writing.\n", output); } } else { ATwriteToNamedBinaryFile((ATerm) PT_makeValidParseTreeFromTree(ptText), output); } } } else { PT_ParseTree ptBox = PT_ParseTreeFromTerm(BOX_StartToTerm(box)); ATwriteToNamedSharedTextFile((ATerm) ptBox, output); } } else { ATwarning("Failed to read ATerm from file: %s\n", input); return 1; } } return 0; }