From 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 17 Dec 2008 18:05:15 -0800 Subject: Code drop from //branches/cupcake/...@124589 --- WebKit/mac/Misc/WebNSAttributedStringExtras.mm | 480 +++---------------------- 1 file changed, 47 insertions(+), 433 deletions(-) (limited to 'WebKit/mac/Misc/WebNSAttributedStringExtras.mm') diff --git a/WebKit/mac/Misc/WebNSAttributedStringExtras.mm b/WebKit/mac/Misc/WebNSAttributedStringExtras.mm index 04d9075..ef472aa 100644 --- a/WebKit/mac/Misc/WebNSAttributedStringExtras.mm +++ b/WebKit/mac/Misc/WebNSAttributedStringExtras.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,8 +31,8 @@ #import "DOMRangeInternal.h" #import "WebDataSourcePrivate.h" #import "WebFrame.h" -#import "WebFrameBridge.h" #import "WebFrameInternal.h" +#import "WebTypesInternal.h" #import #import #import @@ -43,7 +43,6 @@ #import #import #import -#import #import #import #import @@ -52,6 +51,7 @@ #import #import #import +#import using namespace WebCore; using namespace HTMLNames; @@ -61,32 +61,6 @@ struct ListItemInfo { unsigned end; }; -static Element* listParent(Element* item) -{ - while (!item->hasTagName(ulTag) && !item->hasTagName(olTag)) { - item = static_cast(item->parentNode()); - if (!item) - break; - } - return item; -} - -static Node* isTextFirstInListItem(Node* e) -{ - if (!e->isTextNode()) - return 0; - Node* par = e->parentNode(); - while (par) { - if (par->firstChild() != e) - return 0; - if (par->hasTagName(liTag)) - return par; - e = par; - par = par->parentNode(); - } - return 0; -} - static NSFileWrapper *fileWrapperForElement(Element* e) { NSFileWrapper *wrapper = nil; @@ -94,7 +68,7 @@ static NSFileWrapper *fileWrapperForElement(Element* e) const AtomicString& attr = e->getAttribute(srcAttr); if (!attr.isEmpty()) { - NSURL *URL = KURL(e->document()->completeURL(attr.deprecatedString())).getNSURL(); + NSURL *URL = e->document()->completeURL(attr); wrapper = [[kit(e->document()->frame()) _dataSource] _fileWrapperForURL:URL]; } if (!wrapper) { @@ -145,417 +119,57 @@ static NSFileWrapper *fileWrapperForElement(Element* e) return self; } -// FIXME: Use WebCore::TextIterator to iterate text runs. - + (NSAttributedString *)_web_attributedStringFromRange:(Range*)range { - ListItemInfo info; - ExceptionCode ec = 0; // dummy variable -- we ignore DOM exceptions - NSMutableAttributedString *result; - BEGIN_BLOCK_OBJC_EXCEPTIONS; - - if (!range || !range->boundaryPointsValid()) - return nil; - - Node* firstNode = range->startNode(); - if (!firstNode) - return nil; - Node* pastEndNode = range->pastEndNode(); - - int startOffset = range->startOffset(ec); - int endOffset = range->endOffset(ec); - Node* endNode = range->endContainer(ec); - - result = [[[NSMutableAttributedString alloc] init] autorelease]; - - bool hasNewLine = true; - bool addedSpace = true; - NSAttributedString *pendingStyledSpace = nil; - bool hasParagraphBreak = true; - const Element *linkStartNode = 0; - unsigned linkStartLocation = 0; - Vector listItems; - Vector listItemLocations; - float maxMarkerWidth = 0; - - Node *currentNode = firstNode; - - // If the first item is the entire text of a list item, use the list item node as the start of the - // selection, not the text node. The user's intent was probably to select the list. - if (currentNode->isTextNode() && startOffset == 0) { - Node *startListNode = isTextFirstInListItem(firstNode); - if (startListNode){ - firstNode = startListNode; - currentNode = firstNode; - } - } - - while (currentNode && currentNode != pastEndNode) { - RenderObject *renderer = currentNode->renderer(); - if (renderer) { - RenderStyle *style = renderer->style(); - NSFont *font = style->font().primaryFont()->getNSFont(); - bool needSpace = pendingStyledSpace != nil; - if (currentNode->isTextNode()) { - if (hasNewLine) { - addedSpace = true; - needSpace = false; - [pendingStyledSpace release]; - pendingStyledSpace = nil; - hasNewLine = false; - } - DeprecatedString text; - DeprecatedString str = currentNode->nodeValue().deprecatedString(); - int start = (currentNode == firstNode) ? startOffset : -1; - int end = (currentNode == endNode) ? endOffset : -1; - if (renderer->isText()) { - if (!style->collapseWhiteSpace()) { - if (needSpace && !addedSpace) { - if (text.isEmpty() && linkStartLocation == [result length]) - ++linkStartLocation; - [result appendAttributedString:pendingStyledSpace]; - } - int runStart = (start == -1) ? 0 : start; - int runEnd = (end == -1) ? str.length() : end; - text += str.mid(runStart, runEnd-runStart); - [pendingStyledSpace release]; - pendingStyledSpace = nil; - addedSpace = u_charDirection(str[runEnd - 1].unicode()) == U_WHITE_SPACE_NEUTRAL; - } - else { - RenderText* textObj = static_cast(renderer); - if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) { - // We have no runs, but we do have a length. This means we must be - // whitespace that collapsed away at the end of a line. - text += ' '; - addedSpace = true; - } - else { - addedSpace = false; - for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) { - int runStart = (start == -1) ? box->m_start : start; - int runEnd = (end == -1) ? box->m_start + box->m_len : end; - if (runEnd > box->m_start + box->m_len) - runEnd = box->m_start + box->m_len; - if (runStart >= box->m_start && - runStart < box->m_start + box->m_len) { - if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0) - needSpace = true; // collapsed space at the start - if (needSpace && !addedSpace) { - if (pendingStyledSpace != nil) { - if (text.isEmpty() && linkStartLocation == [result length]) - ++linkStartLocation; - [result appendAttributedString:pendingStyledSpace]; - } else - text += ' '; - } - DeprecatedString runText = str.mid(runStart, runEnd - runStart); - runText.replace('\n', ' '); - text += runText; - int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end - needSpace = nextRunStart > runEnd; - [pendingStyledSpace release]; - pendingStyledSpace = nil; - addedSpace = u_charDirection(str[runEnd - 1].unicode()) == U_WHITE_SPACE_NEUTRAL; - start = -1; - } - if (end != -1 && runEnd >= end) - break; - } - } - } - } - - text.replace('\\', renderer->backslashAsCurrencySymbol()); - - if (text.length() > 0 || needSpace) { - NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; - [attrs setObject:font forKey:NSFontAttributeName]; - if (style && style->color().isValid() && style->color().alpha() != 0) - [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName]; - if (style && style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0) - [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName]; - - if (text.length() > 0) { - hasParagraphBreak = false; - NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs]; - [result appendAttributedString: partialString]; - [partialString release]; - } - - if (needSpace) { - [pendingStyledSpace release]; - pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs]; - } - - [attrs release]; - } - } else { - // This is our simple HTML -> ASCII transformation: - DeprecatedString text; - if (currentNode->hasTagName(aTag)) { - // Note the start of the element. We will add the NSLinkAttributeName - // attribute to the attributed string when navigating to the next sibling - // of this node. - linkStartLocation = [result length]; - linkStartNode = static_cast(currentNode); - } else if (currentNode->hasTagName(brTag)) { - text += "\n"; - hasNewLine = true; - } else if (currentNode->hasTagName(liTag)) { - DeprecatedString listText; - Element *itemParent = listParent(static_cast(currentNode)); - - if (!hasNewLine) - listText += '\n'; - hasNewLine = true; - - listItems.append(static_cast(currentNode)); - info.start = [result length]; - info.end = 0; - listItemLocations.append (info); - - listText += '\t'; - if (itemParent && renderer->isListItem()) { - RenderListItem* listRenderer = static_cast(renderer); - - maxMarkerWidth = MAX([font pointSize], maxMarkerWidth); - - String marker = listRenderer->markerText(); - if (!marker.isEmpty()) { - listText += marker.deprecatedString(); - // Use AppKit metrics, since this will be rendered by AppKit. - NSString *markerNSString = marker; - float markerWidth = [markerNSString sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width; - maxMarkerWidth = MAX(markerWidth, maxMarkerWidth); - } - - listText += ' '; - listText += '\t'; - - NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; - [attrs setObject:font forKey:NSFontAttributeName]; - if (style && style->color().isValid()) - [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName]; - if (style && style->backgroundColor().isValid()) - [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName]; - - NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs]; - [attrs release]; - [result appendAttributedString: partialString]; - [partialString release]; - } - } else if (currentNode->hasTagName(olTag) || currentNode->hasTagName(ulTag)) { - if (!hasNewLine) - text += "\n"; - hasNewLine = true; - } else if (currentNode->hasTagName(blockquoteTag) - || currentNode->hasTagName(ddTag) - || currentNode->hasTagName(divTag) - || currentNode->hasTagName(dlTag) - || currentNode->hasTagName(dtTag) - || currentNode->hasTagName(hrTag) - || currentNode->hasTagName(listingTag) - || currentNode->hasTagName(preTag) - || currentNode->hasTagName(tdTag) - || currentNode->hasTagName(thTag)) { - if (!hasNewLine) - text += '\n'; - hasNewLine = true; - } else if (currentNode->hasTagName(h1Tag) - || currentNode->hasTagName(h2Tag) - || currentNode->hasTagName(h3Tag) - || currentNode->hasTagName(h4Tag) - || currentNode->hasTagName(h5Tag) - || currentNode->hasTagName(h6Tag) - || currentNode->hasTagName(pTag) - || currentNode->hasTagName(trTag)) { - if (!hasNewLine) - text += '\n'; - - // In certain cases, emit a paragraph break. - int bottomMargin = renderer->collapsedMarginBottom(); - int fontSize = style->fontDescription().computedPixelSize(); - if (bottomMargin * 2 >= fontSize) { - if (!hasParagraphBreak) { - text += '\n'; - hasParagraphBreak = true; - } - } - - hasNewLine = true; - } - else if (currentNode->hasTagName(imgTag)) { - if (pendingStyledSpace != nil) { - if (linkStartLocation == [result length]) - ++linkStartLocation; - [result appendAttributedString:pendingStyledSpace]; - [pendingStyledSpace release]; - pendingStyledSpace = nil; - } - NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast(currentNode)); - NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper]; - NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment]; - [result appendAttributedString: iString]; - [attachment release]; - } - - NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()]; - [result appendAttributedString: partialString]; - [partialString release]; - } - } - - Node *nextNode = currentNode->firstChild(); - if (!nextNode) - nextNode = currentNode->nextSibling(); - - while (!nextNode && currentNode->parentNode()) { - DeprecatedString text; - currentNode = currentNode->parentNode(); - if (currentNode == pastEndNode) - break; - nextNode = currentNode->nextSibling(); - - if (currentNode->hasTagName(aTag)) { - // End of a element. Create an attributed string NSLinkAttributeName attribute - // for the range of the link. Note that we create the attributed string from the DOM, which - // will have corrected any illegally nested elements. - if (linkStartNode && currentNode == linkStartNode) { - String href = parseURL(linkStartNode->getAttribute(hrefAttr)); - KURL kURL = linkStartNode->document()->frame()->loader()->completeURL(href.deprecatedString()); - - NSURL *URL = kURL.getNSURL(); - NSRange tempRange = { linkStartLocation, [result length]-linkStartLocation }; // workaround for 4213314 - [result addAttribute:NSLinkAttributeName value:URL range:tempRange]; - linkStartNode = 0; - } - } - else if (currentNode->hasTagName(olTag) || currentNode->hasTagName(ulTag)) { - if (!hasNewLine) - text += '\n'; - hasNewLine = true; - } else if (currentNode->hasTagName(liTag)) { - - int i, count = listItems.size(); - for (i = 0; i < count; i++){ - if (listItems[i] == currentNode){ - listItemLocations[i].end = [result length]; - break; - } - } - if (!hasNewLine) - text += '\n'; - hasNewLine = true; - } else if (currentNode->hasTagName(blockquoteTag) || - currentNode->hasTagName(ddTag) || - currentNode->hasTagName(divTag) || - currentNode->hasTagName(dlTag) || - currentNode->hasTagName(dtTag) || - currentNode->hasTagName(hrTag) || - currentNode->hasTagName(listingTag) || - currentNode->hasTagName(preTag) || - currentNode->hasTagName(tdTag) || - currentNode->hasTagName(thTag)) { - if (!hasNewLine) - text += '\n'; - hasNewLine = true; - } else if (currentNode->hasTagName(pTag) || - currentNode->hasTagName(trTag) || - currentNode->hasTagName(h1Tag) || - currentNode->hasTagName(h2Tag) || - currentNode->hasTagName(h3Tag) || - currentNode->hasTagName(h4Tag) || - currentNode->hasTagName(h5Tag) || - currentNode->hasTagName(h6Tag)) { - if (!hasNewLine) - text += '\n'; - // An extra newline is needed at the start, not the end, of these types of tags, - // so don't add another here. - hasNewLine = true; - } - - NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()]; - [result appendAttributedString:partialString]; - [partialString release]; - } - - currentNode = nextNode; - } - - [pendingStyledSpace release]; - - // Apply paragraph styles from outside in. This ensures that nested lists correctly - // override their parent's paragraph style. - { - unsigned i, count = listItems.size(); - Element *e; - -#ifdef POSITION_LIST - Node *containingBlock; - int containingBlockX, containingBlockY; + NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init]; + NSUInteger stringLength = 0; + RetainPtr attrs(AdoptNS, [[NSMutableDictionary alloc] init]); + + for (TextIterator it(range); !it.atEnd(); it.advance()) { + RefPtr currentTextRange = it.range(); + ExceptionCode ec = 0; + Node* startContainer = currentTextRange->startContainer(ec); + Node* endContainer = currentTextRange->endContainer(ec); + int startOffset = currentTextRange->startOffset(ec); + int endOffset = currentTextRange->endOffset(ec); - // Determine the position of the outermost containing block. All paragraph - // styles and tabs should be relative to this position. So, the horizontal position of - // each item in the list (in the resulting attributed string) will be relative to position - // of the outermost containing block. - if (count > 0){ - containingBlock = firstNode; - while (containingBlock->renderer()->isInline()){ - containingBlock = containingBlock->parentNode(); + if (startContainer == endContainer && (startOffset == endOffset - 1)) { + Node* node = startContainer->childNode(startOffset); + if (node && node->hasTagName(imgTag)) { + NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast(node)); + NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper]; + [string appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]]; + [attachment release]; } - containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY); } -#endif - - for (i = 0; i < count; i++){ - e = listItems[i]; - info = listItemLocations[i]; - - if (info.end < info.start) - info.end = [result length]; - - RenderObject *r = e->renderer(); - RenderStyle *style = r->style(); - - int rx; - NSFont *font = style->font().primaryFont()->getNSFont(); - float pointSize = [font pointSize]; -#ifdef POSITION_LIST - int ry; - r->absolutePosition(rx, ry); - rx -= containingBlockX; - - // Ensure that the text is indented at least enough to allow for the markers. - rx = MAX(rx, (int)maxMarkerWidth); -#else - rx = (int)MAX(maxMarkerWidth, pointSize); -#endif - - // The bullet text will be right aligned at the first tab marker, followed - // by a space, followed by the list item text. The space is arbitrarily - // picked as pointSize*2/3. The space on the first line of the text item - // is established by a left aligned tab, on subsequent lines it's established - // by the head indent. - NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init]; - [mps setFirstLineHeadIndent: 0]; - [mps setHeadIndent: rx]; - [mps setTabStops:[NSArray arrayWithObjects: - [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease], - [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease], - nil]]; - NSRange tempRange = { info.start, info.end-info.start }; // workaround for 4213314 - [result addAttribute:NSParagraphStyleAttributeName value:mps range:tempRange]; - [mps release]; - } + int currentTextLength = it.length(); + if (!currentTextLength) + continue; + + RenderObject* renderer = startContainer->renderer(); + ASSERT(renderer); + if (!renderer) + continue; + RenderStyle* style = renderer->style(); + NSFont *font = style->font().primaryFont()->getNSFont(); + [attrs.get() setObject:font forKey:NSFontAttributeName]; + if (style->color().isValid()) + [attrs.get() setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName]; + else + [attrs.get() removeObjectForKey:NSForegroundColorAttributeName]; + if (style->backgroundColor().isValid()) + [attrs.get() setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName]; + else + [attrs.get() removeObjectForKey:NSBackgroundColorAttributeName]; + + RetainPtr substring(AdoptNS, [[NSString alloc] initWithCharactersNoCopy:const_cast(it.characters()) length:currentTextLength freeWhenDone:NO]); + [string replaceCharactersInRange:NSMakeRange(stringLength, 0) withString:substring.get()]; + [string setAttributes:attrs.get() range:NSMakeRange(stringLength, currentTextLength)]; + stringLength += currentTextLength; } - return result; - - END_BLOCK_OBJC_EXCEPTIONS; - - return nil; + return [string autorelease]; } @end -- cgit v1.1