PageRenderTime 388ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/Quicksilver/Code-QuickStepCore/QSObject_URLHandling.m

https://github.com/quicksilver/Quicksilver
Objective C | 265 lines | 198 code | 40 blank | 27 comment | 37 complexity | 5a4f178ad2af0e22026318918f4f14c8 MD5 | raw file
  1. #import "QSObject_URLHandling.h"
  2. #import "QSObject_FileHandling.h"
  3. #import "QSTypes.h"
  4. #import "QSResourceManager.h"
  5. #import "QSRegistry.h"
  6. #import "QSParser.h"
  7. #import "QSTaskController.h"
  8. #import <QSFoundation/QSFoundation.h>
  9. #define QSURLTypeParsersTableKey @"QSURLTypeParsersTableKey"
  10. @implementation QSURLObjectHandler
  11. // Object Handler Methods
  12. - (NSString *)identifierForObject:(QSObject *)object {
  13. return [object objectForType:QSURLType];
  14. }
  15. - (NSString *)detailsOfObject:(QSObject *)object {
  16. return [object objectForType:QSURLType];
  17. }
  18. #pragma mark Search URL Images
  19. - (NSImage *)getFavIcon:(NSString *)urlString {
  20. NSURL *favIconURL = [NSURL URLWithString:[urlString URLEncoding]];
  21. // URLs without a scheme, NSURL's 'host' method returns nil
  22. if (![favIconURL host]) {
  23. return nil;
  24. }
  25. NSString *favIconString = [NSString stringWithFormat:@"https://icons.duckduckgo.com/ip3/%@.ico", [favIconURL host]];
  26. NSImage *favicon = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:favIconString]];
  27. return favicon;
  28. }
  29. - (void)buildWebSearchIconForObject:(QSObject *)object {
  30. NSImage *webSearchImage = nil;
  31. NSImage *image = [QSResourceManager imageNamed:@"DefaultBookmarkIcon"];
  32. if(!image) {
  33. return;
  34. }
  35. NSRect rect = NSMakeRect(0, 0, QSMaxIconSize.width, QSMaxIconSize.height);
  36. [image setSize:[[image bestRepresentationForSize:rect.size] size]];
  37. NSSize imageSize = [image size];
  38. NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
  39. pixelsWide:imageSize.width
  40. pixelsHigh:imageSize.height
  41. bitsPerSample:8
  42. samplesPerPixel:4
  43. hasAlpha:YES
  44. isPlanar:NO
  45. colorSpaceName:NSCalibratedRGBColorSpace
  46. bitmapFormat:0
  47. bytesPerRow:0
  48. bitsPerPixel:0];
  49. if(!bitmap) {
  50. return;
  51. }
  52. NSGraphicsContext *graphicsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap];
  53. if(!graphicsContext){
  54. return;
  55. }
  56. [NSGraphicsContext saveGraphicsState];
  57. [NSGraphicsContext setCurrentContext:graphicsContext];
  58. rect = NSMakeRect(0, 0, imageSize.width, imageSize.height);
  59. [image setSize:rect.size];
  60. [image drawInRect:rect fromRect:rectFromSize([image size]) operation:NSCompositingOperationSourceOver fraction:1.0];
  61. NSImage *findImage = [[QSResourceManager imageNamed:@"Find"] copy];
  62. NSImage *favIcon = nil;
  63. if(findImage) {
  64. [findImage setSize:rect.size];
  65. // Try and load the site's favicon
  66. favIcon = [self getFavIcon:[object objectForType:QSURLType]];
  67. if(favIcon) {
  68. [favIcon setSize:rect.size];
  69. [favIcon drawInRect:NSMakeRect(rect.origin.x+NSWidth(rect)*0.48, rect.origin.y+NSWidth(rect)*0.32, 30, 30) fromRect:rect operation:NSCompositingOperationSourceOver fraction:1.0];
  70. }
  71. [findImage drawInRect:NSMakeRect(rect.origin.x+NSWidth(rect) *1/3, rect.origin.y, NSWidth(rect)*2/3, NSHeight(rect)*2/3) fromRect:rect operation:NSCompositingOperationSourceOver fraction:1.0];
  72. }
  73. [NSGraphicsContext restoreGraphicsState];
  74. webSearchImage = [[NSImage alloc] initWithData:[bitmap TIFFRepresentation]];
  75. NSImageRep *fav16 = [favIcon bestRepresentationForSize:(NSSize){16.0f, 16.0f}];
  76. if (fav16) [webSearchImage addRepresentation:fav16];
  77. [webSearchImage setName:@"Web Search Icon"];
  78. [object setIcon:webSearchImage];
  79. [object setRetainsIcon:YES];
  80. }
  81. #pragma mark image handling
  82. - (void)setQuickIconForObject:(QSObject *)object {
  83. if ([[object types] containsObject:QSEmailAddressType])
  84. [object setIcon:[QSResourceManager imageNamed:@"ContactEmail"]];
  85. else if ([[object objectForType:QSURLType] hasPrefix:@"ftp:"])
  86. [object setIcon:[QSResourceManager imageNamed:@"InternetLocationFTP"]];
  87. else
  88. [object setIcon:[QSResourceManager imageNamed:@"DefaultBookmarkIcon"]];
  89. }
  90. /*!
  91. * @drawIconForObject
  92. * @abstract Special handler for drawing the objects image on screen
  93. * @discussion Currently does not handle any drawing operations and retruns NO.
  94. *
  95. * @param object The object to draw an image of
  96. * @param inRect The size of the rectangle drawing area
  97. * @param flipped Does the image need to be flipped prior to drawing
  98. * @result Returns YES if the function handled drawing of the object, otherwise
  99. * returns NO.
  100. */
  101. - (BOOL)drawIconForObject:(QSObject *)object inRect:(NSRect)rect flipped:(BOOL)flipped {
  102. return NO;
  103. }
  104. - (BOOL)loadIconForObject:(QSObject *)object {
  105. NSString *urlString = [object objectForType:QSURLType];
  106. if (!urlString) return NO;
  107. // For search URLs
  108. if([object containsType:QSSearchURLType]) {
  109. [self buildWebSearchIconForObject:object];
  110. return YES;
  111. }
  112. // For images that are links on web pages
  113. NSString *imageURL = [object objectForMeta:kQSObjectIconName];
  114. if (imageURL) {
  115. // initWithContentsOfURL accounts for the URL being dynamic
  116. NSImage *image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:imageURL]];
  117. if (image) {
  118. [object setIcon:image];
  119. return YES;
  120. }
  121. }
  122. [object setRetainsIcon:YES];
  123. return NO;
  124. }
  125. #pragma mark children
  126. - (BOOL)objectHasChildren:(QSObject *)object {
  127. // Need a list of TLDs to compare
  128. static NSArray *tldArray = nil;
  129. if(tldArray == nil) {
  130. tldArray = [NSArray arrayWithObjects:@"AC",@"AD",@"AE",@"AERO",@"AF",@"AG",@"AI",@"AL",@"AM",@"AN",@"AO",@"AQ",@"AR",@"ARPA",@"AS",@"ASIA",@"AT",@"AU",@"AW",@"AX",@"AZ",@"BA",@"BB",@"BD",@"BE",@"BF",@"BG",@"BH",@"BI",@"BIZ",
  131. @"BJ",@"BM",@"BN",@"BO",@"BR",@"BS",@"BT",@"BV",@"BW",@"BY",@"BZ",@"CA",@"CAT",@"CC",@"CD",@"CF",@"CG",@"CH",@"CI",@"CK",@"CL",@"CM",@"CN",@"CO",@"COM",@"COOP",@"CR",@"CU",@"CV",@"CX",@"CY",@"CZ",@"DE",@"DJ",@"DK",
  132. @"DM",@"DO",@"DZ",@"EC",@"EDU",@"EE",@"EG",@"ER",@"ES",@"ET",@"EU",@"FI",@"FJ",@"FK",@"FM",@"FO",@"FR",@"GA",@"GB",@"GD",@"GE",@"GF",@"GG",@"GH",@"GI",@"GL",@"GM",@"GN",@"GOV",@"GP",@"GQ",@"GR",@"GS",@"GT",@"GU",
  133. @"GW",@"GY",@"HK",@"HM",@"HN",@"HR",@"HT",@"HU",@"ID",@"IE",@"IL",@"IM",@"IN",@"INFO",@"INT",@"IO",@"IQ",@"IR",@"IS",@"IT",@"JE",@"JM",@"JO",@"JOBS",@"JP",@"KE",@"KG",@"KH",@"KI",@"KM",@"KN",@"KP",@"KR",@"KW",@"KY",
  134. @"KZ",@"LA",@"LB",@"LC",@"LI",@"LK",@"LR",@"LS",@"LT",@"LU",@"LV",@"LY",@"MA",@"MC",@"MD",@"ME",@"MG",@"MH",@"MIL",@"MK",@"ML",@"MM",@"MN",@"MO",@"MOBI",@"MP",@"MQ",@"MR",@"MS",@"MT",@"MU",@"MUSEUM",@"MV",@"MW",@"MX",
  135. @"MY",@"MZ",@"NA",@"NAME",@"NC",@"NE",@"NET",@"NF",@"NG",@"NI",@"NL",@"NO",@"NP",@"NR",@"NU",@"NZ",@"OM",@"ORG",@"PA",@"PE",@"PF",@"PG",@"PH",@"PK",@"PL",@"PM",@"PN",@"PR",@"PRO",@"PS",@"PT",@"PW",@"PY",@"QA",@"RE",@"RO",
  136. @"RS",@"RU",@"RW",@"SA",@"SB",@"SC",@"SD",@"SE",@"SG",@"SH",@"SI",@"SJ",@"SK",@"SL",@"SM",@"SN",@"SO",@"SR",@"ST",@"SU",@"SV",@"SY",@"SZ",@"TC",@"TD",@"TEL",@"TF",@"TG",@"TH",@"TJ",@"TK",@"TL",@"TM",@"TN",@"TO",@"TP",@"TR",
  137. @"TRAVEL",@"TT",@"TV",@"TW",@"TZ",@"UA",@"UG",@"UK",@"US",@"UY",@"UZ",@"VA",@"VC",@"VE",@"VG",@"VI",@"VN",@"VU",@"WF",@"WS",@"XXX",@"YE",@"YT",@"ZA",@"ZM",@"ZW",nil];
  138. }
  139. NSString *urlString = [object objectForType:QSURLType];
  140. // Check the extension of the URL. We're looking for a tld, .php, .html or .htm (set in QSCorePlugin-Info.plist)
  141. NSString *URLExtension = [[[urlString pathExtension] componentsSeparatedByString:@"?"] objectAtIndex:0];
  142. // Check if the URL is a tld
  143. if(URLExtension.length > 0 && [tldArray containsObject:[URLExtension uppercaseString]]) {
  144. URLExtension = @"tld";
  145. }
  146. id <QSParser> parser = [QSReg instanceForKey:URLExtension inTable:@"QSURLTypeParsers"];
  147. if (parser) {
  148. // Store the key for the QSURLTypeParsers table (see QSReg) to save having to load the URL extension again
  149. [object setObject:URLExtension forMeta:QSURLTypeParsersTableKey];
  150. return YES;
  151. }
  152. return NO;
  153. }
  154. - (BOOL)loadChildrenForObject:(QSObject *)object {
  155. QSTask *task = [QSTask taskWithIdentifier:@"DownloadPage"];
  156. task.status = NSLocalizedString(@"Downloading Page", @"");
  157. task.showProgress = NO;
  158. [task start];
  159. if (![object objectForMeta:QSURLTypeParsersTableKey]) {
  160. // it's possible `objectHasChildren:` was never called to populate this
  161. [self objectHasChildren:object];
  162. }
  163. id <QSParser> parser = [QSReg instanceForKey:[object objectForMeta:QSURLTypeParsersTableKey] inTable:@"QSURLTypeParsers"];
  164. NSArray *children = [parser objectsFromURL:[NSURL URLWithString:[object objectForType:QSURLType]] withSettings:nil];
  165. [task stop];
  166. if (children) {
  167. [object setChildren:children];
  168. return YES;
  169. }
  170. return NO;
  171. }
  172. @end
  173. @implementation QSObject (URLHandling)
  174. + (QSObject *)URLObjectWithURL:(NSString *)urlString title:(NSString *)title {
  175. if ([urlString hasPrefix:@"file://"] || [urlString hasPrefix:@"/"]) {
  176. return [QSObject fileObjectWithPath:[[NSURL URLWithString:urlString] path]];
  177. }
  178. return [[QSObject alloc] initWithURL:urlString title:title];
  179. }
  180. - (NSString *)cleanQueryURL:(NSString *)query {
  181. //NSLog(@"query %@", query);
  182. if ([query rangeOfString:@"\%s"] .location != NSNotFound) {
  183. //NSLog(@"%@ > %@", query, [query stringByReplacing:@"\%s" with:QUERY_KEY]);
  184. return [query stringByReplacingOccurrencesOfString:@"\%s" withString:QUERY_KEY];
  185. }
  186. return query;
  187. }
  188. - (id)initWithURL:(NSString *)urlString title:(NSString *)title {
  189. if (!urlString) {
  190. return nil;
  191. }
  192. if (self = [self init]) {
  193. urlString = [self cleanQueryURL:urlString];
  194. [self setName:(title?title:urlString)];
  195. [self assignURLTypesWithURL:urlString];
  196. }
  197. return self;
  198. }
  199. - (void)assignURLTypesWithURL:(NSString *)urlString
  200. {
  201. [[self dataDictionary] setObject:urlString forKey:QSURLType];
  202. // Apple's 'URLWithString' method incorrectly deals with IP addresses, check for "://" and "mailto:" in the string as well
  203. if (([urlString rangeOfString:@"://"].location != NSNotFound || [urlString hasPrefix:@"mailto:"] || [urlString hasPrefix:@"javascript:"]) && [[NSURL URLWithString:[urlString URLEncoding]] scheme])
  204. {
  205. [self setObject:urlString forType:QSURLType];
  206. } else {
  207. // a plain string (host or FQDN?) was passed - add a scheme prefix
  208. [self setObject:[@"http://" stringByAppendingString:urlString] forType:QSURLType];
  209. }
  210. if ([urlString hasPrefix:@"mailto:"]) {
  211. NSString *email = [urlString substringWithRange:NSMakeRange(7, [urlString length] -7)];
  212. [self setObject:email forType:QSEmailAddressType];
  213. [self setObject:email forType:QSTextType];
  214. [self setPrimaryType:QSEmailAddressType];
  215. } else {
  216. [self setObject:urlString forType:QSTextType];
  217. [self setPrimaryType:QSURLType];
  218. }
  219. // search URLs
  220. if ([urlString rangeOfString:QUERY_KEY].location != NSNotFound) {
  221. [self setObject:urlString forType:QSSearchURLType];
  222. [self setPrimaryType:QSSearchURLType];
  223. }
  224. }
  225. @end