4 Copyright 2002, Apple, Inc. All rights reserved.
7 #import <WebKit/WebNSURLExtras.h>
9 #import <WebKit/WebAssertions.h>
10 #import <WebKit/WebNSDataExtras.h>
11 #import <WebKit/WebNSObjectExtras.h>
13 #import <Foundation/NSString_NSURLExtras.h>
14 #import <Foundation/NSURLProtocolPrivate.h>
15 #import <Foundation/NSURLRequest.h>
16 #import <Foundation/NSURL_NSURLExtras.h>
18 #import <unicode/uidna.h>
20 typedef void (* StringRangeApplierFunction)(NSString *string, NSRange range, void *context);
22 // Needs to be big enough to hold an IDN-encoded name.
23 // For host names bigger than this, we won't do IDN encoding, which is almost certainly OK.
24 #define HOST_NAME_BUFFER_LENGTH 2048
26 #define URL_BYTES_BUFFER_LENGTH 2048
28 static char hexDigit(int i)
30 if (i < 0 || i > 16) {
31 ERROR("illegal hex digit");
44 static BOOL isHexDigit(char c)
46 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
49 static int hexDigitValue(char c)
51 if (c >= '0' && c <= '9') {
54 if (c >= 'A' && c <= 'F') {
57 if (c >= 'a' && c <= 'f') {
60 ERROR("illegal hex digit");
64 static void applyHostNameFunctionToMailToURLString(NSString *string, StringRangeApplierFunction f, void *context)
66 // In a mailto: URL, host names come after a '@' character and end with a '>' or ',' or '?' character.
67 // Skip quoted strings so that characters in them don't confuse us.
68 // When we find a '?' character, we are past the part of the URL that contains host names.
70 static NSCharacterSet *hostNameOrStringStartCharacters;
71 if (hostNameOrStringStartCharacters == nil) {
72 hostNameOrStringStartCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"\"@?"] retain];
74 static NSCharacterSet *hostNameEndCharacters;
75 if (hostNameEndCharacters == nil) {
76 hostNameEndCharacters = [[NSCharacterSet characterSetWithCharactersInString:@">,?"] retain];
78 static NSCharacterSet *quotedStringCharacters;
79 if (quotedStringCharacters == nil) {
80 quotedStringCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"\"\\"] retain];
83 unsigned stringLength = [string length];
84 NSRange remaining = NSMakeRange(0, stringLength);
87 // Find start of host name or of quoted string.
88 NSRange hostNameOrStringStart = [string rangeOfCharacterFromSet:hostNameOrStringStartCharacters options:0 range:remaining];
89 if (hostNameOrStringStart.location == NSNotFound) {
92 unichar c = [string characterAtIndex:hostNameOrStringStart.location];
93 remaining.location = NSMaxRange(hostNameOrStringStart);
94 remaining.length = stringLength - remaining.location;
101 // Find end of host name.
102 unsigned hostNameStart = remaining.location;
103 NSRange hostNameEnd = [string rangeOfCharacterFromSet:hostNameEndCharacters options:0 range:remaining];
105 if (hostNameEnd.location == NSNotFound) {
106 hostNameEnd.location = stringLength;
109 remaining.location = hostNameEnd.location;
110 remaining.length = stringLength - remaining.location;
114 // Process host name range.
115 f(string, NSMakeRange(hostNameStart, hostNameEnd.location - hostNameStart), context);
121 // Skip quoted string.
124 NSRange escapedCharacterOrStringEnd = [string rangeOfCharacterFromSet:quotedStringCharacters options:0 range:remaining];
125 if (escapedCharacterOrStringEnd.location == NSNotFound) {
128 c = [string characterAtIndex:escapedCharacterOrStringEnd.location];
129 remaining.location = NSMaxRange(escapedCharacterOrStringEnd);
130 remaining.length = stringLength - remaining.location;
132 // If we are the end of the string, then break from the string loop back to the host name loop.
137 // Skip escaped character.
139 if (remaining.length == 0) {
142 remaining.location += 1;
143 remaining.length -= 1;
149 static void applyHostNameFunctionToURLString(NSString *string, StringRangeApplierFunction f, void *context)
151 // Find hostnames. Too bad we can't use any real URL-parsing code to do this,
152 // but we have to do it before doing all the %-escaping, and this is the only
153 // code we have that parses mailto URLs anyway.
155 // Maybe we should implement this using a character buffer instead?
157 if ([string _web_hasCaseInsensitivePrefix:@"mailto:"]) {
158 applyHostNameFunctionToMailToURLString(string, f, context);
162 // Find the host name in a hierarchical URL.
163 // It comes after a "://" sequence, with scheme characters preceding.
164 // If ends with the end of the string or a ":", "/", or a "?".
165 // If there is a "@" character, the host part is just the part after the "@".
166 NSRange separatorRange = [string rangeOfString:@"://"];
167 if (separatorRange.location == NSNotFound) {
171 // Check that all characters before the :// are valid scheme characters.
172 static NSCharacterSet *nonSchemeCharacters;
173 if (nonSchemeCharacters == nil) {
174 nonSchemeCharacters = [[[NSCharacterSet characterSetWithCharactersInString:@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-."] invertedSet] retain];
176 if ([string rangeOfCharacterFromSet:nonSchemeCharacters options:0 range:NSMakeRange(0, separatorRange.location)].location != NSNotFound) {
180 unsigned stringLength = [string length];
182 static NSCharacterSet *hostTerminators;
183 if (hostTerminators == nil) {
184 hostTerminators = [[NSCharacterSet characterSetWithCharactersInString:@":/?#"] retain];
187 // Start after the separator.
188 unsigned authorityStart = NSMaxRange(separatorRange);
190 // Find terminating character.
191 NSRange hostNameTerminator = [string rangeOfCharacterFromSet:hostTerminators options:0 range:NSMakeRange(authorityStart, stringLength - authorityStart)];
192 unsigned hostNameEnd = hostNameTerminator.location == NSNotFound ? stringLength : hostNameTerminator.location;
194 // Find "@" for the start of the host name.
195 NSRange userInfoTerminator = [string rangeOfString:@"@" options:0 range:NSMakeRange(authorityStart, hostNameEnd - authorityStart)];
196 unsigned hostNameStart = userInfoTerminator.location == NSNotFound ? authorityStart : NSMaxRange(userInfoTerminator);
198 f(string, NSMakeRange(hostNameStart, hostNameEnd - hostNameStart), context);
201 @implementation NSURL (WebNSURLExtras)
203 static void collectRangesThatNeedMapping(NSString *string, NSRange range, void *context, BOOL encode)
205 BOOL needsMapping = encode
206 ? [string _web_hostNameNeedsEncodingWithRange:range]
207 : [string _web_hostNameNeedsDecodingWithRange:range];
212 NSMutableArray **array = (NSMutableArray **)context;
214 *array = [[NSMutableArray alloc] init];
217 [*array addObject:[NSValue valueWithRange:range]];
220 static void collectRangesThatNeedEncoding(NSString *string, NSRange range, void *context)
222 return collectRangesThatNeedMapping(string, range, context, YES);
225 static void collectRangesThatNeedDecoding(NSString *string, NSRange range, void *context)
227 return collectRangesThatNeedMapping(string, range, context, NO);
230 static NSString *mapHostNames(NSString *string, BOOL encode)
232 // Generally, we want to optimize for the case where there is one host name that does not need mapping.
234 if (encode && [string canBeConvertedToEncoding:NSASCIIStringEncoding])
237 // Make a list of ranges that actually need mapping.
238 NSMutableArray *hostNameRanges = nil;
239 StringRangeApplierFunction f = encode
240 ? collectRangesThatNeedEncoding
241 : collectRangesThatNeedDecoding;
242 applyHostNameFunctionToURLString(string, f, &hostNameRanges);
243 if (hostNameRanges == nil) {
248 NSMutableString *mutableCopy = [string mutableCopy];
249 unsigned i = [hostNameRanges count];
251 NSRange hostNameRange = [[hostNameRanges objectAtIndex:i] rangeValue];
252 NSString *mappedHostName = encode
253 ? [string _web_encodeHostNameWithRange:hostNameRange]
254 : [string _web_decodeHostNameWithRange:hostNameRange];
255 [mutableCopy replaceCharactersInRange:hostNameRange withString:mappedHostName];
257 [hostNameRanges release];
258 return [mutableCopy autorelease];
261 + (NSURL *)_web_URLWithUserTypedString:(NSString *)string
266 string = mapHostNames([string _web_stringByTrimmingWhitespace], YES);
268 NSData *userTypedData = [string dataUsingEncoding:NSUTF8StringEncoding];
269 ASSERT(userTypedData);
271 const UInt8 *inBytes = [userTypedData bytes];
272 int inLength = [userTypedData length];
274 return [NSURL URLWithString:@""];
277 char *outBytes = malloc(inLength * 3); // large enough to %-escape every character
281 for (i = 0; i < inLength; i++) {
282 UInt8 c = inBytes[i];
283 if (c <= 0x20 || c >= 0x7f) {
285 *p++ = hexDigit(c >> 4);
286 *p++ = hexDigit(c & 0xf);
295 NSData *data = [NSData dataWithBytesNoCopy:outBytes length:outLength]; // adopts outBytes
296 return [self _web_URLWithData:data relativeToURL:nil];
299 + (NSURL *)_web_URLWithDataAsString:(NSString *)string
304 return [self _web_URLWithDataAsString:string relativeToURL:nil];
307 + (NSURL *)_web_URLWithDataAsString:(NSString *)string relativeToURL:(NSURL *)baseURL
312 string = [string _web_stringByTrimmingWhitespace];
313 NSData *data = [string dataUsingEncoding:NSISOLatin1StringEncoding];
314 return [self _web_URLWithData:data relativeToURL:baseURL];
317 + (NSURL *)_web_URLWithData:(NSData *)data
322 return [self _web_URLWithData:data relativeToURL:nil];
325 + (NSURL *)_web_URLWithData:(NSData *)data relativeToURL:(NSURL *)baseURL
332 int length = [data length];
334 const UInt8 *bytes = [data bytes];
335 // NOTE: We use UTF-8 here since this encoding is used when computing strings when returning URL components
336 // (e.g calls to NSURL -path). However, this function is not tolerant of illegal UTF-8 sequences, which
337 // could either be a malformed string or bytes in a different encoding, like shift-jis, so we fall back
338 // onto using ISO Latin 1 in those cases.
339 result = WebNSRetainCFRelease(CFURLCreateAbsoluteURLWithBytes(NULL, bytes, length, kCFStringEncodingUTF8, (CFURLRef)baseURL, YES));
341 result = WebNSRetainCFRelease(CFURLCreateAbsoluteURLWithBytes(NULL, bytes, length, kCFStringEncodingISOLatin1, (CFURLRef)baseURL, YES));
343 [result autorelease];
346 result = [NSURL URLWithString:@""];
351 - (NSData *)_web_originalData
355 UInt8 static_buffer[URL_BYTES_BUFFER_LENGTH];
356 CFIndex bytesFilled = CFURLGetBytes((CFURLRef)self, static_buffer, URL_BYTES_BUFFER_LENGTH);
357 if (bytesFilled != -1) {
358 data = [NSData dataWithBytes:static_buffer length:bytesFilled];
361 CFIndex bytesToAllocate = CFURLGetBytes((CFURLRef)self, NULL, 0);
362 UInt8 *buffer = malloc(bytesToAllocate);
363 bytesFilled = CFURLGetBytes((CFURLRef)self, buffer, bytesToAllocate);
364 ASSERT(bytesFilled == bytesToAllocate);
365 // buffer is adopted by the NSData
366 data = [NSData dataWithBytesNoCopy:buffer length:bytesFilled];
369 NSURL *baseURL = (NSURL *)CFURLGetBaseURL((CFURLRef)self);
371 NSURL *URL = [NSURL _web_URLWithData:data relativeToURL:baseURL];
372 return [URL _web_originalData];
379 - (NSString *)_web_originalDataAsString
381 return [[[NSString alloc] initWithData:[self _web_originalData] encoding:NSISOLatin1StringEncoding] autorelease];
384 - (NSString *)_web_userVisibleString
386 NSData *data = [self _web_originalData];
387 const unsigned char *before = [data bytes];
388 int length = [data length];
390 bool needsHostNameDecoding = false;
392 const unsigned char *p = before;
393 int bufferLength = (length * 3) + 1;
394 char *after = malloc(bufferLength); // large enough to %-escape every character
397 for (i = 0; i < length; i++) {
398 unsigned char c = p[i];
399 // escape control characters, space, and delete
400 if (c <= 0x20 || c == 0x7f) {
402 *q++ = hexDigit(c >> 4);
403 *q++ = hexDigit(c & 0xf);
405 // unescape escape sequences that indicate bytes greater than 0x7f
406 else if (c == '%' && (i + 1 < length && isHexDigit(p[i + 1])) && i + 2 < length && isHexDigit(p[i + 2])) {
407 unsigned char u = (hexDigitValue(p[i + 1]) << 4) | hexDigitValue(p[i + 2]);
423 // Check for "xn--" in an efficient, non-case-sensitive, way.
424 if (c == '-' && i >= 3 && !needsHostNameDecoding && (q[-4] | 0x20) == 'x' && (q[-3] | 0x20) == 'n' && q[-2] == '-')
425 needsHostNameDecoding = true;
430 // Check string to see if it can be converted to display using UTF-8
431 NSString *result = [NSString stringWithUTF8String:after];
433 // Could not convert to UTF-8.
434 // Convert characters greater than 0x7f to escape sequences.
435 // Shift current string to the end of the buffer
436 // then we will copy back bytes to the start of the buffer
438 int afterlength = q - after;
439 char *p = after + bufferLength - afterlength - 1;
440 memmove(p, after, afterlength + 1); // copies trailing '\0'
443 unsigned char c = *p;
446 *q++ = hexDigit(c >> 4);
447 *q++ = hexDigit(c & 0xf);
455 result = [NSString stringWithUTF8String:after];
460 // As an optimization, only do host name decoding if we have "xn--" somewhere.
461 return needsHostNameDecoding ? mapHostNames(result, NO) : result;
467 if (!CFURLGetBaseURL((CFURLRef)self)) {
468 length = CFURLGetBytes((CFURLRef)self, NULL, 0);
471 length = [[self _web_userVisibleString] length];
476 - (const char *)_web_URLCString
478 NSMutableData *data = [NSMutableData data];
479 [data appendData:[self _web_originalData]];
480 [data appendBytes:"\0" length:1];
481 return (const char *)[data bytes];
484 - (NSURL *)_webkit_canonicalize
486 NSURLRequest *request = [[NSURLRequest alloc] initWithURL:self];
487 Class concreteClass = [NSURLProtocol _protocolClassForRequest:request];
488 if (!concreteClass) {
494 NSURLRequest *newRequest = [concreteClass canonicalRequestForRequest:request];
495 NSURL *newURL = [newRequest URL];
496 result = [[newURL retain] autorelease];
502 - (NSURL *)_webkit_URLByRemovingFragment
504 // Check to see if a fragment exists before decomposing the URL.
505 CFStringRef frag = CFURLCopyFragment((CFURLRef)self, NULL);
511 WebURLComponents components = [self _web_URLComponents];
512 components.fragment = nil;
513 NSURL *result = [NSURL _web_URLWithComponents:components];
514 return result ? result : self;
517 - (BOOL)_webkit_isJavaScriptURL
519 return [[self _web_originalDataAsString] _webkit_isJavaScriptURL];
522 - (NSString *)_webkit_scriptIfJavaScriptURL
524 return [[self _web_originalDataAsString] _webkit_scriptIfJavaScriptURL];
527 - (BOOL)_webkit_isFTPDirectoryURL
529 return [[self _web_originalDataAsString] _webkit_isFTPDirectoryURL];
532 - (BOOL)_webkit_shouldLoadAsEmptyDocument
534 return [[self _web_originalDataAsString] _web_hasCaseInsensitivePrefix:@"about:"] || [self _web_isEmpty];
537 - (NSURL *)_web_URLWithLowercasedScheme
540 CFURLGetByteRangeForComponent((CFURLRef)self, kCFURLComponentScheme, &range);
541 if (range.location == kCFNotFound) {
545 UInt8 static_buffer[URL_BYTES_BUFFER_LENGTH];
546 UInt8 *buffer = static_buffer;
547 CFIndex bytesFilled = CFURLGetBytes((CFURLRef)self, buffer, URL_BYTES_BUFFER_LENGTH);
548 if (bytesFilled == -1) {
549 CFIndex bytesToAllocate = CFURLGetBytes((CFURLRef)self, NULL, 0);
550 buffer = malloc(bytesToAllocate);
551 bytesFilled = CFURLGetBytes((CFURLRef)self, buffer, bytesToAllocate);
552 ASSERT(bytesFilled == bytesToAllocate);
557 for (i = 0; i < range.length; ++i) {
558 UInt8 c = buffer[range.location + i];
559 UInt8 lower = tolower(c);
561 buffer[range.location + i] = lower;
566 NSURL *result = changed
567 ? [WebNSRetainCFRelease(CFURLCreateAbsoluteURLWithBytes(NULL, buffer, bytesFilled, kCFStringEncodingUTF8, nil, YES)) autorelease]
570 if (buffer != static_buffer) {
578 -(BOOL)_web_hasQuestionMarkOnlyQueryString
580 CFRange rangeWithSeparators;
581 CFURLGetByteRangeForComponent((CFURLRef)self, kCFURLComponentQuery, &rangeWithSeparators);
582 if (rangeWithSeparators.location != kCFNotFound && rangeWithSeparators.length == 1) {
588 -(NSData *)_web_schemeSeparatorWithoutColon
590 NSData *result = nil;
591 CFRange rangeWithSeparators;
592 CFRange range = CFURLGetByteRangeForComponent((CFURLRef)self, kCFURLComponentScheme, &rangeWithSeparators);
593 if (rangeWithSeparators.location != kCFNotFound) {
594 NSString *absoluteString = [self absoluteString];
595 NSRange separatorsRange = NSMakeRange(range.location + range.length + 1, rangeWithSeparators.length - range.length - 1);
596 if (separatorsRange.location + separatorsRange.length <= [absoluteString length]) {
597 NSString *slashes = [absoluteString substringWithRange:separatorsRange];
598 result = [slashes dataUsingEncoding:NSISOLatin1StringEncoding];
604 #define completeURL (CFURLComponentType)-1
606 -(NSData *)_web_dataForURLComponentType:(CFURLComponentType)componentType
608 static int URLComponentTypeBufferLength = 2048;
610 UInt8 staticAllBytesBuffer[URLComponentTypeBufferLength];
611 UInt8 *allBytesBuffer = staticAllBytesBuffer;
613 CFIndex bytesFilled = CFURLGetBytes((CFURLRef)self, allBytesBuffer, URLComponentTypeBufferLength);
614 if (bytesFilled == -1) {
615 CFIndex bytesToAllocate = CFURLGetBytes((CFURLRef)self, NULL, 0);
616 allBytesBuffer = malloc(bytesToAllocate);
617 bytesFilled = CFURLGetBytes((CFURLRef)self, allBytesBuffer, bytesToAllocate);
621 if (componentType != completeURL) {
622 range = CFURLGetByteRangeForComponent((CFURLRef)self, componentType, NULL);
623 if (range.location == kCFNotFound) {
629 range.length = bytesFilled;
632 NSData *componentData = [NSData dataWithBytes:allBytesBuffer + range.location length:range.length];
634 const unsigned char *bytes = [componentData bytes];
635 NSMutableData *resultData = [NSMutableData data];
636 // NOTE: add leading '?' to query strings non-zero length query strings.
637 // NOTE: retain question-mark only query strings.
638 if (componentType == kCFURLComponentQuery) {
639 if (range.length > 0 || [self _web_hasQuestionMarkOnlyQueryString]) {
640 [resultData appendBytes:"?" length:1];
644 for (i = 0; i < range.length; i++) {
645 unsigned char c = bytes[i];
646 if (c <= 0x20 || c >= 0x7f) {
649 escaped[1] = hexDigit(c >> 4);
650 escaped[2] = hexDigit(c & 0xf);
651 [resultData appendBytes:escaped length:3];
656 [resultData appendBytes:b length:1];
660 if (staticAllBytesBuffer != allBytesBuffer) {
661 free(allBytesBuffer);
667 -(NSData *)_web_schemeData
669 return [self _web_dataForURLComponentType:kCFURLComponentScheme];
672 -(NSData *)_web_hostData
674 NSData *result = [self _web_dataForURLComponentType:kCFURLComponentHost];
675 NSData *scheme = [self _web_schemeData];
676 // Take off localhost for file
677 if ([scheme _web_isCaseInsensitiveEqualToCString:"file"]) {
678 return ([result _web_isCaseInsensitiveEqualToCString:"localhost"]) ? nil : result;
683 - (NSString *)_web_hostString
685 NSData *data = [self _web_hostData];
687 data = [NSData data];
689 return [[[NSString alloc] initWithData:[self _web_hostData] encoding:NSUTF8StringEncoding] autorelease];
694 @implementation NSString (WebNSURLExtras)
696 - (BOOL)_web_isUserVisibleURL
701 char static_buffer[1024];
703 BOOL success = CFStringGetCString((CFStringRef)self, static_buffer, 1023, kCFStringEncodingUTF8);
707 p = [self UTF8String];
710 int length = strlen(p);
712 // check for characters <= 0x20 or >=0x7f, %-escape sequences of %7f, and xn--, these
713 // are the things that will lead _web_userVisisbleString to actually change things.
715 for (i = 0; i < length; i++) {
716 unsigned char c = p[i];
717 // escape control characters, space, and delete
718 if (c <= 0x20 || c == 0x7f) {
721 } else if (c == '%' && (i + 1 < length && isHexDigit(p[i + 1])) && i + 2 < length && isHexDigit(p[i + 2])) {
722 unsigned char u = (hexDigitValue(p[i + 1]) << 4) | hexDigitValue(p[i + 2]);
729 // Check for "xn--" in an efficient, non-case-sensitive, way.
730 if (c == '-' && i >= 3 && (p[-3] | 0x20) == 'x' && (p[-2] | 0x20) == 'n' && p[-1] == '-') {
741 - (BOOL)_webkit_isJavaScriptURL
743 return [self _web_hasCaseInsensitivePrefix:@"javascript:"];
746 - (NSString *)_webkit_stringByReplacingValidPercentEscapes
748 NSString *s = [self stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
752 - (NSString *)_webkit_scriptIfJavaScriptURL
754 if (![self _webkit_isJavaScriptURL]) {
757 return [[self substringFromIndex:11] _webkit_stringByReplacingValidPercentEscapes];
760 - (BOOL)_webkit_isFTPDirectoryURL
762 int length = [self length];
763 if (length < 5) { // 5 is length of "ftp:/"
766 unichar lastChar = [self characterAtIndex:length - 1];
767 return lastChar == '/' && [self _web_hasCaseInsensitivePrefix:@"ftp:"];
770 // Return value of nil means no mapping is necessary.
771 // If makeString is NO, then return value is either nil or self to indicate mapping is necessary.
772 // If makeString is YES, then return value is either nil or the mapped string.
773 - (NSString *)_web_mapHostNameWithRange:(NSRange)range encode:(BOOL)encode makeString:(BOOL)makeString
775 if (range.length > HOST_NAME_BUFFER_LENGTH) {
779 if ([self length] == 0)
782 UChar sourceBuffer[HOST_NAME_BUFFER_LENGTH];
783 UChar destinationBuffer[HOST_NAME_BUFFER_LENGTH];
785 NSString *string = self;
786 if (encode && [self rangeOfString:@"%" options:NSLiteralSearch range:range].location != NSNotFound) {
787 NSString *substring = [self substringWithRange:range];
788 substring = (NSString *)CFURLCreateStringByReplacingPercentEscapes(NULL, (CFStringRef)substring, CFSTR(""));
789 if (substring != nil) {
790 string = [[substring retain] autorelease];
791 CFRelease(substring);
792 range = NSMakeRange(0, [string length]);
796 int length = range.length;
797 [string getCharacters:sourceBuffer range:range];
799 UErrorCode error = U_ZERO_ERROR;
800 int32_t numCharactersConverted = (encode ? uidna_IDNToASCII : uidna_IDNToUnicode)
801 (sourceBuffer, length, destinationBuffer, HOST_NAME_BUFFER_LENGTH, UIDNA_ALLOW_UNASSIGNED, NULL, &error);
802 if (error != U_ZERO_ERROR) {
805 if (numCharactersConverted == length && memcmp(sourceBuffer, destinationBuffer, length * sizeof(UChar)) == 0) {
808 return makeString ? [NSString stringWithCharacters:destinationBuffer length:numCharactersConverted] : self;
811 - (BOOL)_web_hostNameNeedsDecodingWithRange:(NSRange)range
813 return [self _web_mapHostNameWithRange:range encode:NO makeString:NO] != nil;
816 - (BOOL)_web_hostNameNeedsEncodingWithRange:(NSRange)range
818 return [self _web_mapHostNameWithRange:range encode:YES makeString:NO] != nil;
821 - (NSString *)_web_decodeHostNameWithRange:(NSRange)range
823 return [self _web_mapHostNameWithRange:range encode:NO makeString:YES];
826 - (NSString *)_web_encodeHostNameWithRange:(NSRange)range
828 return [self _web_mapHostNameWithRange:range encode:YES makeString:YES];
831 - (NSString *)_web_decodeHostName
833 NSString *name = [self _web_mapHostNameWithRange:NSMakeRange(0, [self length]) encode:NO makeString:YES];
834 return name == nil ? self : name;
837 - (NSString *)_web_encodeHostName
839 NSString *name = [self _web_mapHostNameWithRange:NSMakeRange(0, [self length]) encode:YES makeString:YES];
840 return name == nil ? self : name;