summaryrefslogtreecommitdiffstats
path: root/WebKit/mac/Misc/WebNSAttributedStringExtras.mm
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:15 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:15 -0800
commit1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch)
tree4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebKit/mac/Misc/WebNSAttributedStringExtras.mm
parent9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff)
downloadexternal_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip
external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz
external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebKit/mac/Misc/WebNSAttributedStringExtras.mm')
-rw-r--r--WebKit/mac/Misc/WebNSAttributedStringExtras.mm480
1 files changed, 47 insertions, 433 deletions
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 <WebCore/BlockExceptions.h>
#import <WebCore/ColorMac.h>
#import <WebCore/CSSHelper.h>
@@ -43,7 +43,6 @@
#import <WebCore/HTMLNames.h>
#import <WebCore/Image.h>
#import <WebCore/InlineTextBox.h>
-#import <WebCore/KURL.h>
#import <WebCore/Range.h>
#import <WebCore/RenderImage.h>
#import <WebCore/RenderListItem.h>
@@ -52,6 +51,7 @@
#import <WebCore/RenderText.h>
#import <WebCore/SimpleFontData.h>
#import <WebCore/Text.h>
+#import <WebCore/TextIterator.h>
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<Element*>(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<Element*> listItems;
- Vector<ListItemInfo> 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<RenderText*>(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 <a> 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<Element*>(currentNode);
- } else if (currentNode->hasTagName(brTag)) {
- text += "\n";
- hasNewLine = true;
- } else if (currentNode->hasTagName(liTag)) {
- DeprecatedString listText;
- Element *itemParent = listParent(static_cast<Element*>(currentNode));
-
- if (!hasNewLine)
- listText += '\n';
- hasNewLine = true;
-
- listItems.append(static_cast<Element*>(currentNode));
- info.start = [result length];
- info.end = 0;
- listItemLocations.append (info);
-
- listText += '\t';
- if (itemParent && renderer->isListItem()) {
- RenderListItem* listRenderer = static_cast<RenderListItem*>(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<Element*>(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 <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 <a> 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<NSMutableDictionary> attrs(AdoptNS, [[NSMutableDictionary alloc] init]);
+
+ for (TextIterator it(range); !it.atEnd(); it.advance()) {
+ RefPtr<Range> 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<Element*>(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<NSString> substring(AdoptNS, [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(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