DocumentFragment* createFragmentFromMarkupWithContext( Document& document, const String& markup, unsigned fragmentStart, unsigned fragmentEnd, const String& baseURL, ParserContentPolicy parserContentPolicy) { // FIXME: Need to handle the case where the markup already contains these // markers. StringBuilder taggedMarkup; taggedMarkup.append(markup.left(fragmentStart)); MarkupFormatter::appendComment(taggedMarkup, fragmentMarkerTag); taggedMarkup.append( markup.substring(fragmentStart, fragmentEnd - fragmentStart)); MarkupFormatter::appendComment(taggedMarkup, fragmentMarkerTag); taggedMarkup.append(markup.substring(fragmentEnd)); DocumentFragment* taggedFragment = createFragmentFromMarkup( document, taggedMarkup.toString(), baseURL, parserContentPolicy); Comment* nodeBeforeContext = nullptr; Comment* nodeAfterContext = nullptr; if (!findNodesSurroundingContext(taggedFragment, nodeBeforeContext, nodeAfterContext)) return nullptr; Document* taggedDocument = Document::create(); taggedDocument->setContextFeatures(document.contextFeatures()); Element* root = Element::create(QualifiedName::null(), taggedDocument); root->appendChild(taggedFragment); taggedDocument->appendChild(root); Range* range = Range::create( *taggedDocument, Position::afterNode(nodeBeforeContext).parentAnchoredEquivalent(), Position::beforeNode(nodeAfterContext).parentAnchoredEquivalent()); Node* commonAncestor = range->commonAncestorContainer(); HTMLElement* specialCommonAncestor = ancestorToRetainStructureAndAppearanceWithNoLayoutObject(commonAncestor); // When there's a special common ancestor outside of the fragment, we must // include it as well to preserve the structure and appearance of the // fragment. For example, if the fragment contains TD, we need to include the // enclosing TABLE tag as well. DocumentFragment* fragment = DocumentFragment::create(document); if (specialCommonAncestor) fragment->appendChild(specialCommonAncestor); else fragment->parserTakeAllChildrenFrom(toContainerNode(*commonAncestor)); trimFragment(fragment, nodeBeforeContext, nodeAfterContext); return fragment; }
JSValue* JSRange::getValueProperty(ExecState* exec, int token) const { switch (token) { case StartContainerAttrNum: { ExceptionCode ec = 0; Range* imp = static_cast<Range*>(impl()); KJS::JSValue* result = toJS(exec, WTF::getPtr(imp->startContainer(ec))); setDOMException(exec, ec); return result; } case StartOffsetAttrNum: { ExceptionCode ec = 0; Range* imp = static_cast<Range*>(impl()); KJS::JSValue* result = jsNumber(imp->startOffset(ec)); setDOMException(exec, ec); return result; } case EndContainerAttrNum: { ExceptionCode ec = 0; Range* imp = static_cast<Range*>(impl()); KJS::JSValue* result = toJS(exec, WTF::getPtr(imp->endContainer(ec))); setDOMException(exec, ec); return result; } case EndOffsetAttrNum: { ExceptionCode ec = 0; Range* imp = static_cast<Range*>(impl()); KJS::JSValue* result = jsNumber(imp->endOffset(ec)); setDOMException(exec, ec); return result; } case CollapsedAttrNum: { ExceptionCode ec = 0; Range* imp = static_cast<Range*>(impl()); KJS::JSValue* result = jsBoolean(imp->collapsed(ec)); setDOMException(exec, ec); return result; } case CommonAncestorContainerAttrNum: { ExceptionCode ec = 0; Range* imp = static_cast<Range*>(impl()); KJS::JSValue* result = toJS(exec, WTF::getPtr(imp->commonAncestorContainer(ec))); setDOMException(exec, ec); return result; } case ConstructorAttrNum: return getConstructor(exec); } return 0; }
// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange? // FIXME: At least, annotation and style info should probably not be included in range.markupString() static String createMarkupInternal(Document& document, const Range& range, const Range& updatedRange, Vector<Node*>* nodes, EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs) { DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, (ASCIILiteral("<br class=\"" AppleInterchangeNewline "\">"))); bool collapsed = updatedRange.collapsed(ASSERT_NO_EXCEPTION); if (collapsed) return emptyString(); Node* commonAncestor = updatedRange.commonAncestorContainer(ASSERT_NO_EXCEPTION); if (!commonAncestor) return emptyString(); document.updateLayoutIgnorePendingStylesheets(); Node* body = enclosingNodeWithTag(firstPositionInNode(commonAncestor), bodyTag); Node* fullySelectedRoot = 0; // FIXME: Do this for all fully selected blocks, not just the body. if (body && areRangesEqual(VisibleSelection::selectionFromContentsOfNode(body).toNormalizedRange().get(), &range)) fullySelectedRoot = body; Node* specialCommonAncestor = highestAncestorToWrapMarkup(&updatedRange, shouldAnnotate); StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, &updatedRange, specialCommonAncestor); Node* pastEnd = updatedRange.pastLastNode(); Node* startNode = updatedRange.firstNode(); VisiblePosition visibleStart(updatedRange.startPosition(), VP_DEFAULT_AFFINITY); VisiblePosition visibleEnd(updatedRange.endPosition(), VP_DEFAULT_AFFINITY); if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleStart)) { if (visibleStart == visibleEnd.previous()) return interchangeNewlineString; accumulator.appendString(interchangeNewlineString); startNode = visibleStart.next().deepEquivalent().deprecatedNode(); if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0, ASSERT_NO_EXCEPTION) >= 0) return interchangeNewlineString; } Node* lastClosed = accumulator.serializeNodes(startNode, pastEnd); if (specialCommonAncestor && lastClosed) { // Also include all of the ancestors of lastClosed up to this special ancestor. for (ContainerNode* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) { if (ancestor == fullySelectedRoot && !convertBlocksToInlines) { RefPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot); // Bring the background attribute over, but not as an attribute because a background attribute on a div // appears to have no effect. if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style() || !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundImage)) && toElement(fullySelectedRoot)->hasAttribute(backgroundAttr)) fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + toElement(fullySelectedRoot)->getAttribute(backgroundAttr) + "')"); if (fullySelectedRootStyle->style()) { // Reset the CSS properties to avoid an assertion error in addStyleMarkup(). // This assertion is caused at least when we select all text of a <body> element whose // 'text-decoration' property is "inherit", and copy it. if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyTextDecoration)) fullySelectedRootStyle->style()->setProperty(CSSPropertyTextDecoration, CSSValueNone); if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyWebkitTextDecorationsInEffect)) fullySelectedRootStyle->style()->setProperty(CSSPropertyWebkitTextDecorationsInEffect, CSSValueNone); accumulator.wrapWithStyleNode(fullySelectedRootStyle->style(), document, true); } } else { // Since this node and all the other ancestors are not in the selection we want to set RangeFullySelectsNode to DoesNotFullySelectNode // so that styles that affect the exterior of the node are not included. accumulator.wrapWithNode(*ancestor, convertBlocksToInlines, StyledMarkupAccumulator::DoesNotFullySelectNode); } if (nodes) nodes->append(ancestor); lastClosed = ancestor; if (ancestor == specialCommonAncestor) break; } } // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally. if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous())) accumulator.appendString(interchangeNewlineString); return accumulator.takeResults(); }