WebKit/mac/Misc/WebKitNSStringExtras.mm
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2005, 2007 Apple Inc. All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  *
       
     8  * 1.  Redistributions of source code must retain the above copyright
       
     9  *     notice, this list of conditions and the following disclaimer. 
       
    10  * 2.  Redistributions in binary form must reproduce the above copyright
       
    11  *     notice, this list of conditions and the following disclaimer in the
       
    12  *     documentation and/or other materials provided with the distribution. 
       
    13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
       
    14  *     its contributors may be used to endorse or promote products derived
       
    15  *     from this software without specific prior written permission. 
       
    16  *
       
    17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
       
    18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
       
    21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
       
    22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
       
    23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
       
    24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
       
    26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    27  */
       
    28 
       
    29 #import "WebKitNSStringExtras.h"
       
    30 
       
    31 #import <WebCore/Font.h>
       
    32 #import <WebCore/GraphicsContext.h>
       
    33 #import <WebCore/WebCoreNSStringExtras.h>
       
    34 #import <WebKit/WebNSFileManagerExtras.h>
       
    35 #import <WebKit/WebNSObjectExtras.h>
       
    36 #import <unicode/uchar.h>
       
    37 #import <sys/param.h>
       
    38 
       
    39 NSString *WebKitLocalCacheDefaultsKey = @"WebKitLocalCache";
       
    40 
       
    41 static inline CGFloat webkit_CGCeiling(CGFloat value)
       
    42 {
       
    43     if (sizeof(value) == sizeof(float))
       
    44         return ceilf(value);
       
    45     return ceil(value);
       
    46 }
       
    47 
       
    48 using namespace WebCore;
       
    49 
       
    50 @implementation NSString (WebKitExtras)
       
    51 
       
    52 static BOOL canUseFastRenderer(const UniChar *buffer, unsigned length)
       
    53 {
       
    54     unsigned i;
       
    55     for (i = 0; i < length; i++) {
       
    56         UCharDirection direction = u_charDirection(buffer[i]);
       
    57         if (direction == U_RIGHT_TO_LEFT || direction > U_OTHER_NEUTRAL)
       
    58             return NO;
       
    59     }
       
    60     return YES;
       
    61 }
       
    62 
       
    63 - (void)_web_drawAtPoint:(NSPoint)point font:(NSFont *)font textColor:(NSColor *)textColor
       
    64 {
       
    65     [self _web_drawAtPoint:point font:font textColor:textColor allowingFontSmoothing:YES];
       
    66 }
       
    67 
       
    68 - (void)_web_drawAtPoint:(NSPoint)point font:(NSFont *)font textColor:(NSColor *)textColor allowingFontSmoothing:(BOOL)fontSmoothingIsAllowed
       
    69 {
       
    70     unsigned length = [self length];
       
    71     Vector<UniChar, 2048> buffer(length);
       
    72 
       
    73     [self getCharacters:buffer.data()];
       
    74 
       
    75     if (canUseFastRenderer(buffer.data(), length)) {
       
    76         // The following is a half-assed attempt to match AppKit's rounding rules for drawAtPoint.
       
    77         // It's probably incorrect for high DPI.
       
    78         // If you change this, be sure to test all the text drawn this way in Safari, including
       
    79         // the status bar, bookmarks bar, tab bar, and activity window.
       
    80         point.y = webkit_CGCeiling(point.y);
       
    81 
       
    82         NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
       
    83         CGContextRef cgContext = static_cast<CGContextRef>([nsContext graphicsPort]);
       
    84         GraphicsContext graphicsContext(cgContext);    
       
    85 
       
    86         // Safari doesn't flip the NSGraphicsContext before calling WebKit, yet WebCore requires a flipped graphics context.
       
    87         BOOL flipped = [nsContext isFlipped];
       
    88         if (!flipped)
       
    89             CGContextScaleCTM(cgContext, 1, -1);
       
    90 
       
    91         Font webCoreFont(FontPlatformData(font), ![nsContext isDrawingToScreen], fontSmoothingIsAllowed ? AutoSmoothing : Antialiased);
       
    92         TextRun run(buffer.data(), length);
       
    93         run.disableRoundingHacks();
       
    94 
       
    95         CGFloat red;
       
    96         CGFloat green;
       
    97         CGFloat blue;
       
    98         CGFloat alpha;
       
    99         [[textColor colorUsingColorSpaceName:NSDeviceRGBColorSpace] getRed:&red green:&green blue:&blue alpha:&alpha];
       
   100         graphicsContext.setFillColor(makeRGBA(red * 255, green * 255, blue * 255, alpha * 255), DeviceColorSpace);
       
   101 
       
   102         webCoreFont.drawText(&graphicsContext, run, FloatPoint(point.x, (flipped ? point.y : (-1 * point.y))));
       
   103 
       
   104         if (!flipped)
       
   105             CGContextScaleCTM(cgContext, 1, -1);
       
   106     } else {
       
   107         // The given point is on the baseline.
       
   108         if ([[NSView focusView] isFlipped])
       
   109             point.y -= [font ascender];
       
   110         else
       
   111             point.y += [font descender];
       
   112 
       
   113         [self drawAtPoint:point withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil]];
       
   114     }
       
   115 }
       
   116 
       
   117 - (void)_web_drawDoubledAtPoint:(NSPoint)textPoint
       
   118              withTopColor:(NSColor *)topColor
       
   119               bottomColor:(NSColor *)bottomColor
       
   120                      font:(NSFont *)font
       
   121 {
       
   122     // turn off font smoothing so translucent text draws correctly (Radar 3118455)
       
   123     [self _web_drawAtPoint:textPoint font:font textColor:bottomColor allowingFontSmoothing:NO];
       
   124 
       
   125     textPoint.y += 1;
       
   126     [self _web_drawAtPoint:textPoint font:font textColor:topColor allowingFontSmoothing:NO];
       
   127 }
       
   128 
       
   129 - (float)_web_widthWithFont:(NSFont *)font
       
   130 {
       
   131     unsigned length = [self length];
       
   132     Vector<UniChar, 2048> buffer(length);
       
   133 
       
   134     [self getCharacters:buffer.data()];
       
   135 
       
   136     if (canUseFastRenderer(buffer.data(), length)) {
       
   137         Font webCoreFont(FontPlatformData(font), ![[NSGraphicsContext currentContext] isDrawingToScreen]);
       
   138         TextRun run(buffer.data(), length);
       
   139         run.disableRoundingHacks();
       
   140         return webCoreFont.floatWidth(run);
       
   141     }
       
   142 
       
   143     return [self sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]].width;
       
   144 }
       
   145 
       
   146 - (NSString *)_web_stringByAbbreviatingWithTildeInPath
       
   147 {
       
   148     NSString *resolvedHomeDirectory = [NSHomeDirectory() stringByResolvingSymlinksInPath];
       
   149     NSString *path;
       
   150     
       
   151     if ([self hasPrefix:resolvedHomeDirectory]) {
       
   152         NSString *relativePath = [self substringFromIndex:[resolvedHomeDirectory length]];
       
   153         path = [NSHomeDirectory() stringByAppendingPathComponent:relativePath];
       
   154     } else {
       
   155         path = self;
       
   156     }
       
   157         
       
   158     return [path stringByAbbreviatingWithTildeInPath];
       
   159 }
       
   160 
       
   161 - (NSString *)_web_stringByStrippingReturnCharacters
       
   162 {
       
   163     NSMutableString *newString = [[self mutableCopy] autorelease];
       
   164     [newString replaceOccurrencesOfString:@"\r" withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [newString length])];
       
   165     [newString replaceOccurrencesOfString:@"\n" withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [newString length])];
       
   166     return newString;
       
   167 }
       
   168 
       
   169 + (NSStringEncoding)_web_encodingForResource:(Handle)resource
       
   170 {
       
   171     return CFStringConvertEncodingToNSStringEncoding(stringEncodingForResource(resource));
       
   172 }
       
   173 
       
   174 - (BOOL)_webkit_isCaseInsensitiveEqualToString:(NSString *)string
       
   175 {
       
   176     return stringIsCaseInsensitiveEqualToString(self, string);
       
   177 }
       
   178 
       
   179 -(BOOL)_webkit_hasCaseInsensitivePrefix:(NSString *)prefix
       
   180 {
       
   181     return [self rangeOfString:prefix options:(NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound;
       
   182 }
       
   183 
       
   184 -(BOOL)_webkit_hasCaseInsensitiveSuffix:(NSString *)suffix
       
   185 {
       
   186     return hasCaseInsensitiveSuffix(self, suffix);
       
   187 }
       
   188 
       
   189 -(BOOL)_webkit_hasCaseInsensitiveSubstring:(NSString *)substring
       
   190 {
       
   191     return hasCaseInsensitiveSubstring(self, substring);
       
   192 }
       
   193 
       
   194 -(NSString *)_webkit_filenameByFixingIllegalCharacters
       
   195 {
       
   196     return filenameByFixingIllegalCharacters(self);
       
   197 }
       
   198 
       
   199 -(NSString *)_webkit_stringByTrimmingWhitespace
       
   200 {
       
   201     NSMutableString *trimmed = [[self mutableCopy] autorelease];
       
   202     CFStringTrimWhitespace((CFMutableStringRef)trimmed);
       
   203     return trimmed;
       
   204 }
       
   205 
       
   206 - (NSString *)_webkit_stringByCollapsingNonPrintingCharacters
       
   207 {
       
   208     NSMutableString *result = [NSMutableString string];
       
   209     static NSCharacterSet *charactersToTurnIntoSpaces = nil;
       
   210     static NSCharacterSet *charactersToNotTurnIntoSpaces = nil;
       
   211     
       
   212     if (charactersToTurnIntoSpaces == nil) {
       
   213         NSMutableCharacterSet *set = [[NSMutableCharacterSet alloc] init];
       
   214         [set addCharactersInRange:NSMakeRange(0x00, 0x21)];
       
   215         [set addCharactersInRange:NSMakeRange(0x7F, 0x01)];
       
   216         charactersToTurnIntoSpaces = [set copy];
       
   217         [set release];
       
   218         charactersToNotTurnIntoSpaces = [[charactersToTurnIntoSpaces invertedSet] retain];
       
   219     }
       
   220     
       
   221     unsigned length = [self length];
       
   222     unsigned position = 0;
       
   223     while (position != length) {
       
   224         NSRange nonSpace = [self rangeOfCharacterFromSet:charactersToNotTurnIntoSpaces
       
   225             options:0 range:NSMakeRange(position, length - position)];
       
   226         if (nonSpace.location == NSNotFound) {
       
   227             break;
       
   228         }
       
   229 
       
   230         NSRange space = [self rangeOfCharacterFromSet:charactersToTurnIntoSpaces
       
   231             options:0 range:NSMakeRange(nonSpace.location, length - nonSpace.location)];
       
   232         if (space.location == NSNotFound) {
       
   233             space.location = length;
       
   234         }
       
   235 
       
   236         if (space.location > nonSpace.location) {
       
   237             if (position != 0) {
       
   238                 [result appendString:@" "];
       
   239             }
       
   240             [result appendString:[self substringWithRange:
       
   241                 NSMakeRange(nonSpace.location, space.location - nonSpace.location)]];
       
   242         }
       
   243 
       
   244         position = space.location;
       
   245     }
       
   246     
       
   247     return result;
       
   248 }
       
   249 
       
   250 - (NSString *)_webkit_stringByCollapsingWhitespaceCharacters
       
   251 {
       
   252     NSMutableString *result = [[NSMutableString alloc] initWithCapacity:[self length]];
       
   253     NSCharacterSet *spaces = [NSCharacterSet whitespaceAndNewlineCharacterSet];
       
   254     static NSCharacterSet *notSpaces = nil;
       
   255 
       
   256     if (notSpaces == nil)
       
   257         notSpaces = [[spaces invertedSet] retain];
       
   258 
       
   259     unsigned length = [self length];
       
   260     unsigned position = 0;
       
   261     while (position != length) {
       
   262         NSRange nonSpace = [self rangeOfCharacterFromSet:notSpaces options:0 range:NSMakeRange(position, length - position)];
       
   263         if (nonSpace.location == NSNotFound)
       
   264             break;
       
   265 
       
   266         NSRange space = [self rangeOfCharacterFromSet:spaces options:0 range:NSMakeRange(nonSpace.location, length - nonSpace.location)];
       
   267         if (space.location == NSNotFound)
       
   268             space.location = length;
       
   269 
       
   270         if (space.location > nonSpace.location) {
       
   271             if (position != 0)
       
   272                 [result appendString:@" "];
       
   273             [result appendString:[self substringWithRange:NSMakeRange(nonSpace.location, space.location - nonSpace.location)]];
       
   274         }
       
   275 
       
   276         position = space.location;
       
   277     }
       
   278 
       
   279     return [result autorelease];
       
   280 }
       
   281 
       
   282 -(NSString *)_webkit_fixedCarbonPOSIXPath
       
   283 {
       
   284     NSFileManager *fileManager = [NSFileManager defaultManager];
       
   285     if ([fileManager fileExistsAtPath:self]) {
       
   286         // Files exists, no need to fix.
       
   287         return self;
       
   288     }
       
   289 
       
   290     NSMutableArray *pathComponents = [[[self pathComponents] mutableCopy] autorelease];
       
   291     NSString *volumeName = [pathComponents objectAtIndex:1];
       
   292     if ([volumeName isEqualToString:@"Volumes"]) {
       
   293         // Path starts with "/Volumes", so the volume name is the next path component.
       
   294         volumeName = [pathComponents objectAtIndex:2];
       
   295         // Remove "Volumes" from the path because it may incorrectly be part of the path (3163647).
       
   296         // We'll add it back if we have to.
       
   297         [pathComponents removeObjectAtIndex:1];
       
   298     }
       
   299 
       
   300     if (!volumeName) {
       
   301         // Should only happen if self == "/", so this shouldn't happen because that always exists.
       
   302         return self;
       
   303     }
       
   304 
       
   305     if ([[fileManager _webkit_startupVolumeName] isEqualToString:volumeName]) {
       
   306         // Startup volume name is included in path, remove it.
       
   307         [pathComponents removeObjectAtIndex:1];
       
   308     } else if ([[fileManager contentsOfDirectoryAtPath:@"/Volumes" error:NULL] containsObject:volumeName]) {
       
   309         // Path starts with other volume name, prepend "/Volumes".
       
   310         [pathComponents insertObject:@"Volumes" atIndex:1];
       
   311     } else
       
   312         // It's valid.
       
   313         return self;
       
   314 
       
   315     NSString *path = [NSString pathWithComponents:pathComponents];
       
   316 
       
   317     if (![fileManager fileExistsAtPath:path])
       
   318         // File at canonicalized path doesn't exist, return original.
       
   319         return self;
       
   320 
       
   321     return path;
       
   322 }
       
   323 
       
   324 + (NSString *)_webkit_localCacheDirectoryWithBundleIdentifier:(NSString*)bundleIdentifier
       
   325 {
       
   326     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
       
   327     NSString *cacheDir = [defaults objectForKey:WebKitLocalCacheDefaultsKey];
       
   328 
       
   329     if (!cacheDir || ![cacheDir isKindOfClass:[NSString class]]) {
       
   330 #ifdef BUILDING_ON_TIGER
       
   331         cacheDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"];
       
   332 #else
       
   333         char cacheDirectory[MAXPATHLEN];
       
   334         size_t cacheDirectoryLen = confstr(_CS_DARWIN_USER_CACHE_DIR, cacheDirectory, MAXPATHLEN);
       
   335     
       
   336         if (cacheDirectoryLen)
       
   337             cacheDir = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:cacheDirectory length:cacheDirectoryLen - 1];
       
   338 #endif
       
   339     }
       
   340 
       
   341     return [cacheDir stringByAppendingPathComponent:bundleIdentifier];
       
   342 }
       
   343 
       
   344 @end