📄 applystylecommand.cpp
字号:
if (before ? n->previousSibling() : n->nextSibling()) splitElement(parent, before ? n : n->nextSibling()); if (parent == highestAncestorWithUnicodeBidi) break; n = n->parent(); } return unsplitAncestor;}void ApplyStyleCommand::removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsplitAncestor){ Node* block = enclosingBlock(node); if (!block) return; Node* n = node->parent(); while (n != block && n != unsplitAncestor) { Node* parent = n->parent(); if (!n->isStyledElement()) { n = parent; continue; } StyledElement* element = static_cast<StyledElement*>(n); RefPtr<CSSValue> unicodeBidi = computedStyle(element)->getPropertyCSSValue(CSSPropertyUnicodeBidi); if (unicodeBidi) { ASSERT(unicodeBidi->isPrimitiveValue()); if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() != CSSValueNormal) { // FIXME: This code should really consider the mapped attribute 'dir', the inline style declaration, // and all matching style rules in order to determine how to best set the unicode-bidi property to 'normal'. // For now, it assumes that if the 'dir' attribute is present, then removing it will suffice, and // otherwise it sets the property in the inline style declaration. if (element->hasAttribute(dirAttr)) { // FIXME: If this is a BDO element, we should probably just remove it if it has no // other attributes, like we (should) do with B and I elements. removeNodeAttribute(element, dirAttr); } else { RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy(); inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal); inlineStyle->removeProperty(CSSPropertyDirection); setNodeAttribute(element, styleAttr, inlineStyle->cssText()); // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan? Need a test. if (isUnstyledStyleSpan(element)) removeNodePreservingChildren(element); } } } n = parent; }}void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style){ Node* startDummySpanAncestor = 0; Node* endDummySpanAncestor = 0; // update document layout once before removing styles // so that we avoid the expense of updating before each and every call // to check a computed style updateLayout(); // adjust to the positions we want to use for applying style Position start = startPosition(); Position end = endPosition(); if (Range::compareBoundaryPoints(end, start) < 0) { Position swap = start; start = end; end = swap; } // split the start node and containing element if the selection starts inside of it bool splitStart = splitTextElementAtStartIfNeeded(start, end); if (splitStart) { start = startPosition(); end = endPosition(); startDummySpanAncestor = dummySpanAncestorForNode(start.node()); } // split the end node and containing element if the selection ends inside of it bool splitEnd = splitTextElementAtEndIfNeeded(start, end); if (splitEnd) { start = startPosition(); end = endPosition(); endDummySpanAncestor = dummySpanAncestorForNode(end.node()); } RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi); RefPtr<CSSValue> direction; HTMLElement* startUnsplitAncestor = 0; HTMLElement* endUnsplitAncestor = 0; if (unicodeBidi) { RefPtr<CSSPrimitiveValue> allowedDirection; ASSERT(unicodeBidi->isPrimitiveValue()); if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() == CSSValueEmbed) { // Leave alone an ancestor that provides the desired single level embedding, if there is one. direction = style->getPropertyCSSValue(CSSPropertyDirection); ASSERT(direction->isPrimitiveValue()); allowedDirection = static_cast<CSSPrimitiveValue*>(direction.get()); } startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.node(), true, allowedDirection); endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.node(), false, allowedDirection); removeEmbeddingUpToEnclosingBlock(start.node(), startUnsplitAncestor); removeEmbeddingUpToEnclosingBlock(end.node(), endUnsplitAncestor); } // Remove style from the selection. // Use the upstream position of the start for removing style. // This will ensure we remove all traces of the relevant styles from the selection // and prevent us from adding redundant ones, as described in: // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags Position removeStart = start.upstream(); Position embeddingRemoveStart = removeStart; Position embeddingRemoveEnd = end; if (unicodeBidi) { // Avoid removing the dir attribute and the unicode-bidi and direction properties from the unsplit ancestors. if (startUnsplitAncestor && nodeFullySelected(startUnsplitAncestor, removeStart, end)) embeddingRemoveStart = positionAfterNode(startUnsplitAncestor); if (endUnsplitAncestor && nodeFullySelected(endUnsplitAncestor, removeStart, end)) embeddingRemoveEnd = positionBeforeNode(endUnsplitAncestor).downstream(); } if (embeddingRemoveStart != removeStart || embeddingRemoveEnd != end) { RefPtr<CSSMutableStyleDeclaration> embeddingStyle = CSSMutableStyleDeclaration::create(); embeddingStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed); embeddingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent()); if (Range::compareBoundaryPoints(embeddingRemoveStart, embeddingRemoveEnd) <= 0) removeInlineStyle(embeddingStyle, embeddingRemoveStart, embeddingRemoveEnd); RefPtr<CSSMutableStyleDeclaration> styleWithoutEmbedding = style->copy(); styleWithoutEmbedding->removeProperty(CSSPropertyUnicodeBidi); styleWithoutEmbedding->removeProperty(CSSPropertyDirection); removeInlineStyle(styleWithoutEmbedding, removeStart, end); } else removeInlineStyle(style, removeStart, end); start = startPosition(); end = endPosition(); if (splitStart) { bool mergedStart = mergeStartWithPreviousIfIdentical(start, end); if (mergedStart) { start = startPosition(); end = endPosition(); } } if (splitEnd) { mergeEndWithNextIfIdentical(start, end); start = startPosition(); end = endPosition(); } // update document layout once before running the rest of the function // so that we avoid the expense of updating before each and every call // to check a computed style updateLayout(); Position embeddingApplyStart = start; Position embeddingApplyEnd = end; if (unicodeBidi) { // Avoid applying the unicode-bidi and direction properties beneath ancestors that already have them. Node* startEnclosingBlock = enclosingBlock(start.node()); for (Node* n = start.node(); n != startEnclosingBlock; n = n->parent()) { if (n->isHTMLElement()) { RefPtr<CSSValue> ancestorUnicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi); if (ancestorUnicodeBidi) { ASSERT(ancestorUnicodeBidi->isPrimitiveValue()); if (static_cast<CSSPrimitiveValue*>(ancestorUnicodeBidi.get())->getIdent() == CSSValueEmbed) { embeddingApplyStart = positionAfterNode(n); break; } } } } Node* endEnclosingBlock = enclosingBlock(end.node()); for (Node* n = end.node(); n != endEnclosingBlock; n = n->parent()) { if (n->isHTMLElement()) { RefPtr<CSSValue> ancestorUnicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi); if (ancestorUnicodeBidi) { ASSERT(ancestorUnicodeBidi->isPrimitiveValue()); if (static_cast<CSSPrimitiveValue*>(ancestorUnicodeBidi.get())->getIdent() == CSSValueEmbed) { embeddingApplyEnd = positionBeforeNode(n); break; } } } } } if (embeddingApplyStart != start || embeddingApplyEnd != end) { if (embeddingApplyStart.isNotNull() && embeddingApplyEnd.isNotNull()) { RefPtr<CSSMutableStyleDeclaration> embeddingStyle = CSSMutableStyleDeclaration::create(); embeddingStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed); embeddingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent()); applyInlineStyleToRange(embeddingStyle.get(), embeddingApplyStart, embeddingApplyEnd); } RefPtr<CSSMutableStyleDeclaration> styleWithoutEmbedding = style->copy(); styleWithoutEmbedding->removeProperty(CSSPropertyUnicodeBidi); styleWithoutEmbedding->removeProperty(CSSPropertyDirection); applyInlineStyleToRange(styleWithoutEmbedding.get(), start, end); } else applyInlineStyleToRange(style, start, end); // Remove dummy style spans created by splitting text elements. cleanupUnstyledAppleStyleSpans(startDummySpanAncestor); if (endDummySpanAncestor != startDummySpanAncestor) cleanupUnstyledAppleStyleSpans(endDummySpanAncestor);}void ApplyStyleCommand::applyInlineStyleToRange(CSSMutableStyleDeclaration* style, const Position& start, const Position& rangeEnd){ Node* node = start.node(); Position end = rangeEnd; bool rangeIsEmpty = false; if (start.offset() >= caretMaxOffset(start.node())) { node = node->traverseNextNode(); Position newStart = Position(node, 0); if (Range::compareBoundaryPoints(end, newStart) < 0) rangeIsEmpty = true; } if (!rangeIsEmpty) { // pastEndNode is the node after the last fully selected node. Node* pastEndNode = end.node(); if (end.offset() >= caretMaxOffset(end.node())) pastEndNode = end.node()->traverseNextSibling(); // FIXME: Callers should perform this operation on a Range that includes the br // if they want style applied to the empty line. if (start == end && start.node()->hasTagName(brTag)) pastEndNode = start.node()->traverseNextNode(); // Add the style to selected inline runs. for (Node* next; node && node != pastEndNode; node = next) { next = node->traverseNextNode(); if (!node->renderer() || !node->isContentEditable()) continue; if (!node->isContentRichlyEditable() && node->isHTMLElement()) { // This is a plaintext-only region. Only proceed if it's fully selected. // pastEndNode is the node after the last fully selected node, so if it's inside node then // node isn't fully selected. if (pastEndNode->isDescendantOf(node)) break; // Add to this element's inline style and skip over its contents. HTMLElement* element = static_cast<HTMLElement*>(node); RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy(); inlineStyle->merge(style); setNodeAttribute(element, styleAttr, inlineStyle->cssText()); next = node->traverseNextSibling(); continue; } if (isBlock(node)) continue; if (node->childNodeCount()) { if (editingIgnoresContent(node)) { next = node->traverseNextSibling(); continue; } continue; } Node* runStart = node; // Find the end of the run. Node* sibling = node->nextSibling(); while (sibling && sibling != pastEndNode && (!sibling->isElementNode() || sibling->hasTagName(brTag)) && !isBlock(sibling)) { node = sibling; sibling = node->nextSibling(); } // Recompute next, since node has changed. next = node->traverseNextNode(); // Apply the style to the run. addInlineStyleIfNeeded(style, runStart, node); } }}// This function maps from styling tags to CSS styles. Used for knowing which// styling tags should be removed when toggling styles.bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclaration* style, HTMLElement* elem){ CSSMutableStyleDeclaration::const_iterator end = style->end(); for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) { switch ((*it).id()) { case CSSPropertyFontWeight: // IE inserts "strong" tags for execCommand("bold"), so we remove them, even though they're not strictly presentational if (elem->hasLocalName(bTag) || elem->hasLocalName(strongTag)) return true; break; case CSSPropertyVerticalAlign: if (elem->hasLocalName(subTag) || elem->hasLocalName(supTag)) return true; break; case CSSPropertyFontStyle: // IE inserts "em" tags for execCommand("italic"), so we remove them, even though they're not strictly presentational if (elem->hasLocalName(iTag) || elem->hasLocalName(emTag)) return true; } } return false;}void ApplyStyleCommand::removeHTMLStyleNode(HTMLElement *elem){ // This node can be removed. // EDIT FIXME: This does not handle the case where the node // has attributes. But how often do people add attributes to <B> tags? // Not so often I think. ASSERT(elem); removeNodePreservingChildren(elem);}void ApplyStyleCommand::removeHTMLFontStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem){ ASSERT(style); ASSERT(elem); if (!elem->hasLocalName(fontTag)) return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -