📄 markup.cpp
字号:
} else { preMarkups.append(getStartMarkup(ancestor, updatedRange.get(), annotate, convertBlocksToInlines)); markups.append(getEndMarkup(ancestor)); } if (nodes) nodes->append(ancestor); lastClosed = ancestor; if (ancestor == specialCommonAncestor) break; } } DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\"")); DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>")); // Add a wrapper span with the styles that all of the nodes in the markup inherit. Node* parentOfLastClosed = lastClosed ? lastClosed->parentNode() : 0; if (parentOfLastClosed && parentOfLastClosed->renderer()) { RefPtr<CSSMutableStyleDeclaration> style = computedStyle(parentOfLastClosed)->copyInheritableProperties(); // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, to help // us differentiate those styles from ones that the user has applied. This helps us // get the color of content pasted into blockquotes right. removeEnclosingMailBlockquoteStyle(style.get(), parentOfLastClosed); // Document default styles will be added on another wrapper span. removeDefaultStyles(style.get(), document); // Since we are converting blocks to inlines, remove any inherited block properties that are in the style. // This cuts out meaningless properties and prevents properties from magically affecting blocks later // if the style is cloned for a new block element during a future editing operation. if (convertBlocksToInlines) style->removeBlockProperties(); if (style->length() > 0) { Vector<UChar> openTag; append(openTag, styleSpanOpen); appendAttributeValue(openTag, style->cssText(), documentIsHTML); openTag.append('\"'); openTag.append('>'); preMarkups.append(String::adopt(openTag)); markups.append(styleSpanClose); } } if (lastClosed && lastClosed != document->documentElement()) { // Add a style span with the document's default styles. We add these in a separate // span so that at paste time we can differentiate between document defaults and user // applied styles. RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle(document->documentElement())->copyInheritableProperties(); if (defaultStyle->length() > 0) { Vector<UChar> openTag; append(openTag, styleSpanOpen); appendAttributeValue(openTag, defaultStyle->cssText(), documentIsHTML); openTag.append('\"'); openTag.append('>'); preMarkups.append(String::adopt(openTag)); markups.append(styleSpanClose); } } // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally. if (annotate && needInterchangeNewlineAfter(visibleEnd.previous())) markups.append(interchangeNewlineString); if (deleteButton) deleteButton->enable(); return joinMarkups(preMarkups, markups);}PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const String& markup, const String& baseURL){ ASSERT(document->documentElement()->isHTMLElement()); // FIXME: What if the document element is not an HTML element? HTMLElement *element = static_cast<HTMLElement*>(document->documentElement()); RefPtr<DocumentFragment> fragment = element->createContextualFragment(markup); if (fragment && !baseURL.isEmpty() && baseURL != blankURL() && baseURL != document->baseURL()) completeURLs(fragment.get(), baseURL); return fragment.release();}String createMarkup(const Node* node, EChildrenOnly includeChildren, Vector<Node*>* nodes){ Vector<UChar> result; if (!node) return ""; Document* document = node->document(); Frame* frame = document->frame(); DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0; // disable the delete button so it's elements are not serialized into the markup if (deleteButton) { if (node->isDescendantOf(deleteButton->containerElement())) return ""; deleteButton->disable(); } appendMarkup(result, const_cast<Node*>(node), includeChildren, nodes); if (deleteButton) deleteButton->enable(); return String::adopt(result);}static void fillContainerFromString(ContainerNode* paragraph, const String& string){ Document* document = paragraph->document(); ExceptionCode ec = 0; if (string.isEmpty()) { paragraph->appendChild(createBlockPlaceholderElement(document), ec); ASSERT(ec == 0); return; } ASSERT(string.find('\n') == -1); Vector<String> tabList; string.split('\t', true, tabList); String tabText = ""; bool first = true; size_t numEntries = tabList.size(); for (size_t i = 0; i < numEntries; ++i) { const String& s = tabList[i]; // append the non-tab textual part if (!s.isEmpty()) { if (!tabText.isEmpty()) { paragraph->appendChild(createTabSpanElement(document, tabText), ec); ASSERT(ec == 0); tabText = ""; } RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries)); paragraph->appendChild(textNode.release(), ec); ASSERT(ec == 0); } // there is a tab after every entry, except the last entry // (if the last character is a tab, the list gets an extra empty entry) if (i + 1 != numEntries) tabText.append('\t'); else if (!tabText.isEmpty()) { paragraph->appendChild(createTabSpanElement(document, tabText), ec); ASSERT(ec == 0); } first = false; }}PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text){ if (!context) return 0; Node* styleNode = context->firstNode(); if (!styleNode) { styleNode = context->startPosition().node(); if (!styleNode) return 0; } Document* document = styleNode->document(); RefPtr<DocumentFragment> fragment = document->createDocumentFragment(); if (text.isEmpty()) return fragment.release(); String string = text; string.replace("\r\n", "\n"); string.replace('\r', '\n'); ExceptionCode ec = 0; RenderObject* renderer = styleNode->renderer(); if (renderer && renderer->style()->preserveNewline()) { fragment->appendChild(document->createTextNode(string), ec); ASSERT(ec == 0); if (string.endsWith("\n")) { RefPtr<Element> element = createBreakElement(document); element->setAttribute(classAttr, AppleInterchangeNewline); fragment->appendChild(element.release(), ec); ASSERT(ec == 0); } return fragment.release(); } // A string with no newlines gets added inline, rather than being put into a paragraph. if (string.find('\n') == -1) { fillContainerFromString(fragment.get(), string); return fragment.release(); } // Break string into paragraphs. Extra line breaks turn into empty paragraphs. Node* blockNode = enclosingBlock(context->firstNode()); Element* block = static_cast<Element*>(blockNode); bool useClonesOfEnclosingBlock = blockNode && blockNode->isElementNode() && !block->hasTagName(bodyTag) && !block->hasTagName(htmlTag) && block != editableRootForPosition(context->startPosition()); Vector<String> list; string.split('\n', true, list); // true gets us empty strings in the list size_t numLines = list.size(); for (size_t i = 0; i < numLines; ++i) { const String& s = list[i]; RefPtr<Element> element; if (s.isEmpty() && i + 1 == numLines) { // For last line, use the "magic BR" rather than a P. element = createBreakElement(document); element->setAttribute(classAttr, AppleInterchangeNewline); } else { if (useClonesOfEnclosingBlock) element = block->cloneElement(); else element = createDefaultParagraphElement(document); fillContainerFromString(element.get(), s); } fragment->appendChild(element.release(), ec); ASSERT(ec == 0); } return fragment.release();}PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const Vector<Node*>& nodes){ if (!document) return 0; // disable the delete button so it's elements are not serialized into the markup if (document->frame()) document->frame()->editor()->deleteButtonController()->disable(); RefPtr<DocumentFragment> fragment = document->createDocumentFragment(); ExceptionCode ec = 0; size_t size = nodes.size(); for (size_t i = 0; i < size; ++i) { RefPtr<Element> element = createDefaultParagraphElement(document); element->appendChild(nodes[i], ec); ASSERT(ec == 0); fragment->appendChild(element.release(), ec); ASSERT(ec == 0); } if (document->frame()) document->frame()->editor()->deleteButtonController()->enable(); return fragment.release();}String createFullMarkup(const Node* node){ if (!node) return String(); Document* document = node->document(); if (!document) return String(); Frame* frame = document->frame(); if (!frame) return String(); // FIXME: This is never "for interchange". Is that right? String markupString = createMarkup(node, IncludeNode, 0); Node::NodeType nodeType = node->nodeType(); if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE) markupString = frame->documentTypeString() + markupString; return markupString;}String createFullMarkup(const Range* range){ if (!range) return String(); Node* node = range->startContainer(); if (!node) return String(); Document* document = node->document(); if (!document) return String(); Frame* frame = document->frame(); if (!frame) return String(); // FIXME: This is always "for interchange". Is that right? See the previous method. return frame->documentTypeString() + createMarkup(range, 0, AnnotateForInterchange); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -