📄 markup.cpp
字号:
for (unsigned long i = 0; i < length; i++) {
AttributeImpl *attr = attrs->attributeItem(i);
if (e->isURLAttribute(attr)) {
e->setAttribute(attr->id(), KURL(baseURL, attr->value().string()).url());
}
}
}
}
}
QString createMarkup(const RangeImpl *range, QPtrList<NodeImpl> *nodes, EAnnotateForInterchange annotate)
{
if (!range || range->isDetached())
return QString();
static const QString interchangeNewlineString = QString("<br class=\"") + AppleInterchangeNewline + "\">";
int exceptionCode = 0;
NodeImpl *commonAncestor = range->commonAncestorContainer(exceptionCode);
ASSERT(exceptionCode == 0);
DocumentImpl *doc = commonAncestor->getDocument();
doc->updateLayout();
NodeImpl *commonAncestorBlock = 0;
if (commonAncestor != 0) {
commonAncestorBlock = commonAncestor->enclosingBlockFlowElement();
}
if (commonAncestorBlock == 0) {
return "";
}
QStringList markups;
NodeImpl *pastEnd = range->pastEndNode();
NodeImpl *lastClosed = 0;
QPtrList<NodeImpl> ancestorsToClose;
// calculate the "default style" for this markup
Position pos(doc->documentElement(), 0);
CSSMutableStyleDeclarationImpl *defaultStyle = pos.computedStyle()->copyInheritableProperties();
defaultStyle->ref();
NodeImpl *startNode = range->startNode();
VisiblePosition visibleStart(range->startPosition(), VP_DEFAULT_AFFINITY);
VisiblePosition visibleEnd(range->endPosition(), VP_DEFAULT_AFFINITY);
if (isEndOfBlock(visibleStart)) {
if (visibleStart == visibleEnd.previous())
return interchangeNewlineString;
markups.append(interchangeNewlineString);
startNode = startNode->traverseNextNode();
}
// Iterate through the nodes of the range.
NodeImpl *next;
for (NodeImpl *n = startNode; n != pastEnd; n = next) {
next = n->traverseNextNode();
if (n->isBlockFlow() && next == pastEnd) {
// Don't write out an empty block.
continue;
}
// Add the node to the markup.
if (n->renderer()) {
markups.append(startMarkup(n, range, annotate, defaultStyle));
if (nodes) {
nodes->append(n);
}
}
if (n->firstChild() == 0) {
// Node has no children, add its close tag now.
if (n->renderer()) {
markups.append(endMarkup(n));
lastClosed = n;
}
// Check if the node is the last leaf of a tree.
if (n->nextSibling() == 0 || next == pastEnd) {
if (!ancestorsToClose.isEmpty()) {
// Close up the ancestors.
while (NodeImpl *ancestor = ancestorsToClose.last()) {
if (next != pastEnd && ancestor == next->parentNode()) {
break;
}
// Not at the end of the range, close ancestors up to sibling of next node.
markups.append(endMarkup(ancestor));
lastClosed = ancestor;
ancestorsToClose.removeLast();
}
} else {
// No ancestors to close, but need to add ancestors not in range since next node is in another tree.
if (next != pastEnd) {
NodeImpl *nextParent = next->parentNode();
if (n != nextParent) {
for (NodeImpl *parent = n->parent(); parent != 0 && parent != nextParent; parent = parent->parentNode()) {
markups.prepend(startMarkup(parent, range, annotate, defaultStyle));
markups.append(endMarkup(parent));
if (nodes) {
nodes->append(parent);
}
lastClosed = parent;
}
}
}
}
}
} else if (n->renderer()) {
// Node is an ancestor, set it to close eventually.
ancestorsToClose.append(n);
}
}
NodeImpl *rangeStartNode = range->startNode();
int rangeStartOffset = range->startOffset(exceptionCode);
ASSERT(exceptionCode == 0);
// Add ancestors up to the common ancestor block so inline ancestors such as FONT and B are part of the markup.
if (lastClosed) {
for (NodeImpl *ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
if (RangeImpl::compareBoundaryPoints(ancestor, 0, rangeStartNode, rangeStartOffset) >= 0) {
// we have already added markup for this node
continue;
}
bool breakAtEnd = false;
if (commonAncestorBlock == ancestor) {
NodeImpl::Id id = ancestor->id();
// Include ancestors that are required to retain the appearance of the copied markup.
if (id == ID_PRE || id == ID_TABLE || id == ID_OL || id == ID_UL) {
breakAtEnd = true;
} else {
break;
}
}
markups.prepend(startMarkup(ancestor, range, annotate, defaultStyle));
markups.append(endMarkup(ancestor));
if (nodes) {
nodes->append(ancestor);
}
if (breakAtEnd) {
break;
}
}
}
if (annotate) {
Position pos(endPosition(range));
NodeImpl *block = pos.node()->enclosingBlockFlowElement();
NodeImpl *upstreamBlock = pos.upstream().node()->enclosingBlockFlowElement();
if (block != upstreamBlock) {
markups.append(interchangeNewlineString);
}
}
// Retain the Mail quote level by including all ancestor mail block quotes.
for (NodeImpl *ancestor = commonAncestorBlock; ancestor; ancestor = ancestor->parentNode()) {
if (isMailBlockquote(ancestor)) {
markups.prepend(startMarkup(ancestor, range, annotate, defaultStyle));
markups.append(endMarkup(ancestor));
}
}
// add in the "default style" for this markup
// FIXME: Handle case where value has illegal characters in it, like "
QString openTag = QString("<span class=\"") + AppleStyleSpanClass + "\" style=\"" + defaultStyle->cssText().string() + "\">";
markups.prepend(openTag);
markups.append("</span>");
defaultStyle->deref();
return markups.join("");
}
DocumentFragmentImpl *createFragmentFromMarkup(DocumentImpl *document, const QString &markup, const QString &baseURL)
{
// FIXME: What if the document element is not an HTML element?
HTMLElementImpl *element = static_cast<HTMLElementImpl *>(document->documentElement());
DocumentFragmentImpl *fragment = element->createContextualFragment(markup);
ASSERT(fragment);
if (!baseURL.isEmpty() && baseURL != document->baseURL())
completeURLs(fragment, baseURL);
return fragment;
}
QString createMarkup(const DOM::NodeImpl *node, EChildrenOnly includeChildren,
QPtrList<DOM::NodeImpl> *nodes, EAnnotateForInterchange annotate)
{
ASSERT(annotate == DoNotAnnotateForInterchange); // annotation not yet implemented for this code path
// FIXME: We could take out this if statement if we had more time to test.
// I'm concerned that making this crash when the document is nil might be too risky a change at the moment.
DocumentImpl *doc = node->getDocument();
assert(doc);
if (doc)
doc->updateLayout();
return markup(node, includeChildren, false, nodes);
}
DOM::DocumentFragmentImpl *createFragmentFromText(DOM::DocumentImpl *document, const QString &text)
{
if (!document)
return 0;
DocumentFragmentImpl *fragment = document->createDocumentFragment();
fragment->ref();
if (!text.isEmpty()) {
QString string = text;
// Replace tabs with four plain spaces.
// These spaces will get converted along with the other existing spaces below.
string.replace('\t', " ");
// FIXME: Wrap the NBSP's in a span that says "converted space".
int offset = 0;
int stringLength = string.length();
while (1) {
// FIXME: This only converts plain old spaces, and does not
// deal with more exotic whitespace. Note that we want to
// leave newlines and returns alone at this point anyway,
// since those are handled specially later.
int spacesPos = string.find(" ", offset);
if (spacesPos < 0)
break;
// Found two adjoining spaces.
// Now, lookahead to see if these two spaces are followed by:
// 1. another space and then a non-space
// 2. another space and then the end of the string
// If either 1 or 2 is true, replace the three spaces found with nbsp+nbsp+space,
// otherwise, replace the first two spaces with nbsp+space.
if ((spacesPos + 3 < stringLength && string[spacesPos + 2] == ' ' && string[spacesPos + 3] != ' ')
|| (spacesPos + 3 == stringLength && string[spacesPos + 2] == ' ')) {
string.replace(spacesPos, 3, "\xA0\xA0 ");
} else {
string.replace(spacesPos, 2, "\xA0 ");
}
offset = spacesPos + 2;
}
// Break string into paragraphs. Extra line breaks turn into empty paragraphs.
string.replace(QString("\r\n"), "\n");
string.replace('\r', '\n');
QStringList list = QStringList::split('\n', string, true); // true gets us empty strings in the list
while (!list.isEmpty()) {
QString s = list.first();
list.pop_front();
int exceptionCode = 0;
ElementImpl *element;
if (s.isEmpty() && list.isEmpty()) {
// For last line, use the "magic BR" rather than a P.
element = document->createHTMLElement("br", exceptionCode);
ASSERT(exceptionCode == 0);
element->ref();
element->setAttribute(ATTR_CLASS, AppleInterchangeNewline);
} else {
element = createDefaultParagraphElement(document);
NodeImpl *paragraphContents;
if (s.isEmpty()) {
paragraphContents = createBlockPlaceholderElement(document);
} else {
paragraphContents = document->createTextNode(s);
ASSERT(exceptionCode == 0);
}
element->ref();
element->appendChild(paragraphContents, exceptionCode);
ASSERT(exceptionCode == 0);
}
fragment->appendChild(element, exceptionCode);
ASSERT(exceptionCode == 0);
element->deref();
}
}
// Trick to get the fragment back to the floating state, with 0
// refs but not destroyed.
fragment->setParent(document);
fragment->deref();
fragment->setParent(0);
return fragment;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -