📄 webnsurlextras.mm
字号:
static int URLComponentTypeBufferLength = 2048; UInt8 staticAllBytesBuffer[URLComponentTypeBufferLength]; UInt8 *allBytesBuffer = staticAllBytesBuffer; CFIndex bytesFilled = CFURLGetBytes((CFURLRef)self, allBytesBuffer, URLComponentTypeBufferLength); if (bytesFilled == -1) { CFIndex bytesToAllocate = CFURLGetBytes((CFURLRef)self, NULL, 0); allBytesBuffer = static_cast<UInt8 *>(malloc(bytesToAllocate)); bytesFilled = CFURLGetBytes((CFURLRef)self, allBytesBuffer, bytesToAllocate); } CFRange range; if (componentType != completeURL) { range = CFURLGetByteRangeForComponent((CFURLRef)self, componentType, NULL); if (range.location == kCFNotFound) { return nil; } } else { range.location = 0; range.length = bytesFilled; } NSData *componentData = [NSData dataWithBytes:allBytesBuffer + range.location length:range.length]; const unsigned char *bytes = static_cast<const unsigned char *>([componentData bytes]); NSMutableData *resultData = [NSMutableData data]; // NOTE: add leading '?' to query strings non-zero length query strings. // NOTE: retain question-mark only query strings. if (componentType == kCFURLComponentQuery) { if (range.length > 0 || [self _web_hasQuestionMarkOnlyQueryString]) { [resultData appendBytes:"?" length:1]; } } int i; for (i = 0; i < range.length; i++) { unsigned char c = bytes[i]; if (c <= 0x20 || c >= 0x7f) { char escaped[3]; escaped[0] = '%'; escaped[1] = hexDigit(c >> 4); escaped[2] = hexDigit(c & 0xf); [resultData appendBytes:escaped length:3]; } else { char b[1]; b[0] = c; [resultData appendBytes:b length:1]; } } if (staticAllBytesBuffer != allBytesBuffer) { free(allBytesBuffer); } return resultData;}-(NSData *)_web_schemeData{ return [self _web_dataForURLComponentType:kCFURLComponentScheme];}-(NSData *)_web_hostData{ NSData *result = [self _web_dataForURLComponentType:kCFURLComponentHost]; NSData *scheme = [self _web_schemeData]; // Take off localhost for file if ([scheme _web_isCaseInsensitiveEqualToCString:"file"]) { return ([result _web_isCaseInsensitiveEqualToCString:"localhost"]) ? nil : result; } return result;}- (NSString *)_web_hostString{ NSData *data = [self _web_hostData]; if (!data) { data = [NSData data]; } return [[[NSString alloc] initWithData:[self _web_hostData] encoding:NSUTF8StringEncoding] autorelease];}- (NSString *)_webkit_suggestedFilenameWithMIMEType:(NSString *)MIMEType{ return suggestedFilenameWithMIMEType(self, MIMEType);}@end@implementation NSString (WebNSURLExtras)- (BOOL)_web_isUserVisibleURL{ BOOL valid = YES; // get buffer char static_buffer[1024]; const char *p; BOOL success = CFStringGetCString((CFStringRef)self, static_buffer, 1023, kCFStringEncodingUTF8); if (success) { p = static_buffer; } else { p = [self UTF8String]; } int length = strlen(p); // check for characters <= 0x20 or >=0x7f, %-escape sequences of %7f, and xn--, these // are the things that will lead _web_userVisibleString to actually change things. int i; for (i = 0; i < length; i++) { unsigned char c = p[i]; // escape control characters, space, and delete if (c <= 0x20 || c == 0x7f) { valid = NO; break; } else if (c == '%' && (i + 1 < length && isHexDigit(p[i + 1])) && i + 2 < length && isHexDigit(p[i + 2])) { unsigned char u = (hexDigitValue(p[i + 1]) << 4) | hexDigitValue(p[i + 2]); if (u > 0x7f) { valid = NO; break; } i += 2; } else { // Check for "xn--" in an efficient, non-case-sensitive, way. if (c == '-' && i >= 3 && (p[i - 3] | 0x20) == 'x' && (p[i - 2] | 0x20) == 'n' && p[i - 1] == '-') { valid = NO; break; } } } return valid;}- (BOOL)_webkit_isJavaScriptURL{ return [self _webkit_hasCaseInsensitivePrefix:@"javascript:"];}- (BOOL)_webkit_isFileURL{ return [self rangeOfString:@"file:" options:(NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound;}- (NSString *)_webkit_stringByReplacingValidPercentEscapes{ return decodeURLEscapeSequences(self);}- (NSString *)_webkit_scriptIfJavaScriptURL{ if (![self _webkit_isJavaScriptURL]) { return nil; } return [[self substringFromIndex:11] _webkit_stringByReplacingValidPercentEscapes];}- (BOOL)_webkit_isFTPDirectoryURL{ int length = [self length]; if (length < 5) { // 5 is length of "ftp:/" return NO; } unichar lastChar = [self characterAtIndex:length - 1]; return lastChar == '/' && [self _webkit_hasCaseInsensitivePrefix:@"ftp:"];}static BOOL readIDNScriptWhiteListFile(NSString *filename){ if (!filename) { return NO; } FILE *file = fopen([filename fileSystemRepresentation], "r"); if (file == NULL) { return NO; } // Read a word at a time. // Allow comments, starting with # character to the end of the line. while (1) { // Skip a comment if present. int result = fscanf(file, " #%*[^\n\r]%*[\n\r]"); if (result == EOF) { break; } // Read a script name if present. char word[33]; result = fscanf(file, " %32[^# \t\n\r]%*[^# \t\n\r] ", word); if (result == EOF) { break; } if (result == 1) { // Got a word, map to script code and put it into the array. int32_t script = u_getPropertyValueEnum(UCHAR_SCRIPT, word); if (script >= 0 && script < USCRIPT_CODE_LIMIT) { size_t index = script / 32; uint32_t mask = 1 << (script % 32); IDNScriptWhiteList[index] |= mask; } } } fclose(file); return YES;}static void readIDNScriptWhiteList(void){ // Read white list from library. NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, YES); int i, numDirs = [dirs count]; for (i = 0; i < numDirs; i++) { NSString *dir = [dirs objectAtIndex:i]; if (readIDNScriptWhiteListFile([dir stringByAppendingPathComponent:@"IDNScriptWhiteList.txt"])) { return; } } // Fall back on white list inside bundle. NSBundle *bundle = [NSBundle bundleWithIdentifier:@"com.apple.WebKit"]; readIDNScriptWhiteListFile([bundle pathForResource:@"IDNScriptWhiteList" ofType:@"txt"]);}static BOOL allCharactersInIDNScriptWhiteList(const UChar *buffer, int32_t length){ pthread_once(&IDNScriptWhiteListFileRead, readIDNScriptWhiteList); int32_t i = 0; while (i < length) { UChar32 c; U16_NEXT(buffer, i, length, c) UErrorCode error = U_ZERO_ERROR; UScriptCode script = uscript_getScript(c, &error); if (error != U_ZERO_ERROR) { LOG_ERROR("got ICU error while trying to look at scripts: %d", error); return NO; } if (script < 0) { LOG_ERROR("got negative number for script code from ICU: %d", script); return NO; } if (script >= USCRIPT_CODE_LIMIT) { return NO; } size_t index = script / 32; uint32_t mask = 1 << (script % 32); if (!(IDNScriptWhiteList[index] & mask)) { return NO; } if (isLookalikeCharacter(c)) return NO; } return YES;}// Return value of nil means no mapping is necessary.// If makeString is NO, then return value is either nil or self to indicate mapping is necessary.// If makeString is YES, then return value is either nil or the mapped string.- (NSString *)_web_mapHostNameWithRange:(NSRange)range encode:(BOOL)encode makeString:(BOOL)makeString{ if (range.length > HOST_NAME_BUFFER_LENGTH) { return nil; } if ([self length] == 0) return nil; UChar sourceBuffer[HOST_NAME_BUFFER_LENGTH]; UChar destinationBuffer[HOST_NAME_BUFFER_LENGTH]; NSString *string = self; if (encode && [self rangeOfString:@"%" options:NSLiteralSearch range:range].location != NSNotFound) { NSString *substring = [self substringWithRange:range]; substring = WebCFAutorelease(CFURLCreateStringByReplacingPercentEscapes(NULL, (CFStringRef)substring, CFSTR(""))); if (substring != nil) { string = substring; range = NSMakeRange(0, [string length]); } } int length = range.length; [string getCharacters:sourceBuffer range:range]; UErrorCode error = U_ZERO_ERROR; int32_t numCharactersConverted = (encode ? uidna_IDNToASCII : uidna_IDNToUnicode) (sourceBuffer, length, destinationBuffer, HOST_NAME_BUFFER_LENGTH, UIDNA_ALLOW_UNASSIGNED, NULL, &error); if (error != U_ZERO_ERROR) { return nil; } if (numCharactersConverted == length && memcmp(sourceBuffer, destinationBuffer, length * sizeof(UChar)) == 0) { return nil; } if (!encode && !allCharactersInIDNScriptWhiteList(destinationBuffer, numCharactersConverted)) { return nil; } return makeString ? (NSString *)[NSString stringWithCharacters:destinationBuffer length:numCharactersConverted] : (NSString *)self;}- (BOOL)_web_hostNameNeedsDecodingWithRange:(NSRange)range{ return [self _web_mapHostNameWithRange:range encode:NO makeString:NO] != nil;}- (BOOL)_web_hostNameNeedsEncodingWithRange:(NSRange)range{ return [self _web_mapHostNameWithRange:range encode:YES makeString:NO] != nil;}- (NSString *)_web_decodeHostNameWithRange:(NSRange)range{ return [self _web_mapHostNameWithRange:range encode:NO makeString:YES];}- (NSString *)_web_encodeHostNameWithRange:(NSRange)range{ return [self _web_mapHostNameWithRange:range encode:YES makeString:YES];}- (NSString *)_web_decodeHostName{ NSString *name = [self _web_mapHostNameWithRange:NSMakeRange(0, [self length]) encode:NO makeString:YES]; return name == nil ? self : name;}- (NSString *)_web_encodeHostName{ NSString *name = [self _web_mapHostNameWithRange:NSMakeRange(0, [self length]) encode:YES makeString:YES]; return name == nil ? self : name;}-(NSRange)_webkit_rangeOfURLScheme{ NSRange colon = [self rangeOfString:@":"]; if (colon.location != NSNotFound && colon.location > 0) { NSRange scheme = {0, colon.location}; static NSCharacterSet *InverseSchemeCharacterSet = nil; if (!InverseSchemeCharacterSet) { /* This stuff is very expensive. 10-15 msec on a 2x1.2GHz. If not cached it swamps everything else when adding items to the autocomplete DB. Makes me wonder if we even need to enforce the character set here. */ NSString *acceptableCharacters = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+.-"; InverseSchemeCharacterSet = [[[NSCharacterSet characterSetWithCharactersInString:acceptableCharacters] invertedSet] retain]; } NSRange illegals = [self rangeOfCharacterFromSet:InverseSchemeCharacterSet options:0 range:scheme]; if (illegals.location == NSNotFound) return scheme; } return NSMakeRange(NSNotFound, 0);}-(BOOL)_webkit_looksLikeAbsoluteURL{ // Trim whitespace because _web_URLWithString allows whitespace. return [[self _webkit_stringByTrimmingWhitespace] _webkit_rangeOfURLScheme].location != NSNotFound;}- (NSString *)_webkit_URLFragment{ NSRange fragmentRange; fragmentRange = [self rangeOfString:@"#" options:NSLiteralSearch]; if (fragmentRange.location == NSNotFound) return nil; return [self substringFromIndex:fragmentRange.location + 1];}@end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -