|
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 |