Changeset 166342 in webkit
- Timestamp:
- Mar 26, 2014 9:16:32 PM (10 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r166341 r166342 1 2014-03-26 Ryosuke Niwa <rniwa@webkit.org> 2 3 Make _processText and _traverseNode in HTMLConverter more efficient 4 https://bugs.webkit.org/show_bug.cgi?id=130769 5 6 Reviewed by Sam Weinig. 7 8 Rewrote a bunch of code in C++ and avoided creating wrappers. 9 This reduces the runtime of Interactive/CopyAll.html from ~16.5s to 15s. 10 11 * editing/cocoa/HTMLConverter.mm: 12 (HTMLConverterCaches::isAncestorsOfStartToBeConverted): 13 (HTMLConverter::HTMLConverter): 14 (HTMLConverter::~HTMLConverter): 15 (HTMLConverter::_processElement): 16 (HTMLConverter::_processText): 17 (HTMLConverter::_traverseNode): 18 (HTMLConverter::_traverseFooterNode): 19 (HTMLConverterCaches::cacheAncestorsOfStartToBeConverted): 20 (HTMLConverter::_loadFromDOMRange): 21 1 22 2014-03-26 Adenilson Cavalcanti <cavalcantii@gmail.com> 2 23 -
trunk/Source/WebCore/editing/cocoa/HTMLConverter.mm
r166335 r166342 50 50 #import "FrameLoader.h" 51 51 #import "HTMLElement.h" 52 #import "HTMLFrameElementBase.h" 52 53 #import "HTMLNames.h" 53 54 #import "HTMLParserIdioms.h" … … 61 62 #import <objc/runtime.h> 62 63 #import <wtf/ASCIICType.h> 64 #import <wtf/text/StringBuilder.h> 63 65 64 66 #if PLATFORM(IOS) … … 417 419 PassRefPtr<CSSValue> inlineStylePropertyForElement(Element&, CSSPropertyID); 418 420 421 Node* cacheAncestorsOfStartToBeConverted(const Range&); 422 bool isAncestorsOfStartToBeConverted(Node* node) const { return m_ancestorsUnderCommonAncestor.contains(node); } 423 419 424 private: 420 425 HashMap<Element*, std::unique_ptr<ComputedStyleExtractor>> m_computedStyles; 426 HashSet<Node*> m_ancestorsUnderCommonAncestor; 421 427 }; 422 428 … … 455 461 DOMDocument *_document; 456 462 DOMRange *_domRange; 457 NSMutableArray *_domStartAncestors;458 463 WebCore::DocumentLoader *_dataSource; 459 464 NSMutableArray *_textLists; … … 488 493 PlatformColor *_colorForElement(Element&, CSSPropertyID); 489 494 490 void _traverseNode( DOMNode *node, NSInteger depth, BOOLembedded);495 void _traverseNode(Node* node, unsigned depth, bool embedded); 491 496 void _traverseFooterNode(DOMNode *node, NSInteger depth); 492 497 … … 511 516 void _addMarkersToList(NSTextList *list, NSRange range); 512 517 void _exitElement(DOMElement *element, NSInteger depth, NSUInteger startIndex); 513 void _processText( DOMCharacterData *text);518 void _processText(CharacterData&); 514 519 void _adjustTrailingNewline(); 515 520 }; … … 522 527 _baseURL = nil; 523 528 _document = nil; 524 _domStartAncestors = nil;525 529 _dataSource = nullptr; 526 530 _textLists = [[NSMutableArray alloc] init]; … … 553 557 [_documentAttrs release]; 554 558 [_domRange release]; 555 [_domStartAncestors release];556 559 [_textLists release]; 557 560 [_textBlocks release]; … … 1981 1984 retval = !_addAttachmentForElement(element, url, isBlockLevel, NO); 1982 1985 } 1983 } else if (coreElement.hasTagName(frameTag)) { 1984 if ([element respondsToSelector:@selector(contentDocument)]) { 1985 DOMDocument *contentDocument = [(DOMHTMLFrameElement *)element contentDocument]; 1986 if (contentDocument) 1987 _traverseNode(contentDocument, depth + 1, YES); 1988 } 1989 retval = NO; 1990 } else if (coreElement.hasTagName(iframeTag)) { 1991 if ([element respondsToSelector:@selector(contentDocument)]) { 1992 DOMDocument *contentDocument = [(DOMHTMLIFrameElement *)element contentDocument]; 1993 if (contentDocument) { 1994 _traverseNode(contentDocument, depth + 1, YES); 1995 retval = NO; 1996 } 1986 } else if (coreElement.hasTagName(frameTag) || coreElement.hasTagName(iframeTag)) { 1987 if (Document* contentDocument = toHTMLFrameElementBase(coreElement).contentDocument()) { 1988 _traverseNode(contentDocument, depth + 1, true /* embedded */); 1989 retval = NO; 1997 1990 } 1998 1991 } else if (coreElement.hasTagName(brTag)) { … … 2270 2263 } 2271 2264 2272 void HTMLConverter::_processText(DOMCharacterData *text) 2273 { 2274 ASSERT(text); 2275 CharacterData& characterData = *core(text); 2276 NSString *instr = [text data]; 2277 NSString *outstr = instr; 2265 void HTMLConverter::_processText(CharacterData& characterData) 2266 { 2278 2267 NSUInteger textLength = [_attrStr length]; 2279 NSUInteger startOffset = 0;2280 NSUInteger endOffset = [instr length];2281 2268 unichar lastChar = (textLength > 0) ? [[_attrStr string] characterAtIndex:textLength - 1] : '\n'; 2282 BOOL wasSpace = NO;2283 BOOL wasLeading = YES;2284 2269 BOOL suppressLeadingSpace = ((_flags.isSoft && lastChar == ' ') || lastChar == '\n' || lastChar == '\r' || lastChar == '\t' || lastChar == NSParagraphSeparatorCharacter || lastChar == NSLineSeparatorCharacter || lastChar == NSFormFeedCharacter || lastChar == WebNextLineCharacter); 2285 2270 NSRange rangeToReplace = NSMakeRange(textLength, 0); 2286 2271 CFMutableStringRef mutstr = NULL; 2287 2272 2273 String originalString = characterData.data(); 2274 unsigned startOffset = 0; 2275 unsigned endOffset = originalString.length(); 2288 2276 if (_domRange) { 2289 if ( text == [_domRange startContainer]) {2277 if (&characterData == core([_domRange startContainer])) { 2290 2278 startOffset = (NSUInteger)[_domRange startOffset]; 2291 2279 _domRangeStartIndex = [_attrStr length]; 2292 2280 _flags.reachedStart = YES; 2293 2281 } 2294 if ( text == [_domRange endContainer]) {2282 if (&characterData == core([_domRange endContainer])) { 2295 2283 endOffset = (NSUInteger)[_domRange endOffset]; 2296 2284 _flags.reachedEnd = YES; 2297 2285 } 2298 if ((startOffset > 0 || endOffset < [instr length]) && endOffset >= startOffset) { 2299 instr = [instr substringWithRange:NSMakeRange(startOffset, endOffset - startOffset)]; 2300 outstr = instr; 2301 } 2302 } 2303 2286 if ((startOffset > 0 || endOffset < originalString.length()) && endOffset >= startOffset) 2287 originalString = originalString.substring(startOffset, endOffset - startOffset); 2288 } 2289 String outputString = originalString; 2290 2291 // FIXME: Use RenderText's content instead. 2292 bool wasSpace = false; 2304 2293 if (_caches->propertyValueForNode(characterData, CSSPropertyWhiteSpace).startsWith("pre")) { 2305 if (textLength > 0 && [instr length] > 0&& _flags.isSoft) {2306 unichar c = [instr characterAtIndex:0];2294 if (textLength && originalString.length() && _flags.isSoft) { 2295 unichar c = originalString.at(0); 2307 2296 if (c == '\n' || c == '\r' || c == NSParagraphSeparatorCharacter || c == NSLineSeparatorCharacter || c == NSFormFeedCharacter || c == WebNextLineCharacter) 2308 2297 rangeToReplace = NSMakeRange(textLength - 1, 1); 2309 2298 } 2310 2299 } else { 2311 CFStringInlineBuffer inlineBuffer; 2312 const unsigned int TextBufferSize = 255; 2313 2314 unichar buffer[TextBufferSize + 1]; 2315 NSUInteger i, count = [instr length], idx = 0; 2316 2317 mutstr = CFStringCreateMutable(NULL, 0); 2318 CFStringInitInlineBuffer((CFStringRef)instr, &inlineBuffer, CFRangeMake(0, count)); 2319 for (i = 0; i < count; i++) { 2320 unichar c = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, i); 2321 if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == 0xc || c == 0x200b) { 2300 unsigned count = originalString.length(); 2301 bool wasLeading = true; 2302 StringBuilder builder; 2303 for (unsigned i = 0; i < count; i++) { 2304 UChar c = originalString.at(i); 2305 bool isWhitespace = c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == 0xc || c == 0x200b; 2306 if (isWhitespace) 2322 2307 wasSpace = (!wasLeading || !suppressLeadingSpace); 2323 }else {2308 else { 2324 2309 if (wasSpace) 2325 buffer[idx++] = ' '; 2326 buffer[idx++] = c; 2327 if (idx >= TextBufferSize) { 2328 CFStringAppendCharacters(mutstr, buffer, idx); 2329 idx = 0; 2330 } 2331 wasSpace = wasLeading = NO; 2310 builder.append(' '); 2311 builder.append(c); 2312 wasSpace = false; 2313 wasLeading = false; 2332 2314 } 2333 2315 } 2334 2316 if (wasSpace) 2335 buffer[idx++] = ' '; 2336 if (idx > 0) 2337 CFStringAppendCharacters(mutstr, buffer, idx); 2338 outstr = (NSString *)mutstr; 2339 } 2340 if ([outstr length] > 0) { 2317 builder.append(' '); 2318 outputString = builder.toString(); 2319 } 2320 2321 if (outputString.length()) { 2341 2322 String textTransform = _caches->propertyValueForNode(characterData, CSSPropertyTextTransform); 2342 2323 if (textTransform.length()) { 2343 if (textTransform == "capitalize") 2344 outstr = [outstr capitalizedString]; 2345 else if (textTransform == "uppercase") 2346 outstr = [outstr uppercaseString]; 2324 if (textTransform == "capitalize") {// FIXME: This is extremely inefficient. 2325 NSString *temporaryString = outputString; 2326 outputString = [temporaryString capitalizedString]; 2327 } else if (textTransform == "uppercase") 2328 outputString = outputString.upper(); 2347 2329 else if (textTransform == "lowercase") 2348 out str = [outstr lowercaseString];2349 } 2350 2351 [_attrStr replaceCharactersInRange:rangeToReplace withString:out str];2352 rangeToReplace.length = [outstr length];2330 outputString = outputString.lower(); 2331 } 2332 2333 [_attrStr replaceCharactersInRange:rangeToReplace withString:outputString]; 2334 rangeToReplace.length = outputString.length(); 2353 2335 RetainPtr<NSDictionary> attrs; 2354 DOMElement *element = (DOMElement *)text;2355 while ( element) {2336 Node* ancestor = characterData.parentNode(); 2337 while (ancestor) { 2356 2338 // Fill attrs dictionary with attributes from parent nodes, not overwriting ones deeper in the tree 2357 if( [element nodeType] == DOM_ELEMENT_NODE) {2358 RetainPtr<NSMutableDictionary> newAttrs = adoptNS([_attributesForElement( element) mutableCopy]);2339 if(ancestor->isElementNode()) { 2340 RetainPtr<NSMutableDictionary> newAttrs = adoptNS([_attributesForElement(kit(toElement(ancestor))) mutableCopy]); 2359 2341 if (attrs) { 2360 2342 // Already-set attributes (from lower in the tree) overwrite the higher ones. … … 2363 2345 attrs = newAttrs; 2364 2346 } 2365 element = (DOMElement *)[element parentNode];2347 ancestor = ancestor->parentNode(); 2366 2348 } 2367 2349 if (rangeToReplace.length > 0) … … 2373 2355 } 2374 2356 2375 void HTMLConverter::_traverseNode(DOMNode *node, NSInteger depth, BOOL embedded) 2376 { 2377 unsigned short nodeType; 2378 NSArray *childNodes; 2379 NSUInteger count; 2380 NSUInteger startOffset; 2381 NSUInteger endOffset; 2382 BOOL isStart = NO; 2383 BOOL isEnd = NO; 2384 2385 if (_flags.reachedEnd) 2357 void HTMLConverter::_traverseNode(Node* node, unsigned depth, bool embedded) 2358 { 2359 if (!node || _flags.reachedEnd) 2386 2360 return; 2387 if (_domRange && !_flags.reachedStart && _domStartAncestors && ![_domStartAncestors containsObject:node])2361 if (_domRange && !_flags.reachedStart && !_caches->isAncestorsOfStartToBeConverted(node)) 2388 2362 return; 2389 2390 nodeType = [node nodeType]; 2391 childNodes = _childrenForNode(node); 2392 count = [childNodes count]; 2393 startOffset = 0; 2394 endOffset = count; 2395 2363 2364 unsigned startOffset = 0; 2365 unsigned endOffset = UINT_MAX; 2366 bool isStart = false; 2367 bool isEnd = false; 2396 2368 if (_domRange) { 2397 if (node == [_domRange startContainer]) {2369 if (node == core([_domRange startContainer])) { 2398 2370 startOffset = (NSUInteger)[_domRange startOffset]; 2399 isStart = YES;2371 isStart = true; 2400 2372 _flags.reachedStart = YES; 2401 2373 } 2402 if (node == [_domRange endContainer]) {2374 if (node == core([_domRange endContainer])) { 2403 2375 endOffset = (NSUInteger)[_domRange endOffset]; 2404 isEnd = YES; 2405 } 2406 } 2407 2408 if (nodeType == DOM_DOCUMENT_NODE || nodeType == DOM_DOCUMENT_FRAGMENT_NODE) { 2409 for (NSUInteger i = 0; i < count; i++) { 2376 isEnd = true; 2377 } 2378 } 2379 2380 if (node->isDocumentNode() || node->isDocumentFragment()) { 2381 Node* child = node->firstChild(); 2382 for (NSUInteger i = 0; child; i++) { 2410 2383 if (isStart && i == startOffset) 2411 2384 _domRangeStartIndex = [_attrStr length]; 2412 2385 if ((!isStart || startOffset <= i) && (!isEnd || endOffset > i)) 2413 _traverseNode( [childNodes objectAtIndex:i], depth + 1, embedded);2386 _traverseNode(child, depth + 1, embedded); 2414 2387 if (isEnd && i + 1 >= endOffset) 2415 2388 _flags.reachedEnd = YES; 2416 if (_flags.reachedEnd) break; 2417 } 2418 } else if (nodeType == DOM_ELEMENT_NODE) { 2419 DOMElement *element = (DOMElement *)node; 2389 if (_flags.reachedEnd) 2390 break; 2391 child = child->nextSibling(); 2392 } 2393 } else if (node->isElementNode()) { 2394 DOMElement* element = kit(toElement(node)); 2420 2395 if (_enterElement(element, embedded)) { 2421 2396 NSUInteger startIndex = [_attrStr length]; 2422 2397 if (_processElement(element, depth)) { 2423 for (NSUInteger i = 0; i < count; i++) { 2398 Node* child = node->firstChild(); 2399 for (NSUInteger i = 0; child; i++) { 2424 2400 if (isStart && i == startOffset) 2425 2401 _domRangeStartIndex = [_attrStr length]; 2426 2402 if ((!isStart || startOffset <= i) && (!isEnd || endOffset > i)) 2427 _traverseNode( [childNodes objectAtIndex:i], depth + 1, embedded);2403 _traverseNode(child, depth + 1, embedded); 2428 2404 if (isEnd && i + 1 >= endOffset) 2429 2405 _flags.reachedEnd = YES; 2430 2406 if (_flags.reachedEnd) 2431 2407 break; 2408 child = child->nextSibling(); 2432 2409 } 2433 2410 _exitElement(element, depth, startIndex); 2434 2411 } 2435 2412 } 2436 } else if (nodeType == DOM_TEXT_NODE || nodeType == DOM_CDATA_SECTION_NODE) { 2437 _processText((DOMCharacterData *)node); 2438 } 2439 2413 } else if (node->isCharacterDataNode()) 2414 _processText(*toCharacterData(node)); 2415 2440 2416 if (isEnd) 2441 2417 _flags.reachedEnd = YES; … … 2445 2421 { 2446 2422 DOMElement *element = (DOMElement *)node; 2447 NSArray *childNodes = _childrenForNode(node);2448 NSUInteger count = [childNodes count];2449 NSUInteger startOffset = 0;2450 NSUInteger endOffset = count;2451 BOOL isStart = NO;2452 BOOL isEnd = NO;2453 2423 2454 2424 if (_flags.reachedEnd) 2455 2425 return; 2456 if (_domRange && !_flags.reachedStart && _domStartAncestors && ![_domStartAncestors containsObject:node])2426 if (_domRange && !_flags.reachedStart && !_caches->isAncestorsOfStartToBeConverted(core(node))) 2457 2427 return; 2428 2429 unsigned startOffset = 0; 2430 unsigned endOffset = UINT_MAX; 2431 bool isStart = false; 2432 bool isEnd = false; 2458 2433 if (_domRange) { 2459 2434 if (node == [_domRange startContainer]) { 2460 2435 startOffset = (NSUInteger)[_domRange startOffset]; 2461 isStart = YES;2436 isStart = true; 2462 2437 _flags.reachedStart = YES; 2463 2438 } 2464 2439 if (node == [_domRange endContainer]) { 2465 2440 endOffset = (NSUInteger)[_domRange endOffset]; 2466 isEnd = YES;2441 isEnd = true; 2467 2442 } 2468 2443 } … … 2470 2445 NSUInteger startIndex = [_attrStr length]; 2471 2446 if (_processElement(element, depth)) { 2472 for (NSUInteger i = 0; i < count; i++) { 2447 Node* child = core(element)->firstChild(); 2448 for (NSUInteger i = 0; child; i++) { 2473 2449 if (isStart && i == startOffset) 2474 2450 _domRangeStartIndex = [_attrStr length]; 2475 2451 if ((!isStart || startOffset <= i) && (!isEnd || endOffset > i)) 2476 _traverseNode( [childNodes objectAtIndex:i], depth + 1, YES);2452 _traverseNode(child, depth + 1, true /* embedded */); 2477 2453 if (isEnd && i + 1 >= endOffset) 2478 2454 _flags.reachedEnd = YES; 2479 2455 if (_flags.reachedEnd) 2480 2456 break; 2457 child = child->nextSibling(); 2481 2458 } 2482 2459 _exitElement(element, depth, startIndex); … … 2496 2473 } 2497 2474 2475 Node* HTMLConverterCaches::cacheAncestorsOfStartToBeConverted(const Range& range) 2476 { 2477 Node* commonAncestor = range.commonAncestorContainer(ASSERT_NO_EXCEPTION); 2478 Node* ancestor = range.startContainer(); 2479 2480 while (ancestor) { 2481 m_ancestorsUnderCommonAncestor.add(ancestor); 2482 if (ancestor == commonAncestor) 2483 break; 2484 ancestor = ancestor->parentNode(); 2485 } 2486 2487 return commonAncestor; 2488 } 2489 2498 2490 void HTMLConverter::_loadFromDOMRange() 2499 2491 { 2492 ASSERT(_domRange); 2500 2493 if (-1 == _errorCode) { 2501 DOMNode *commonAncestorContainer = [_domRange commonAncestorContainer]; 2502 DOMNode *ancestorContainer = [_domRange startContainer]; 2503 2504 _domStartAncestors = [[NSMutableArray alloc] init]; 2505 while (ancestorContainer) { 2506 [_domStartAncestors addObject:ancestorContainer]; 2507 if (ancestorContainer == commonAncestorContainer) 2508 break; 2509 ancestorContainer = [ancestorContainer parentNode]; 2510 } 2511 2512 _document = [commonAncestorContainer ownerDocument]; 2494 Node* commonAncestorContainer = _caches->cacheAncestorsOfStartToBeConverted(*core(_domRange)); 2495 ASSERT(commonAncestorContainer); 2496 2497 _document = kit(commonAncestorContainer->ownerDocument()); 2513 2498 _dataSource = (DocumentLoader *)core(_document)->frame()->loader().documentLoader(); 2514 2499 … … 2516 2501 _domRangeStartIndex = 0; 2517 2502 _errorCode = 0; 2518 _traverseNode(commonAncestorContainer, 0, NO);2503 _traverseNode(commonAncestorContainer, 0, false /* embedded */); 2519 2504 if (_domRangeStartIndex > 0 && _domRangeStartIndex <= [_attrStr length]) 2520 2505 [_attrStr deleteCharactersInRange:NSMakeRange(0, _domRangeStartIndex)];
Note: See TracChangeset
for help on using the changeset viewer.