public
Description: Safari plug-in that allows web applications to post Growl notifications using Javascript.
Homepage: http://adityamukherjee.com/geekaholic/projects/growler
Clone URL: git://github.com/adityavm/growler.git
growler / GrowlerView.m
100644 194 lines (163 sloc) 5.84 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
//
// GrowlerView.m
// Growler
//
// Created by Aditya Mukherjee on 08/07/09.
// Copyright 2009 All rights reserved.
//
 
#import "GrowlerView.h"
#import </usr/include/objc/objc-class.h>
 
static BOOL PerformSwizzle(Class aClass, SEL orig_sel, SEL alt_sel, BOOL forInstance) {
    // First, make sure the class isn't nil
if (aClass) {
Method orig_method = nil, alt_method = nil;
 
// Next, look for the methods
if (forInstance) {
orig_method = class_getInstanceMethod(aClass, orig_sel);
alt_method = class_getInstanceMethod(aClass, alt_sel);
} else {
orig_method = class_getClassMethod(aClass, orig_sel);
alt_method = class_getClassMethod(aClass, alt_sel);
}
 
// If both are found, swizzle them
if (orig_method && alt_method) {
IMP temp;
 
temp = orig_method->method_imp;
orig_method->method_imp = alt_method->method_imp;
alt_method->method_imp = temp;
return YES;
} else {
// This bit stolen from SubEthaFari's source
NSLog(@"Growler: Original (selector %s) %@, Alternate (selector %s) %@",
orig_sel,
orig_method ? @"was found" : @"not found",
alt_sel,
alt_method ? @"was found" : @"not found");
}
} else {
NSLog(@"%@", @"Growler Error: No class to swizzle methods in");
}
 
return NO;
}
 
@implementation InTheBeginning
 
+ (void) load {
NSLog(@"%@", @"Plug-in Loaded");
 
PerformSwizzle(NSClassFromString(@"LocationChangeHandler"),
@selector(webView:didClearWindowObject:forFrame:),
@selector(my_webView:didClearWindowObject:forFrame:),
YES);
 
/* because this is called for plug-in initialization but
* Growler is out actual delegate. So I'm doing it here
*/
NSLog(@"Hello from Growler");//phew, it works
NSString *growlPath = [[[NSBundle bundleForClass:[Growler class]] privateFrameworksPath]
stringByAppendingPathComponent:@"Growl-WithInstaller.framework"];
NSBundle *growlBundle = [NSBundle bundleWithPath:growlPath];//to dynamically link to the framework
if (growlBundle && [growlBundle load])
[NSClassFromString(@"GrowlApplicationBridge") setGrowlDelegate:[[Growler alloc] init]]; // Register Growler as a Growl delegate
else
NSLog(@"Could not load Growl.framework");
}
@end
 
@implementation Growler
 
+ (void) initialize { }
 
- (void) setWindow : (id) arg {
_windowObj = arg;
}
 
- (id) getFavicon {
return [@"http://" stringByAppendingString:[[[[_windowObj valueForKey:@"window"] valueForKey:@"location"] valueForKey:@"host"] stringByAppendingString:@"/favicon.ico"]];
}
 
- (BOOL) isKeyDefined:(id)aKey forValues:(id)values{
BOOL result = YES;
@try {
[values valueForKey:aKey];
} @catch (NSException *e) {
result = NO;
}
return result;
}
 
- (NSDictionary *) registrationDictionaryForGrowl {//register the kind of notifications that Growl will get from us
// TODO: Differentiate notifications for different
// apps to allow fine tuning from Growl preferences
 
NSArray *notifications;
notifications = [NSArray arrayWithObject: @"Web Application"];
NSDictionary *dict;
dict = [NSDictionary dictionaryWithObjectsAndKeys:
notifications, GROWL_NOTIFICATIONS_ALL,
notifications, GROWL_NOTIFICATIONS_DEFAULT, nil];
 
return (dict);
}
 
// Decide which methods will be exposed to JavaScript code
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector
{
    if (selector == @selector(showGrowlNotification:) ||
selector == @selector(showNotification:::) ||
selector == @selector(growler:))
        return NO;
        
    return YES;
}
 
// Produce JavaScript-readable function names that will be
// mapped to our exposed plug-in methods
+ (NSString *)webScriptNameForSelector:(SEL)sel
{
    if (sel == @selector(showGrowlNotification:))
return @"showGrowlNotification";
else if (sel == @selector(showNotification:::))
return @"showNotification";
else if (sel == @selector(growler:))
return @"growler";
    
    return nil;
}
 
// Requested by the hosting Web Kit application to make JavaScript access possible
- (id)objectForWebScript
{
    return self;
}
 
- (void) growl : (WebScriptObject *) values {
NSLog(@"icon: %@", [self getFavicon]);
[NSClassFromString(@"GrowlApplicationBridge")
notifyWithTitle: [values valueForKey:@"title"]
description: [values valueForKey:@"description"]//epic wtf material
notificationName: @"Web Application"
iconData: ([self isKeyDefined:@"icon" forValues:values])
? [NSData dataWithContentsOfURL:[NSURL URLWithString:[values valueForKey:@"icon"]]]
: [NSData dataWithContentsOfURL:[NSURL URLWithString:[self getFavicon]]]
priority: 0
isSticky: ([self isKeyDefined:@"sticky" forValues:values])
? [[values valueForKey:@"sticky"] boolValue]
: NO
clickContext: nil];
}
 
- (void) showGrowlNotification : (WebScriptObject *) values {
[self growl:values];
}
 
- (void) showNotification : (NSString *)arg1 :(NSString *)arg2 :(NSString *)arg3 {
/* TODO: make this call to `[self growl]`, without crashing */
NSData *icon;
icon = ([arg3 isKindOfClass:NSClassFromString(@"WebUndefined")])
? [NSData dataWithContentsOfURL:[NSURL URLWithString:[self getFavicon]]]
: [NSData dataWithContentsOfURL:[NSURL URLWithString:arg3]];
 
[NSClassFromString(@"GrowlApplicationBridge")
notifyWithTitle:arg1
description:arg2
notificationName:@"Web Application"
iconData:icon
priority:0
isSticky:NO
clickContext:nil];
//[self growl:values];
}
 
- (void) growler : (WebScriptObject *) values {
[self growl:values];
}
@end
 
@implementation NSObject (gpGrowler)
 
- (void) my_webView:(id)arg1 didClearWindowObject:(id)arg2 forFrame:(id)arg3 { // this is SPARTA!
Growler *m = [[Growler alloc] init];
[m setWindow:arg2];
[arg2 setValue:m forKey:@"fluid"];
[arg2 setValue:m forKey:@"platform"];
[arg2 setValue:m forKey:@"growler"];
[self my_webView:arg1 didClearWindowObject:arg2 forFrame:arg3];
}
 
@end