Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Side by Side Diff: Source/WebCore/platform/mac/HTMLConverter.mm

Issue 13713003: Remove all of WebCore/platform/mac which is not mentioned in WebCore.gypi. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Added back a couple needed headers Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/WebCore/platform/mac/HTMLConverter.h ('k') | Source/WebCore/platform/mac/KURLMac.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #import "config.h"
27 #import "HTMLConverter.h"
28
29 #import "ArchiveResource.h"
30 #import "CachedImage.h"
31 #import "ColorMac.h"
32 #import "Document.h"
33 #import "DocumentLoader.h"
34 #import "DOMDocumentInternal.h"
35 #import "DOMElementInternal.h"
36 #import "DOMHTMLTableCellElement.h"
37 #import "DOMPrivate.h"
38 #import "DOMRangeInternal.h"
39 #import "Element.h"
40 #import "Font.h"
41 #import "Frame.h"
42 #import "FrameLoader.h"
43 #import "HTMLNames.h"
44 #import "HTMLParserIdioms.h"
45 #import "LoaderNSURLExtras.h"
46 #import "RenderImage.h"
47 #import "TextIterator.h"
48 #import <wtf/ASCIICType.h>
49
50 using namespace WebCore;
51 using namespace HTMLNames;
52
53 static NSFileWrapper *fileWrapperForURL(DocumentLoader *, NSURL *);
54 static NSFileWrapper *fileWrapperForElement(Element*);
55
56 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
57
58 // Additional control Unicode characters
59 const unichar WebNextLineCharacter = 0x0085;
60
61 @interface NSTextList (WebCoreNSTextListDetails)
62 + (NSDictionary *)_standardMarkerAttributesForAttributes:(NSDictionary *)attrs;
63 @end
64
65 @interface NSTextAttachment (NSIgnoreOrientation)
66 - (void)setIgnoresOrientation:(BOOL)flag;
67 - (BOOL)ignoresOrientation;
68 @end
69
70 @interface NSURL (WebCoreNSURLDetails)
71 // FIXME: What is the reason to use this Foundation method, and not +[NSURL URLW ithString:relativeToURL:]?
72 + (NSURL *)_web_URLWithString:(NSString *)string relativeToURL:(NSURL *)baseURL;
73 @end
74
75 @interface WebHTMLConverter(WebHTMLConverterInternal)
76
77 - (NSString *)_stringForNode:(DOMNode *)node property:(NSString *)key;
78 - (NSColor *)_colorForNode:(DOMNode *)node property:(NSString *)key;
79 - (BOOL)_getFloat:(CGFloat *)val forNode:(DOMNode *)node property:(NSString *)ke y;
80 - (void)_traverseNode:(DOMNode *)node depth:(NSInteger)depth embedded:(BOOL)embe dded;
81 - (void)_traverseFooterNode:(DOMNode *)node depth:(NSInteger)depth;
82
83 @end
84
85 // Returns the font to be used if the NSFontAttributeName doesn't exist
86 static NSFont *WebDefaultFont()
87 {
88 static NSFont *defaultFont = nil;
89
90 if (defaultFont)
91 return defaultFont;
92
93 NSFont *font = [NSFont fontWithName:@"Helvetica" size:12];
94 if (!font)
95 font = [NSFont systemFontOfSize:12];
96
97 defaultFont = [font retain];
98
99 return defaultFont;
100 }
101
102 #endif
103
104 @implementation WebHTMLConverter
105
106 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
107
108 static NSFont *_fontForNameAndSize(NSString *fontName, CGFloat size, NSMutableDi ctionary *cache)
109 {
110 NSFontManager *fontManager = [NSFontManager sharedFontManager];
111 NSFont *font = [cache objectForKey:fontName];
112
113 if (font) {
114 font = [fontManager convertFont:font toSize:size];
115 return font;
116 }
117 font = [fontManager fontWithFamily:fontName traits:0 weight:0 size:size];
118 if (!font) {
119 NSArray *availableFamilyNames = [fontManager availableFontFamilies];
120 NSRange dividingRange, dividingSpaceRange = [fontName rangeOfString:@" " options:NSBackwardsSearch], dividingDashRange = [fontName rangeOfString:@"-" op tions:NSBackwardsSearch];
121 dividingRange = (0 < dividingSpaceRange.length && 0 < dividingDashRange. length) ? (dividingSpaceRange.location > dividingDashRange.location ? dividingSp aceRange : dividingDashRange) : (0 < dividingSpaceRange.length ? dividingSpaceRa nge : dividingDashRange);
122 while (0 < dividingRange.length) {
123 NSString *familyName = [fontName substringToIndex:dividingRange.loca tion];
124 if ([availableFamilyNames containsObject:familyName]) {
125 NSArray *familyMemberArray;
126 NSString *faceName = [fontName substringFromIndex:(dividingRange .location + dividingRange.length)];
127 NSArray *familyMemberArrays = [fontManager availableMembersOfFon tFamily:familyName];
128 NSEnumerator *familyMemberArraysEnum = [familyMemberArrays objec tEnumerator];
129 while ((familyMemberArray = [familyMemberArraysEnum nextObject]) ) {
130 NSString *familyMemberFaceName = [familyMemberArray objectAt Index:1];
131 if ([familyMemberFaceName compare:faceName options:NSCaseIns ensitiveSearch] == NSOrderedSame) {
132 NSFontTraitMask traits = [[familyMemberArray objectAtInd ex:3] integerValue];
133 NSInteger weight = [[familyMemberArray objectAtIndex:2] integerValue];
134 font = [fontManager fontWithFamily:familyName traits:tra its weight:weight size:size];
135 break;
136 }
137 }
138 if (!font) {
139 if (0 < [familyMemberArrays count]) {
140 NSArray *familyMemberArray = [familyMemberArrays objectA tIndex:0];
141 NSFontTraitMask traits = [[familyMemberArray objectAtInd ex:3] integerValue];
142 NSInteger weight = [[familyMemberArray objectAtIndex:2] integerValue];
143 font = [fontManager fontWithFamily:familyName traits:tra its weight:weight size:size];
144 }
145 }
146 break;
147 } else {
148 dividingSpaceRange = [familyName rangeOfString:@" " options:NSBa ckwardsSearch];
149 dividingDashRange = [familyName rangeOfString:@"-" options:NSBac kwardsSearch];
150 dividingRange = (0 < dividingSpaceRange.length && 0 < dividingDa shRange.length) ? (dividingSpaceRange.location > dividingDashRange.location ? di vidingSpaceRange : dividingDashRange) : (0 < dividingSpaceRange.length ? dividin gSpaceRange : dividingDashRange);
151 }
152 }
153 }
154 if (!font) font = [NSFont fontWithName:@"Times" size:size];
155 if (!font) font = [NSFont userFontOfSize:size];
156 if (!font) font = [fontManager convertFont:WebDefaultFont() toSize:size];
157 if (!font) font = WebDefaultFont();
158 [cache setObject:font forKey:fontName];
159 return font;
160 }
161
162 + (NSParagraphStyle *)defaultParagraphStyle
163 {
164 static NSMutableParagraphStyle *defaultParagraphStyle = nil;
165 if (!defaultParagraphStyle) {
166 defaultParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutabl eCopy];
167 [defaultParagraphStyle setDefaultTabInterval:36];
168 [defaultParagraphStyle setTabStops:[NSArray array]];
169 }
170 return defaultParagraphStyle;
171 }
172
173 - (NSArray *)_childrenForNode:(DOMNode *)node
174 {
175 NSMutableArray *array = [NSMutableArray array];
176 DOMNode *child = [node firstChild];
177 while (child) {
178 [array addObject:child];
179 child = [child nextSibling];
180 }
181 return array;
182 }
183
184 - (DOMCSSStyleDeclaration *)_computedStyleForElement:(DOMElement *)element
185 {
186 DOMDocument *document = [element ownerDocument];
187 DOMCSSStyleDeclaration *result = nil;
188 result = [_computedStylesForElements objectForKey:element];
189 if (result) {
190 if ([[NSNull null] isEqual:result]) result = nil;
191 } else {
192 result = [document getComputedStyle:element pseudoElement:@""] ;
193 [_computedStylesForElements setObject:(result ? (id)result : (id)[NSNull null]) forKey:element];
194 }
195 return result;
196 }
197
198 - (DOMCSSStyleDeclaration *)_specifiedStyleForElement:(DOMElement *)element
199 {
200 DOMCSSStyleDeclaration *result = [_specifiedStylesForElements objectForKey:e lement];
201 if (result) {
202 if ([[NSNull null] isEqual:result]) result = nil;
203 } else {
204 result = [element style];
205 [_specifiedStylesForElements setObject:(result ? (id)result : (id)[NSNul l null]) forKey:element];
206 }
207 return result;
208 }
209
210 - (NSString *)_computedStringForNode:(DOMNode *)node property:(NSString *)key
211 {
212 NSString *result = nil;
213 BOOL inherit = YES;
214 DOMElement *element = (DOMElement *)node;
215 if (element && [element nodeType] == DOM_ELEMENT_NODE) {
216 DOMCSSStyleDeclaration *computedStyle, *specifiedStyle;
217 inherit = NO;
218 if (!result && (computedStyle = [self _computedStyleForElement:element]) ) {
219 DOMCSSPrimitiveValue *computedValue = (DOMCSSPrimitiveValue *)[compu tedStyle getPropertyCSSValue:key];
220 if (computedValue) {
221 unsigned short valueType = [computedValue cssValueType];
222 if (valueType == DOM_CSS_PRIMITIVE_VALUE) {
223 unsigned short primitiveType = [computedValue primitiveType] ;
224 if (primitiveType == DOM_CSS_STRING || primitiveType == DOM_ CSS_URI || primitiveType == DOM_CSS_IDENT || primitiveType == DOM_CSS_ATTR) {
225 result = [computedValue getStringValue];
226 if (result && [result length] == 0) result = nil;
227 }
228 } else if (valueType == DOM_CSS_VALUE_LIST) {
229 result = [computedStyle getPropertyValue:key];
230 }
231 }
232 }
233 if (!result && (specifiedStyle = [self _specifiedStyleForElement:element ])) {
234 DOMCSSPrimitiveValue *specifiedValue = (DOMCSSPrimitiveValue *)[spec ifiedStyle getPropertyCSSValue:key];
235 if (specifiedValue) {
236 unsigned short valueType = [specifiedValue cssValueType];
237 if (valueType == DOM_CSS_PRIMITIVE_VALUE) {
238 unsigned short primitiveType = [specifiedValue primitiveType ];
239 if (primitiveType == DOM_CSS_STRING || primitiveType == DOM_ CSS_URI || primitiveType == DOM_CSS_IDENT || primitiveType == DOM_CSS_ATTR) {
240 result = [specifiedValue getStringValue];
241 if (result && [result length] == 0) result = nil;
242 // ??? hack alert
243 if (!result) {
244 result = [specifiedStyle getPropertyValue:key];
245 }
246 }
247 } else if (valueType == DOM_CSS_INHERIT) {
248 inherit = YES;
249 } else if (valueType == DOM_CSS_VALUE_LIST) {
250 result = [specifiedStyle getPropertyValue:key];
251 }
252 }
253 }
254 if (!result) {
255 Element* coreElement = core(element);
256 if ([@"display" isEqualToString:key]) {
257 if (coreElement->hasTagName(headTag) || coreElement->hasTagName( scriptTag) || coreElement->hasTagName(appletTag) || coreElement->hasTagName(nofr amesTag))
258 result = @"none";
259 else if (coreElement->hasTagName(addressTag) || coreElement->has TagName(blockquoteTag) || coreElement->hasTagName(bodyTag) || coreElement->hasTa gName(centerTag)
260 || coreElement->hasTagName(ddTag) || coreElement->hasTa gName(dirTag) || coreElement->hasTagName(divTag) || coreElement->hasTagName(dlTa g)
261 || coreElement->hasTagName(dtTag) || coreElement->hasTa gName(fieldsetTag) || coreElement->hasTagName(formTag) || coreElement->hasTagNam e(frameTag)
262 || coreElement->hasTagName(framesetTag) || coreElement- >hasTagName(hrTag) || coreElement->hasTagName(htmlTag) || coreElement->hasTagNam e(h1Tag)
263 || coreElement->hasTagName(h2Tag) || coreElement->hasTa gName(h3Tag) || coreElement->hasTagName(h4Tag) || coreElement->hasTagName(h5Tag)
264 || coreElement->hasTagName(h6Tag) || coreElement->hasTa gName(iframeTag) || coreElement->hasTagName(menuTag) || coreElement->hasTagName( noscriptTag)
265 || coreElement->hasTagName(olTag) || coreElement->hasTa gName(pTag) || coreElement->hasTagName(preTag) || coreElement->hasTagName(ulTag) )
266 result = @"block";
267 else if (coreElement->hasTagName(liTag))
268 result = @"list-item";
269 else if (coreElement->hasTagName(tableTag))
270 result = @"table";
271 else if (coreElement->hasTagName(trTag))
272 result = @"table-row";
273 else if (coreElement->hasTagName(thTag) || coreElement->hasTagNa me(tdTag))
274 result = @"table-cell";
275 else if (coreElement->hasTagName(theadTag))
276 result = @"table-header-group";
277 else if (coreElement->hasTagName(tbodyTag))
278 result = @"table-row-group";
279 else if (coreElement->hasTagName(tfootTag))
280 result = @"table-footer-group";
281 else if (coreElement->hasTagName(colTag))
282 result = @"table-column";
283 else if (coreElement->hasTagName(colgroupTag))
284 result = @"table-column-group";
285 else if (coreElement->hasTagName(captionTag))
286 result = @"table-caption";
287 } else if ([@"white-space" isEqualToString:key]) {
288 if (coreElement->hasTagName(preTag))
289 result = @"pre";
290 else
291 inherit = YES;
292 } else if ([@"font-style" isEqualToString:key]) {
293 if (coreElement->hasTagName(iTag) || coreElement->hasTagName(cit eTag) || coreElement->hasTagName(emTag) || coreElement->hasTagName(varTag) || co reElement->hasTagName(addressTag))
294 result = @"italic";
295 else
296 inherit = YES;
297 } else if ([@"font-weight" isEqualToString:key]) {
298 if (coreElement->hasTagName(bTag) || coreElement->hasTagName(str ongTag) || coreElement->hasTagName(thTag))
299 result = @"bolder";
300 else
301 inherit = YES;
302 } else if ([@"text-decoration" isEqualToString:key]) {
303 if (coreElement->hasTagName(uTag) || coreElement->hasTagName(ins Tag))
304 result = @"underline";
305 else if (coreElement->hasTagName(sTag) || coreElement->hasTagNam e(strikeTag) || coreElement->hasTagName(delTag))
306 result = @"line-through";
307 else
308 inherit = YES; // ??? this is not strictly correct
309 } else if ([@"text-align" isEqualToString:key]) {
310 if (coreElement->hasTagName(centerTag) || coreElement->hasTagNam e(captionTag) || coreElement->hasTagName(thTag))
311 result = @"center";
312 else
313 inherit = YES;
314 } else if ([@"vertical-align" isEqualToString:key]) {
315 if (coreElement->hasTagName(supTag))
316 result = @"super";
317 else if (coreElement->hasTagName(subTag))
318 result = @"sub";
319 else if (coreElement->hasTagName(theadTag) || coreElement->hasTa gName(tbodyTag) || coreElement->hasTagName(tfootTag))
320 result = @"middle";
321 else if (coreElement->hasTagName(trTag) || coreElement->hasTagNa me(thTag) || coreElement->hasTagName(tdTag))
322 inherit = YES;
323 } else if ([@"font-family" isEqualToString:key] || [@"font-variant" isEqualToString:key] || [@"font-effect" isEqualToString:key]
324 || [@"text-transform" isEqualToString:key] || [@"text-sha dow" isEqualToString:key] || [@"visibility" isEqualToString:key]
325 || [@"border-collapse" isEqualToString:key] || [@"empty-c ells" isEqualToString:key] || [@"word-spacing" isEqualToString:key]
326 || [@"list-style-type" isEqualToString:key] || [@"directi on" isEqualToString:key]) {
327 inherit = YES;
328 }
329 }
330 }
331 if (!result && inherit) {
332 DOMNode *parentNode = [node parentNode];
333 if (parentNode) result = [self _stringForNode:parentNode property:key];
334 }
335 return result ? [result lowercaseString] : nil;
336 }
337
338 - (NSString *)_stringForNode:(DOMNode *)node property:(NSString *)key
339 {
340 NSString *result = nil;
341 NSMutableDictionary *attributeDictionary = [_stringsForNodes objectForKey:no de];
342 if (!attributeDictionary) {
343 attributeDictionary = [[NSMutableDictionary alloc] init];
344 [_stringsForNodes setObject:attributeDictionary forKey:node];
345 [attributeDictionary release];
346 }
347 result = [attributeDictionary objectForKey:key];
348 if (result) {
349 if ([@"" isEqualToString:result]) result = nil;
350 } else {
351 result = [self _computedStringForNode:node property:key];
352 [attributeDictionary setObject:(result ? result : @"") forKey:key];
353 }
354 return result;
355 }
356
357 static inline BOOL _getFloat(DOMCSSPrimitiveValue *primitiveValue, CGFloat *val)
358 {
359 if (!val)
360 return NO;
361 switch ([primitiveValue primitiveType]) {
362 case DOM_CSS_PX:
363 *val = [primitiveValue getFloatValue:DOM_CSS_PX];
364 return YES;
365 case DOM_CSS_PT:
366 *val = 4 * [primitiveValue getFloatValue:DOM_CSS_PT] / 3;
367 return YES;
368 case DOM_CSS_PC:
369 *val = 16 * [primitiveValue getFloatValue:DOM_CSS_PC];
370 return YES;
371 case DOM_CSS_CM:
372 *val = 96 * [primitiveValue getFloatValue:DOM_CSS_CM] / (CGFloat)2.5 4;
373 return YES;
374 case DOM_CSS_MM:
375 *val = 96 * [primitiveValue getFloatValue:DOM_CSS_MM] / (CGFloat)25. 4;
376 return YES;
377 case DOM_CSS_IN:
378 *val = 96 * [primitiveValue getFloatValue:DOM_CSS_IN];
379 return YES;
380 default:
381 return NO;
382 }
383 }
384
385 - (BOOL)_getComputedFloat:(CGFloat *)val forNode:(DOMNode *)node property:(NSStr ing *)key
386 {
387 BOOL result = NO, inherit = YES;
388 CGFloat floatVal = 0;
389 DOMElement *element = (DOMElement *)node;
390 if (element && [element nodeType] == DOM_ELEMENT_NODE) {
391 DOMCSSStyleDeclaration *computedStyle, *specifiedStyle;
392 inherit = NO;
393 if (!result && (computedStyle = [self _computedStyleForElement:element]) ) {
394 DOMCSSPrimitiveValue *computedValue = (DOMCSSPrimitiveValue *)[compu tedStyle getPropertyCSSValue:key];
395 if (computedValue && [computedValue cssValueType] == DOM_CSS_PRIMITI VE_VALUE) {
396 result = _getFloat(computedValue, &floatVal);
397 }
398 }
399 if (!result && (specifiedStyle = [self _specifiedStyleForElement:element ])) {
400 DOMCSSPrimitiveValue *specifiedValue = (DOMCSSPrimitiveValue *)[spec ifiedStyle getPropertyCSSValue:key];
401 if (specifiedValue) {
402 unsigned short valueType = [specifiedValue cssValueType];
403 if (valueType == DOM_CSS_PRIMITIVE_VALUE) {
404 result = _getFloat(specifiedValue, &floatVal);
405 } else if (valueType == DOM_CSS_INHERIT) {
406 inherit = YES;
407 }
408 }
409 }
410 if (!result) {
411 if ([@"text-indent" isEqualToString:key] || [@"letter-spacing" isEqu alToString:key] || [@"word-spacing" isEqualToString:key]
412 || [@"line-height" isEqualToString:key] || [@"widows" isEqualToS tring:key] || [@"orphans" isEqualToString:key])
413 inherit = YES;
414 }
415 }
416 if (!result && inherit) {
417 DOMNode *parentNode = [node parentNode];
418 if (parentNode) result = [self _getFloat:&floatVal forNode:parentNode pr operty:key];
419 }
420 if (result && val)
421 *val = floatVal;
422 return result;
423 }
424
425 - (BOOL)_getFloat:(CGFloat *)val forNode:(DOMNode *)node property:(NSString *)ke y
426 {
427 BOOL result = NO;
428 CGFloat floatVal = 0;
429 NSNumber *floatNumber;
430 NSMutableDictionary *attributeDictionary = [_floatsForNodes objectForKey:nod e];
431 if (!attributeDictionary) {
432 attributeDictionary = [[NSMutableDictionary alloc] init];
433 [_floatsForNodes setObject:attributeDictionary forKey:node];
434 [attributeDictionary release];
435 }
436 floatNumber = [attributeDictionary objectForKey:key];
437 if (floatNumber) {
438 if (![[NSNull null] isEqual:floatNumber]) {
439 result = YES;
440 floatVal = [floatNumber floatValue];
441 }
442 } else {
443 result = [self _getComputedFloat:&floatVal forNode:node property:key];
444 [attributeDictionary setObject:(result ? (id)[NSNumber numberWithDouble: floatVal] : (id)[NSNull null]) forKey:key];
445 }
446 if (result && val) *val = floatVal;
447 return result;
448 }
449
450 static inline NSColor *_colorForRGBColor(DOMRGBColor *domRGBColor, BOOL ignoreBl ack)
451 {
452 NSColor *color = [domRGBColor _color];
453 NSColorSpace *colorSpace = [color colorSpace];
454 const CGFloat ColorEpsilon = 1 / (2 * (CGFloat)255.0);
455
456 if (color) {
457 if ([colorSpace isEqual:[NSColorSpace genericGrayColorSpace]] || [colorS pace isEqual:[NSColorSpace deviceGrayColorSpace]]) {
458 CGFloat white, alpha;
459 [color getWhite:&white alpha:&alpha];
460 if (white < ColorEpsilon && (ignoreBlack || alpha < ColorEpsilon)) c olor = nil;
461 } else {
462 NSColor *rgbColor = nil;
463 if ([colorSpace isEqual:[NSColorSpace genericRGBColorSpace]] || [col orSpace isEqual:[NSColorSpace deviceRGBColorSpace]]) rgbColor = color;
464 if (!rgbColor) rgbColor = [color colorUsingColorSpaceName:NSDeviceRG BColorSpace];
465 if (rgbColor) {
466 CGFloat red, green, blue, alpha;
467 [rgbColor getRed:&red green:&green blue:&blue alpha:&alpha];
468 if (red < ColorEpsilon && green < ColorEpsilon && blue < ColorEp silon && (ignoreBlack || alpha < ColorEpsilon)) color = nil;
469 }
470 }
471 }
472 return color;
473 }
474
475 static inline NSShadow *_shadowForShadowStyle(NSString *shadowStyle)
476 {
477 NSShadow *shadow = nil;
478 NSUInteger shadowStyleLength = [shadowStyle length];
479 NSRange openParenRange = [shadowStyle rangeOfString:@"("], closeParenRange = [shadowStyle rangeOfString:@")"], firstRange = NSMakeRange(NSNotFound, 0), seco ndRange = NSMakeRange(NSNotFound, 0), thirdRange = NSMakeRange(NSNotFound, 0), s paceRange;
480 if (openParenRange.length > 0 && closeParenRange.length > 0 && NSMaxRange(op enParenRange) < closeParenRange.location) {
481 NSArray *components = [[shadowStyle substringWithRange:NSMakeRange(NSMax Range(openParenRange), closeParenRange.location - NSMaxRange(openParenRange))] c omponentsSeparatedByString:@","];
482 if ([components count] >= 3) {
483 CGFloat red = [[components objectAtIndex:0] floatValue] / 255, green = [[components objectAtIndex:1] floatValue] / 255, blue = [[components objectAt Index:2] floatValue] / 255, alpha = ([components count] >= 4) ? [[components obj ectAtIndex:3] floatValue] / 255 : 1;
484 NSColor *shadowColor = [NSColor colorWithCalibratedRed:red green:gre en blue:blue alpha:alpha];
485 NSSize shadowOffset;
486 CGFloat shadowBlurRadius;
487 firstRange = [shadowStyle rangeOfString:@"px"];
488 if (firstRange.length > 0 && NSMaxRange(firstRange) < shadowStyleLen gth) secondRange = [shadowStyle rangeOfString:@"px" options:0 range:NSMakeRange( NSMaxRange(firstRange), shadowStyleLength - NSMaxRange(firstRange))];
489 if (secondRange.length > 0 && NSMaxRange(secondRange) < shadowStyleL ength) thirdRange = [shadowStyle rangeOfString:@"px" options:0 range:NSMakeRange (NSMaxRange(secondRange), shadowStyleLength - NSMaxRange(secondRange))];
490 if (firstRange.location > 0 && firstRange.length > 0 && secondRange. length > 0 && thirdRange.length > 0) {
491 spaceRange = [shadowStyle rangeOfString:@" " options:NSBackwards Search range:NSMakeRange(0, firstRange.location)];
492 if (spaceRange.length == 0) spaceRange = NSMakeRange(0, 0);
493 shadowOffset.width = [[shadowStyle substringWithRange:NSMakeRang e(NSMaxRange(spaceRange), firstRange.location - NSMaxRange(spaceRange))] floatVa lue];
494 spaceRange = [shadowStyle rangeOfString:@" " options:NSBackwards Search range:NSMakeRange(0, secondRange.location)];
495 if (spaceRange.length == 0) spaceRange = NSMakeRange(0, 0);
496 shadowOffset.height = -[[shadowStyle substringWithRange:NSMakeRa nge(NSMaxRange(spaceRange), secondRange.location - NSMaxRange(spaceRange))] floa tValue];
497 spaceRange = [shadowStyle rangeOfString:@" " options:NSBackwards Search range:NSMakeRange(0, thirdRange.location)];
498 if (spaceRange.length == 0) spaceRange = NSMakeRange(0, 0);
499 shadowBlurRadius = [[shadowStyle substringWithRange:NSMakeRange( NSMaxRange(spaceRange), thirdRange.location - NSMaxRange(spaceRange))] floatValu e];
500 shadow = [[[NSShadow alloc] init] autorelease];
501 [shadow setShadowColor:shadowColor];
502 [shadow setShadowOffset:shadowOffset];
503 [shadow setShadowBlurRadius:shadowBlurRadius];
504 }
505 }
506 }
507 return shadow;
508 }
509
510 - (BOOL)_elementIsBlockLevel:(DOMElement *)element
511 {
512 BOOL isBlockLevel = NO;
513 NSNumber *val = nil;
514 val = [_elementIsBlockLevel objectForKey:element];
515 if (val) {
516 isBlockLevel = [val boolValue];
517 } else {
518 NSString *displayVal = [self _stringForNode:element property:@"display"] , *floatVal = [self _stringForNode:element property:@"float"];
519 if (floatVal && ([@"left" isEqualToString:floatVal] || [@"right" isEqual ToString:floatVal])) {
520 isBlockLevel = YES;
521 } else if (displayVal) {
522 isBlockLevel = ([@"block" isEqualToString:displayVal] || [@"list-ite m" isEqualToString:displayVal] || [displayVal hasPrefix:@"table"]);
523 }
524 [_elementIsBlockLevel setObject:[NSNumber numberWithBool:isBlockLevel] f orKey:element];
525 }
526 return isBlockLevel;
527 }
528
529 - (BOOL)_elementHasOwnBackgroundColor:(DOMElement *)element
530 {
531 // In the text system, text blocks (table elements) and documents (body elem ents) have their own background colors, which should not be inherited
532 if ([self _elementIsBlockLevel:element]) {
533 Element* coreElement = core(element);
534 NSString *displayVal = [self _stringForNode:element property:@"display"] ;
535 if (coreElement->hasTagName(htmlTag) || coreElement->hasTagName(bodyTag) || [displayVal hasPrefix:@"table"])
536 return YES;
537 }
538 return NO;
539 }
540
541 - (DOMElement *)_blockLevelElementForNode:(DOMNode *)node
542 {
543 DOMElement *element = (DOMElement *)node;
544 while (element && [element nodeType] != DOM_ELEMENT_NODE)
545 element = (DOMElement *)[element parentNode];
546 if (element && ![self _elementIsBlockLevel:element])
547 element = [self _blockLevelElementForNode:[element parentNode]];
548 return element;
549 }
550
551 - (NSColor *)_computedColorForNode:(DOMNode *)node property:(NSString *)key
552 {
553 NSColor *result = nil;
554 BOOL inherit = YES, haveResult = NO, isColor = [@"color" isEqualToString:key ], isBackgroundColor = [@"background-color" isEqualToString:key];
555 DOMElement *element = (DOMElement *)node;
556 if (element && [element nodeType] == DOM_ELEMENT_NODE) {
557 DOMCSSStyleDeclaration *computedStyle, *specifiedStyle;
558 inherit = NO;
559 if (!haveResult && (computedStyle = [self _computedStyleForElement:eleme nt])) {
560 DOMCSSPrimitiveValue *computedValue = (DOMCSSPrimitiveValue *)[compu tedStyle getPropertyCSSValue:key];
561 if (computedValue && [computedValue cssValueType] == DOM_CSS_PRIMITI VE_VALUE && [computedValue primitiveType] == DOM_CSS_RGBCOLOR) {
562 result = _colorForRGBColor([computedValue getRGBColorValue], isC olor);
563 haveResult = YES;
564 }
565 }
566 if (!haveResult && (specifiedStyle = [self _specifiedStyleForElement:ele ment])) {
567 DOMCSSPrimitiveValue *specifiedValue = (DOMCSSPrimitiveValue *)[spec ifiedStyle getPropertyCSSValue:key];
568 if (specifiedValue) {
569 unsigned short valueType = [specifiedValue cssValueType];
570 if (valueType == DOM_CSS_PRIMITIVE_VALUE && [specifiedValue prim itiveType] == DOM_CSS_RGBCOLOR) {
571 result = _colorForRGBColor([specifiedValue getRGBColorValue] , isColor);
572 haveResult = YES;
573 } else if (valueType == DOM_CSS_INHERIT) {
574 inherit = YES;
575 }
576 }
577 }
578 if (!result) {
579 if ((isColor && !haveResult) || (isBackgroundColor && ![self _elemen tHasOwnBackgroundColor:element])) inherit = YES;
580 }
581 }
582 if (!result && inherit) {
583 DOMNode *parentNode = [node parentNode];
584 if (parentNode && !(isBackgroundColor && [parentNode nodeType] == DOM_EL EMENT_NODE && [self _elementHasOwnBackgroundColor:(DOMElement *)parentNode])) {
585 result = [self _colorForNode:parentNode property:key];
586 }
587 }
588 return result;
589 }
590
591 - (NSColor *)_colorForNode:(DOMNode *)node property:(NSString *)key
592 {
593 NSColor *result = nil;
594 NSMutableDictionary *attributeDictionary = [_colorsForNodes objectForKey:nod e];
595 if (!attributeDictionary) {
596 attributeDictionary = [[NSMutableDictionary alloc] init];
597 [_colorsForNodes setObject:attributeDictionary forKey:node];
598 [attributeDictionary release];
599 }
600 result = [attributeDictionary objectForKey:key];
601 if (result) {
602 if ([[NSColor clearColor] isEqual:result]) result = nil;
603 } else {
604 result = [self _computedColorForNode:node property:key];
605 [attributeDictionary setObject:(result ? result : [NSColor clearColor]) forKey:key];
606 }
607 return result;
608 }
609
610 - (NSDictionary *)_computedAttributesForElement:(DOMElement *)element
611 {
612 DOMElement *blockElement = [self _blockLevelElementForNode:element];
613 NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
614 NSFontManager *fontManager = [NSFontManager sharedFontManager];
615 NSString *fontEffect = [self _stringForNode:element property:@"font-effect"] , *textDecoration = [self _stringForNode:element property:@"text-decoration"], * verticalAlign = [self _stringForNode:element property:@"vertical-align"], *textS hadow = [self _stringForNode:element property:@"text-shadow"];
616 CGFloat fontSize = 0, baselineOffset = 0, kerning = 0;
617 NSFont *font = nil, *actualFont = [element _font];
618 NSColor *foregroundColor = [self _colorForNode:element property:@"color"], * backgroundColor = [self _colorForNode:element property:@"background-color"];
619
620 if (![self _getFloat:&fontSize forNode:element property:@"font-size"] || fon tSize <= 0.0) fontSize = _defaultFontSize;
621 fontSize *= _textSizeMultiplier;
622 if (fontSize < _minimumFontSize) fontSize = _minimumFontSize;
623 if (fabs(floor(2.0 * fontSize + 0.5) / 2.0 - fontSize) < 0.05) {
624 fontSize = (CGFloat)floor(2.0 * fontSize + 0.5) / 2;
625 } else if (fabs(floor(10.0 * fontSize + 0.5) / 10.0 - fontSize) < 0.005) {
626 fontSize = (CGFloat)floor(10.0 * fontSize + 0.5) / 10;
627 }
628 if (fontSize <= 0.0) fontSize = 12;
629
630 if (actualFont) font = [fontManager convertFont:actualFont toSize:fontSize];
631 if (!font) {
632 NSString *fontName = [[self _stringForNode:element property:@"font-famil y"] capitalizedString], *fontStyle = [self _stringForNode:element property:@"fon t-style"], *fontWeight = [self _stringForNode:element property:@"font-weight"], *fontVariant = [self _stringForNode:element property:@"font-variant"];
633
634 if (!fontName) fontName = _standardFontFamily;
635 if (fontName) font = _fontForNameAndSize(fontName, fontSize, _fontCache) ;
636 if (!font) font = [NSFont fontWithName:@"Times" size:fontSize];
637 if ([@"italic" isEqualToString:fontStyle] || [@"oblique" isEqualToString :fontStyle]) {
638 NSFont *originalFont = font;
639 font = [fontManager convertFont:font toHaveTrait:NSItalicFontMask];
640 if (!font) font = originalFont;
641 }
642 if ([fontWeight hasPrefix:@"bold"] || [fontWeight integerValue] >= 700) {
643 // ??? handle weight properly using NSFontManager
644 NSFont *originalFont = font;
645 font = [fontManager convertFont:font toHaveTrait:NSBoldFontMask];
646 if (!font) font = originalFont;
647 }
648 if ([@"small-caps" isEqualToString:fontVariant]) {
649 // ??? synthesize small-caps if [font isEqual:originalFont]
650 NSFont *originalFont = font;
651 font = [fontManager convertFont:font toHaveTrait:NSSmallCapsFontMask ];
652 if (!font) font = originalFont;
653 }
654 }
655 if (font) [attrs setObject:font forKey:NSFontAttributeName];
656 if (foregroundColor) [attrs setObject:foregroundColor forKey:NSForegroundCol orAttributeName];
657 if (backgroundColor && ![self _elementHasOwnBackgroundColor:element]) [attrs setObject:backgroundColor forKey:NSBackgroundColorAttributeName];
658 if (fontEffect) {
659 if ([fontEffect rangeOfString:@"outline"].location != NSNotFound) [attrs setObject:[NSNumber numberWithDouble:3.0] forKey:NSStrokeWidthAttributeName];
660 if ([fontEffect rangeOfString:@"emboss"].location != NSNotFound) [attrs setObject:[[[NSShadow alloc] init] autorelease] forKey:NSShadowAttributeName];
661 }
662 if (textDecoration && [textDecoration length] > 4) {
663 if ([textDecoration rangeOfString:@"underline"].location != NSNotFound) [attrs setObject:[NSNumber numberWithInteger:NSUnderlineStyleSingle] forKey:NSUn derlineStyleAttributeName];
664 if ([textDecoration rangeOfString:@"line-through"].location != NSNotFoun d) [attrs setObject:[NSNumber numberWithInteger:NSUnderlineStyleSingle] forKey:N SStrikethroughStyleAttributeName];
665 }
666 if (verticalAlign) {
667 if ([verticalAlign rangeOfString:@"super"].location != NSNotFound) [attr s setObject:[NSNumber numberWithInteger:1] forKey:NSSuperscriptAttributeName];
668 if ([verticalAlign rangeOfString:@"sub"].location != NSNotFound) [attrs setObject:[NSNumber numberWithInteger:-1] forKey:NSSuperscriptAttributeName];
669 }
670 if ([self _getFloat:&baselineOffset forNode:element property:@"vertical-alig n"]) [attrs setObject:[NSNumber numberWithDouble:baselineOffset] forKey:NSBaseli neOffsetAttributeName];
671 if ([self _getFloat:&kerning forNode:element property:@"letter-spacing"]) [a ttrs setObject:[NSNumber numberWithDouble:kerning] forKey:NSKernAttributeName];
672 if (textShadow && [textShadow length] > 4) {
673 NSShadow *shadow = _shadowForShadowStyle(textShadow);
674 if (shadow) [attrs setObject:shadow forKey:NSShadowAttributeName];
675 }
676 if (element != blockElement && [_writingDirectionArray count] > 0) [attrs se tObject:[NSArray arrayWithArray:_writingDirectionArray] forKey:NSWritingDirectio nAttributeName];
677
678 if (blockElement) {
679 NSMutableParagraphStyle *paragraphStyle = [[[self class] defaultParagrap hStyle] mutableCopy];
680 NSString *blockTag = [blockElement tagName];
681 BOOL isParagraph = ([@"P" isEqualToString:blockTag] || [@"LI" isEqualToS tring:blockTag] || ([blockTag hasPrefix:@"H"] && 2 == [blockTag length]));
682 NSString *textAlign = [self _stringForNode:blockElement property:@"text- align"], *direction = [self _stringForNode:blockElement property:@"direction"];
683 CGFloat leftMargin = 0, rightMargin = 0, bottomMargin = 0, textIndent = 0, lineHeight = 0;
684 if (textAlign) {
685 // WebKit can return -khtml-left, -khtml-right, -khtml-center
686 if ([textAlign hasSuffix:@"left"]) [paragraphStyle setAlignment:NSLe ftTextAlignment];
687 else if ([textAlign hasSuffix:@"right"]) [paragraphStyle setAlignmen t:NSRightTextAlignment];
688 else if ([textAlign hasSuffix:@"center"]) [paragraphStyle setAlignme nt:NSCenterTextAlignment];
689 else if ([textAlign hasSuffix:@"justify"]) [paragraphStyle setAlignm ent:NSJustifiedTextAlignment];
690 }
691 if (direction) {
692 if ([direction isEqualToString:@"ltr"]) [paragraphStyle setBaseWriti ngDirection:NSWritingDirectionLeftToRight];
693 else if ([direction isEqualToString:@"rtl"]) [paragraphStyle setBase WritingDirection:NSWritingDirectionRightToLeft];
694 }
695 if ([blockTag hasPrefix:@"H"] && 2 == [blockTag length]) {
696 NSInteger headerLevel = [blockTag characterAtIndex:1] - '0';
697 if (1 <= headerLevel && headerLevel <= 6) [paragraphStyle setHeaderL evel:headerLevel];
698 }
699 if (isParagraph) {
700 //if ([self _getFloat:&topMargin forNode:blockElement property:@"mar gin-top"] && topMargin > 0.0) [paragraphStyle setParagraphSpacingBefore:topMargi n];
701 if ([self _getFloat:&leftMargin forNode:blockElement property:@"marg in-left"] && leftMargin > 0.0) [paragraphStyle setHeadIndent:leftMargin];
702 if ([self _getFloat:&textIndent forNode:blockElement property:@"text -indent"]) [paragraphStyle setFirstLineHeadIndent:[paragraphStyle headIndent] + textIndent];
703 if ([self _getFloat:&rightMargin forNode:blockElement property:@"mar gin-right"] && rightMargin > 0.0) [paragraphStyle setTailIndent:-rightMargin];
704 if ([self _getFloat:&bottomMargin forNode:blockElement property:@"ma rgin-bottom"] && bottomMargin > 0.0) [paragraphStyle setParagraphSpacing:bottomM argin];
705 }
706 if (_webViewTextSizeMultiplier > 0.0 && [self _getFloat:&lineHeight forN ode:element property:@"line-height"] && lineHeight > 0.0) {
707 [paragraphStyle setMinimumLineHeight:lineHeight / _webViewTextSizeMu ltiplier];
708 }
709 if ([_textLists count] > 0) [paragraphStyle setTextLists:_textLists];
710 if ([_textBlocks count] > 0) [paragraphStyle setTextBlocks:_textBlocks];
711 [attrs setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
712 [paragraphStyle release];
713 }
714 return attrs;
715 }
716
717 - (NSDictionary *)_attributesForElement:(DOMElement *)element
718 {
719 NSDictionary *result;
720 if (element) {
721 result = [_attributesForElements objectForKey:element];
722 if (!result) {
723 result = [self _computedAttributesForElement:element];
724 [_attributesForElements setObject:result forKey:element];
725 }
726 } else {
727 result = [NSDictionary dictionary];
728 }
729 return result;
730
731 }
732
733 - (void)_newParagraphForElement:(DOMElement *)element tag:(NSString *)tag allowE mpty:(BOOL)flag suppressTrailingSpace:(BOOL)suppress
734 {
735 NSUInteger textLength = [_attrStr length];
736 unichar lastChar = (textLength > 0) ? [[_attrStr string] characterAtIndex:te xtLength - 1] : '\n';
737 NSRange rangeToReplace = (suppress && _flags.isSoft && (lastChar == ' ' || l astChar == NSLineSeparatorCharacter)) ? NSMakeRange(textLength - 1, 1) : NSMakeR ange(textLength, 0);
738 BOOL needBreak = (flag || lastChar != '\n');
739 if (needBreak) {
740 NSString *string = (([@"BODY" isEqualToString:tag] || [@"HTML" isEqualTo String:tag]) ? @"" : @"\n");
741 [_writingDirectionArray removeAllObjects];
742 [_attrStr replaceCharactersInRange:rangeToReplace withString:string];
743 if (rangeToReplace.location < _domRangeStartIndex) _domRangeStartIndex + = [string length] - rangeToReplace.length;
744 rangeToReplace.length = [string length];
745 if (!_flags.isIndexing) {
746 NSDictionary *attrs = [self _attributesForElement:element];
747 if (!_flags.isTesting && rangeToReplace.length > 0) [_attrStr setAtt ributes:attrs range:rangeToReplace];
748 }
749 _flags.isSoft = YES;
750 }
751 }
752
753 - (void)_newLineForElement:(DOMElement *)element
754 {
755 unichar c = NSLineSeparatorCharacter;
756 NSString *string = [[NSString alloc] initWithCharacters:&c length:1];
757 NSUInteger textLength = [_attrStr length];
758 NSRange rangeToReplace = NSMakeRange(textLength, 0);
759 [_attrStr replaceCharactersInRange:rangeToReplace withString:string];
760 rangeToReplace.length = [string length];
761 if (rangeToReplace.location < _domRangeStartIndex) _domRangeStartIndex += ra ngeToReplace.length;
762 if (!_flags.isIndexing) {
763 NSDictionary *attrs = [self _attributesForElement:element];
764 if (!_flags.isTesting && rangeToReplace.length > 0) [_attrStr setAttribu tes:attrs range:rangeToReplace];
765 }
766 [string release];
767 _flags.isSoft = YES;
768 }
769
770 - (void)_newTabForElement:(DOMElement *)element
771 {
772 NSString *string = @"\t";
773 NSUInteger textLength = [_attrStr length];
774 unichar lastChar = (textLength > 0) ? [[_attrStr string] characterAtIndex:te xtLength - 1] : '\n';
775 NSRange rangeToReplace = (_flags.isSoft && lastChar == ' ') ? NSMakeRange(te xtLength - 1, 1) : NSMakeRange(textLength, 0);
776 [_attrStr replaceCharactersInRange:rangeToReplace withString:string];
777 rangeToReplace.length = [string length];
778 if (rangeToReplace.location < _domRangeStartIndex) _domRangeStartIndex += ra ngeToReplace.length;
779 if (!_flags.isIndexing) {
780 NSDictionary *attrs = [self _attributesForElement:element];
781 if (!_flags.isTesting && rangeToReplace.length > 0) [_attrStr setAttribu tes:attrs range:rangeToReplace];
782 }
783 [string release];
784 _flags.isSoft = YES;
785 }
786
787 - (BOOL)_addAttachmentForElement:(DOMElement *)element URL:(NSURL *)url needsPar agraph:(BOOL)needsParagraph usePlaceholder:(BOOL)flag
788 {
789 BOOL retval = NO, notFound = NO;
790 NSFileWrapper *fileWrapper = nil;
791 static NSImage *missingImage = nil;
792 Frame* frame = core([element ownerDocument])->frame();
793 DocumentLoader *dataSource = frame->loader()->frameHasLoaded() ? frame->load er()->documentLoader() : 0;
794 BOOL ignoreOrientation = YES;
795
796 if (_flags.isIndexing) return NO;
797 if ([url isFileURL]) {
798 NSString *path = [[url path] stringByStandardizingPath];
799 if (path) fileWrapper = [[[NSFileWrapper alloc] initWithPath:path] autor elease];
800 }
801 if (!fileWrapper) {
802 RefPtr<ArchiveResource> resource = dataSource->subresource(url);
803 if (!resource)
804 resource = dataSource->subresource(url);
805
806 const String& mimeType = resource->mimeType();
807 if (flag && resource && mimeType == "text/html")
808 notFound = YES;
809 if (resource && !notFound) {
810 fileWrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[r esource->data()->createNSData() autorelease]] autorelease];
811 [fileWrapper setPreferredFilename:suggestedFilenameWithMIMEType(url, mimeType)];
812 }
813 }
814 if (!fileWrapper && !notFound) {
815 fileWrapper = fileWrapperForURL(dataSource, url);
816 if (flag && fileWrapper && [[[[fileWrapper preferredFilename] pathExtens ion] lowercaseString] hasPrefix:@"htm"]) notFound = YES;
817 if (notFound) fileWrapper = nil;
818 }
819 if (!fileWrapper && !notFound) {
820 fileWrapper = fileWrapperForURL(_dataSource, url);
821 if (flag && fileWrapper && [[[[fileWrapper preferredFilename] pathExtens ion] lowercaseString] hasPrefix:@"htm"]) notFound = YES;
822 if (notFound) fileWrapper = nil;
823 }
824 if (fileWrapper || flag) {
825 NSUInteger textLength = [_attrStr length];
826 NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWra pper:fileWrapper];
827 NSTextAttachmentCell *cell;
828 NSString *string = [[NSString alloc] initWithFormat:(needsParagraph ? @" %C\n" : @"%C"), static_cast<unichar>(NSAttachmentCharacter)];
829 NSRange rangeToReplace = NSMakeRange(textLength, 0);
830 NSDictionary *attrs;
831 if (fileWrapper) {
832 if (ignoreOrientation) [attachment setIgnoresOrientation:YES];
833 } else {
834 cell = [[NSTextAttachmentCell alloc] initImageCell:missingImage];
835 [attachment setAttachmentCell:cell];
836 [cell release];
837 }
838 [_attrStr replaceCharactersInRange:rangeToReplace withString:string];
839 rangeToReplace.length = [string length];
840 if (rangeToReplace.location < _domRangeStartIndex) _domRangeStartIndex + = rangeToReplace.length;
841 attrs = [self _attributesForElement:element];
842 if (!_flags.isTesting && rangeToReplace.length > 0) {
843 [_attrStr setAttributes:attrs range:rangeToReplace];
844 rangeToReplace.length = 1;
845 [_attrStr addAttribute:NSAttachmentAttributeName value:attachment ra nge:rangeToReplace];
846 }
847 [string release];
848 [attachment release];
849 _flags.isSoft = NO;
850 retval = YES;
851 }
852 return retval;
853 }
854
855 - (void)_addQuoteForElement:(DOMElement *)element opening:(BOOL)opening level:(N SInteger)level
856 {
857 unichar c = ((level % 2) == 0) ? (opening ? 0x201c : 0x201d) : (opening ? 0x 2018 : 0x2019);
858 NSString *string = [[NSString alloc] initWithCharacters:&c length:1];
859 NSUInteger textLength = [_attrStr length];
860 NSRange rangeToReplace = NSMakeRange(textLength, 0);
861 [_attrStr replaceCharactersInRange:rangeToReplace withString:string];
862 rangeToReplace.length = [string length];
863 if (rangeToReplace.location < _domRangeStartIndex) _domRangeStartIndex += ra ngeToReplace.length;
864 if (!_flags.isIndexing) {
865 NSDictionary *attrs = [self _attributesForElement:element];
866 if (!_flags.isTesting && rangeToReplace.length > 0) [_attrStr setAttribu tes:attrs range:rangeToReplace];
867 }
868 [string release];
869 _flags.isSoft = NO;
870 }
871
872 - (void)_addValue:(NSString *)value forElement:(DOMElement *)element
873 {
874 NSUInteger textLength = [_attrStr length], valueLength = [value length];
875 NSRange rangeToReplace = NSMakeRange(textLength, 0);
876 if (valueLength > 0) {
877 [_attrStr replaceCharactersInRange:rangeToReplace withString:value];
878 rangeToReplace.length = valueLength;
879 if (rangeToReplace.location < _domRangeStartIndex) _domRangeStartIndex + = rangeToReplace.length;
880 if (!_flags.isIndexing) {
881 NSDictionary *attrs = [self _attributesForElement:element];
882 if (!_flags.isTesting && rangeToReplace.length > 0) [_attrStr setAtt ributes:attrs range:rangeToReplace];
883 }
884 _flags.isSoft = NO;
885 }
886 }
887
888 - (void)_fillInBlock:(NSTextBlock *)block forElement:(DOMElement *)element backg roundColor:(NSColor *)backgroundColor extraMargin:(CGFloat)extraMargin extraPadd ing:(CGFloat)extraPadding isTable:(BOOL)isTable
889 {
890 CGFloat val = 0;
891 NSColor *color = nil;
892 BOOL isTableCellElement = [element isKindOfClass:[DOMHTMLTableCellElement cl ass]];
893 NSString *width = isTableCellElement ? [(DOMHTMLTableCellElement *)element w idth] : [element getAttribute:@"width"];
894
895 if ((width && [width length] > 0) || !isTable) {
896 if ([self _getFloat:&val forNode:element property:@"width"]) [block setV alue:val type:NSTextBlockAbsoluteValueType forDimension:NSTextBlockWidth];
897 }
898
899 if ([self _getFloat:&val forNode:element property:@"min-width"]) [block setV alue:val type:NSTextBlockAbsoluteValueType forDimension:NSTextBlockMinimumWidth] ;
900 if ([self _getFloat:&val forNode:element property:@"max-width"]) [block setV alue:val type:NSTextBlockAbsoluteValueType forDimension:NSTextBlockMaximumWidth] ;
901 if ([self _getFloat:&val forNode:element property:@"min-height"]) [block set Value:val type:NSTextBlockAbsoluteValueType forDimension:NSTextBlockMinimumHeigh t];
902 if ([self _getFloat:&val forNode:element property:@"max-height"]) [block set Value:val type:NSTextBlockAbsoluteValueType forDimension:NSTextBlockMaximumHeigh t];
903
904 if ([self _getFloat:&val forNode:element property:@"padding-left"]) [block s etWidth:val + extraPadding type:NSTextBlockAbsoluteValueType forLayer:NSTextBloc kPadding edge:NSMinXEdge]; else [block setWidth:extraPadding type:NSTextBlockAbs oluteValueType forLayer:NSTextBlockPadding edge:NSMinXEdge];
905 if ([self _getFloat:&val forNode:element property:@"padding-top"]) [block se tWidth:val + extraPadding type:NSTextBlockAbsoluteValueType forLayer:NSTextBlock Padding edge:NSMinYEdge]; else [block setWidth:extraPadding type:NSTextBlockAbso luteValueType forLayer:NSTextBlockPadding edge:NSMinYEdge];
906 if ([self _getFloat:&val forNode:element property:@"padding-right"]) [block setWidth:val + extraPadding type:NSTextBlockAbsoluteValueType forLayer:NSTextBlo ckPadding edge:NSMaxXEdge]; else [block setWidth:extraPadding type:NSTextBlockAb soluteValueType forLayer:NSTextBlockPadding edge:NSMaxXEdge];
907 if ([self _getFloat:&val forNode:element property:@"padding-bottom"]) [block setWidth:val + extraPadding type:NSTextBlockAbsoluteValueType forLayer:NSTextBl ockPadding edge:NSMaxYEdge]; else [block setWidth:extraPadding type:NSTextBlockA bsoluteValueType forLayer:NSTextBlockPadding edge:NSMaxYEdge];
908
909 if ([self _getFloat:&val forNode:element property:@"border-left-width"]) [bl ock setWidth:val type:NSTextBlockAbsoluteValueType forLayer:NSTextBlockBorder ed ge:NSMinXEdge];
910 if ([self _getFloat:&val forNode:element property:@"border-top-width"]) [blo ck setWidth:val type:NSTextBlockAbsoluteValueType forLayer:NSTextBlockBorder edg e:NSMinYEdge];
911 if ([self _getFloat:&val forNode:element property:@"border-right-width"]) [b lock setWidth:val type:NSTextBlockAbsoluteValueType forLayer:NSTextBlockBorder e dge:NSMaxXEdge];
912 if ([self _getFloat:&val forNode:element property:@"border-bottom-width"]) [ block setWidth:val type:NSTextBlockAbsoluteValueType forLayer:NSTextBlockBorder edge:NSMaxYEdge];
913
914 if ([self _getFloat:&val forNode:element property:@"margin-left"]) [block se tWidth:val + extraMargin type:NSTextBlockAbsoluteValueType forLayer:NSTextBlockM argin edge:NSMinXEdge]; else [block setWidth:extraMargin type:NSTextBlockAbsolut eValueType forLayer:NSTextBlockMargin edge:NSMinXEdge];
915 if ([self _getFloat:&val forNode:element property:@"margin-top"]) [block set Width:val + extraMargin type:NSTextBlockAbsoluteValueType forLayer:NSTextBlockMa rgin edge:NSMinYEdge]; else [block setWidth:extraMargin type:NSTextBlockAbsolute ValueType forLayer:NSTextBlockMargin edge:NSMinYEdge];
916 if ([self _getFloat:&val forNode:element property:@"margin-right"]) [block s etWidth:val + extraMargin type:NSTextBlockAbsoluteValueType forLayer:NSTextBlock Margin edge:NSMaxXEdge]; else [block setWidth:extraMargin type:NSTextBlockAbsolu teValueType forLayer:NSTextBlockMargin edge:NSMaxXEdge];
917 if ([self _getFloat:&val forNode:element property:@"margin-bottom"]) [block setWidth:val + extraMargin type:NSTextBlockAbsoluteValueType forLayer:NSTextBloc kMargin edge:NSMaxYEdge]; else [block setWidth:extraMargin type:NSTextBlockAbsol uteValueType forLayer:NSTextBlockMargin edge:NSMaxYEdge];
918
919 if ((color = [self _colorForNode:element property:@"background-color"])) [bl ock setBackgroundColor:color];
920 if (!color && backgroundColor) [block setBackgroundColor:backgroundColor];
921 if ((color = [self _colorForNode:element property:@"border-left-color"])) [b lock setBorderColor:color forEdge:NSMinXEdge];
922 if ((color = [self _colorForNode:element property:@"border-top-color"])) [bl ock setBorderColor:color forEdge:NSMinYEdge];
923 if ((color = [self _colorForNode:element property:@"border-right-color"])) [ block setBorderColor:color forEdge:NSMaxXEdge];
924 if ((color = [self _colorForNode:element property:@"border-bottom-color"])) [block setBorderColor:color forEdge:NSMaxYEdge];
925 }
926
927 static inline BOOL read2DigitNumber(const char **pp, int8_t *outval)
928 {
929 BOOL result = NO;
930 char c1 = *(*pp)++, c2;
931 if (isASCIIDigit(c1)) {
932 c2 = *(*pp)++;
933 if (isASCIIDigit(c2)) {
934 *outval = 10 * (c1 - '0') + (c2 - '0');
935 result = YES;
936 }
937 }
938 return result;
939 }
940
941 static inline NSDate *_dateForString(NSString *string)
942 {
943 CFGregorianDate date;
944 const char *p = [string UTF8String];
945 int8_t secval = 0;
946 BOOL wellFormed = YES;
947
948 date.year = 0;
949 while (*p && isASCIIDigit(*p)) date.year = 10 * date.year + *p++ - '0';
950 if (*p++ != '-') wellFormed = NO;
951 if (!wellFormed || !read2DigitNumber(&p, &date.month) || *p++ != '-') wellFo rmed = NO;
952 if (!wellFormed || !read2DigitNumber(&p, &date.day) || *p++ != 'T') wellForm ed = NO;
953 if (!wellFormed || !read2DigitNumber(&p, &date.hour) || *p++ != ':') wellFor med = NO;
954 if (!wellFormed || !read2DigitNumber(&p, &date.minute) || *p++ != ':') wellF ormed = NO;
955 if (!wellFormed || !read2DigitNumber(&p, &secval) || *p++ != 'Z') wellFormed = NO;
956 if (wellFormed) date.second = secval;
957 return wellFormed ? [(NSDate *)CFDateCreate(NULL, CFGregorianDateGetAbsolute Time(date, NULL)) autorelease] : nil;
958 }
959
960 static NSInteger _colCompare(id block1, id block2, void *)
961 {
962 NSInteger col1 = [(NSTextTableBlock *)block1 startingColumn], col2 = [(NSTex tTableBlock *)block2 startingColumn];
963 return ((col1 < col2) ? NSOrderedAscending : ((col1 == col2) ? NSOrderedSame : NSOrderedDescending));
964 }
965
966 - (BOOL)_enterElement:(DOMElement *)element tag:(NSString *)tag display:(NSStrin g *)displayVal
967 {
968 if (!displayVal || !([@"none" isEqualToString:displayVal] || [@"table-column " isEqualToString:displayVal] || [@"table-column-group" isEqualToString:displayV al])) {
969 if ([self _elementIsBlockLevel:element] && ![@"BR" isEqualToString:tag] && !([@"table-cell" isEqualToString:displayVal] && [_textTables count] == 0)
970 && !([_textLists count] > 0 && [@"block" isEqualToString:displayVal] && ![@"LI" isEqualToString:tag] && ![@"UL" isEqualToString:tag] && ![@"OL" isEq ualToString:tag]))
971 [self _newParagraphForElement:element tag:tag allowEmpty:NO suppress TrailingSpace:YES];
972 return YES;
973 }
974 return NO;
975 }
976
977 - (void)_addTableForElement:(DOMElement *)tableElement
978 {
979 NSTextTable *table = [[NSTextTable alloc] init];
980 CGFloat cellSpacingVal = 1, cellPaddingVal = 1;
981 [table setNumberOfColumns:1];
982 [table setLayoutAlgorithm:NSTextTableAutomaticLayoutAlgorithm];
983 [table setCollapsesBorders:NO];
984 [table setHidesEmptyCells:NO];
985 if (tableElement) {
986 NSString *borderCollapse = [self _stringForNode:tableElement property:@" border-collapse"], *emptyCells = [self _stringForNode:tableElement property:@"em pty-cells"], *tableLayout = [self _stringForNode:tableElement property:@"table-l ayout"];
987 if ([tableElement respondsToSelector:@selector(cellSpacing)]) {
988 NSString *cellSpacing = [(DOMHTMLTableElement *)tableElement cellSpa cing];
989 if (cellSpacing && [cellSpacing length] > 0 && ![cellSpacing hasSuff ix:@"%"]) cellSpacingVal = [cellSpacing floatValue];
990 }
991 if ([tableElement respondsToSelector:@selector(cellPadding)]) {
992 NSString *cellPadding = [(DOMHTMLTableElement *)tableElement cellPad ding];
993 if (cellPadding && [cellPadding length] > 0 && ![cellPadding hasSuff ix:@"%"]) cellPaddingVal = [cellPadding floatValue];
994 }
995 [self _fillInBlock:table forElement:tableElement backgroundColor:nil ext raMargin:0 extraPadding:0 isTable:YES];
996 if ([@"collapse" isEqualToString:borderCollapse]) {
997 [table setCollapsesBorders:YES];
998 cellSpacingVal = 0;
999 }
1000 if ([@"hide" isEqualToString:emptyCells]) [table setHidesEmptyCells:YES] ;
1001 if ([@"fixed" isEqualToString:tableLayout]) [table setLayoutAlgorithm:NS TextTableFixedLayoutAlgorithm];
1002 }
1003 [_textTables addObject:table];
1004 [_textTableSpacings addObject:[NSNumber numberWithDouble:cellSpacingVal]];
1005 [_textTablePaddings addObject:[NSNumber numberWithDouble:cellPaddingVal]];
1006 [_textTableRows addObject:[NSNumber numberWithInteger:0]];
1007 [_textTableRowArrays addObject:[NSMutableArray array]];
1008 [table release];
1009 }
1010
1011 - (void)_addTableCellForElement:(DOMElement *)tableCellElement
1012 {
1013 NSTextTable *table = [_textTables lastObject];
1014 NSInteger rowNumber = [[_textTableRows lastObject] integerValue], columnNumb er = 0, rowSpan = 1, colSpan = 1;
1015 NSMutableArray *rowArray = [_textTableRowArrays lastObject];
1016 NSUInteger i, count = [rowArray count];
1017 NSColor *color = ([_textTableRowBackgroundColors count] > 0) ? [_textTableRo wBackgroundColors lastObject] : nil;
1018 NSTextTableBlock *block, *previousBlock;
1019 CGFloat cellSpacingVal = [[_textTableSpacings lastObject] floatValue];
1020 if ([color isEqual:[NSColor clearColor]]) color = nil;
1021 for (i = 0; i < count; i++) {
1022 previousBlock = [rowArray objectAtIndex:i];
1023 if (columnNumber >= [previousBlock startingColumn] && columnNumber < [pr eviousBlock startingColumn] + [previousBlock columnSpan]) columnNumber = [previo usBlock startingColumn] + [previousBlock columnSpan];
1024 }
1025 if (tableCellElement) {
1026 if ([tableCellElement respondsToSelector:@selector(rowSpan)]) {
1027 rowSpan = [(DOMHTMLTableCellElement *)tableCellElement rowSpan];
1028 if (rowSpan < 1) rowSpan = 1;
1029 }
1030 if ([tableCellElement respondsToSelector:@selector(colSpan)]) {
1031 colSpan = [(DOMHTMLTableCellElement *)tableCellElement colSpan];
1032 if (colSpan < 1) colSpan = 1;
1033 }
1034 }
1035 block = [[NSTextTableBlock alloc] initWithTable:table startingRow:rowNumber rowSpan:rowSpan startingColumn:columnNumber columnSpan:colSpan];
1036 if (tableCellElement) {
1037 NSString *verticalAlign = [self _stringForNode:tableCellElement property :@"vertical-align"];
1038 [self _fillInBlock:block forElement:tableCellElement backgroundColor:col or extraMargin:cellSpacingVal / 2 extraPadding:0 isTable:NO];
1039 if ([@"middle" isEqualToString:verticalAlign]) [block setVerticalAlignme nt:NSTextBlockMiddleAlignment];
1040 else if ([@"bottom" isEqualToString:verticalAlign]) [block setVerticalAl ignment:NSTextBlockBottomAlignment];
1041 else if ([@"baseline" isEqualToString:verticalAlign]) [block setVertical Alignment:NSTextBlockBaselineAlignment];
1042 else if ([@"top" isEqualToString:verticalAlign]) [block setVerticalAlign ment:NSTextBlockTopAlignment];
1043 }
1044 [_textBlocks addObject:block];
1045 [rowArray addObject:block];
1046 [rowArray sortUsingFunction:_colCompare context:NULL];
1047 [block release];
1048 }
1049
1050 - (BOOL)_processElement:(DOMElement *)element tag:(NSString *)tag display:(NSStr ing *)displayVal depth:(NSInteger)depth
1051 {
1052 BOOL retval = YES, isBlockLevel = [self _elementIsBlockLevel:element];
1053 if (isBlockLevel) {
1054 [_writingDirectionArray removeAllObjects];
1055 } else {
1056 NSString *bidi = [self _stringForNode:element property:@"unicode-bidi"];
1057 if (bidi && [bidi isEqualToString:@"embed"]) {
1058 NSUInteger val = NSTextWritingDirectionEmbedding;
1059 NSString *direction = [self _stringForNode:element property:@"direct ion"];
1060 if ([direction isEqualToString:@"rtl"]) val |= NSWritingDirectionRig htToLeft;
1061 [_writingDirectionArray addObject:[NSNumber numberWithUnsignedIntege r:val]];
1062 } else if (bidi && [bidi isEqualToString:@"bidi-override"]) {
1063 NSUInteger val = NSTextWritingDirectionOverride;
1064 NSString *direction = [self _stringForNode:element property:@"direct ion"];
1065 if ([direction isEqualToString:@"rtl"]) val |= NSWritingDirectionRig htToLeft;
1066 [_writingDirectionArray addObject:[NSNumber numberWithUnsignedIntege r:val]];
1067 }
1068 }
1069 if ([@"table" isEqualToString:displayVal] || ([_textTables count] == 0 && [@ "table-row-group" isEqualToString:displayVal])) {
1070 DOMElement *tableElement = element;
1071 if ([@"table-row-group" isEqualToString:displayVal]) {
1072 // If we are starting in medias res, the first thing we see may be t he tbody, so go up to the table
1073 tableElement = [self _blockLevelElementForNode:[element parentNode]] ;
1074 if (![@"table" isEqualToString:[self _stringForNode:tableElement pro perty:@"display"]]) tableElement = element;
1075 }
1076 while ([_textTables count] > [_textBlocks count]) {
1077 [self _addTableCellForElement:nil];
1078 }
1079 [self _addTableForElement:tableElement];
1080 } else if ([@"table-footer-group" isEqualToString:displayVal] && [_textTable s count] > 0) {
1081 [_textTableFooters setObject:element forKey:[NSValue valueWithNonretaine dObject:[_textTables lastObject]]];
1082 retval = NO;
1083 } else if ([@"table-row" isEqualToString:displayVal] && [_textTables count] > 0) {
1084 NSColor *color = [self _colorForNode:element property:@"background-color "];
1085 if (!color) color = [NSColor clearColor];
1086 [_textTableRowBackgroundColors addObject:color];
1087 } else if ([@"table-cell" isEqualToString:displayVal]) {
1088 while ([_textTables count] < [_textBlocks count] + 1) {
1089 [self _addTableForElement:nil];
1090 }
1091 [self _addTableCellForElement:element];
1092 } else if ([@"IMG" isEqualToString:tag]) {
1093 NSString *urlString = [element getAttribute:@"src"];
1094 if (urlString && [urlString length] > 0) {
1095 NSURL *url = core([element ownerDocument])->completeURL(stripLeading AndTrailingHTMLSpaces(urlString));
1096 if (!url) url = [NSURL _web_URLWithString:[urlString stringByTrimmin gCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] relativeToUR L:_baseURL];
1097 if (url) [self _addAttachmentForElement:element URL:url needsParagra ph:isBlockLevel usePlaceholder:YES];
1098 }
1099 retval = NO;
1100 } else if ([@"OBJECT" isEqualToString:tag]) {
1101 NSString *baseString = [element getAttribute:@"codebase"], *urlString = [element getAttribute:@"data"], *declareString = [element getAttribute:@"declare "];
1102 if (urlString && [urlString length] > 0 && ![@"true" isEqualToString:dec lareString]) {
1103 NSURL *baseURL = nil, *url = nil;
1104 if (baseString && [baseString length] > 0) {
1105 baseURL = core([element ownerDocument])->completeURL(stripLeadin gAndTrailingHTMLSpaces(baseString));
1106 if (!baseURL) baseURL = [NSURL _web_URLWithString:[baseString st ringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] relativeToURL:_baseURL];
1107 }
1108 if (baseURL) url = [NSURL _web_URLWithString:[urlString stringByTrim mingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] relativeT oURL:baseURL];
1109 if (!url) url =core([element ownerDocument])->completeURL(stripLeadi ngAndTrailingHTMLSpaces(urlString));
1110 if (!url) url = [NSURL _web_URLWithString:[urlString stringByTrimmin gCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] relativeToUR L:_baseURL];
1111 if (url) retval = ![self _addAttachmentForElement:element URL:url ne edsParagraph:isBlockLevel usePlaceholder:NO];
1112 }
1113 } else if ([@"FRAME" isEqualToString:tag]) {
1114 if ([element respondsToSelector:@selector(contentDocument)]) {
1115 DOMDocument *contentDocument = [(DOMHTMLFrameElement *)element conte ntDocument];
1116 if (contentDocument) [self _traverseNode:contentDocument depth:depth + 1 embedded:YES];
1117 }
1118 retval = NO;
1119 } else if ([@"IFRAME" isEqualToString:tag]) {
1120 if ([element respondsToSelector:@selector(contentDocument)]) {
1121 DOMDocument *contentDocument = [(DOMHTMLIFrameElement *)element cont entDocument];
1122 if (contentDocument) {
1123 [self _traverseNode:contentDocument depth:depth + 1 embedded:YES ];
1124 retval = NO;
1125 }
1126 }
1127 } else if ([@"BR" isEqualToString:tag]) {
1128 DOMElement *blockElement = [self _blockLevelElementForNode:[element pare ntNode]];
1129 NSString *breakClass = [element getAttribute:@"class"], *blockTag = [blo ckElement tagName];
1130 BOOL isExtraBreak = [@"Apple-interchange-newline" isEqualToString:breakC lass], blockElementIsParagraph = ([@"P" isEqualToString:blockTag] || [@"LI" isEq ualToString:blockTag] || ([blockTag hasPrefix:@"H"] && 2 == [blockTag length]));
1131 if (isExtraBreak) {
1132 _flags.hasTrailingNewline = YES;
1133 } else {
1134 if (blockElement && blockElementIsParagraph) {
1135 [self _newLineForElement:element];
1136 } else {
1137 [self _newParagraphForElement:element tag:tag allowEmpty:YES sup pressTrailingSpace:NO];
1138 }
1139 }
1140 } else if ([@"UL" isEqualToString:tag]) {
1141 NSTextList *list;
1142 NSString *listStyleType = [self _stringForNode:element property:@"list-s tyle-type"];
1143 if (!listStyleType || [listStyleType length] == 0) listStyleType = @"dis c";
1144 list = [[NSTextList alloc] initWithMarkerFormat:[NSString stringWithForm at:@"{%@}", listStyleType] options:0];
1145 [_textLists addObject:list];
1146 [list release];
1147 } else if ([@"OL" isEqualToString:tag]) {
1148 NSTextList *list;
1149 NSString *listStyleType = [self _stringForNode:element property:@"list-s tyle-type"];
1150 if (!listStyleType || [listStyleType length] == 0) listStyleType = @"dec imal";
1151 list = [[NSTextList alloc] initWithMarkerFormat:[NSString stringWithForm at:@"{%@}.", listStyleType] options:0];
1152 if ([element respondsToSelector:@selector(start)]) {
1153 NSInteger startingItemNumber = [(DOMHTMLOListElement *)element start ];
1154 [list setStartingItemNumber:startingItemNumber];
1155 }
1156 [_textLists addObject:list];
1157 [list release];
1158 } else if ([@"Q" isEqualToString:tag]) {
1159 [self _addQuoteForElement:element opening:YES level:_quoteLevel++];
1160 } else if ([@"INPUT" isEqualToString:tag]) {
1161 if ([element respondsToSelector:@selector(type)] && [element respondsToS elector:@selector(value)] && [@"text" compare:[(DOMHTMLInputElement *)element ty pe] options:NSCaseInsensitiveSearch] == NSOrderedSame) {
1162 NSString *value = [(DOMHTMLInputElement *)element value];
1163 if (value && [value length] > 0) [self _addValue:value forElement:el ement];
1164 }
1165 } else if ([@"TEXTAREA" isEqualToString:tag]) {
1166 if ([element respondsToSelector:@selector(value)]) {
1167 NSString *value = [(DOMHTMLTextAreaElement *)element value];
1168 if (value && [value length] > 0) [self _addValue:value forElement:el ement];
1169 }
1170 retval = NO;
1171 }
1172 return retval;
1173 }
1174
1175 - (void)_addMarkersToList:(NSTextList *)list range:(NSRange)range
1176 {
1177 NSInteger itemNum = [list startingItemNumber];
1178 NSString *string = [_attrStr string], *stringToInsert;
1179 NSDictionary *attrsToInsert = nil;
1180 NSFont *font;
1181 NSParagraphStyle *paragraphStyle;
1182 NSMutableParagraphStyle *newStyle;
1183 NSTextTab *tab = nil, *tabToRemove;
1184 NSRange paragraphRange, styleRange;
1185 NSUInteger textLength = [_attrStr length], listIndex, idx, insertLength, i, count;
1186 NSArray *textLists;
1187 CGFloat markerLocation, listLocation, pointSize;
1188
1189 if (range.length == 0 || range.location >= textLength) return;
1190 if (NSMaxRange(range) > textLength) range.length = textLength - range.locati on;
1191 paragraphStyle = [_attrStr attribute:NSParagraphStyleAttributeName atIndex:r ange.location effectiveRange:NULL];
1192 if (paragraphStyle) {
1193 textLists = [paragraphStyle textLists];
1194 listIndex = [textLists indexOfObject:list];
1195 if (textLists && listIndex != NSNotFound) {
1196 for (idx = range.location; idx < NSMaxRange(range);) {
1197 paragraphRange = [string paragraphRangeForRange:NSMakeRange(idx, 0)];
1198 paragraphStyle = [_attrStr attribute:NSParagraphStyleAttributeNa me atIndex:idx effectiveRange:&styleRange];
1199 font = [_attrStr attribute:NSFontAttributeName atIndex:idx effec tiveRange:NULL];
1200 pointSize = font ? [font pointSize] : 12;
1201 if ([[paragraphStyle textLists] count] == listIndex + 1) {
1202 stringToInsert = [NSString stringWithFormat:@"\t%@\t", [list markerForItemNumber:itemNum++]];
1203 insertLength = [stringToInsert length];
1204 if (!_flags.isIndexing && !_flags.isTesting) attrsToInsert = [NSTextList _standardMarkerAttributesForAttributes:[_attrStr attributesAtIndex: paragraphRange.location effectiveRange:NULL]];
1205 [_attrStr replaceCharactersInRange:NSMakeRange(paragraphRang e.location, 0) withString:stringToInsert];
1206 if (!_flags.isIndexing && !_flags.isTesting) [_attrStr setAt tributes:attrsToInsert range:NSMakeRange(paragraphRange.location, insertLength)] ;
1207 range.length += insertLength;
1208 paragraphRange.length += insertLength;
1209 if (paragraphRange.location < _domRangeStartIndex) _domRange StartIndex += insertLength;
1210
1211 newStyle = [paragraphStyle mutableCopy];
1212 listLocation = (listIndex + 1) * 36;
1213 markerLocation = listLocation - 25;
1214 [newStyle setFirstLineHeadIndent:0];
1215 [newStyle setHeadIndent:listLocation];
1216 while ((count = [[newStyle tabStops] count]) > 0) {
1217 for (i = 0, tabToRemove = nil; !tabToRemove && i < count ; i++) {
1218 tab = [[newStyle tabStops] objectAtIndex:i];
1219 if ([tab location] <= listLocation) tabToRemove = ta b;
1220 }
1221 if (tabToRemove) [newStyle removeTabStop:tab]; else brea k;
1222 }
1223 tab = [[NSTextTab alloc] initWithType:NSLeftTabStopType loca tion:markerLocation];
1224 [newStyle addTabStop:tab];
1225 [tab release];
1226 tab = [[NSTextTab alloc] initWithTextAlignment:NSNaturalText Alignment location:listLocation options:nil];
1227 [newStyle addTabStop:tab];
1228 [tab release];
1229 if (!_flags.isIndexing && !_flags.isTesting) [_attrStr addAt tribute:NSParagraphStyleAttributeName value:newStyle range:paragraphRange];
1230 [newStyle release];
1231
1232 idx = NSMaxRange(paragraphRange);
1233 } else {
1234 // skip any deeper-nested lists
1235 idx = NSMaxRange(styleRange);
1236 }
1237 }
1238 }
1239 }
1240 }
1241
1242 - (void)_exitElement:(DOMElement *)element tag:(NSString *)tag display:(NSString *)displayVal depth:(NSInteger)depth startIndex:(NSUInteger)startIndex
1243 {
1244 NSRange range = NSMakeRange(startIndex, [_attrStr length] - startIndex);
1245 if (range.length > 0 && [@"A" isEqualToString:tag]) {
1246 NSString *urlString = [element getAttribute:@"href"], *strippedString = [urlString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineC haracterSet]];
1247 if (urlString && [urlString length] > 0 && strippedString && [strippedSt ring length] > 0 && ![strippedString hasPrefix:@"#"]) {
1248 NSURL *url = core([element ownerDocument])->completeURL(stripLeading AndTrailingHTMLSpaces(urlString));
1249 if (!url) url = core([element ownerDocument])->completeURL(stripLead ingAndTrailingHTMLSpaces(strippedString));
1250 if (!url) url = [NSURL _web_URLWithString:strippedString relativeToU RL:_baseURL];
1251 if (!_flags.isIndexing && !_flags.isTesting) [_attrStr addAttribute: NSLinkAttributeName value:url ? (id)url : (id)urlString range:range];
1252 }
1253 }
1254 if (!_flags.reachedEnd && [self _elementIsBlockLevel:element]) {
1255 [_writingDirectionArray removeAllObjects];
1256 if ([@"table-cell" isEqualToString:displayVal] && [_textBlocks count] == 0) {
1257 [self _newTabForElement:element];
1258 } else if ([_textLists count] > 0 && [@"block" isEqualToString:displayVa l] && ![@"LI" isEqualToString:tag] && ![@"UL" isEqualToString:tag] && ![@"OL" is EqualToString:tag]) {
1259 [self _newLineForElement:element];
1260 } else {
1261 [self _newParagraphForElement:element tag:tag allowEmpty:(range.leng th == 0) suppressTrailingSpace:YES];
1262 }
1263 } else if ([_writingDirectionArray count] > 0) {
1264 NSString *bidi = [self _stringForNode:element property:@"unicode-bidi"];
1265 if (bidi && ([bidi isEqualToString:@"embed"] || [bidi isEqualToString:@" bidi-override"])) {
1266 [_writingDirectionArray removeLastObject];
1267 }
1268 }
1269 range = NSMakeRange(startIndex, [_attrStr length] - startIndex);
1270 if ([@"table" isEqualToString:displayVal] && [_textTables count] > 0) {
1271 NSValue *key = [NSValue valueWithNonretainedObject:[_textTables lastObje ct]];
1272 DOMNode *footer = [_textTableFooters objectForKey:key];
1273 while ([_textTables count] < [_textBlocks count] + 1) {
1274 [_textBlocks removeLastObject];
1275 }
1276 if (footer) {
1277 [self _traverseFooterNode:footer depth:depth + 1];
1278 [_textTableFooters removeObjectForKey:key];
1279 }
1280 [_textTables removeLastObject];
1281 [_textTableSpacings removeLastObject];
1282 [_textTablePaddings removeLastObject];
1283 [_textTableRows removeLastObject];
1284 [_textTableRowArrays removeLastObject];
1285 } else if ([@"table-row" isEqualToString:displayVal] && [_textTables count] > 0) {
1286 NSTextTable *table = [_textTables lastObject];
1287 NSTextTableBlock *block;
1288 NSMutableArray *rowArray = [_textTableRowArrays lastObject], *previousRo wArray;
1289 NSUInteger i, count;
1290 NSInteger numberOfColumns = [table numberOfColumns];
1291 NSInteger openColumn;
1292 NSInteger rowNumber = [[_textTableRows lastObject] integerValue];
1293 do {
1294 rowNumber++;
1295 previousRowArray = rowArray;
1296 rowArray = [NSMutableArray array];
1297 count = [previousRowArray count];
1298 for (i = 0; i < count; i++) {
1299 block = [previousRowArray objectAtIndex:i];
1300 if ([block startingColumn] + [block columnSpan] > numberOfColumn s) numberOfColumns = [block startingColumn] + [block columnSpan];
1301 if ([block startingRow] + [block rowSpan] > rowNumber) [rowArray addObject:block];
1302 }
1303 count = [rowArray count];
1304 openColumn = 0;
1305 for (i = 0; i < count; i++) {
1306 block = [rowArray objectAtIndex:i];
1307 if (openColumn >= [block startingColumn] && openColumn < [block startingColumn] + [block columnSpan]) openColumn = [block startingColumn] + [blo ck columnSpan];
1308 }
1309 } while (openColumn >= numberOfColumns);
1310 if ((NSUInteger)numberOfColumns > [table numberOfColumns]) [table setNum berOfColumns:numberOfColumns];
1311 [_textTableRows removeLastObject];
1312 [_textTableRows addObject:[NSNumber numberWithInteger:rowNumber]];
1313 [_textTableRowArrays removeLastObject];
1314 [_textTableRowArrays addObject:rowArray];
1315 if ([_textTableRowBackgroundColors count] > 0) [_textTableRowBackgroundC olors removeLastObject];
1316 } else if ([@"table-cell" isEqualToString:displayVal] && [_textBlocks count] > 0) {
1317 while ([_textTables count] > [_textBlocks count]) {
1318 [_textTables removeLastObject];
1319 [_textTableSpacings removeLastObject];
1320 [_textTablePaddings removeLastObject];
1321 [_textTableRows removeLastObject];
1322 [_textTableRowArrays removeLastObject];
1323 }
1324 [_textBlocks removeLastObject];
1325 } else if (([@"UL" isEqualToString:tag] || [@"OL" isEqualToString:tag]) && [ _textLists count] > 0) {
1326 NSTextList *list = [_textLists lastObject];
1327 [self _addMarkersToList:list range:range];
1328 [_textLists removeLastObject];
1329 } else if ([@"Q" isEqualToString:tag]) {
1330 [self _addQuoteForElement:element opening:NO level:--_quoteLevel];
1331 } else if ([@"SPAN" isEqualToString:tag]) {
1332 NSString *className = [element getAttribute:@"class"];
1333 NSMutableString *mutableString;
1334 NSUInteger i, count = 0;
1335 unichar c;
1336 if ([@"Apple-converted-space" isEqualToString:className]) {
1337 mutableString = [_attrStr mutableString];
1338 for (i = range.location; i < NSMaxRange(range); i++) {
1339 c = [mutableString characterAtIndex:i];
1340 if (0xa0 == c) [mutableString replaceCharactersInRange:NSMakeRan ge(i, 1) withString:@" "];
1341 }
1342 } else if ([@"Apple-converted-tab" isEqualToString:className]) {
1343 mutableString = [_attrStr mutableString];
1344 for (i = range.location; i < NSMaxRange(range); i++) {
1345 NSRange rangeToReplace = NSMakeRange(NSNotFound, 0);
1346 c = [mutableString characterAtIndex:i];
1347 if (' ' == c || 0xa0 == c) {
1348 count++;
1349 if (count >= 4 || i + 1 >= NSMaxRange(range)) rangeToReplace = NSMakeRange(i + 1 - count, count);
1350 } else {
1351 if (count > 0) rangeToReplace = NSMakeRange(i - count, count );
1352 }
1353 if (rangeToReplace.length > 0) {
1354 [mutableString replaceCharactersInRange:rangeToReplace withS tring:@"\t"];
1355 range.length -= (rangeToReplace.length - 1);
1356 i -= (rangeToReplace.length - 1);
1357 if (NSMaxRange(rangeToReplace) <= _domRangeStartIndex) {
1358 _domRangeStartIndex -= (rangeToReplace.length - 1);
1359 } else if (rangeToReplace.location < _domRangeStartIndex) {
1360 _domRangeStartIndex = rangeToReplace.location;
1361 }
1362 count = 0;
1363 }
1364 }
1365 }
1366 }
1367 }
1368
1369 - (void)_processText:(DOMCharacterData *)text
1370 {
1371 NSString *instr = [text data], *outstr = instr, *whitespaceVal, *transformVa l;
1372 NSUInteger textLength = [_attrStr length], startOffset = 0, endOffset = [ins tr length];
1373 unichar lastChar = (textLength > 0) ? [[_attrStr string] characterAtIndex:te xtLength - 1] : '\n';
1374 BOOL wasSpace = NO, wasLeading = YES, suppressLeadingSpace = ((_flags.isSoft && lastChar == ' ') || lastChar == '\n' || lastChar == '\r' || lastChar == '\t' || lastChar == NSParagraphSeparatorCharacter || lastChar == NSLineSeparatorChar acter || lastChar == NSFormFeedCharacter || lastChar == WebNextLineCharacter);
1375 NSRange rangeToReplace = NSMakeRange(textLength, 0);
1376 CFMutableStringRef mutstr = NULL;
1377 whitespaceVal = [self _stringForNode:text property:@"white-space"];
1378 transformVal = [self _stringForNode:text property:@"text-transform"];
1379
1380 if (_domRange) {
1381 if (text == [_domRange startContainer]) {
1382 startOffset = (NSUInteger)[_domRange startOffset];
1383 _domRangeStartIndex = [_attrStr length];
1384 _flags.reachedStart = YES;
1385 }
1386 if (text == [_domRange endContainer]) {
1387 endOffset = (NSUInteger)[_domRange endOffset];
1388 _flags.reachedEnd = YES;
1389 }
1390 if ((startOffset > 0 || endOffset < [instr length]) && endOffset >= star tOffset) {
1391 instr = [instr substringWithRange:NSMakeRange(startOffset, endOffset - startOffset)];
1392 outstr = instr;
1393 }
1394 }
1395 if ([whitespaceVal hasPrefix:@"pre"]) {
1396 if (textLength > 0 && [instr length] > 0 && _flags.isSoft) {
1397 unichar c = [instr characterAtIndex:0];
1398 if (c == '\n' || c == '\r' || c == NSParagraphSeparatorCharacter || c == NSLineSeparatorCharacter || c == NSFormFeedCharacter || c == WebNextLineCha racter) rangeToReplace = NSMakeRange(textLength - 1, 1);
1399 }
1400 } else {
1401 CFStringInlineBuffer inlineBuffer;
1402 const unsigned int TextBufferSize = 255;
1403
1404 unichar buffer[TextBufferSize + 1];
1405 NSUInteger i, count = [instr length], idx = 0;
1406
1407 mutstr = CFStringCreateMutable(NULL, 0);
1408 CFStringInitInlineBuffer((CFStringRef)instr, &inlineBuffer, CFRangeMake( 0, count));
1409 for (i = 0; i < count; i++) {
1410 unichar c = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, i);
1411 if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == 0xc || c == 0x200b) {
1412 wasSpace = (!wasLeading || !suppressLeadingSpace);
1413 } else {
1414 if (wasSpace) buffer[idx++] = ' ';
1415 buffer[idx++] = c;
1416 if (idx >= TextBufferSize) {
1417 CFStringAppendCharacters(mutstr, buffer, idx);
1418 idx = 0;
1419 }
1420 wasSpace = wasLeading = NO;
1421 }
1422 }
1423 if (wasSpace) buffer[idx++] = ' ';
1424 if (idx > 0) CFStringAppendCharacters(mutstr, buffer, idx);
1425 outstr = (NSString *)mutstr;
1426 }
1427 if ([outstr length] > 0) {
1428 if ([@"capitalize" isEqualToString:transformVal]) {
1429 outstr = [outstr capitalizedString];
1430 } else if ([@"uppercase" isEqualToString:transformVal]) {
1431 outstr = [outstr uppercaseString];
1432 } else if ([@"lowercase" isEqualToString:transformVal]) {
1433 outstr = [outstr lowercaseString];
1434 }
1435 [_attrStr replaceCharactersInRange:rangeToReplace withString:outstr];
1436 rangeToReplace.length = [outstr length];
1437 if (!_flags.isIndexing) {
1438 NSDictionary *attrs;
1439 DOMElement *element = (DOMElement *)text;
1440 while (element && [element nodeType] != DOM_ELEMENT_NODE) element = (DOMElement *)[element parentNode];
1441 attrs = [self _attributesForElement:element];
1442 if (!_flags.isTesting && rangeToReplace.length > 0) [_attrStr setAtt ributes:attrs range:rangeToReplace];
1443 }
1444 _flags.isSoft = wasSpace;
1445 }
1446 if (mutstr) CFRelease(mutstr);
1447 }
1448
1449 - (void)_traverseNode:(DOMNode *)node depth:(NSInteger)depth embedded:(BOOL)embe dded
1450 {
1451 unsigned short nodeType;
1452 NSArray *childNodes;
1453 NSUInteger i, count, startOffset, endOffset;
1454 BOOL isStart = NO, isEnd = NO;
1455
1456 if (_flags.reachedEnd) return;
1457 if (_domRange && !_flags.reachedStart && _domStartAncestors && ![_domStartAn cestors containsObject:node]) return;
1458
1459 nodeType = [node nodeType];
1460 childNodes = [self _childrenForNode:node];
1461 count = [childNodes count];
1462 startOffset = 0;
1463 endOffset = count;
1464
1465 if (_domRange) {
1466 if (node == [_domRange startContainer]) {
1467 startOffset = (NSUInteger)[_domRange startOffset];
1468 isStart = YES;
1469 _flags.reachedStart = YES;
1470 }
1471 if (node == [_domRange endContainer]) {
1472 endOffset = (NSUInteger)[_domRange endOffset];
1473 isEnd = YES;
1474 }
1475 }
1476
1477 if (nodeType == DOM_DOCUMENT_NODE || nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
1478 for (i = 0; i < count; i++) {
1479 if (isStart && i == startOffset) _domRangeStartIndex = [_attrStr len gth];
1480 if ((!isStart || startOffset <= i) && (!isEnd || endOffset > i)) [se lf _traverseNode:[childNodes objectAtIndex:i] depth:depth + 1 embedded:embedded] ;
1481 if (isEnd && i + 1 >= endOffset) _flags.reachedEnd = YES;
1482 if (_thumbnailLimit > 0 && [_attrStr length] >= _thumbnailLimit) _fl ags.reachedEnd = YES;
1483 if (_flags.reachedEnd) break;
1484 }
1485 } else if (nodeType == DOM_ELEMENT_NODE) {
1486 DOMElement *element = (DOMElement *)node;
1487 NSString *tag = [element tagName], *displayVal = [self _stringForNode:el ement property:@"display"], *floatVal = [self _stringForNode:element property:@" float"];
1488 BOOL isBlockLevel = NO;
1489 if (floatVal && ([@"left" isEqualToString:floatVal] || [@"right" isEqual ToString:floatVal])) {
1490 isBlockLevel = YES;
1491 } else if (displayVal) {
1492 isBlockLevel = ([@"block" isEqualToString:displayVal] || [@"list-ite m" isEqualToString:displayVal] || [displayVal hasPrefix:@"table"]);
1493 }
1494 [_elementIsBlockLevel setObject:[NSNumber numberWithBool:isBlockLevel] f orKey:element];
1495 if ([self _enterElement:element tag:tag display:displayVal]) {
1496 NSUInteger startIndex = [_attrStr length];
1497 if ([self _processElement:element tag:tag display:displayVal depth:d epth]) {
1498 for (i = 0; i < count; i++) {
1499 if (isStart && i == startOffset) _domRangeStartIndex = [_att rStr length];
1500 if ((!isStart || startOffset <= i) && (!isEnd || endOffset > i)) [self _traverseNode:[childNodes objectAtIndex:i] depth:depth + 1 embedded:e mbedded];
1501 if (isEnd && i + 1 >= endOffset) _flags.reachedEnd = YES;
1502 if (_flags.reachedEnd) break;
1503 }
1504 [self _exitElement:element tag:tag display:displayVal depth:dept h startIndex:startIndex];
1505 }
1506 }
1507 } else if (nodeType == DOM_TEXT_NODE || nodeType == DOM_CDATA_SECTION_NODE) {
1508 [self _processText:(DOMCharacterData *)node];
1509 }
1510
1511 if (isEnd) _flags.reachedEnd = YES;
1512 }
1513
1514 - (void)_traverseFooterNode:(DOMNode *)node depth:(NSInteger)depth
1515 {
1516 DOMElement *element = (DOMElement *)node;
1517 NSArray *childNodes = [self _childrenForNode:node];
1518 NSString *tag = @"TBODY", *displayVal = @"table-row-group";
1519 NSUInteger i, count = [childNodes count], startOffset = 0, endOffset = count ;
1520 BOOL isStart = NO, isEnd = NO;
1521
1522 if (_flags.reachedEnd) return;
1523 if (_domRange && !_flags.reachedStart && _domStartAncestors && ![_domStartAn cestors containsObject:node]) return;
1524 if (_domRange) {
1525 if (node == [_domRange startContainer]) {
1526 startOffset = (NSUInteger)[_domRange startOffset];
1527 isStart = YES;
1528 _flags.reachedStart = YES;
1529 }
1530 if (node == [_domRange endContainer]) {
1531 endOffset = (NSUInteger)[_domRange endOffset];
1532 isEnd = YES;
1533 }
1534 }
1535 if ([self _enterElement:element tag:tag display:displayVal]) {
1536 NSUInteger startIndex = [_attrStr length];
1537 if ([self _processElement:element tag:tag display:displayVal depth:depth ]) {
1538 for (i = 0; i < count; i++) {
1539 if (isStart && i == startOffset) _domRangeStartIndex = [_attrStr length];
1540 if ((!isStart || startOffset <= i) && (!isEnd || endOffset > i)) [self _traverseNode:[childNodes objectAtIndex:i] depth:depth + 1 embedded:YES];
1541 if (isEnd && i + 1 >= endOffset) _flags.reachedEnd = YES;
1542 if (_flags.reachedEnd) break;
1543 }
1544 [self _exitElement:element tag:tag display:displayVal depth:depth st artIndex:startIndex];
1545 }
1546 }
1547 if (isEnd) _flags.reachedEnd = YES;
1548 }
1549
1550 - (void)_adjustTrailingNewline
1551 {
1552 NSUInteger textLength = [_attrStr length];
1553 unichar lastChar = (textLength > 0) ? [[_attrStr string] characterAtIndex:te xtLength - 1] : 0;
1554 BOOL alreadyHasTrailingNewline = (lastChar == '\n' || lastChar == '\r' || la stChar == NSParagraphSeparatorCharacter || lastChar == NSLineSeparatorCharacter || lastChar == WebNextLineCharacter);
1555 if (_flags.hasTrailingNewline && !alreadyHasTrailingNewline)
1556 [_attrStr replaceCharactersInRange:NSMakeRange(textLength, 0) withString :@"\n"];
1557 }
1558
1559 - (void)_loadFromDOMRange
1560 {
1561 if (-1 == _errorCode) {
1562 DOMNode *commonAncestorContainer = [_domRange commonAncestorContainer], *ancestorContainer = [_domRange startContainer];
1563
1564 _domStartAncestors = [[NSMutableArray alloc] init];
1565 while (ancestorContainer) {
1566 [_domStartAncestors addObject:ancestorContainer];
1567 if (ancestorContainer == commonAncestorContainer) break;
1568 ancestorContainer = [ancestorContainer parentNode];
1569 }
1570 _document = [commonAncestorContainer ownerDocument];
1571 _dataSource = (DocumentLoader *)core(_document)->frame()->loader()->docu mentLoader();
1572 if (_textSizeMultiplier <= 0.0) _textSizeMultiplier = 1;
1573 if (_defaultFontSize <= 0.0) _defaultFontSize = 12;
1574 if (_minimumFontSize < 1.0) _minimumFontSize = 1;
1575 if (_document && _dataSource) {
1576 _domRangeStartIndex = 0;
1577 _errorCode = 0;
1578 [self _traverseNode:commonAncestorContainer depth:0 embedded:NO];
1579 if (_domRangeStartIndex > 0 && _domRangeStartIndex <= [_attrStr leng th]) [_attrStr deleteCharactersInRange:NSMakeRange(0, _domRangeStartIndex)];
1580 }
1581 }
1582 }
1583
1584 - (void)dealloc
1585 {
1586 [_attrStr release];
1587 [_domRange release];
1588 [_domStartAncestors release];
1589 [_standardFontFamily release];
1590 [_textLists release];
1591 [_textBlocks release];
1592 [_textTables release];
1593 [_textTableFooters release];
1594 [_textTableSpacings release];
1595 [_textTablePaddings release];
1596 [_textTableRows release];
1597 [_textTableRowArrays release];
1598 [_textTableRowBackgroundColors release];
1599 [_computedStylesForElements release];
1600 [_specifiedStylesForElements release];
1601 [_stringsForNodes release];
1602 [_floatsForNodes release];
1603 [_colorsForNodes release];
1604 [_attributesForElements release];
1605 [_elementIsBlockLevel release];
1606 [_fontCache release];
1607 [_writingDirectionArray release];
1608 [super dealloc];
1609 }
1610
1611 - (id)init
1612 {
1613 self = [super init];
1614 if (!self) return nil;
1615
1616 _attrStr = [[NSMutableAttributedString alloc] init];
1617
1618 _textLists = [[NSMutableArray alloc] init];
1619 _textBlocks = [[NSMutableArray alloc] init];
1620 _textTables = [[NSMutableArray alloc] init];
1621 _textTableFooters = [[NSMutableDictionary alloc] init];
1622 _textTableSpacings = [[NSMutableArray alloc] init];
1623 _textTablePaddings = [[NSMutableArray alloc] init];
1624 _textTableRows = [[NSMutableArray alloc] init];
1625 _textTableRowArrays = [[NSMutableArray alloc] init];
1626 _textTableRowBackgroundColors = [[NSMutableArray alloc] init];
1627 _computedStylesForElements = [[NSMutableDictionary alloc] init];
1628 _specifiedStylesForElements = [[NSMutableDictionary alloc] init];
1629 _stringsForNodes = [[NSMutableDictionary alloc] init];
1630 _floatsForNodes = [[NSMutableDictionary alloc] init];
1631 _colorsForNodes = [[NSMutableDictionary alloc] init];
1632 _attributesForElements = [[NSMutableDictionary alloc] init];
1633 _elementIsBlockLevel = [[NSMutableDictionary alloc] init];
1634 _fontCache = [[NSMutableDictionary alloc] init];
1635 _writingDirectionArray = [[NSMutableArray alloc] init];
1636
1637 _textSizeMultiplier = 1;
1638 _webViewTextSizeMultiplier = 0;
1639 _defaultTabInterval = 36;
1640 _defaultFontSize = 12;
1641 _minimumFontSize = 1;
1642 _errorCode = -1;
1643 _indexingLimit = 0;
1644 _thumbnailLimit = 0;
1645
1646 _flags.isIndexing = (_indexingLimit > 0);
1647 _flags.isTesting = 0;
1648
1649 return self;
1650 }
1651
1652 - (id)initWithDOMRange:(DOMRange *)domRange
1653 {
1654 self = [self init];
1655 if (!self) return nil;
1656 _domRange = [domRange retain];
1657 return self;
1658 }
1659
1660 // This function supports more HTML features than the editing variant below, suc h as tables.
1661 - (NSAttributedString *)attributedString
1662 {
1663 [self _loadFromDOMRange];
1664 return (0 == _errorCode) ? [[_attrStr retain] autorelease] : nil;
1665 }
1666
1667 #endif // PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1668
1669 // This function uses TextIterator, which makes offsets in its result compatible with HTML editing.
1670 + (NSAttributedString *)editingAttributedStringFromRange:(Range*)range
1671 {
1672 NSFontManager *fontManager = [NSFontManager sharedFontManager];
1673 NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init] ;
1674 NSUInteger stringLength = 0;
1675 RetainPtr<NSMutableDictionary> attrs(AdoptNS, [[NSMutableDictionary alloc] i nit]);
1676
1677 for (TextIterator it(range); !it.atEnd(); it.advance()) {
1678 RefPtr<Range> currentTextRange = it.range();
1679 Node* startContainer = currentTextRange->startContainer();
1680 Node* endContainer = currentTextRange->endContainer();
1681 int startOffset = currentTextRange->startOffset();
1682 int endOffset = currentTextRange->endOffset();
1683
1684 if (startContainer == endContainer && (startOffset == endOffset - 1)) {
1685 Node* node = startContainer->childNode(startOffset);
1686 if (node && node->hasTagName(imgTag)) {
1687 NSFileWrapper* fileWrapper = fileWrapperForElement(toElement(nod e));
1688 NSTextAttachment* attachment = [[NSTextAttachment alloc] initWit hFileWrapper:fileWrapper];
1689 [string appendAttributedString:[NSAttributedString attributedStr ingWithAttachment:attachment]];
1690 [attachment release];
1691 }
1692 }
1693
1694 int currentTextLength = it.length();
1695 if (!currentTextLength)
1696 continue;
1697
1698 RenderObject* renderer = startContainer->renderer();
1699 ASSERT(renderer);
1700 if (!renderer)
1701 continue;
1702 RenderStyle* style = renderer->style();
1703 if (style->textDecorationsInEffect() & UNDERLINE)
1704 [attrs.get() setObject:[NSNumber numberWithInteger:NSUnderlineStyleS ingle] forKey:NSUnderlineStyleAttributeName];
1705 if (NSFont *font = style->font().primaryFont()->getNSFont())
1706 [attrs.get() setObject:font forKey:NSFontAttributeName];
1707 else
1708 [attrs.get() setObject:[fontManager convertFont:WebDefaultFont() toS ize:style->font().primaryFont()->platformData().size()] forKey:NSFontAttributeNa me];
1709 if (style->visitedDependentColor(CSSPropertyColor).alpha())
1710 [attrs.get() setObject:nsColor(style->visitedDependentColor(CSSPrope rtyColor)) forKey:NSForegroundColorAttributeName];
1711 else
1712 [attrs.get() removeObjectForKey:NSForegroundColorAttributeName];
1713 if (style->visitedDependentColor(CSSPropertyBackgroundColor).alpha())
1714 [attrs.get() setObject:nsColor(style->visitedDependentColor(CSSPrope rtyBackgroundColor)) forKey:NSBackgroundColorAttributeName];
1715 else
1716 [attrs.get() removeObjectForKey:NSBackgroundColorAttributeName];
1717
1718 RetainPtr<NSString> substring(AdoptNS, [[NSString alloc] initWithCharact ersNoCopy:const_cast<UChar*>(it.characters()) length:currentTextLength freeWhenD one:NO]);
1719 [string replaceCharactersInRange:NSMakeRange(stringLength, 0) withString :substring.get()];
1720 [string setAttributes:attrs.get() range:NSMakeRange(stringLength, curren tTextLength)];
1721 stringLength += currentTextLength;
1722 }
1723
1724 return [string autorelease];
1725 }
1726
1727 @end
1728
1729 static NSFileWrapper *fileWrapperForURL(DocumentLoader *dataSource, NSURL *URL)
1730 {
1731 if ([URL isFileURL]) {
1732 NSString *path = [[URL path] stringByResolvingSymlinksInPath];
1733 return [[[NSFileWrapper alloc] initWithPath:path] autorelease];
1734 }
1735
1736 RefPtr<ArchiveResource> resource = dataSource->subresource(URL);
1737 if (resource) {
1738 NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithCont ents:[resource->data()->createNSData() autorelease]] autorelease];
1739 NSString *filename = resource->response().suggestedFilename();
1740 if (!filename || ![filename length])
1741 filename = suggestedFilenameWithMIMEType(resource->url(), resource-> mimeType());
1742 [wrapper setPreferredFilename:filename];
1743 return wrapper;
1744 }
1745
1746 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL] ;
1747
1748 NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedRes ponseForRequest:request];
1749 [request release];
1750
1751 if (cachedResponse) {
1752 NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithCont ents:[cachedResponse data]] autorelease];
1753 [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilena me]];
1754 return wrapper;
1755 }
1756
1757 return nil;
1758 }
1759
1760 static NSFileWrapper *fileWrapperForElement(Element* element)
1761 {
1762 NSFileWrapper *wrapper = nil;
1763
1764 const AtomicString& attr = element->getAttribute(srcAttr);
1765 if (!attr.isEmpty()) {
1766 NSURL *URL = element->document()->completeURL(attr);
1767 if (DocumentLoader* loader = element->document()->loader())
1768 wrapper = fileWrapperForURL(loader, URL);
1769 }
1770 if (!wrapper) {
1771 RenderImage* renderer = toRenderImage(element->renderer());
1772 if (renderer->cachedImage() && !renderer->cachedImage()->errorOccurred() ) {
1773 wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:(NSData *)(renderer->cachedImage()->imageForRenderer(renderer)->getTIFFRepresentation() )];
1774 [wrapper setPreferredFilename:@"image.tiff"];
1775 [wrapper autorelease];
1776 }
1777 }
1778
1779 return wrapper;
1780 }
OLDNEW
« no previous file with comments | « Source/WebCore/platform/mac/HTMLConverter.h ('k') | Source/WebCore/platform/mac/KURLMac.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698