📄 applystylecommand.cpp
字号:
, m_style(CSSMutableStyleDeclaration::create()) , m_editingAction(editingAction) , m_propertyLevel(PropertyDefault) , m_start(endingSelection().start().downstream()) , m_end(endingSelection().end().upstream()) , m_useEndingSelection(true) , m_styledInlineElement(element) , m_removeOnly(removeOnly){}void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position& newEnd){ ASSERT(Range::compareBoundaryPoints(newEnd, newStart) >= 0); if (!m_useEndingSelection && (newStart != m_start || newEnd != m_end)) m_useEndingSelection = true; setEndingSelection(VisibleSelection(newStart, newEnd, VP_DEFAULT_AFFINITY)); m_start = newStart; m_end = newEnd;}Position ApplyStyleCommand::startPosition(){ if (m_useEndingSelection) return endingSelection().start(); return m_start;}Position ApplyStyleCommand::endPosition(){ if (m_useEndingSelection) return endingSelection().end(); return m_end;}void ApplyStyleCommand::doApply(){ switch (m_propertyLevel) { case PropertyDefault: { // apply the block-centric properties of the style RefPtr<CSSMutableStyleDeclaration> blockStyle = m_style->copyBlockProperties(); if (blockStyle->length()) applyBlockStyle(blockStyle.get()); // apply any remaining styles to the inline elements // NOTE: hopefully, this string comparison is the same as checking for a non-null diff if (blockStyle->length() < m_style->length() || m_styledInlineElement) { RefPtr<CSSMutableStyleDeclaration> inlineStyle = m_style->copy(); applyRelativeFontStyleChange(inlineStyle.get()); blockStyle->diff(inlineStyle.get()); applyInlineStyle(inlineStyle.get()); } break; } case ForceBlockProperties: // Force all properties to be applied as block styles. applyBlockStyle(m_style.get()); break; }}EditAction ApplyStyleCommand::editingAction() const{ return m_editingAction;}void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclaration *style){ // 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(); // get 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; } VisiblePosition visibleStart(start); VisiblePosition visibleEnd(end); // Save and restore the selection endpoints using their indices in the document, since // addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints. // Calculate start and end indices from the start of the tree that they're in. Node* scope = highestAncestor(visibleStart.deepEquivalent().node()); Position rangeStart(scope, 0); RefPtr<Range> startRange = Range::create(document(), rangeStart, rangeCompliantEquivalent(visibleStart.deepEquivalent())); RefPtr<Range> endRange = Range::create(document(), rangeStart, rangeCompliantEquivalent(visibleEnd.deepEquivalent())); int startIndex = TextIterator::rangeLength(startRange.get(), true); int endIndex = TextIterator::rangeLength(endRange.get(), true); VisiblePosition paragraphStart(startOfParagraph(visibleStart)); VisiblePosition nextParagraphStart(endOfParagraph(paragraphStart).next()); VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next()); while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) { StyleChange styleChange(style, paragraphStart.deepEquivalent()); if (styleChange.cssStyle().length() > 0 || m_removeOnly) { RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().node()); RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent()); if (newBlock) block = newBlock; ASSERT(block->isHTMLElement()); if (block->isHTMLElement()) { removeCSSStyle(style, static_cast<HTMLElement*>(block.get())); if (!m_removeOnly) addBlockStyle(styleChange, static_cast<HTMLElement*>(block.get())); } } paragraphStart = nextParagraphStart; nextParagraphStart = endOfParagraph(paragraphStart).next(); } startRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), startIndex, 0, true); endRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), endIndex, 0, true); if (startRange && endRange) updateStartEnd(startRange->startPosition(), endRange->startPosition());}#define NoFontDelta (0.0f)#define MinimumFontSize (0.1f)void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclaration *style){ RefPtr<CSSValue> value = style->getPropertyCSSValue(CSSPropertyFontSize); if (value) { // Explicit font size overrides any delta. style->removeProperty(CSSPropertyWebkitFontSizeDelta); return; } // Get the adjustment amount out of the style. value = style->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta); if (!value) return; float adjustment = NoFontDelta; if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) { CSSPrimitiveValue *primitiveValue = static_cast<CSSPrimitiveValue *>(value.get()); if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_PX) { // Only PX handled now. If we handle more types in the future, perhaps // a switch statement here would be more appropriate. adjustment = primitiveValue->getFloatValue(); } } style->removeProperty(CSSPropertyWebkitFontSizeDelta); if (adjustment == NoFontDelta) return; Position start = startPosition(); Position end = endPosition(); if (Range::compareBoundaryPoints(end, start) < 0) { Position swap = start; start = end; end = swap; } // Join up any adjacent text nodes. if (start.node()->isTextNode()) { joinChildTextNodes(start.node()->parentNode(), start, end); start = startPosition(); end = endPosition(); } if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) { joinChildTextNodes(end.node()->parentNode(), start, end); start = startPosition(); end = endPosition(); } // Split the start text nodes if needed to apply style. bool splitStart = splitTextAtStartIfNeeded(start, end); if (splitStart) { start = startPosition(); end = endPosition(); } bool splitEnd = splitTextAtEndIfNeeded(start, end); if (splitEnd) { start = startPosition(); end = endPosition(); } // Calculate loop end point. // If the end node is before the start node (can only happen if the end node is // an ancestor of the start node), we gather nodes up to the next sibling of the end node Node *beyondEnd; if (start.node()->isDescendantOf(end.node())) beyondEnd = end.node()->traverseNextSibling(); else beyondEnd = end.node()->traverseNextNode(); start = start.upstream(); // Move upstream to ensure we do not add redundant spans. Node *startNode = start.node(); if (startNode->isTextNode() && start.offset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters. startNode = startNode->traverseNextNode(); // Store away font size before making any changes to the document. // This ensures that changes to one node won't effect another. HashMap<Node*, float> startingFontSizes; for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode()) startingFontSizes.set(node, computedFontSize(node)); // These spans were added by us. If empty after font size changes, they can be removed. Vector<RefPtr<HTMLElement> > unstyledSpans; Node* lastStyledNode = 0; for (Node* node = startNode; node != beyondEnd; node = node->traverseNextNode()) { RefPtr<HTMLElement> element; if (node->isHTMLElement()) { // Only work on fully selected nodes. if (!nodeFullySelected(node, start, end)) continue; element = static_cast<HTMLElement*>(node); } else if (node->isTextNode() && node->renderer() && node->parentNode() != lastStyledNode) { // Last styled node was not parent node of this text node, but we wish to style this // text node. To make this possible, add a style span to surround this text node. RefPtr<HTMLElement> span = createStyleSpanElement(document()); surroundNodeRangeWithElement(node, node, span.get()); element = span.release(); } else { // Only handle HTML elements and text nodes. continue; } lastStyledNode = node; CSSMutableStyleDeclaration* inlineStyleDecl = element->getInlineStyleDecl(); float currentFontSize = computedFontSize(node); float desiredFontSize = max(MinimumFontSize, startingFontSizes.get(node) + adjustment); RefPtr<CSSValue> value = inlineStyleDecl->getPropertyCSSValue(CSSPropertyFontSize); if (value) { inlineStyleDecl->removeProperty(CSSPropertyFontSize, true); currentFontSize = computedFontSize(node); } if (currentFontSize != desiredFontSize) { inlineStyleDecl->setProperty(CSSPropertyFontSize, String::number(desiredFontSize) + "px", false, false); setNodeAttribute(element.get(), styleAttr, inlineStyleDecl->cssText()); } if (inlineStyleDecl->length() == 0) { removeNodeAttribute(element.get(), styleAttr); // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan? Need a test. if (isUnstyledStyleSpan(element.get())) unstyledSpans.append(element.release()); } } size_t size = unstyledSpans.size(); for (size_t i = 0; i < size; ++i) removeNodePreservingChildren(unstyledSpans[i].get());}#undef NoFontDelta#undef MinimumFontSizestatic Node* dummySpanAncestorForNode(const Node* node){ while (node && !isStyleSpan(node)) node = node->parent(); return node ? node->parent() : 0;}void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor){ if (!dummySpanAncestor) return; // Dummy spans are created when text node is split, so that style information // can be propagated, which can result in more splitting. If a dummy span gets // cloned/split, the new node is always a sibling of it. Therefore, we scan // all the children of the dummy's parent Node* next; for (Node* node = dummySpanAncestor->firstChild(); node; node = next) { next = node->nextSibling(); if (isUnstyledStyleSpan(node)) removeNodePreservingChildren(node); node = next; }}HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool before, RefPtr<CSSPrimitiveValue> allowedDirection){ // We are allowed to leave the highest ancestor with unicode-bidi unsplit if it is unicode-bidi: embed and direction: allowedDirection. // In that case, we return the unsplit ancestor. Otherwise, we return 0. Node* block = enclosingBlock(node); if (!block) return 0; Node* highestAncestorWithUnicodeBidi = 0; Node* nextHighestAncestorWithUnicodeBidi = 0; RefPtr<CSSPrimitiveValue> highestAncestorUnicodeBidi; for (Node* n = node->parent(); n != block; n = n->parent()) { RefPtr<CSSValue> unicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi); if (unicodeBidi) { ASSERT(unicodeBidi->isPrimitiveValue()); if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() != CSSValueNormal) { highestAncestorUnicodeBidi = static_cast<CSSPrimitiveValue*>(unicodeBidi.get()); nextHighestAncestorWithUnicodeBidi = highestAncestorWithUnicodeBidi; highestAncestorWithUnicodeBidi = n; } } } if (!highestAncestorWithUnicodeBidi) return 0; HTMLElement* unsplitAncestor = 0; if (allowedDirection && highestAncestorUnicodeBidi->getIdent() != CSSValueBidiOverride) { RefPtr<CSSValue> highestAncestorDirection = computedStyle(highestAncestorWithUnicodeBidi)->getPropertyCSSValue(CSSPropertyDirection); ASSERT(highestAncestorDirection->isPrimitiveValue()); if (static_cast<CSSPrimitiveValue*>(highestAncestorDirection.get())->getIdent() == allowedDirection->getIdent() && highestAncestorWithUnicodeBidi->isHTMLElement()) { if (!nextHighestAncestorWithUnicodeBidi) return static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi); unsplitAncestor = static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi); highestAncestorWithUnicodeBidi = nextHighestAncestorWithUnicodeBidi; } } // Split every ancestor through highest ancestor with embedding. Node* n = node; while (true) { Element* parent = static_cast<Element*>(n->parent());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -