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