1/* 2 * Copyright (C) 2005, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#import "WebNSURLExtras.h" 31 32#import "WebKitNSStringExtras.h" 33#import "WebLocalizableStrings.h" 34#import "WebNSDataExtras.h" 35#import "WebSystemInterface.h" 36#import <Foundation/NSURLRequest.h> 37#import <WebCore/KURL.h> 38#import <WebCore/LoaderNSURLExtras.h> 39#import <WebCore/WebCoreNSURLExtras.h> 40#import <WebKitSystemInterface.h> 41#import <wtf/Assertions.h> 42#import <wtf/ObjcRuntimeExtras.h> 43#import <unicode/uchar.h> 44#import <unicode/uscript.h> 45 46using namespace WebCore; 47using namespace WTF; 48 49#define URL_BYTES_BUFFER_LENGTH 2048 50 51@implementation NSURL (WebNSURLExtras) 52 53+ (NSURL *)_web_URLWithUserTypedString:(NSString *)string relativeToURL:(NSURL *)URL 54{ 55 return URLWithUserTypedString(string, URL); 56} 57 58+ (NSURL *)_web_URLWithUserTypedString:(NSString *)string 59{ 60 return URLWithUserTypedString(string, nil); 61} 62 63+ (NSURL *)_web_URLWithDataAsString:(NSString *)string 64{ 65 if (string == nil) { 66 return nil; 67 } 68 return [self _web_URLWithDataAsString:string relativeToURL:nil]; 69} 70 71+ (NSURL *)_web_URLWithDataAsString:(NSString *)string relativeToURL:(NSURL *)baseURL 72{ 73 if (string == nil) { 74 return nil; 75 } 76 string = [string _webkit_stringByTrimmingWhitespace]; 77 NSData *data = [string dataUsingEncoding:NSISOLatin1StringEncoding]; 78 return URLWithData(data, baseURL); 79} 80 81+ (NSURL *)_web_URLWithData:(NSData *)data 82{ 83 return URLWithData(data, nil); 84} 85 86+ (NSURL *)_web_URLWithData:(NSData *)data relativeToURL:(NSURL *)baseURL 87{ 88 return URLWithData(data, baseURL); 89} 90 91- (NSData *)_web_originalData 92{ 93 return originalURLData(self); 94} 95 96- (NSString *)_web_originalDataAsString 97{ 98 return [[[NSString alloc] initWithData:originalURLData(self) encoding:NSISOLatin1StringEncoding] autorelease]; 99} 100 101- (NSString *)_web_userVisibleString 102{ 103 return userVisibleString(self); 104} 105 106- (BOOL)_web_isEmpty 107{ 108 if (!CFURLGetBaseURL((CFURLRef)self)) 109 return CFURLGetBytes((CFURLRef)self, NULL, 0) == 0; 110 return [originalURLData(self) length] == 0; 111} 112 113- (const char *)_web_URLCString 114{ 115 NSMutableData *data = [NSMutableData data]; 116 [data appendData:originalURLData(self)]; 117 [data appendBytes:"\0" length:1]; 118 return (const char *)[data bytes]; 119 } 120 121- (NSURL *)_webkit_canonicalize 122{ 123 NSURLRequest *request = [[NSURLRequest alloc] initWithURL:self]; 124 Class concreteClass = WKNSURLProtocolClassForRequest(request); 125 if (!concreteClass) { 126 [request release]; 127 return self; 128 } 129 130 // This applies NSURL's concept of canonicalization, but not KURL's concept. It would 131 // make sense to apply both, but when we tried that it caused a performance degradation 132 // (see 5315926). It might make sense to apply only the KURL concept and not the NSURL 133 // concept, but it's too risky to make that change for WebKit 3.0. 134 NSURLRequest *newRequest = [concreteClass canonicalRequestForRequest:request]; 135 NSURL *newURL = [newRequest URL]; 136 NSURL *result = [[newURL retain] autorelease]; 137 [request release]; 138 139 return result; 140} 141 142- (NSURL *)_web_URLByTruncatingOneCharacterBeforeComponent:(CFURLComponentType)component 143{ 144 return URLByTruncatingOneCharacterBeforeComponent(self, component); 145} 146 147- (NSURL *)_webkit_URLByRemovingFragment 148{ 149 return URLByTruncatingOneCharacterBeforeComponent(self, kCFURLComponentFragment); 150} 151 152- (NSURL *)_webkit_URLByRemovingResourceSpecifier 153{ 154 return URLByTruncatingOneCharacterBeforeComponent(self, kCFURLComponentResourceSpecifier); 155} 156 157- (NSURL *)_web_URLByRemovingUserInfo 158{ 159 return URLByRemovingUserInfo(self); 160} 161 162- (BOOL)_webkit_isJavaScriptURL 163{ 164 return [[self _web_originalDataAsString] _webkit_isJavaScriptURL]; 165} 166 167- (NSString *)_webkit_scriptIfJavaScriptURL 168{ 169 return [[self absoluteString] _webkit_scriptIfJavaScriptURL]; 170} 171 172- (BOOL)_webkit_isFileURL 173{ 174 return [[self _web_originalDataAsString] _webkit_isFileURL]; 175} 176 177- (BOOL)_webkit_isFTPDirectoryURL 178{ 179 return [[self _web_originalDataAsString] _webkit_isFTPDirectoryURL]; 180} 181 182- (BOOL)_webkit_shouldLoadAsEmptyDocument 183{ 184 return [[self _web_originalDataAsString] _webkit_hasCaseInsensitivePrefix:@"about:"] || [self _web_isEmpty]; 185} 186 187- (NSURL *)_web_URLWithLowercasedScheme 188{ 189 CFRange range; 190 CFURLGetByteRangeForComponent((CFURLRef)self, kCFURLComponentScheme, &range); 191 if (range.location == kCFNotFound) { 192 return self; 193 } 194 195 UInt8 static_buffer[URL_BYTES_BUFFER_LENGTH]; 196 UInt8 *buffer = static_buffer; 197 CFIndex bytesFilled = CFURLGetBytes((CFURLRef)self, buffer, URL_BYTES_BUFFER_LENGTH); 198 if (bytesFilled == -1) { 199 CFIndex bytesToAllocate = CFURLGetBytes((CFURLRef)self, NULL, 0); 200 buffer = static_cast<UInt8 *>(malloc(bytesToAllocate)); 201 bytesFilled = CFURLGetBytes((CFURLRef)self, buffer, bytesToAllocate); 202 ASSERT(bytesFilled == bytesToAllocate); 203 } 204 205 int i; 206 BOOL changed = NO; 207 for (i = 0; i < range.length; ++i) { 208 char c = buffer[range.location + i]; 209 char lower = toASCIILower(c); 210 if (c != lower) { 211 buffer[range.location + i] = lower; 212 changed = YES; 213 } 214 } 215 216 NSURL *result = changed 217 ? (NSURL *)HardAutorelease(CFURLCreateAbsoluteURLWithBytes(NULL, buffer, bytesFilled, kCFStringEncodingUTF8, nil, YES)) 218 : (NSURL *)self; 219 220 if (buffer != static_buffer) { 221 free(buffer); 222 } 223 224 return result; 225} 226 227 228-(NSData *)_web_schemeSeparatorWithoutColon 229{ 230 NSData *result = nil; 231 CFRange rangeWithSeparators; 232 CFRange range = CFURLGetByteRangeForComponent((CFURLRef)self, kCFURLComponentScheme, &rangeWithSeparators); 233 if (rangeWithSeparators.location != kCFNotFound) { 234 NSString *absoluteString = [self absoluteString]; 235 NSRange separatorsRange = NSMakeRange(range.location + range.length + 1, rangeWithSeparators.length - range.length - 1); 236 if (separatorsRange.location + separatorsRange.length <= [absoluteString length]) { 237 NSString *slashes = [absoluteString substringWithRange:separatorsRange]; 238 result = [slashes dataUsingEncoding:NSISOLatin1StringEncoding]; 239 } 240 } 241 return result; 242} 243 244-(NSData *)_web_dataForURLComponentType:(CFURLComponentType)componentType 245{ 246 return dataForURLComponentType(self, componentType); 247} 248 249-(NSData *)_web_schemeData 250{ 251 return dataForURLComponentType(self, kCFURLComponentScheme); 252} 253 254-(NSData *)_web_hostData 255{ 256 NSData *result = dataForURLComponentType(self, kCFURLComponentHost); 257 NSData *scheme = [self _web_schemeData]; 258 // Take off localhost for file 259 if ([scheme _web_isCaseInsensitiveEqualToCString:"file"]) { 260 return ([result _web_isCaseInsensitiveEqualToCString:"localhost"]) ? nil : result; 261 } 262 return result; 263} 264 265- (NSString *)_web_hostString 266{ 267 NSData *data = [self _web_hostData]; 268 if (!data) { 269 data = [NSData data]; 270 } 271 return [[[NSString alloc] initWithData:[self _web_hostData] encoding:NSUTF8StringEncoding] autorelease]; 272} 273 274- (NSString *)_webkit_suggestedFilenameWithMIMEType:(NSString *)MIMEType 275{ 276 return suggestedFilenameWithMIMEType(self, MIMEType); 277} 278 279- (NSURL *)_webkit_URLFromURLOrSchemelessFileURL 280{ 281 if ([self scheme]) 282 return self; 283 284 return [NSURL URLWithString:[@"file:" stringByAppendingString:[self absoluteString]]]; 285} 286 287@end 288 289@implementation NSString (WebNSURLExtras) 290 291- (BOOL)_web_isUserVisibleURL 292{ 293 return isUserVisibleURL(self); 294} 295 296- (BOOL)_webkit_isJavaScriptURL 297{ 298 return [self _webkit_hasCaseInsensitivePrefix:@"javascript:"]; 299} 300 301- (BOOL)_webkit_isFileURL 302{ 303 return [self rangeOfString:@"file:" options:(NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound; 304} 305 306- (NSString *)_webkit_stringByReplacingValidPercentEscapes 307{ 308 return decodeURLEscapeSequences(self); 309} 310 311- (NSString *)_webkit_scriptIfJavaScriptURL 312{ 313 if (![self _webkit_isJavaScriptURL]) { 314 return nil; 315 } 316 return [[self substringFromIndex:11] _webkit_stringByReplacingValidPercentEscapes]; 317} 318 319- (BOOL)_webkit_isFTPDirectoryURL 320{ 321 int length = [self length]; 322 if (length < 5) { // 5 is length of "ftp:/" 323 return NO; 324 } 325 unichar lastChar = [self characterAtIndex:length - 1]; 326 return lastChar == '/' && [self _webkit_hasCaseInsensitivePrefix:@"ftp:"]; 327} 328 329- (BOOL)_web_hostNameNeedsDecodingWithRange:(NSRange)range 330{ 331 return hostNameNeedsDecodingWithRange(self, range); 332} 333 334- (BOOL)_web_hostNameNeedsEncodingWithRange:(NSRange)range 335{ 336 return hostNameNeedsEncodingWithRange(self, range); 337} 338 339- (NSString *)_web_decodeHostNameWithRange:(NSRange)range 340{ 341 return decodeHostNameWithRange(self, range); 342} 343 344- (NSString *)_web_encodeHostNameWithRange:(NSRange)range 345{ 346 return encodeHostNameWithRange(self, range); 347} 348 349- (NSString *)_web_decodeHostName 350{ 351 return decodeHostName(self); 352} 353 354- (NSString *)_web_encodeHostName 355{ 356 return encodeHostName(self); 357} 358 359-(NSRange)_webkit_rangeOfURLScheme 360{ 361 NSRange colon = [self rangeOfString:@":"]; 362 if (colon.location != NSNotFound && colon.location > 0) { 363 NSRange scheme = {0, colon.location}; 364 static NSCharacterSet *InverseSchemeCharacterSet = nil; 365 if (!InverseSchemeCharacterSet) { 366 /* 367 This stuff is very expensive. 10-15 msec on a 2x1.2GHz. If not cached it swamps 368 everything else when adding items to the autocomplete DB. Makes me wonder if we 369 even need to enforce the character set here. 370 */ 371 NSString *acceptableCharacters = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+.-"; 372 InverseSchemeCharacterSet = [[[NSCharacterSet characterSetWithCharactersInString:acceptableCharacters] invertedSet] retain]; 373 } 374 NSRange illegals = [self rangeOfCharacterFromSet:InverseSchemeCharacterSet options:0 range:scheme]; 375 if (illegals.location == NSNotFound) 376 return scheme; 377 } 378 return NSMakeRange(NSNotFound, 0); 379} 380 381-(BOOL)_webkit_looksLikeAbsoluteURL 382{ 383 // Trim whitespace because _web_URLWithString allows whitespace. 384 return [[self _webkit_stringByTrimmingWhitespace] _webkit_rangeOfURLScheme].location != NSNotFound; 385} 386 387- (NSString *)_webkit_URLFragment 388{ 389 NSRange fragmentRange; 390 391 fragmentRange = [self rangeOfString:@"#" options:NSLiteralSearch]; 392 if (fragmentRange.location == NSNotFound) 393 return nil; 394 return [self substringFromIndex:fragmentRange.location + 1]; 395} 396 397@end 398