diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:52 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:52 -0800 |
commit | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch) | |
tree | 11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebKit/mac/Misc/WebNSDataExtras.m | |
parent | 648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff) | |
download | external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebKit/mac/Misc/WebNSDataExtras.m')
-rw-r--r-- | WebKit/mac/Misc/WebNSDataExtras.m | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/WebKit/mac/Misc/WebNSDataExtras.m b/WebKit/mac/Misc/WebNSDataExtras.m new file mode 100644 index 0000000..0bd4555 --- /dev/null +++ b/WebKit/mac/Misc/WebNSDataExtras.m @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <WebKit/WebNSDataExtras.h> +#import <WebKit/WebNSDataExtrasPrivate.h> + +#import <wtf/Assertions.h> + +@interface NSString (WebNSDataExtrasInternal) +- (NSString *)_web_capitalizeRFC822HeaderFieldName; +@end + +@implementation NSString (WebNSDataExtrasInternal) + +-(NSString *)_web_capitalizeRFC822HeaderFieldName +{ + CFStringRef name = (CFStringRef)self; + NSString *result = nil; + + CFIndex i; + CFIndex len = CFStringGetLength(name); + char *charPtr = NULL; + UniChar *uniCharPtr = NULL; + Boolean useUniCharPtr = FALSE; + Boolean shouldCapitalize = TRUE; + Boolean somethingChanged = FALSE; + + for (i = 0; i < len; i ++) { + UniChar ch = CFStringGetCharacterAtIndex(name, i); + Boolean replace = FALSE; + if (shouldCapitalize && ch >= 'a' && ch <= 'z') { + ch = ch + 'A' - 'a'; + replace = TRUE; + } + else if (!shouldCapitalize && ch >= 'A' && ch <= 'Z') { + ch = ch + 'a' - 'A'; + replace = TRUE; + } + if (replace) { + if (!somethingChanged) { + somethingChanged = TRUE; + if (CFStringGetBytes(name, CFRangeMake(0, len), kCFStringEncodingISOLatin1, 0, FALSE, NULL, 0, NULL) == len) { + // Can be encoded in ISOLatin1 + useUniCharPtr = FALSE; + charPtr = CFAllocatorAllocate(NULL, len + 1, 0); + CFStringGetCString(name, charPtr, len+1, kCFStringEncodingISOLatin1); + } + else { + useUniCharPtr = TRUE; + uniCharPtr = CFAllocatorAllocate(NULL, len * sizeof(UniChar), 0); + CFStringGetCharacters(name, CFRangeMake(0, len), uniCharPtr); + } + } + if (useUniCharPtr) { + uniCharPtr[i] = ch; + } + else { + charPtr[i] = ch; + } + } + if (ch == '-') { + shouldCapitalize = TRUE; + } + else { + shouldCapitalize = FALSE; + } + } + if (somethingChanged) { + if (useUniCharPtr) { + result = (NSString *)CFMakeCollectable(CFStringCreateWithCharactersNoCopy(NULL, uniCharPtr, len, NULL)); + } + else { + result = (NSString *)CFMakeCollectable(CFStringCreateWithCStringNoCopy(NULL, charPtr, kCFStringEncodingISOLatin1, NULL)); + } + } + else { + result = [self retain]; + } + + return [result autorelease]; +} + +@end + +@implementation NSData (WebKitExtras) + +-(NSString *)_webkit_guessedMIMETypeForXML +{ + int length = [self length]; + const UInt8 *bytes = [self bytes]; + +#define CHANNEL_TAG_LENGTH 7 + + const char *p = (const char *)bytes; + int remaining = MIN(length, WEB_GUESS_MIME_TYPE_PEEK_LENGTH) - (CHANNEL_TAG_LENGTH - 1); + + BOOL foundRDF = false; + + while (remaining > 0) { + // Look for a "<". + const char *hit = memchr(p, '<', remaining); + if (!hit) { + break; + } + + // We are trying to identify RSS or Atom. RSS has a top-level + // element of either <rss> or <rdf>. However, there are + // non-RSS RDF files, so in the case of <rdf> we further look + // for a <channel> element. In the case of an Atom file, a + // top-level <feed> element is all we need to see. Only tags + // starting with <? or <! can precede the root element. We + // bail if we don't find an <rss>, <feed> or <rdf> element + // right after those. + + if (foundRDF) { + if (strncasecmp(hit, "<channel", strlen("<channel")) == 0) { + return @"application/rss+xml"; + } + } else if (strncasecmp(hit, "<rdf", strlen("<rdf")) == 0) { + foundRDF = TRUE; + } else if (strncasecmp(hit, "<rss", strlen("<rss")) == 0) { + return @"application/rss+xml"; + } else if (strncasecmp(hit, "<feed", strlen("<feed")) == 0) { + return @"application/atom+xml"; + } else if (strncasecmp(hit, "<?", strlen("<?")) != 0 && strncasecmp(hit, "<!", strlen("<!")) != 0) { + return nil; + } + + // Skip the "<" and continue. + remaining -= (hit + 1) - p; + p = hit + 1; + } + + return nil; +} + +-(NSString *)_webkit_guessedMIMEType +{ +#define JPEG_MAGIC_NUMBER_LENGTH 4 +#define SCRIPT_TAG_LENGTH 7 +#define TEXT_HTML_LENGTH 9 +#define VCARD_HEADER_LENGTH 11 +#define VCAL_HEADER_LENGTH 15 + + NSString *MIMEType = [self _webkit_guessedMIMETypeForXML]; + if ([MIMEType length]) + return MIMEType; + + int length = [self length]; + const char *bytes = [self bytes]; + + const char *p = bytes; + int remaining = MIN(length, WEB_GUESS_MIME_TYPE_PEEK_LENGTH) - (SCRIPT_TAG_LENGTH - 1); + while (remaining > 0) { + // Look for a "<". + const char *hit = memchr(p, '<', remaining); + if (!hit) { + break; + } + + // If we found a "<", look for "<html>" or "<a " or "<script". + if (strncasecmp(hit, "<html>", strlen("<html>")) == 0 || + strncasecmp(hit, "<a ", strlen("<a ")) == 0 || + strncasecmp(hit, "<script", strlen("<script")) == 0 || + strncasecmp(hit, "<title>", strlen("<title>")) == 0) { + return @"text/html"; + } + + // Skip the "<" and continue. + remaining -= (hit + 1) - p; + p = hit + 1; + } + + // Test for a broken server which has sent the content type as part of the content. + // This code could be improved to look for other mime types. + p = bytes; + remaining = MIN(length, WEB_GUESS_MIME_TYPE_PEEK_LENGTH) - (TEXT_HTML_LENGTH - 1); + while (remaining > 0) { + // Look for a "t" or "T". + const char *hit = NULL; + const char *lowerhit = memchr(p, 't', remaining); + const char *upperhit = memchr(p, 'T', remaining); + if (!lowerhit && !upperhit) { + break; + } + if (!lowerhit) { + hit = upperhit; + } + else if (!upperhit) { + hit = lowerhit; + } + else { + hit = MIN(lowerhit, upperhit); + } + + // If we found a "t/T", look for "text/html". + if (strncasecmp(hit, "text/html", TEXT_HTML_LENGTH) == 0) { + return @"text/html"; + } + + // Skip the "t/T" and continue. + remaining -= (hit + 1) - p; + p = hit + 1; + } + + if ((length >= VCARD_HEADER_LENGTH) && strncmp(bytes, "BEGIN:VCARD", VCARD_HEADER_LENGTH) == 0) { + return @"text/vcard"; + } + if ((length >= VCAL_HEADER_LENGTH) && strncmp(bytes, "BEGIN:VCALENDAR", VCAL_HEADER_LENGTH) == 0) { + return @"text/calendar"; + } + + // Test for plain text. + int i; + for(i=0; i<length; i++){ + char c = bytes[i]; + if ((c < 0x20 || c > 0x7E) && (c != '\t' && c != '\r' && c != '\n')) { + break; + } + } + if (i == length) { + // Didn't encounter any bad characters, looks like plain text. + return @"text/plain"; + } + + // Looks like this is a binary file. + + // Sniff for the JPEG magic number. + if ((length >= JPEG_MAGIC_NUMBER_LENGTH) && strncmp(bytes, "\xFF\xD8\xFF\xE0", JPEG_MAGIC_NUMBER_LENGTH) == 0) { + return @"image/jpeg"; + } + +#undef JPEG_MAGIC_NUMBER_LENGTH +#undef SCRIPT_TAG_LENGTH +#undef TEXT_HTML_LENGTH +#undef VCARD_HEADER_LENGTH +#undef VCAL_HEADER_LENGTH + + return nil; +} + +@end + +@implementation NSData (WebNSDataExtras) + +-(BOOL)_web_isCaseInsensitiveEqualToCString:(const char *)string +{ + ASSERT(string); + + const char *bytes = [self bytes]; + return strncasecmp(bytes, string, [self length]) == 0; +} + +static const UInt8 *_findEOL(const UInt8 *bytes, CFIndex len) { + + // According to the HTTP specification EOL is defined as + // a CRLF pair. Unfortunately, some servers will use LF + // instead. Worse yet, some servers will use a combination + // of both (e.g. <header>CRLFLF<body>), so findEOL needs + // to be more forgiving. It will now accept CRLF, LF, or + // CR. + // + // It returns NULL if EOL is not found or it will return + // a pointer to the first terminating character. + CFIndex i; + for (i = 0; i < len; i++) + { + UInt8 c = bytes[i]; + if ('\n' == c) return bytes + i; + if ('\r' == c) + { + // Check to see if spanning buffer bounds + // (CRLF is across reads). If so, wait for + // next read. + if (i + 1 == len) break; + + return bytes + i; + } + } + + return NULL; +} + +-(NSMutableDictionary *)_webkit_parseRFC822HeaderFields +{ + NSMutableDictionary *headerFields = [NSMutableDictionary dictionary]; + + const UInt8 *bytes = [self bytes]; + unsigned length = [self length]; + NSString *lastKey = nil; + const UInt8 *eol; + + // Loop over lines until we're past the header, or we can't find any more end-of-lines + while ((eol = _findEOL(bytes, length))) { + const UInt8 *line = bytes; + SInt32 lineLength = eol - bytes; + + // Move bytes to the character after the terminator as returned by _findEOL. + bytes = eol + 1; + if (('\r' == *eol) && ('\n' == *bytes)) { + bytes++; // Safe since _findEOL won't return a spanning CRLF. + } + + length -= (bytes - line); + if (lineLength == 0) { + // Blank line; we're at the end of the header + break; + } + else if (*line == ' ' || *line == '\t') { + // Continuation of the previous header + if (!lastKey) { + // malformed header; ignore it and continue + continue; + } + else { + // Merge the continuation of the previous header + NSString *currentValue = [headerFields objectForKey:lastKey]; + NSString *newValue = (NSString *)CFMakeCollectable(CFStringCreateWithBytes(NULL, line, lineLength, kCFStringEncodingISOLatin1, FALSE)); + ASSERT(currentValue); + ASSERT(newValue); + NSString *mergedValue = [[NSString alloc] initWithFormat:@"%@%@", currentValue, newValue]; + [headerFields setObject:(NSString *)mergedValue forKey:lastKey]; + [newValue release]; + [mergedValue release]; + // Note: currentValue is autoreleased + } + } + else { + // Brand new header + const UInt8 *colon; + for (colon = line; *colon != ':' && colon != eol; colon ++) { + // empty loop + } + if (colon == eol) { + // malformed header; ignore it and continue + continue; + } + else { + lastKey = (NSString *)CFMakeCollectable(CFStringCreateWithBytes(NULL, line, colon - line, kCFStringEncodingISOLatin1, FALSE)); + [lastKey autorelease]; + NSString *value = [lastKey _web_capitalizeRFC822HeaderFieldName]; + lastKey = value; + for (colon++; colon != eol; colon++) { + if (*colon != ' ' && *colon != '\t') { + break; + } + } + if (colon == eol) { + value = [[NSString alloc] initWithString:@""]; + [value autorelease]; + } + else { + value = (NSString *)CFMakeCollectable(CFStringCreateWithBytes(NULL, colon, eol-colon, kCFStringEncodingISOLatin1, FALSE)); + [value autorelease]; + } + NSString *oldValue = [headerFields objectForKey:lastKey]; + if (oldValue) { + NSString *newValue = [[NSString alloc] initWithFormat:@"%@, %@", oldValue, value]; + value = newValue; + [newValue autorelease]; + } + [headerFields setObject:(NSString *)value forKey:lastKey]; + } + } + } + + return headerFields; +} + +@end |