07710150f4539cb00e6b9ca9eaa751a05f08f40b
[WebKit-https.git] / Source / WebKit / mac / Misc / WebNSURLExtras.mm
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/URL.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
46 using namespace WebCore;
47 using 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 URL'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 URL 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         ? CFBridgingRelease(CFURLCreateAbsoluteURLWithBytes(NULL, buffer, bytesFilled, kCFStringEncodingUTF8, nil, YES))
218         : 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     return [[[NSString alloc] initWithData:[self _web_hostData] encoding:NSUTF8StringEncoding] autorelease];
268 }
269
270 - (NSString *)_webkit_suggestedFilenameWithMIMEType:(NSString *)MIMEType
271 {
272     return suggestedFilenameWithMIMEType(self, MIMEType);
273 }
274
275 - (NSURL *)_webkit_URLFromURLOrSchemelessFileURL
276 {
277     if ([self scheme])
278         return self;
279
280     return [NSURL URLWithString:[@"file:" stringByAppendingString:[self absoluteString]]];
281 }
282
283 @end
284
285 @implementation NSString (WebNSURLExtras)
286
287 - (BOOL)_web_isUserVisibleURL
288 {
289     return isUserVisibleURL(self);
290 }
291
292 - (BOOL)_webkit_isJavaScriptURL
293 {
294     return [self _webkit_hasCaseInsensitivePrefix:@"javascript:"];
295 }
296
297 - (BOOL)_webkit_isFileURL
298 {
299     return [self rangeOfString:@"file:" options:(NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound;
300 }
301
302 - (NSString *)_webkit_stringByReplacingValidPercentEscapes
303 {
304     return decodeURLEscapeSequences(self);
305 }
306
307 - (NSString *)_webkit_scriptIfJavaScriptURL
308 {
309     if (![self _webkit_isJavaScriptURL]) {
310         return nil;
311     }
312     return [[self substringFromIndex:11] _webkit_stringByReplacingValidPercentEscapes];
313 }
314
315 - (BOOL)_webkit_isFTPDirectoryURL
316 {
317     int length = [self length];
318     if (length < 5) {  // 5 is length of "ftp:/"
319         return NO;
320     }
321     unichar lastChar = [self characterAtIndex:length - 1];
322     return lastChar == '/' && [self _webkit_hasCaseInsensitivePrefix:@"ftp:"];
323 }
324
325 - (BOOL)_web_hostNameNeedsDecodingWithRange:(NSRange)range
326 {
327     return hostNameNeedsDecodingWithRange(self, range);
328 }
329
330 - (BOOL)_web_hostNameNeedsEncodingWithRange:(NSRange)range
331 {
332     return hostNameNeedsEncodingWithRange(self, range);
333 }
334
335 - (NSString *)_web_decodeHostNameWithRange:(NSRange)range
336 {
337     return decodeHostNameWithRange(self, range);
338 }
339
340 - (NSString *)_web_encodeHostNameWithRange:(NSRange)range
341 {
342     return encodeHostNameWithRange(self, range);
343 }
344
345 - (NSString *)_web_decodeHostName
346 {
347     return decodeHostName(self);
348 }
349
350 - (NSString *)_web_encodeHostName
351 {
352     return encodeHostName(self);
353 }
354
355 -(NSRange)_webkit_rangeOfURLScheme
356 {
357     NSRange colon = [self rangeOfString:@":"];
358     if (colon.location != NSNotFound && colon.location > 0) {
359         NSRange scheme = {0, colon.location};
360         static NSCharacterSet *InverseSchemeCharacterSet = nil;
361         if (!InverseSchemeCharacterSet) {
362             /*
363              This stuff is very expensive.  10-15 msec on a 2x1.2GHz.  If not cached it swamps
364              everything else when adding items to the autocomplete DB.  Makes me wonder if we
365              even need to enforce the character set here.
366             */
367             NSString *acceptableCharacters = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+.-";
368             InverseSchemeCharacterSet = [[[NSCharacterSet characterSetWithCharactersInString:acceptableCharacters] invertedSet] retain];
369         }
370         NSRange illegals = [self rangeOfCharacterFromSet:InverseSchemeCharacterSet options:0 range:scheme];
371         if (illegals.location == NSNotFound)
372             return scheme;
373     }
374     return NSMakeRange(NSNotFound, 0);
375 }
376
377 -(BOOL)_webkit_looksLikeAbsoluteURL
378 {
379     // Trim whitespace because _web_URLWithString allows whitespace.
380     return [[self _webkit_stringByTrimmingWhitespace] _webkit_rangeOfURLScheme].location != NSNotFound;
381 }
382
383 - (NSString *)_webkit_URLFragment
384 {
385     NSRange fragmentRange;
386     
387     fragmentRange = [self rangeOfString:@"#" options:NSLiteralSearch];
388     if (fragmentRange.location == NSNotFound)
389         return nil;
390     return [self substringFromIndex:fragmentRange.location + 1];
391 }
392
393 @end