adium 5421:b53d5958872b: Added the ability for JS-based plugins ...
commits at adium.im
commits at adium.im
Tue Apr 16 18:26:44 UTC 2013
details: http://hg.adium.im/adium/rev/b53d5958872b
revision: 5421:b53d5958872b
branch: JSXtras
author: Thijs Alkemade <thijsalkemade at gmail.com>
date: Tue Apr 16 20:24:51 2013 +0200
Added the ability for JS-based plugins to register a content filter.
Also added an event that is fired any time new messages are appended to the chat.
diffs (280 lines):
diff -r acd4ea48abd0 -r b53d5958872b Adium.xcodeproj/project.pbxproj
--- a/Adium.xcodeproj/project.pbxproj Wed Mar 20 14:50:47 2013 +0100
+++ b/Adium.xcodeproj/project.pbxproj Tue Apr 16 20:24:51 2013 +0200
@@ -1350,6 +1350,8 @@
6EC1684D06C170A000F9FAD3 /* DCInviteToChatPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EC1684806C170A000F9FAD3 /* DCInviteToChatPlugin.m */; };
6EC1684F06C170A000F9FAD3 /* DCInviteToChatWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EC1684A06C170A000F9FAD3 /* DCInviteToChatWindowController.m */; };
6EC1685006C170A000F9FAD3 /* InviteToChatWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6EC1684B06C170A000F9FAD3 /* InviteToChatWindow.xib */; };
+ 7616273C16FF5E230051A792 /* AIJSHTMLContentFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 7616273B16FF5E230051A792 /* AIJSHTMLContentFilter.m */; };
+ 7616274116FF81540051A792 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7616274016FF81540051A792 /* JavaScriptCore.framework */; };
761CE33916F9087C000EE361 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 761CE33816F9087C000EE361 /* Cocoa.framework */; };
761CE34E16F90950000EE361 /* AIDockTilePlugin.docktileplugin in CopyFiles */ = {isa = PBXBuildFile; fileRef = 761CE33616F9087C000EE361 /* AIDockTilePlugin.docktileplugin */; };
761CE35416F90A03000EE361 /* AIDockTile.m in Sources */ = {isa = PBXBuildFile; fileRef = 761CE35316F90A03000EE361 /* AIDockTile.m */; };
@@ -4412,6 +4414,9 @@
6FB330A20C7235BF00B001A8 /* EKEzvIncomingFileTransfer.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = EKEzvIncomingFileTransfer.m; path = Plugins/Bonjour/libezv/Classes/EKEzvIncomingFileTransfer.m; sourceTree = SOURCE_ROOT; };
6FB330A30C7235BF00B001A8 /* EKEzvOutgoingFileTransfer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = EKEzvOutgoingFileTransfer.h; path = Plugins/Bonjour/libezv/Classes/EKEzvOutgoingFileTransfer.h; sourceTree = SOURCE_ROOT; };
6FB330A40C7235BF00B001A8 /* EKEzvOutgoingFileTransfer.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = EKEzvOutgoingFileTransfer.m; path = Plugins/Bonjour/libezv/Classes/EKEzvOutgoingFileTransfer.m; sourceTree = SOURCE_ROOT; };
+ 7616273A16FF5E230051A792 /* AIJSHTMLContentFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIJSHTMLContentFilter.h; path = "Plugins/WebKit Message View/AIJSHTMLContentFilter.h"; sourceTree = "<group>"; };
+ 7616273B16FF5E230051A792 /* AIJSHTMLContentFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIJSHTMLContentFilter.m; path = "Plugins/WebKit Message View/AIJSHTMLContentFilter.m"; sourceTree = "<group>"; };
+ 7616274016FF81540051A792 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = ../../../../../../System/Library/Frameworks/JavaScriptCore.framework; sourceTree = "<group>"; };
761CE33616F9087C000EE361 /* AIDockTilePlugin.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AIDockTilePlugin.docktileplugin; sourceTree = BUILT_PRODUCTS_DIR; };
761CE33816F9087C000EE361 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
761CE33B16F9087C000EE361 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -4851,6 +4856,7 @@
76731DE215F90538007728C3 /* libgcrypt.framework in Frameworks */,
76731DE315F90538007728C3 /* libgpgerror.framework in Frameworks */,
7664EAA5162E086A008CF995 /* libffi.framework in Frameworks */,
+ 7616274116FF81540051A792 /* JavaScriptCore.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -8196,6 +8202,7 @@
761CE33A16F9087C000EE361 /* Other Frameworks */ = {
isa = PBXGroup;
children = (
+ 7616274016FF81540051A792 /* JavaScriptCore.framework */,
761CE33B16F9087C000EE361 /* AppKit.framework */,
761CE33C16F9087C000EE361 /* CoreData.framework */,
761CE33D16F9087C000EE361 /* Foundation.framework */,
@@ -8423,6 +8430,8 @@
3470663806015FB6004F0D20 /* WebKit Additions */,
347065E406015DC5004F0D20 /* WebKit Defaults.plist */,
348EE1B3080A57480015E0FC /* WebKitPreferencesView.xib */,
+ 7616273A16FF5E230051A792 /* AIJSHTMLContentFilter.h */,
+ 7616273B16FF5E230051A792 /* AIJSHTMLContentFilter.m */,
);
name = "WebKit Message View";
sourceTree = "<group>";
@@ -10606,6 +10615,7 @@
761D58831636EDE100210B12 /* AINewMessageTextFieldCell.m in Sources */,
761D58861636F94300210B12 /* AINewMessageSearchField.m in Sources */,
5A44595E169143130078AB0A /* AIPreferenceCVPrototypeView.m in Sources */,
+ 7616273C16FF5E230051A792 /* AIJSHTMLContentFilter.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff -r acd4ea48abd0 -r b53d5958872b Plugins/WebKit Message View/AIJSHTMLContentFilter.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/WebKit Message View/AIJSHTMLContentFilter.h Tue Apr 16 20:24:51 2013 +0200
@@ -0,0 +1,26 @@
+//
+// AIJSHTMLContentFilter.h
+// Adium
+//
+// Created by Thijs Alkemade on 24-03-13.
+// Copyright (c) 2013 The Adium Team. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "AdiumContentFiltering.h"
+#import <WebKit/WebKit.h>
+
+ at interface AIJSHTMLContentFilter : NSObject <AIHTMLContentFilter> {
+ CGFloat priority;
+ WebScriptObject *func;
+ AIChat *chat;
+
+ WebView *view;
+}
+
+ at property (nonatomic, retain) WebScriptObject *func;
+ at property (nonatomic, retain) AIChat *chat;
+ at property (nonatomic, retain) WebView *view;
+ at property (assign) CGFloat priority;
+
+ at end
diff -r acd4ea48abd0 -r b53d5958872b Plugins/WebKit Message View/AIJSHTMLContentFilter.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/WebKit Message View/AIJSHTMLContentFilter.m Tue Apr 16 20:24:51 2013 +0200
@@ -0,0 +1,56 @@
+//
+// AIJSHTMLContentFilter.m
+// Adium
+//
+// Created by Thijs Alkemade on 24-03-13.
+// Copyright (c) 2013 The Adium Team. All rights reserved.
+//
+
+#import "AIJSHTMLContentFilter.h"
+#import <Adium/AIContentControllerProtocol.h>
+#import <JavaScriptCore/JavaScriptCore.h>
+
+ at implementation AIJSHTMLContentFilter
+
+ at synthesize chat, func, view, priority;
+
+- (NSString *)filterHTMLString:(NSString *)inHTMLString content:(AIContentObject*)content
+{
+ if ([content chat] != self.chat) return inHTMLString;
+
+ JSObjectRef ref = [self.func JSObject];
+ JSContextRef ctx = [[self.view mainFrame] globalContext];
+ JSStringRef str = JSStringCreateWithCFString((CFStringRef)inHTMLString);
+ JSValueRef args[1] = { JSValueMakeString(ctx, str) };
+
+ JSValueRef result = JSObjectCallAsFunction(ctx, ref, NULL, 1, args, NULL);
+
+ JSStringRelease(str);
+
+ if (!result) return inHTMLString;
+
+ JSStringRef resultJSString = JSValueToStringCopy(ctx, result, NULL);
+ CFStringRef resultString = JSStringCopyCFString(kCFAllocatorDefault, resultJSString);
+
+ JSStringRelease(resultJSString);
+
+ return [[(NSString *)resultString retain] autorelease];
+}
+
+- (CGFloat)filterPriority
+{
+ return priority;
+}
+
+- (void)dealloc
+{
+ [func release]; func = nil;
+ [chat release]; chat = nil;
+ [view release]; view = nil;
+
+ [adium.contentController unregisterHTMLContentFilter:self];
+
+ [super dealloc];
+}
+
+ at end
diff -r acd4ea48abd0 -r b53d5958872b Plugins/WebKit Message View/AIWebKitMessageViewController.h
--- a/Plugins/WebKit Message View/AIWebKitMessageViewController.h Wed Mar 20 14:50:47 2013 +0100
+++ b/Plugins/WebKit Message View/AIWebKitMessageViewController.h Tue Apr 16 20:24:51 2013 +0200
@@ -52,6 +52,8 @@
//Focus tracking
BOOL nextMessageFocus;
BOOL nextMessageRegainedFocus;
+
+ NSMutableArray *jsContentFilters;
}
/*!
diff -r acd4ea48abd0 -r b53d5958872b Plugins/WebKit Message View/AIWebKitMessageViewController.m
--- a/Plugins/WebKit Message View/AIWebKitMessageViewController.m Wed Mar 20 14:50:47 2013 +0100
+++ b/Plugins/WebKit Message View/AIWebKitMessageViewController.m Tue Apr 16 20:24:51 2013 +0200
@@ -15,6 +15,7 @@
*/
#import "AIWebKitMessageViewController.h"
+#import "AIJSHTMLContentFilter.h"
#import "AIWebKitMessageViewStyle.h"
#import "AIWebKitMessageViewPlugin.h"
#import "ESWebKitMessageViewPreferences.h"
@@ -38,6 +39,7 @@
#import <Adium/AIMetaContact.h>
#import <Adium/AIListObject.h>
#import <Adium/AIService.h>
+#import <Adium/AIContentControllerProtocol.h>
#import <Adium/ESFileTransfer.h>
#import <Adium/ESTextAndButtonsWindowController.h>
#import <Adium/AIHTMLDecoder.h>
@@ -123,6 +125,7 @@
contentQueue = [[NSMutableArray alloc] init];
objectIconPathDict = [[NSMutableDictionary alloc] init];
objectsWithUserIconsArray = [[NSMutableArray alloc] init];
+ jsContentFilters = [[NSMutableArray alloc] init];
shouldReflectPreferenceChanges = NO;
storedContentObjects = nil;
/* If we receive content before gaining focus, we'll want to know the first content received is the first to be
@@ -219,6 +222,8 @@
//Release the chat
[chat release]; chat = nil;
+
+ [jsContentFilters release]; jsContentFilters = nil;
//Release the marked scroller
[self.markedScroller release];
@@ -623,7 +628,7 @@
//Display the content
AIContentObject *content = [contentQueue objectAtIndex:0];
- [self _processContentObject:content
+ [self _processContentObject:content
willAddMoreContentObjects:willAddMoreContent];
//If we are going to reflect preference changes, store this content object
@@ -648,6 +653,13 @@
[webView stringByEvaluatingJavaScriptFromString:scrollToBottomScript];
}
}
+
+ if (objectsAdded > 0) {
+ [webView stringByEvaluatingJavaScriptFromString:
+ @"var evt = document.createEvent(\"Event\");\n"
+ @"evt.initEvent(\"contentAdded\", true, true);\n"
+ @"document.dispatchEvent(evt);"];
+ }
//If there is still content to process (the webview wasn't ready), we'll try again after a brief delay
if (contentQueueCount) {
@@ -670,7 +682,7 @@
BOOL replaceLastContent = NO;
/*
- If the day has changed since our last message (or if there was no previous message and
+ If the day has changed since our last message (or if there was no previous message and
we are about to display context), insert a date line.
*/
if ((!previousContent && [content isKindOfClass:[AIContentContext class]]) ||
@@ -1654,13 +1666,45 @@
sel_isEqual(aSelector, @selector(handleAction:forFileTransfer:)) ||
sel_isEqual(aSelector, @selector(debugLog:)) ||
sel_isEqual(aSelector, @selector(zoomImage:)) ||
- sel_isEqual(aSelector, @selector(_setDocumentReady))
+ sel_isEqual(aSelector, @selector(_setDocumentReady)) ||
+ sel_isEqual(aSelector, @selector(registerHTMLContentFilter:direction:priority:))
)
return NO;
return YES;
}
+- (void)registerHTMLContentFilter:(WebScriptObject *)filter direction:(NSString *)direction priority:(NSNumber *)priority
+{
+ AIJSHTMLContentFilter *contentFilter = [[AIJSHTMLContentFilter alloc] init];
+ contentFilter.view = webView;
+ contentFilter.func = filter;
+ contentFilter.chat = chat;
+ contentFilter.priority = [priority doubleValue];
+
+ [jsContentFilters addObject:contentFilter];
+
+ BOOL incoming = FALSE, outgoing = FALSE;
+
+ if ([direction isEqualToString:@"both"]) {
+ incoming = TRUE;
+ outgoing = TRUE;
+ } else if ([direction isEqualToString:@"incoming"]) {
+ incoming = TRUE;
+ } else if ([direction isEqualToString:@"outgoing"]) {
+ outgoing = TRUE;
+ }
+
+ if (incoming) {
+ [adium.contentController registerHTMLContentFilter:contentFilter
+ direction:AIFilterIncoming];
+ }
+ if (outgoing) {
+ [adium.contentController registerHTMLContentFilter:contentFilter
+ direction:AIFilterOutgoing];
+ }
+}
+
/*
* This method returns the name to be used in the scripting environment for the selector specified by aSelector.
* It is your responsibility to ensure that the returned name is unique to the script invoking this method.
@@ -1675,6 +1719,7 @@
if (sel_isEqual(aSelector, @selector(handleAction:forFileTransfer:))) return @"handleFileTransfer";
if (sel_isEqual(aSelector, @selector(debugLog:))) return @"debugLog";
if (sel_isEqual(aSelector, @selector(zoomImage:))) return @"zoomImage";
+ if (sel_isEqual(aSelector, @selector(registerHTMLContentFilter:direction:priority:))) return @"registerFilter";
return @"";
}
More information about the commits
mailing list