an experiment in making a cocoa webkit browser manageable under X11

show error page when a page won't load

bring in html encoding helper from MWFeedParser/goog

+1036 -1
+66
GTMNSString+HTML.h
··· 1 + // 2 + // GTMNSString+HTML.h 3 + // Dealing with NSStrings that contain HTML 4 + // 5 + // Copyright 2006-2008 Google Inc. 6 + // 7 + // Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 + // use this file except in compliance with the License. You may obtain a copy 9 + // of the License at 10 + // 11 + // http://www.apache.org/licenses/LICENSE-2.0 12 + // 13 + // Unless required by applicable law or agreed to in writing, software 14 + // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 + // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 + // License for the specific language governing permissions and limitations under 17 + // the License. 18 + // 19 + 20 + #import <Foundation/Foundation.h> 21 + 22 + /// Utilities for NSStrings containing HTML 23 + @interface NSString (GTMNSStringHTMLAdditions) 24 + 25 + /// Get a string where internal characters that need escaping for HTML are escaped 26 + // 27 + /// For example, '&' become '&amp;'. This will only cover characters from table 28 + /// A.2.2 of http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters 29 + /// which is what you want for a unicode encoded webpage. If you have a ascii 30 + /// or non-encoded webpage, please use stringByEscapingAsciiHTML which will 31 + /// encode all characters. 32 + /// 33 + /// For obvious reasons this call is only safe once. 34 + // 35 + // Returns: 36 + // Autoreleased NSString 37 + // 38 + - (NSString *)gtm_stringByEscapingForHTML; 39 + 40 + /// Get a string where internal characters that need escaping for HTML are escaped 41 + // 42 + /// For example, '&' become '&amp;' 43 + /// All non-mapped characters (unicode that don't have a &keyword; mapping) 44 + /// will be converted to the appropriate &#xxx; value. If your webpage is 45 + /// unicode encoded (UTF16 or UTF8) use stringByEscapingHTML instead as it is 46 + /// faster, and produces less bloated and more readable HTML (as long as you 47 + /// are using a unicode compliant HTML reader). 48 + /// 49 + /// For obvious reasons this call is only safe once. 50 + // 51 + // Returns: 52 + // Autoreleased NSString 53 + // 54 + - (NSString *)gtm_stringByEscapingForAsciiHTML; 55 + 56 + /// Get a string where internal characters that are escaped for HTML are unescaped 57 + // 58 + /// For example, '&amp;' becomes '&' 59 + /// Handles &#32; and &#x32; cases as well 60 + /// 61 + // Returns: 62 + // Autoreleased NSString 63 + // 64 + - (NSString *)gtm_stringByUnescapingFromHTML; 65 + 66 + @end
+522
GTMNSString+HTML.m
··· 1 + // 2 + // GTMNSString+HTML.m 3 + // Dealing with NSStrings that contain HTML 4 + // 5 + // Copyright 2006-2008 Google Inc. 6 + // 7 + // Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 + // use this file except in compliance with the License. You may obtain a copy 9 + // of the License at 10 + // 11 + // http://www.apache.org/licenses/LICENSE-2.0 12 + // 13 + // Unless required by applicable law or agreed to in writing, software 14 + // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 + // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 + // License for the specific language governing permissions and limitations under 17 + // the License. 18 + // 19 + 20 + //#import "GTMDefines.h" 21 + #import "GTMNSString+HTML.h" 22 + 23 + typedef struct { 24 + NSString *escapeSequence; 25 + unichar uchar; 26 + } HTMLEscapeMap; 27 + 28 + // Taken from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters 29 + // Ordered by uchar lowest to highest for bsearching 30 + static HTMLEscapeMap gAsciiHTMLEscapeMap[] = { 31 + // A.2.2. Special characters 32 + { @"&quot;", 34 }, 33 + { @"&amp;", 38 }, 34 + { @"&apos;", 39 }, 35 + { @"&lt;", 60 }, 36 + { @"&gt;", 62 }, 37 + 38 + // A.2.1. Latin-1 characters 39 + { @"&nbsp;", 160 }, 40 + { @"&iexcl;", 161 }, 41 + { @"&cent;", 162 }, 42 + { @"&pound;", 163 }, 43 + { @"&curren;", 164 }, 44 + { @"&yen;", 165 }, 45 + { @"&brvbar;", 166 }, 46 + { @"&sect;", 167 }, 47 + { @"&uml;", 168 }, 48 + { @"&copy;", 169 }, 49 + { @"&ordf;", 170 }, 50 + { @"&laquo;", 171 }, 51 + { @"&not;", 172 }, 52 + { @"&shy;", 173 }, 53 + { @"&reg;", 174 }, 54 + { @"&macr;", 175 }, 55 + { @"&deg;", 176 }, 56 + { @"&plusmn;", 177 }, 57 + { @"&sup2;", 178 }, 58 + { @"&sup3;", 179 }, 59 + { @"&acute;", 180 }, 60 + { @"&micro;", 181 }, 61 + { @"&para;", 182 }, 62 + { @"&middot;", 183 }, 63 + { @"&cedil;", 184 }, 64 + { @"&sup1;", 185 }, 65 + { @"&ordm;", 186 }, 66 + { @"&raquo;", 187 }, 67 + { @"&frac14;", 188 }, 68 + { @"&frac12;", 189 }, 69 + { @"&frac34;", 190 }, 70 + { @"&iquest;", 191 }, 71 + { @"&Agrave;", 192 }, 72 + { @"&Aacute;", 193 }, 73 + { @"&Acirc;", 194 }, 74 + { @"&Atilde;", 195 }, 75 + { @"&Auml;", 196 }, 76 + { @"&Aring;", 197 }, 77 + { @"&AElig;", 198 }, 78 + { @"&Ccedil;", 199 }, 79 + { @"&Egrave;", 200 }, 80 + { @"&Eacute;", 201 }, 81 + { @"&Ecirc;", 202 }, 82 + { @"&Euml;", 203 }, 83 + { @"&Igrave;", 204 }, 84 + { @"&Iacute;", 205 }, 85 + { @"&Icirc;", 206 }, 86 + { @"&Iuml;", 207 }, 87 + { @"&ETH;", 208 }, 88 + { @"&Ntilde;", 209 }, 89 + { @"&Ograve;", 210 }, 90 + { @"&Oacute;", 211 }, 91 + { @"&Ocirc;", 212 }, 92 + { @"&Otilde;", 213 }, 93 + { @"&Ouml;", 214 }, 94 + { @"&times;", 215 }, 95 + { @"&Oslash;", 216 }, 96 + { @"&Ugrave;", 217 }, 97 + { @"&Uacute;", 218 }, 98 + { @"&Ucirc;", 219 }, 99 + { @"&Uuml;", 220 }, 100 + { @"&Yacute;", 221 }, 101 + { @"&THORN;", 222 }, 102 + { @"&szlig;", 223 }, 103 + { @"&agrave;", 224 }, 104 + { @"&aacute;", 225 }, 105 + { @"&acirc;", 226 }, 106 + { @"&atilde;", 227 }, 107 + { @"&auml;", 228 }, 108 + { @"&aring;", 229 }, 109 + { @"&aelig;", 230 }, 110 + { @"&ccedil;", 231 }, 111 + { @"&egrave;", 232 }, 112 + { @"&eacute;", 233 }, 113 + { @"&ecirc;", 234 }, 114 + { @"&euml;", 235 }, 115 + { @"&igrave;", 236 }, 116 + { @"&iacute;", 237 }, 117 + { @"&icirc;", 238 }, 118 + { @"&iuml;", 239 }, 119 + { @"&eth;", 240 }, 120 + { @"&ntilde;", 241 }, 121 + { @"&ograve;", 242 }, 122 + { @"&oacute;", 243 }, 123 + { @"&ocirc;", 244 }, 124 + { @"&otilde;", 245 }, 125 + { @"&ouml;", 246 }, 126 + { @"&divide;", 247 }, 127 + { @"&oslash;", 248 }, 128 + { @"&ugrave;", 249 }, 129 + { @"&uacute;", 250 }, 130 + { @"&ucirc;", 251 }, 131 + { @"&uuml;", 252 }, 132 + { @"&yacute;", 253 }, 133 + { @"&thorn;", 254 }, 134 + { @"&yuml;", 255 }, 135 + 136 + // A.2.2. Special characters cont'd 137 + { @"&OElig;", 338 }, 138 + { @"&oelig;", 339 }, 139 + { @"&Scaron;", 352 }, 140 + { @"&scaron;", 353 }, 141 + { @"&Yuml;", 376 }, 142 + 143 + // A.2.3. Symbols 144 + { @"&fnof;", 402 }, 145 + 146 + // A.2.2. Special characters cont'd 147 + { @"&circ;", 710 }, 148 + { @"&tilde;", 732 }, 149 + 150 + // A.2.3. Symbols cont'd 151 + { @"&Alpha;", 913 }, 152 + { @"&Beta;", 914 }, 153 + { @"&Gamma;", 915 }, 154 + { @"&Delta;", 916 }, 155 + { @"&Epsilon;", 917 }, 156 + { @"&Zeta;", 918 }, 157 + { @"&Eta;", 919 }, 158 + { @"&Theta;", 920 }, 159 + { @"&Iota;", 921 }, 160 + { @"&Kappa;", 922 }, 161 + { @"&Lambda;", 923 }, 162 + { @"&Mu;", 924 }, 163 + { @"&Nu;", 925 }, 164 + { @"&Xi;", 926 }, 165 + { @"&Omicron;", 927 }, 166 + { @"&Pi;", 928 }, 167 + { @"&Rho;", 929 }, 168 + { @"&Sigma;", 931 }, 169 + { @"&Tau;", 932 }, 170 + { @"&Upsilon;", 933 }, 171 + { @"&Phi;", 934 }, 172 + { @"&Chi;", 935 }, 173 + { @"&Psi;", 936 }, 174 + { @"&Omega;", 937 }, 175 + { @"&alpha;", 945 }, 176 + { @"&beta;", 946 }, 177 + { @"&gamma;", 947 }, 178 + { @"&delta;", 948 }, 179 + { @"&epsilon;", 949 }, 180 + { @"&zeta;", 950 }, 181 + { @"&eta;", 951 }, 182 + { @"&theta;", 952 }, 183 + { @"&iota;", 953 }, 184 + { @"&kappa;", 954 }, 185 + { @"&lambda;", 955 }, 186 + { @"&mu;", 956 }, 187 + { @"&nu;", 957 }, 188 + { @"&xi;", 958 }, 189 + { @"&omicron;", 959 }, 190 + { @"&pi;", 960 }, 191 + { @"&rho;", 961 }, 192 + { @"&sigmaf;", 962 }, 193 + { @"&sigma;", 963 }, 194 + { @"&tau;", 964 }, 195 + { @"&upsilon;", 965 }, 196 + { @"&phi;", 966 }, 197 + { @"&chi;", 967 }, 198 + { @"&psi;", 968 }, 199 + { @"&omega;", 969 }, 200 + { @"&thetasym;", 977 }, 201 + { @"&upsih;", 978 }, 202 + { @"&piv;", 982 }, 203 + 204 + // A.2.2. Special characters cont'd 205 + { @"&ensp;", 8194 }, 206 + { @"&emsp;", 8195 }, 207 + { @"&thinsp;", 8201 }, 208 + { @"&zwnj;", 8204 }, 209 + { @"&zwj;", 8205 }, 210 + { @"&lrm;", 8206 }, 211 + { @"&rlm;", 8207 }, 212 + { @"&ndash;", 8211 }, 213 + { @"&mdash;", 8212 }, 214 + { @"&lsquo;", 8216 }, 215 + { @"&rsquo;", 8217 }, 216 + { @"&sbquo;", 8218 }, 217 + { @"&ldquo;", 8220 }, 218 + { @"&rdquo;", 8221 }, 219 + { @"&bdquo;", 8222 }, 220 + { @"&dagger;", 8224 }, 221 + { @"&Dagger;", 8225 }, 222 + // A.2.3. Symbols cont'd 223 + { @"&bull;", 8226 }, 224 + { @"&hellip;", 8230 }, 225 + 226 + // A.2.2. Special characters cont'd 227 + { @"&permil;", 8240 }, 228 + 229 + // A.2.3. Symbols cont'd 230 + { @"&prime;", 8242 }, 231 + { @"&Prime;", 8243 }, 232 + 233 + // A.2.2. Special characters cont'd 234 + { @"&lsaquo;", 8249 }, 235 + { @"&rsaquo;", 8250 }, 236 + 237 + // A.2.3. Symbols cont'd 238 + { @"&oline;", 8254 }, 239 + { @"&frasl;", 8260 }, 240 + 241 + // A.2.2. Special characters cont'd 242 + { @"&euro;", 8364 }, 243 + 244 + // A.2.3. Symbols cont'd 245 + { @"&image;", 8465 }, 246 + { @"&weierp;", 8472 }, 247 + { @"&real;", 8476 }, 248 + { @"&trade;", 8482 }, 249 + { @"&alefsym;", 8501 }, 250 + { @"&larr;", 8592 }, 251 + { @"&uarr;", 8593 }, 252 + { @"&rarr;", 8594 }, 253 + { @"&darr;", 8595 }, 254 + { @"&harr;", 8596 }, 255 + { @"&crarr;", 8629 }, 256 + { @"&lArr;", 8656 }, 257 + { @"&uArr;", 8657 }, 258 + { @"&rArr;", 8658 }, 259 + { @"&dArr;", 8659 }, 260 + { @"&hArr;", 8660 }, 261 + { @"&forall;", 8704 }, 262 + { @"&part;", 8706 }, 263 + { @"&exist;", 8707 }, 264 + { @"&empty;", 8709 }, 265 + { @"&nabla;", 8711 }, 266 + { @"&isin;", 8712 }, 267 + { @"&notin;", 8713 }, 268 + { @"&ni;", 8715 }, 269 + { @"&prod;", 8719 }, 270 + { @"&sum;", 8721 }, 271 + { @"&minus;", 8722 }, 272 + { @"&lowast;", 8727 }, 273 + { @"&radic;", 8730 }, 274 + { @"&prop;", 8733 }, 275 + { @"&infin;", 8734 }, 276 + { @"&ang;", 8736 }, 277 + { @"&and;", 8743 }, 278 + { @"&or;", 8744 }, 279 + { @"&cap;", 8745 }, 280 + { @"&cup;", 8746 }, 281 + { @"&int;", 8747 }, 282 + { @"&there4;", 8756 }, 283 + { @"&sim;", 8764 }, 284 + { @"&cong;", 8773 }, 285 + { @"&asymp;", 8776 }, 286 + { @"&ne;", 8800 }, 287 + { @"&equiv;", 8801 }, 288 + { @"&le;", 8804 }, 289 + { @"&ge;", 8805 }, 290 + { @"&sub;", 8834 }, 291 + { @"&sup;", 8835 }, 292 + { @"&nsub;", 8836 }, 293 + { @"&sube;", 8838 }, 294 + { @"&supe;", 8839 }, 295 + { @"&oplus;", 8853 }, 296 + { @"&otimes;", 8855 }, 297 + { @"&perp;", 8869 }, 298 + { @"&sdot;", 8901 }, 299 + { @"&lceil;", 8968 }, 300 + { @"&rceil;", 8969 }, 301 + { @"&lfloor;", 8970 }, 302 + { @"&rfloor;", 8971 }, 303 + { @"&lang;", 9001 }, 304 + { @"&rang;", 9002 }, 305 + { @"&loz;", 9674 }, 306 + { @"&spades;", 9824 }, 307 + { @"&clubs;", 9827 }, 308 + { @"&hearts;", 9829 }, 309 + { @"&diams;", 9830 } 310 + }; 311 + 312 + // Taken from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters 313 + // This is table A.2.2 Special Characters 314 + static HTMLEscapeMap gUnicodeHTMLEscapeMap[] = { 315 + // C0 Controls and Basic Latin 316 + { @"&quot;", 34 }, 317 + { @"&amp;", 38 }, 318 + { @"&apos;", 39 }, 319 + { @"&lt;", 60 }, 320 + { @"&gt;", 62 }, 321 + 322 + // Latin Extended-A 323 + { @"&OElig;", 338 }, 324 + { @"&oelig;", 339 }, 325 + { @"&Scaron;", 352 }, 326 + { @"&scaron;", 353 }, 327 + { @"&Yuml;", 376 }, 328 + 329 + // Spacing Modifier Letters 330 + { @"&circ;", 710 }, 331 + { @"&tilde;", 732 }, 332 + 333 + // General Punctuation 334 + { @"&ensp;", 8194 }, 335 + { @"&emsp;", 8195 }, 336 + { @"&thinsp;", 8201 }, 337 + { @"&zwnj;", 8204 }, 338 + { @"&zwj;", 8205 }, 339 + { @"&lrm;", 8206 }, 340 + { @"&rlm;", 8207 }, 341 + { @"&ndash;", 8211 }, 342 + { @"&mdash;", 8212 }, 343 + { @"&lsquo;", 8216 }, 344 + { @"&rsquo;", 8217 }, 345 + { @"&sbquo;", 8218 }, 346 + { @"&ldquo;", 8220 }, 347 + { @"&rdquo;", 8221 }, 348 + { @"&bdquo;", 8222 }, 349 + { @"&dagger;", 8224 }, 350 + { @"&Dagger;", 8225 }, 351 + { @"&permil;", 8240 }, 352 + { @"&lsaquo;", 8249 }, 353 + { @"&rsaquo;", 8250 }, 354 + { @"&euro;", 8364 }, 355 + }; 356 + 357 + 358 + // Utility function for Bsearching table above 359 + static int EscapeMapCompare(const void *ucharVoid, const void *mapVoid) { 360 + const unichar *uchar = (const unichar*)ucharVoid; 361 + const HTMLEscapeMap *map = (const HTMLEscapeMap*)mapVoid; 362 + int val; 363 + if (*uchar > map->uchar) { 364 + val = 1; 365 + } else if (*uchar < map->uchar) { 366 + val = -1; 367 + } else { 368 + val = 0; 369 + } 370 + return val; 371 + } 372 + 373 + @implementation NSString (GTMNSStringHTMLAdditions) 374 + 375 + - (NSString *)gtm_stringByEscapingHTMLUsingTable:(HTMLEscapeMap*)table 376 + ofSize:(NSUInteger)size 377 + escapingUnicode:(BOOL)escapeUnicode { 378 + NSUInteger length = [self length]; 379 + if (!length) { 380 + return self; 381 + } 382 + 383 + NSMutableString *finalString = [NSMutableString string]; 384 + NSMutableData *data2 = [NSMutableData dataWithCapacity:sizeof(unichar) * length]; 385 + 386 + // this block is common between GTMNSString+HTML and GTMNSString+XML but 387 + // it's so short that it isn't really worth trying to share. 388 + const unichar *buffer = CFStringGetCharactersPtr((CFStringRef)self); 389 + if (!buffer) { 390 + // We want this buffer to be autoreleased. 391 + NSMutableData *data = [NSMutableData dataWithLength:length * sizeof(UniChar)]; 392 + if (!data) { 393 + // COV_NF_START - Memory fail case 394 + // _GTMDevLog(@"couldn't alloc buffer"); 395 + return nil; 396 + // COV_NF_END 397 + } 398 + [self getCharacters:[data mutableBytes]]; 399 + buffer = [data bytes]; 400 + } 401 + 402 + if (!buffer || !data2) { 403 + // COV_NF_START 404 + // _GTMDevLog(@"Unable to allocate buffer or data2"); 405 + return nil; 406 + // COV_NF_END 407 + } 408 + 409 + unichar *buffer2 = (unichar *)[data2 mutableBytes]; 410 + 411 + NSUInteger buffer2Length = 0; 412 + 413 + for (NSUInteger i = 0; i < length; ++i) { 414 + HTMLEscapeMap *val = bsearch(&buffer[i], table, 415 + size / sizeof(HTMLEscapeMap), 416 + sizeof(HTMLEscapeMap), EscapeMapCompare); 417 + if (val || (escapeUnicode && buffer[i] > 127)) { 418 + if (buffer2Length) { 419 + CFStringAppendCharacters((CFMutableStringRef)finalString, 420 + buffer2, 421 + buffer2Length); 422 + buffer2Length = 0; 423 + } 424 + if (val) { 425 + [finalString appendString:val->escapeSequence]; 426 + } 427 + else { 428 + // _GTMDevAssert(escapeUnicode && buffer[i] > 127, @"Illegal Character"); 429 + [finalString appendFormat:@"&#%d;", buffer[i]]; 430 + } 431 + } else { 432 + buffer2[buffer2Length] = buffer[i]; 433 + buffer2Length += 1; 434 + } 435 + } 436 + if (buffer2Length) { 437 + CFStringAppendCharacters((CFMutableStringRef)finalString, 438 + buffer2, 439 + buffer2Length); 440 + } 441 + return finalString; 442 + } 443 + 444 + - (NSString *)gtm_stringByEscapingForHTML { 445 + return [self gtm_stringByEscapingHTMLUsingTable:gUnicodeHTMLEscapeMap 446 + ofSize:sizeof(gUnicodeHTMLEscapeMap) 447 + escapingUnicode:NO]; 448 + } // gtm_stringByEscapingHTML 449 + 450 + - (NSString *)gtm_stringByEscapingForAsciiHTML { 451 + return [self gtm_stringByEscapingHTMLUsingTable:gAsciiHTMLEscapeMap 452 + ofSize:sizeof(gAsciiHTMLEscapeMap) 453 + escapingUnicode:YES]; 454 + } // gtm_stringByEscapingAsciiHTML 455 + 456 + - (NSString *)gtm_stringByUnescapingFromHTML { 457 + NSRange range = NSMakeRange(0, [self length]); 458 + NSRange subrange = [self rangeOfString:@"&" options:NSBackwardsSearch range:range]; 459 + 460 + // if no ampersands, we've got a quick way out 461 + if (subrange.length == 0) return self; 462 + NSMutableString *finalString = [NSMutableString stringWithString:self]; 463 + do { 464 + NSRange semiColonRange = NSMakeRange(subrange.location, NSMaxRange(range) - subrange.location); 465 + semiColonRange = [self rangeOfString:@";" options:0 range:semiColonRange]; 466 + range = NSMakeRange(0, subrange.location); 467 + // if we don't find a semicolon in the range, we don't have a sequence 468 + if (semiColonRange.location == NSNotFound) { 469 + continue; 470 + } 471 + NSRange escapeRange = NSMakeRange(subrange.location, semiColonRange.location - subrange.location + 1); 472 + NSString *escapeString = [self substringWithRange:escapeRange]; 473 + NSUInteger length = [escapeString length]; 474 + // a squence must be longer than 3 (&lt;) and less than 11 (&thetasym;) 475 + if (length > 3 && length < 11) { 476 + if ([escapeString characterAtIndex:1] == '#') { 477 + unichar char2 = [escapeString characterAtIndex:2]; 478 + if (char2 == 'x' || char2 == 'X') { 479 + // Hex escape squences &#xa3; 480 + NSString *hexSequence = [escapeString substringWithRange:NSMakeRange(3, length - 4)]; 481 + NSScanner *scanner = [NSScanner scannerWithString:hexSequence]; 482 + unsigned value; 483 + if ([scanner scanHexInt:&value] && 484 + value < USHRT_MAX && 485 + value > 0 486 + && [scanner scanLocation] == length - 4) { 487 + unichar uchar = value; 488 + NSString *charString = [NSString stringWithCharacters:&uchar length:1]; 489 + [finalString replaceCharactersInRange:escapeRange withString:charString]; 490 + } 491 + 492 + } else { 493 + // Decimal Sequences &#123; 494 + NSString *numberSequence = [escapeString substringWithRange:NSMakeRange(2, length - 3)]; 495 + NSScanner *scanner = [NSScanner scannerWithString:numberSequence]; 496 + int value; 497 + if ([scanner scanInt:&value] && 498 + value < USHRT_MAX && 499 + value > 0 500 + && [scanner scanLocation] == length - 3) { 501 + unichar uchar = value; 502 + NSString *charString = [NSString stringWithCharacters:&uchar length:1]; 503 + [finalString replaceCharactersInRange:escapeRange withString:charString]; 504 + } 505 + } 506 + } else { 507 + // "standard" sequences 508 + for (unsigned i = 0; i < sizeof(gAsciiHTMLEscapeMap) / sizeof(HTMLEscapeMap); ++i) { 509 + if ([escapeString isEqualToString:gAsciiHTMLEscapeMap[i].escapeSequence]) { 510 + [finalString replaceCharactersInRange:escapeRange withString:[NSString stringWithCharacters:&gAsciiHTMLEscapeMap[i].uchar length:1]]; 511 + break; 512 + } 513 + } 514 + } 515 + } 516 + } while ((subrange = [self rangeOfString:@"&" options:NSBackwardsSearch range:range]).length != 0); 517 + return finalString; 518 + } // gtm_stringByUnescapingHTML 519 + 520 + 521 + 522 + @end
+67
NSString+HTML.h
··· 1 + // 2 + // NSString+HTML.h 3 + // MWFeedParser 4 + // 5 + // Copyright (c) 2010 Michael Waterfall 6 + // 7 + // Permission is hereby granted, free of charge, to any person obtaining a copy 8 + // of this software and associated documentation files (the "Software"), to deal 9 + // in the Software without restriction, including without limitation the rights 10 + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 + // copies of the Software, and to permit persons to whom the Software is 12 + // furnished to do so, subject to the following conditions: 13 + // 14 + // 1. The above copyright notice and this permission notice shall be included 15 + // in all copies or substantial portions of the Software. 16 + // 17 + // 2. This Software cannot be used to archive or collect data such as (but not 18 + // limited to) that of events, news, experiences and activities, for the 19 + // purpose of any concept relating to diary/journal keeping. 20 + // 21 + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 + // THE SOFTWARE. 28 + // 29 + 30 + #import <Foundation/Foundation.h> 31 + 32 + // Dependant upon GTMNSString+HTML 33 + 34 + @interface NSString (HTML) 35 + 36 + // Strips HTML tags & comments, removes extra whitespace and decodes HTML character entities. 37 + - (NSString *)stringByConvertingHTMLToPlainText; 38 + 39 + // Decode all HTML entities using GTM. 40 + - (NSString *)stringByDecodingHTMLEntities; 41 + 42 + // Encode all HTML entities using GTM. 43 + - (NSString *)stringByEncodingHTMLEntities; 44 + 45 + // Minimal unicode encoding will only cover characters from table 46 + // A.2.2 of http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters 47 + // which is what you want for a unicode encoded webpage. 48 + - (NSString *)stringByEncodingHTMLEntities:(BOOL)isUnicode; 49 + 50 + // Replace newlines with <br /> tags. 51 + - (NSString *)stringWithNewLinesAsBRs; 52 + 53 + // Remove newlines and white space from string. 54 + - (NSString *)stringByRemovingNewLinesAndWhitespace; 55 + 56 + // Wrap plain URLs in <a href="..." class="linkified">...</a> 57 + // - Ignores URLs inside tags (any URL beginning with =") 58 + // - HTTP & HTTPS schemes only 59 + // - Only works in iOS 4+ as we use NSRegularExpression (returns self if not supported so be careful with NSMutableStrings) 60 + // - Expression: (?<!=")\b((http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?) 61 + // - Adapted from http://regexlib.com/REDetails.aspx?regexp_id=96 62 + - (NSString *)stringByLinkifyingURLs; 63 + 64 + // DEPRECIATED - Please use NSString stringByConvertingHTMLToPlainText 65 + - (NSString *)stringByStrippingTags __attribute__((deprecated)); 66 + 67 + @end
+342
NSString+HTML.m
··· 1 + // 2 + // NSString+HTML.m 3 + // MWFeedParser 4 + // 5 + // Copyright (c) 2010 Michael Waterfall 6 + // 7 + // Permission is hereby granted, free of charge, to any person obtaining a copy 8 + // of this software and associated documentation files (the "Software"), to deal 9 + // in the Software without restriction, including without limitation the rights 10 + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 + // copies of the Software, and to permit persons to whom the Software is 12 + // furnished to do so, subject to the following conditions: 13 + // 14 + // 1. The above copyright notice and this permission notice shall be included 15 + // in all copies or substantial portions of the Software. 16 + // 17 + // 2. This Software cannot be used to archive or collect data such as (but not 18 + // limited to) that of events, news, experiences and activities, for the 19 + // purpose of any concept relating to diary/journal keeping. 20 + // 21 + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 + // THE SOFTWARE. 28 + // 29 + 30 + #import "NSString+HTML.h" 31 + #import "GTMNSString+HTML.h" 32 + 33 + @implementation NSString (HTML) 34 + 35 + #pragma mark - Instance Methods 36 + 37 + - (NSString *)stringByConvertingHTMLToPlainText { 38 + 39 + // Pool 40 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 41 + 42 + // Character sets 43 + NSCharacterSet *stopCharacters = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@"< \t\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]]; 44 + NSCharacterSet *newLineAndWhitespaceCharacters = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@" \t\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]]; 45 + NSCharacterSet *tagNameCharacters = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]; 46 + 47 + // Scan and find all tags 48 + NSMutableString *result = [[NSMutableString alloc] initWithCapacity:self.length]; 49 + NSScanner *scanner = [[NSScanner alloc] initWithString:self]; 50 + [scanner setCharactersToBeSkipped:nil]; 51 + [scanner setCaseSensitive:YES]; 52 + NSString *str = nil, *tagName = nil; 53 + BOOL dontReplaceTagWithSpace = NO; 54 + do { 55 + 56 + // Scan up to the start of a tag or whitespace 57 + if ([scanner scanUpToCharactersFromSet:stopCharacters intoString:&str]) { 58 + [result appendString:str]; 59 + str = nil; // reset 60 + } 61 + 62 + // Check if we've stopped at a tag/comment or whitespace 63 + if ([scanner scanString:@"<" intoString:NULL]) { 64 + 65 + // Stopped at a comment or tag 66 + if ([scanner scanString:@"!--" intoString:NULL]) { 67 + 68 + // Comment 69 + [scanner scanUpToString:@"-->" intoString:NULL]; 70 + [scanner scanString:@"-->" intoString:NULL]; 71 + 72 + } else { 73 + 74 + // Tag - remove and replace with space unless it's 75 + // a closing inline tag then dont replace with a space 76 + if ([scanner scanString:@"/" intoString:NULL]) { 77 + 78 + // Closing tag - replace with space unless it's inline 79 + tagName = nil; dontReplaceTagWithSpace = NO; 80 + if ([scanner scanCharactersFromSet:tagNameCharacters intoString:&tagName]) { 81 + tagName = [tagName lowercaseString]; 82 + dontReplaceTagWithSpace = ([tagName isEqualToString:@"a"] || 83 + [tagName isEqualToString:@"b"] || 84 + [tagName isEqualToString:@"i"] || 85 + [tagName isEqualToString:@"q"] || 86 + [tagName isEqualToString:@"span"] || 87 + [tagName isEqualToString:@"em"] || 88 + [tagName isEqualToString:@"strong"] || 89 + [tagName isEqualToString:@"cite"] || 90 + [tagName isEqualToString:@"abbr"] || 91 + [tagName isEqualToString:@"acronym"] || 92 + [tagName isEqualToString:@"label"]); 93 + } 94 + 95 + // Replace tag with string unless it was an inline 96 + if (!dontReplaceTagWithSpace && result.length > 0 && ![scanner isAtEnd]) [result appendString:@" "]; 97 + 98 + } 99 + 100 + // Scan past tag 101 + [scanner scanUpToString:@">" intoString:NULL]; 102 + [scanner scanString:@">" intoString:NULL]; 103 + 104 + } 105 + 106 + } else { 107 + 108 + // Stopped at whitespace - replace all whitespace and newlines with a space 109 + if ([scanner scanCharactersFromSet:newLineAndWhitespaceCharacters intoString:NULL]) { 110 + if (result.length > 0 && ![scanner isAtEnd]) [result appendString:@" "]; // Dont append space to beginning or end of result 111 + } 112 + 113 + } 114 + 115 + } while (![scanner isAtEnd]); 116 + 117 + // Cleanup 118 + [scanner release]; 119 + 120 + // Decode HTML entities and return 121 + NSString *retString = [[result stringByDecodingHTMLEntities] retain]; 122 + [result release]; 123 + 124 + // Drain 125 + [pool drain]; 126 + 127 + // Return 128 + return [retString autorelease]; 129 + 130 + } 131 + 132 + - (NSString *)stringByDecodingHTMLEntities { 133 + // Can return self so create new string if we're a mutable string 134 + return [NSString stringWithString:[self gtm_stringByUnescapingFromHTML]]; 135 + } 136 + 137 + 138 + - (NSString *)stringByEncodingHTMLEntities { 139 + // Can return self so create new string if we're a mutable string 140 + return [NSString stringWithString:[self gtm_stringByEscapingForAsciiHTML]]; 141 + } 142 + 143 + - (NSString *)stringByEncodingHTMLEntities:(BOOL)isUnicode { 144 + // Can return self so create new string if we're a mutable string 145 + return [NSString stringWithString:(isUnicode ? [self gtm_stringByEscapingForHTML] : [self gtm_stringByEscapingForAsciiHTML])]; 146 + } 147 + 148 + - (NSString *)stringWithNewLinesAsBRs { 149 + 150 + // Pool 151 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 152 + 153 + // Strange New lines: 154 + // Next Line, U+0085 155 + // Form Feed, U+000C 156 + // Line Separator, U+2028 157 + // Paragraph Separator, U+2029 158 + 159 + // Scanner 160 + NSScanner *scanner = [[NSScanner alloc] initWithString:self]; 161 + [scanner setCharactersToBeSkipped:nil]; 162 + NSMutableString *result = [[NSMutableString alloc] init]; 163 + NSString *temp; 164 + NSCharacterSet *newLineCharacters = [NSCharacterSet characterSetWithCharactersInString: 165 + [NSString stringWithFormat:@"\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]]; 166 + // Scan 167 + do { 168 + 169 + // Get non new line characters 170 + temp = nil; 171 + [scanner scanUpToCharactersFromSet:newLineCharacters intoString:&temp]; 172 + if (temp) [result appendString:temp]; 173 + temp = nil; 174 + 175 + // Add <br /> s 176 + if ([scanner scanString:@"\r\n" intoString:nil]) { 177 + 178 + // Combine \r\n into just 1 <br /> 179 + [result appendString:@"<br />"]; 180 + 181 + } else if ([scanner scanCharactersFromSet:newLineCharacters intoString:&temp]) { 182 + 183 + // Scan other new line characters and add <br /> s 184 + if (temp) { 185 + for (int i = 0; i < temp.length; i++) { 186 + [result appendString:@"<br />"]; 187 + } 188 + } 189 + 190 + } 191 + 192 + } while (![scanner isAtEnd]); 193 + 194 + // Cleanup & return 195 + [scanner release]; 196 + NSString *retString = [[NSString stringWithString:result] retain]; 197 + [result release]; 198 + 199 + // Drain 200 + [pool drain]; 201 + 202 + // Return 203 + return [retString autorelease]; 204 + 205 + } 206 + 207 + - (NSString *)stringByRemovingNewLinesAndWhitespace { 208 + 209 + // Pool 210 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 211 + 212 + // Strange New lines: 213 + // Next Line, U+0085 214 + // Form Feed, U+000C 215 + // Line Separator, U+2028 216 + // Paragraph Separator, U+2029 217 + 218 + // Scanner 219 + NSScanner *scanner = [[NSScanner alloc] initWithString:self]; 220 + [scanner setCharactersToBeSkipped:nil]; 221 + NSMutableString *result = [[NSMutableString alloc] init]; 222 + NSString *temp; 223 + NSCharacterSet *newLineAndWhitespaceCharacters = [NSCharacterSet characterSetWithCharactersInString: 224 + [NSString stringWithFormat:@" \t\n\r%C%C%C%C", 0x0085, 0x000C, 0x2028, 0x2029]]; 225 + // Scan 226 + while (![scanner isAtEnd]) { 227 + 228 + // Get non new line or whitespace characters 229 + temp = nil; 230 + [scanner scanUpToCharactersFromSet:newLineAndWhitespaceCharacters intoString:&temp]; 231 + if (temp) [result appendString:temp]; 232 + 233 + // Replace with a space 234 + if ([scanner scanCharactersFromSet:newLineAndWhitespaceCharacters intoString:NULL]) { 235 + if (result.length > 0 && ![scanner isAtEnd]) // Dont append space to beginning or end of result 236 + [result appendString:@" "]; 237 + } 238 + 239 + } 240 + 241 + // Cleanup 242 + [scanner release]; 243 + 244 + // Return 245 + NSString *retString = [[NSString stringWithString:result] retain]; 246 + [result release]; 247 + 248 + // Drain 249 + [pool drain]; 250 + 251 + // Return 252 + return [retString autorelease]; 253 + 254 + } 255 + 256 + - (NSString *)stringByLinkifyingURLs { 257 + if (!NSClassFromString(@"NSRegularExpression")) return self; 258 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 259 + NSString *pattern = @"(?<!=\")\\b((http|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%%&amp;:/~\\+#]*[\\w\\-\\@?^=%%&amp;/~\\+#])?)"; 260 + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil]; 261 + NSString *modifiedString = [[regex stringByReplacingMatchesInString:self options:0 range:NSMakeRange(0, [self length]) 262 + withTemplate:@"<a href=\"$1\" class=\"linkified\">$1</a>"] retain]; 263 + [pool drain]; 264 + return [modifiedString autorelease]; 265 + } 266 + 267 + - (NSString *)stringByStrippingTags { 268 + 269 + // Pool 270 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 271 + 272 + // Find first & and short-cut if we can 273 + NSUInteger ampIndex = [self rangeOfString:@"<" options:NSLiteralSearch].location; 274 + if (ampIndex == NSNotFound) { 275 + return [NSString stringWithString:self]; // return copy of string as no tags found 276 + } 277 + 278 + // Scan and find all tags 279 + NSScanner *scanner = [NSScanner scannerWithString:self]; 280 + [scanner setCharactersToBeSkipped:nil]; 281 + NSMutableSet *tags = [[NSMutableSet alloc] init]; 282 + NSString *tag; 283 + do { 284 + 285 + // Scan up to < 286 + tag = nil; 287 + [scanner scanUpToString:@"<" intoString:NULL]; 288 + [scanner scanUpToString:@">" intoString:&tag]; 289 + 290 + // Add to set 291 + if (tag) { 292 + NSString *t = [[NSString alloc] initWithFormat:@"%@>", tag]; 293 + [tags addObject:t]; 294 + [t release]; 295 + } 296 + 297 + } while (![scanner isAtEnd]); 298 + 299 + // Strings 300 + NSMutableString *result = [[NSMutableString alloc] initWithString:self]; 301 + NSString *finalString; 302 + 303 + // Replace tags 304 + NSString *replacement; 305 + for (NSString *t in tags) { 306 + 307 + // Replace tag with space unless it's an inline element 308 + replacement = @" "; 309 + if ([t isEqualToString:@"<a>"] || 310 + [t isEqualToString:@"</a>"] || 311 + [t isEqualToString:@"<span>"] || 312 + [t isEqualToString:@"</span>"] || 313 + [t isEqualToString:@"<strong>"] || 314 + [t isEqualToString:@"</strong>"] || 315 + [t isEqualToString:@"<em>"] || 316 + [t isEqualToString:@"</em>"]) { 317 + replacement = @""; 318 + } 319 + 320 + // Replace 321 + [result replaceOccurrencesOfString:t 322 + withString:replacement 323 + options:NSLiteralSearch 324 + range:NSMakeRange(0, result.length)]; 325 + } 326 + 327 + // Remove multi-spaces and line breaks 328 + finalString = [[result stringByRemovingNewLinesAndWhitespace] retain]; 329 + 330 + // Cleanup 331 + [result release]; 332 + [tags release]; 333 + 334 + // Drain 335 + [pool drain]; 336 + 337 + // Return 338 + return [finalString autorelease]; 339 + 340 + } 341 + 342 + @end
+27 -1
WKWindow.m
··· 1 1 #import "WKWindow.h" 2 2 #import "X11Window.h" 3 + #import "NSString+HTML.h" 3 4 4 5 #include <Cocoa/Cocoa.h> 5 6 ··· 143 144 144 145 /* WebFrameLoadDelegate glue */ 145 146 147 + - (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame 148 + { 149 + NSString *message = [NSString stringWithFormat: 150 + @"<html>" 151 + @"<title>Failed to open page</title>" 152 + @"<body style=\"font-family: helvetica neue; font-size: 10pt;\">" 153 + @"<h2>:(</h2>" 154 + @"<p>Could not access the URL <strong>%@</strong></p>" 155 + @"<p>%@</p>" 156 + @"</body></html>", 157 + [[[[[frame provisionalDataSource] request] URL] absoluteString] 158 + stringByEncodingHTMLEntities], 159 + [[error localizedDescription] stringByEncodingHTMLEntities]]; 160 + 161 + [frame loadAlternateHTMLString:message baseURL:nil 162 + forUnreachableURL:[[[frame provisionalDataSource] request] URL]]; 163 + 164 + [self setStatus:@""]; 165 + } 166 + 167 + - (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame 168 + { 169 + return [self webView:sender didFailProvisionalLoadWithError:error 170 + forFrame:frame]; 171 + } 172 + 146 173 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame 147 174 { 148 175 if (frame != [sender mainFrame]) ··· 204 231 } 205 232 206 233 /* WebUIDelegate glue */ 207 - 208 234 209 235 @end
+12
shadowebkit.xcodeproj/project.pbxproj
··· 12 12 01126E40147D670500C654C9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 01126E3A147D670500C654C9 /* main.m */; }; 13 13 01126E43147D673500C654C9 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01126E42147D673500C654C9 /* Cocoa.framework */; }; 14 14 01126E48147D6CD500C654C9 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01126E47147D6CD500C654C9 /* WebKit.framework */; }; 15 + 015A8C09148E9CDE0021409D /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 015A8C08148E9CDE0021409D /* NSString+HTML.m */; }; 16 + 015A8C0E148E9D2A0021409D /* GTMNSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 015A8C0D148E9D2A0021409D /* GTMNSString+HTML.m */; }; 15 17 01F07C9E1484730800B515E7 /* X11Window.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F07C9D1484730800B515E7 /* X11Window.m */; }; 16 18 /* End PBXBuildFile section */ 17 19 ··· 35 37 01126E3A147D670500C654C9 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; 36 38 01126E42147D673500C654C9 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 37 39 01126E47147D6CD500C654C9 /* WebKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = WebKit.framework; path = ../../../System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 40 + 015A8C08148E9CDE0021409D /* NSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+HTML.m"; sourceTree = "<group>"; }; 41 + 015A8C0B148E9CFA0021409D /* NSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+HTML.h"; sourceTree = "<group>"; }; 42 + 015A8C0C148E9D2A0021409D /* GTMNSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSString+HTML.h"; sourceTree = "<group>"; }; 43 + 015A8C0D148E9D2A0021409D /* GTMNSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSString+HTML.m"; sourceTree = "<group>"; }; 38 44 01F07C9C1484730800B515E7 /* X11Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = X11Window.h; sourceTree = "<group>"; }; 39 45 01F07C9D1484730800B515E7 /* X11Window.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = X11Window.m; sourceTree = "<group>"; }; 40 46 /* End PBXFileReference section */ ··· 56 62 01126E19147D66D700C654C9 = { 57 63 isa = PBXGroup; 58 64 children = ( 65 + 015A8C0C148E9D2A0021409D /* GTMNSString+HTML.h */, 66 + 015A8C0D148E9D2A0021409D /* GTMNSString+HTML.m */, 67 + 015A8C0B148E9CFA0021409D /* NSString+HTML.h */, 68 + 015A8C08148E9CDE0021409D /* NSString+HTML.m */, 59 69 01F07C9C1484730800B515E7 /* X11Window.h */, 60 70 01F07C9D1484730800B515E7 /* X11Window.m */, 61 71 01126E38147D670500C654C9 /* WKWindow.h */, ··· 134 144 01126E3F147D670500C654C9 /* WKWindow.m in Sources */, 135 145 01126E40147D670500C654C9 /* main.m in Sources */, 136 146 01F07C9E1484730800B515E7 /* X11Window.m in Sources */, 147 + 015A8C09148E9CDE0021409D /* NSString+HTML.m in Sources */, 148 + 015A8C0E148E9D2A0021409D /* GTMNSString+HTML.m in Sources */, 137 149 ); 138 150 runOnlyForDeploymentPostprocessing = 0; 139 151 };