Merge branch 'feature/refactor_autotype'

This commit is contained in:
Michael Starke
2020-01-22 15:11:41 +01:00
9 changed files with 307 additions and 155 deletions

View File

@@ -41,6 +41,8 @@
4C1F7FA21E3A12E600D6A40E /* MPModifiedKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1F7FA11E3A12E600D6A40E /* MPModifiedKey.m */; }; 4C1F7FA21E3A12E600D6A40E /* MPModifiedKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1F7FA11E3A12E600D6A40E /* MPModifiedKey.m */; };
4C1FA07B18231900003A3F8C /* MPDocument+Autotype.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1FA07A18231900003A3F8C /* MPDocument+Autotype.m */; }; 4C1FA07B18231900003A3F8C /* MPDocument+Autotype.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1FA07A18231900003A3F8C /* MPDocument+Autotype.m */; };
4C2057EE23CDF6F900C731EC /* MPPathCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C2057ED23CDF6F900C731EC /* MPPathCell.m */; }; 4C2057EE23CDF6F900C731EC /* MPPathCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C2057ED23CDF6F900C731EC /* MPPathCell.m */; };
4C2057F423CF3BA600C731EC /* MPAutotypeEnvironment.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C2057F323CF3BA600C731EC /* MPAutotypeEnvironment.m */; };
4C2057F723CF3E9A00C731EC /* NSRunningApplication+MPAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C2057F623CF3E9A00C731EC /* NSRunningApplication+MPAdditions.m */; };
4C224B4217DFCB2400FF6AEE /* MPNumericalInputFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C224B4117DFCB2400FF6AEE /* MPNumericalInputFormatter.m */; }; 4C224B4217DFCB2400FF6AEE /* MPNumericalInputFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C224B4117DFCB2400FF6AEE /* MPNumericalInputFormatter.m */; };
4C25703F1BF11C2300D39416 /* MPPluginPreferencesController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C25703D1BF11C2300D39416 /* MPPluginPreferencesController.m */; }; 4C25703F1BF11C2300D39416 /* MPPluginPreferencesController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C25703D1BF11C2300D39416 /* MPPluginPreferencesController.m */; };
4C25D58716CF0FAA00F6806C /* EntryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C25D58616CF0FAA00F6806C /* EntryView.xib */; }; 4C25D58716CF0FAA00F6806C /* EntryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C25D58616CF0FAA00F6806C /* EntryView.xib */; };
@@ -405,6 +407,10 @@
4C2057EC23CDF6F900C731EC /* MPPathCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPPathCell.h; sourceTree = "<group>"; }; 4C2057EC23CDF6F900C731EC /* MPPathCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPPathCell.h; sourceTree = "<group>"; };
4C2057ED23CDF6F900C731EC /* MPPathCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPPathCell.m; sourceTree = "<group>"; }; 4C2057ED23CDF6F900C731EC /* MPPathCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPPathCell.m; sourceTree = "<group>"; };
4C2057EF23CDFC2000C731EC /* MPPathControl+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MPPathControl+Private.h"; sourceTree = "<group>"; }; 4C2057EF23CDFC2000C731EC /* MPPathControl+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MPPathControl+Private.h"; sourceTree = "<group>"; };
4C2057F223CF3BA600C731EC /* MPAutotypeEnvironment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPAutotypeEnvironment.h; sourceTree = "<group>"; };
4C2057F323CF3BA600C731EC /* MPAutotypeEnvironment.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPAutotypeEnvironment.m; sourceTree = "<group>"; };
4C2057F523CF3E9A00C731EC /* NSRunningApplication+MPAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSRunningApplication+MPAdditions.h"; sourceTree = "<group>"; };
4C2057F623CF3E9A00C731EC /* NSRunningApplication+MPAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSRunningApplication+MPAdditions.m"; sourceTree = "<group>"; };
4C21F29F195B3A48002D610D /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = "<group>"; }; 4C21F29F195B3A48002D610D /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = "<group>"; };
4C224B4017DFCB2300FF6AEE /* MPNumericalInputFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNumericalInputFormatter.h; sourceTree = "<group>"; }; 4C224B4017DFCB2300FF6AEE /* MPNumericalInputFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNumericalInputFormatter.h; sourceTree = "<group>"; };
4C224B4117DFCB2400FF6AEE /* MPNumericalInputFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNumericalInputFormatter.m; sourceTree = "<group>"; }; 4C224B4117DFCB2400FF6AEE /* MPNumericalInputFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNumericalInputFormatter.m; sourceTree = "<group>"; };
@@ -1128,6 +1134,8 @@
4C58A4A22192EC1600B13370 /* NSIndexPath+MPAdditions.m */, 4C58A4A22192EC1600B13370 /* NSIndexPath+MPAdditions.m */,
3C0CDECD21CFED9000B2A10B /* NSTextView+MPTouchBarExtension.h */, 3C0CDECD21CFED9000B2A10B /* NSTextView+MPTouchBarExtension.h */,
3C0CDECE21CFEDD200B2A10B /* NSTextView+MPTouchBarExtension.m */, 3C0CDECE21CFEDD200B2A10B /* NSTextView+MPTouchBarExtension.m */,
4C2057F523CF3E9A00C731EC /* NSRunningApplication+MPAdditions.h */,
4C2057F623CF3E9A00C731EC /* NSRunningApplication+MPAdditions.m */,
); );
name = Categories; name = Categories;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1521,6 +1529,8 @@
4C90757B18A42E7A00E598DA /* Commands */, 4C90757B18A42E7A00E598DA /* Commands */,
4CEE46DB181C301D006BF1E5 /* MPAutotypeDaemon.h */, 4CEE46DB181C301D006BF1E5 /* MPAutotypeDaemon.h */,
4CEE46DC181C301D006BF1E5 /* MPAutotypeDaemon.m */, 4CEE46DC181C301D006BF1E5 /* MPAutotypeDaemon.m */,
4C2057F223CF3BA600C731EC /* MPAutotypeEnvironment.h */,
4C2057F323CF3BA600C731EC /* MPAutotypeEnvironment.m */,
4CD2B9041849424B0051B395 /* MPAutotypeContext.h */, 4CD2B9041849424B0051B395 /* MPAutotypeContext.h */,
4CD2B9051849424B0051B395 /* MPAutotypeContext.m */, 4CD2B9051849424B0051B395 /* MPAutotypeContext.m */,
4CA3530918A53CB800839B0F /* MPKeyMapper.h */, 4CA3530918A53CB800839B0F /* MPKeyMapper.h */,
@@ -2044,6 +2054,7 @@
4CE39AC416ECE4F7000FE29D /* MPIconImageView.m in Sources */, 4CE39AC416ECE4F7000FE29D /* MPIconImageView.m in Sources */,
4C46B88517063A070046109A /* NSString+MPPasswordCreation.m in Sources */, 4C46B88517063A070046109A /* NSString+MPPasswordCreation.m in Sources */,
4C5A11FE1708DE8700223D8A /* MPPasswordCreatorViewController.m in Sources */, 4C5A11FE1708DE8700223D8A /* MPPasswordCreatorViewController.m in Sources */,
4C2057F423CF3BA600C731EC /* MPAutotypeEnvironment.m in Sources */,
4CE5B54B173AFBA700207B39 /* MPDocument.m in Sources */, 4CE5B54B173AFBA700207B39 /* MPDocument.m in Sources */,
4CE082C31F6FCD2A0034FF56 /* MPCollectionView.m in Sources */, 4CE082C31F6FCD2A0034FF56 /* MPCollectionView.m in Sources */,
4C4A100F176286FD00BBF2CA /* MPTableView.m in Sources */, 4C4A100F176286FD00BBF2CA /* MPTableView.m in Sources */,
@@ -2092,6 +2103,7 @@
4CAD8AA622CF397B0090B2DD /* MPAutotypeDoctorReportViewController.m in Sources */, 4CAD8AA622CF397B0090B2DD /* MPAutotypeDoctorReportViewController.m in Sources */,
4C8990F71EE978EB0043B48D /* MPDuplicateEntryOptionsWindowController.m in Sources */, 4C8990F71EE978EB0043B48D /* MPDuplicateEntryOptionsWindowController.m in Sources */,
4CA3530B18A53CB800839B0F /* MPKeyMapper.m in Sources */, 4CA3530B18A53CB800839B0F /* MPKeyMapper.m in Sources */,
4C2057F723CF3E9A00C731EC /* NSRunningApplication+MPAdditions.m in Sources */,
4CE298EB1795FC2A00DF7BDB /* MPEntryContextMenuDelegate.m in Sources */, 4CE298EB1795FC2A00DF7BDB /* MPEntryContextMenuDelegate.m in Sources */,
4CCCE8011D75CA48006AA951 /* MPArrayController.m in Sources */, 4CCCE8011D75CA48006AA951 /* MPArrayController.m in Sources */,
4CC0D2CE17974A47000B4BDA /* MPCustomFieldTableViewDelegate.m in Sources */, 4CC0D2CE17974A47000B4BDA /* MPCustomFieldTableViewDelegate.m in Sources */,

View File

@@ -24,11 +24,13 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class MPAutotypeEnvironment;
@class MPAutotypeContext;
@interface MPAutotypeCandidateSelectionViewController : NSViewController @interface MPAutotypeCandidateSelectionViewController : NSViewController
@property (copy) NSArray *candidates; @property (strong) MPAutotypeEnvironment *environment;
@property (copy) NSString *windowTitle; @property (copy) NSArray<MPAutotypeContext *> *candidates;
@property (nonatomic, copy, nullable) void (^completionHandler)(void);
- (IBAction)selectAutotypeContext:(id)sender; - (IBAction)selectAutotypeContext:(id)sender;
- (IBAction)cancelSelection:(id)sender; - (IBAction)cancelSelection:(id)sender;

View File

@@ -22,6 +22,7 @@
#import "MPAutotypeCandidateSelectionViewController.h" #import "MPAutotypeCandidateSelectionViewController.h"
#import "MPAutotypeContext.h" #import "MPAutotypeContext.h"
#import "MPAutotypeDaemon.h" #import "MPAutotypeDaemon.h"
#import "MPAutotypeEnvironment.h"
#import "KPKNode+IconImage.h" #import "KPKNode+IconImage.h"
@@ -43,7 +44,7 @@
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
NSString *template = NSLocalizedString(@"AUTOTYPE_CANDIDATE_SELECTION_WINDOW_MESSAGE_%@", "Message text in the autotype selection window. Placeholder is %1 - windowTitle"); NSString *template = NSLocalizedString(@"AUTOTYPE_CANDIDATE_SELECTION_WINDOW_MESSAGE_%@", "Message text in the autotype selection window. Placeholder is %1 - windowTitle");
self.messageTextField.stringValue = [NSString stringWithFormat:template, self.windowTitle]; self.messageTextField.stringValue = [NSString stringWithFormat:template, self.environment.windowTitle];
self.selectAutotypeContextButton.enabled = NO; self.selectAutotypeContextButton.enabled = NO;
NSNotification *notification = [NSNotification notificationWithName:NSTableViewSelectionDidChangeNotification object:self.contextTableView]; NSNotification *notification = [NSNotification notificationWithName:NSTableViewSelectionDidChangeNotification object:self.contextTableView];
[self tableViewSelectionDidChange:notification]; [self tableViewSelectionDidChange:notification];
@@ -80,10 +81,7 @@
- (void)selectAutotypeContext:(id)sender { - (void)selectAutotypeContext:(id)sender {
NSInteger selectedRow = self.contextTableView.selectedRow; NSInteger selectedRow = self.contextTableView.selectedRow;
if(selectedRow >= 0 && selectedRow < self.candidates.count) { if(selectedRow >= 0 && selectedRow < self.candidates.count) {
if(self.completionHandler) { [MPAutotypeDaemon.defaultDaemon selectAutotypeContext:self.candidates[selectedRow] forEnvironment:self.environment];
self.completionHandler();
}
[MPAutotypeDaemon.defaultDaemon selectAutotypeCandiate:self.candidates[selectedRow]];
} }
else { else {
[self cancelSelection:sender]; // cancel since the selection was invalid! [self cancelSelection:sender]; // cancel since the selection was invalid!
@@ -91,10 +89,7 @@
} }
- (void)cancelSelection:(id)sender { - (void)cancelSelection:(id)sender {
if(self.completionHandler) { [MPAutotypeDaemon.defaultDaemon cancelAutotypeContextSelectionForEnvironment:self.environment];
self.completionHandler();
}
[MPAutotypeDaemon.defaultDaemon cancelAutotypeCandidateSelection];
} }

View File

@@ -25,7 +25,7 @@
@class DDHotKey; @class DDHotKey;
@class KPKEntry; @class KPKEntry;
@class MPAutotypeContext; @class MPAutotypeContext;
@class MPAutotypeExecutionContext; @class MPAutotypeEnvironment;
/** /**
* The autotype daemon is responsible for registering the global hotkey and to perform any autotype actions * The autotype daemon is responsible for registering the global hotkey and to perform any autotype actions
*/ */
@@ -40,7 +40,7 @@
- (void)performAutotypeForEntry:(KPKEntry *)entry; - (void)performAutotypeForEntry:(KPKEntry *)entry;
- (void)performAutotypeForEntry:(KPKEntry *)entry overrideSequence:(NSString *)sequence; - (void)performAutotypeForEntry:(KPKEntry *)entry overrideSequence:(NSString *)sequence;
- (void)selectAutotypeCandiate:(MPAutotypeContext *)context; - (void)selectAutotypeContext:(MPAutotypeContext *)context forEnvironment:(MPAutotypeEnvironment *)environment;
- (void)cancelAutotypeCandidateSelection; - (void)cancelAutotypeContextSelectionForEnvironment:(MPAutotypeEnvironment *)environment;
@end @end

View File

@@ -25,6 +25,7 @@
#import "MPDocumentWindowController.h" #import "MPDocumentWindowController.h"
#import "MPAutotypeCommand.h" #import "MPAutotypeCommand.h"
#import "MPAutotypeContext.h" #import "MPAutotypeContext.h"
#import "MPAutotypeEnvironment.h"
#import "MPAutotypePaste.h" #import "MPAutotypePaste.h"
#import "MPAutotypeDelay.h" #import "MPAutotypeDelay.h"
#import "MPPasteBoardController.h" #import "MPPasteBoardController.h"
@@ -45,16 +46,12 @@
#import "KeePassKit/KeePassKit.h" #import "KeePassKit/KeePassKit.h"
#import <Carbon/Carbon.h> #import <Carbon/Carbon.h>
NSString *const kMPWindowTitleKey = @"kMPWindowTitleKey";
NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey";
@interface MPAutotypeDaemon () @interface MPAutotypeDaemon ()
@property (nonatomic, assign) BOOL enabled; @property (nonatomic, assign) BOOL enabled;
@property (nonatomic, copy) NSData *hotKeyData; @property (nonatomic, copy) NSData *hotKeyData;
@property (strong) DDHotKey *registredHotKey; @property (strong) DDHotKey *registredHotKey;
@property (assign) pid_t targetPID; // The pid of the process we want to sent commands to
@property (copy) NSString *targetWindowTitle; // The title of the window that we are targeting
@property (strong) NSRunningApplication *previousApplication; // The application that was active before we got invoked @property (strong) NSRunningApplication *previousApplication; // The application that was active before we got invoked
@property (assign) NSTimeInterval userActionRequested; @property (assign) NSTimeInterval userActionRequested;
@property (strong) id applicationActivationObserver; @property (strong) id applicationActivationObserver;
@@ -85,7 +82,6 @@ static MPAutotypeDaemon *_sharedInstance;
self = [super init]; self = [super init];
if (self) { if (self) {
_enabled = NO; _enabled = NO;
_targetPID = -1;
_userActionRequested = NSDate.distantPast.timeIntervalSinceReferenceDate; _userActionRequested = NSDate.distantPast.timeIntervalSinceReferenceDate;
[self bind:NSStringFromSelector(@selector(enabled)) [self bind:NSStringFromSelector(@selector(enabled))
toObject:NSUserDefaultsController.sharedUserDefaultsController toObject:NSUserDefaultsController.sharedUserDefaultsController
@@ -144,38 +140,55 @@ static MPAutotypeDaemon *_sharedInstance;
- (void)performAutotypeForEntry:(KPKEntry *)entry { - (void)performAutotypeForEntry:(KPKEntry *)entry {
[self performAutotypeForEntry:entry overrideSequence:nil]; [self performAutotypeForEntry:entry overrideSequence:nil];
} }
- (void)performAutotypeForEntry:(KPKEntry *)entry overrideSequence:(NSString *)sequence { - (void)performAutotypeForEntry:(KPKEntry *)entry overrideSequence:(NSString *)sequence {
if(entry) { if(entry) {
[self _updateTargeInformationForApplication:self.previousApplication]; MPAutotypeEnvironment *env = [MPAutotypeEnvironment environmentWithTargetApplication:self.previousApplication entry:entry];
[self _performAutotypeForEntry:entry]; [self _runAutotypeWithEnvironment:env];
} }
} }
- (void)_didPressHotKey { - (void)_didPressHotKey {
[self _updateTargeInformationForApplication:NSWorkspace.sharedWorkspace.frontmostApplication]; MPAutotypeEnvironment *env = [MPAutotypeEnvironment environmentWithTargetApplication:NSWorkspace.sharedWorkspace.frontmostApplication entry:nil];
[self _performAutotypeForEntry:nil]; [self _runAutotypeWithEnvironment:env];
}
#pragma mark -
#pragma mark Actions
- (void)selectAutotypeCandiate:(MPAutotypeContext *)context {
[self.matchSelectionWindow orderOut:self];
self.matchSelectionWindow = nil;
[self _performAutotypeForContext:context];
}
- (void)cancelAutotypeCandidateSelection {
[self.matchSelectionWindow orderOut:self];
self.matchSelectionWindow = nil;
if(self.targetPID) {
[self _orderApplicationToFront:self.targetPID forContext:nil];
}
} }
#pragma mark - #pragma mark -
#pragma mark Autotype Execution #pragma mark Autotype Execution
- (void)selectAutotypeContext:(MPAutotypeContext *)context forEnvironment:(MPAutotypeEnvironment *)environment {
[self.matchSelectionWindow orderOut:self];
self.matchSelectionWindow = nil;
[self _runAutotypeWithEnvironment:environment forContext:context];
if(environment.hidden) {
[NSApplication.sharedApplication hide:nil];
}
}
- (void)cancelAutotypeContextSelectionForEnvironment:(MPAutotypeEnvironment *)environment {
[self.matchSelectionWindow orderOut:self];
self.matchSelectionWindow = nil;
[NSApplication.sharedApplication hide:nil];
if(environment.pid) {
[self _orderApplicationToFront:environment.pid completionHandler:nil];
}
}
- (void)_runAutotypeAfterDatabaseUnlockWithEnvironment:(MPAutotypeEnvironment *)environment requestedAt:(NSTimeInterval)requestTime {
NSTimeInterval now = NSDate.date.timeIntervalSinceReferenceDate;
if(now - requestTime > 30) {
NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = NSApp.applicationName;
notification.informativeText = NSLocalizedString(@"AUTOTYPE_TIMED_OUT", "Notficication: Autotype timed out");
notification.userInfo = @{ MPUserNotificationTypeKey: MPUserNotificationTypeAutotypeFeedback };
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
}
else {
[self _runAutotypeWithEnvironment:environment];
}
}
- (void)_runAutotypeWithEnvironment:(MPAutotypeEnvironment *)env {
- (void)_performAutotypeForEntry:(KPKEntry *)entryOrNil {
if(!self.hasNecessaryAutotypePermissions) { if(!self.hasNecessaryAutotypePermissions) {
NSUserNotification *notification = [[NSUserNotification alloc] init]; NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = NSApp.applicationName; notification.title = NSApp.applicationName;
@@ -186,10 +199,6 @@ static MPAutotypeDaemon *_sharedInstance;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification]; [NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
return; return;
} }
NSInteger pid = NSProcessInfo.processInfo.processIdentifier;
if(self.targetPID == pid) {
return; // We do not perform Autotype on ourselves
}
/* find autotype documents */ /* find autotype documents */
NSArray *documents = NSApp.orderedDocuments; NSArray *documents = NSApp.orderedDocuments;
@@ -202,8 +211,15 @@ static MPAutotypeDaemon *_sharedInstance;
notification.userInfo = @{ MPUserNotificationTypeKey: MPUserNotificationTypeAutotypeOpenDocumentRequest }; notification.userInfo = @{ MPUserNotificationTypeKey: MPUserNotificationTypeAutotypeOpenDocumentRequest };
notification.showsButtons = YES; notification.showsButtons = YES;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification]; [NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
self.userActionRequested = NSDate.date.timeIntervalSinceReferenceDate; NSNotificationCenter * __weak nc = [NSNotificationCenter defaultCenter];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didUnlockDatabase:) name:MPDocumentDidUnlockDatabaseNotification object:nil]; MPAutotypeDaemon * __weak welf = self;
id __block unlockToken = [nc addObserverForName:MPDocumentDidUnlockDatabaseNotification
object:nil
queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *notification) {
[welf _runAutotypeAfterDatabaseUnlockWithEnvironment:env requestedAt:NSDate.date.timeIntervalSinceReferenceDate];
[nc removeObserver:unlockToken];
}];
return; // Unlock should trigger autotype return; // Unlock should trigger autotype
} }
@@ -211,6 +227,7 @@ static MPAutotypeDaemon *_sharedInstance;
MPDocument *document = evaluatedObject; MPDocument *document = evaluatedObject;
return !document.encrypted; return !document.encrypted;
}]; }];
NSArray *unlockedDocuments = [documents filteredArrayUsingPredicate:filterPredicate]; NSArray *unlockedDocuments = [documents filteredArrayUsingPredicate:filterPredicate];
/* We look for all unlocked documents, if all open documents are locked, we pop the front most and try to search again */ /* We look for all unlocked documents, if all open documents are locked, we pop the front most and try to search again */
if(unlockedDocuments.count == 0) { if(unlockedDocuments.count == 0) {
@@ -221,39 +238,47 @@ static MPAutotypeDaemon *_sharedInstance;
[document showWindows]; [document showWindows];
MPDocumentWindowController *wc = document.windowControllers.firstObject; MPDocumentWindowController *wc = document.windowControllers.firstObject;
[wc showPasswordInputWithMessage:NSLocalizedString(@"AUTOTYPE_MESSAGE_UNLOCK_DATABASE", @"Message displayed to the user to unlock the database to perform global autotype")]; [wc showPasswordInputWithMessage:NSLocalizedString(@"AUTOTYPE_MESSAGE_UNLOCK_DATABASE", @"Message displayed to the user to unlock the database to perform global autotype")];
self.userActionRequested = NSDate.date.timeIntervalSinceReferenceDate; NSNotificationCenter * __weak nc = [NSNotificationCenter defaultCenter];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didUnlockDatabase:) name:MPDocumentDidUnlockDatabaseNotification object:nil]; MPAutotypeDaemon * __weak welf = self;
id __block unlockToken = [nc addObserverForName:MPDocumentDidUnlockDatabaseNotification
object:nil
queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *notification) {
[welf _runAutotypeAfterDatabaseUnlockWithEnvironment:env requestedAt:NSDate.date.timeIntervalSinceReferenceDate];
[nc removeObserver:unlockToken];
}];
return; // wait for the unlock to happen return; // wait for the unlock to happen
} }
MPAutotypeContext *context = [self _autotypeContextForDocuments:documents forWindowTitle:self.targetWindowTitle preferredEntry:entryOrNil]; MPAutotypeContext *context = [self _autotypeContextForDocuments:documents withEnvironment:env];
/* TODO: that's popping up if the multi selection dialog goes up! */ /* TODO: that's popping up if the multi selection dialog goes up! */
if(self.matchSelectionWindow) { if(self.matchSelectionWindow) {
return; // we present the match selection window, just return return; // we present the match selection window, just return
} }
if(!entryOrNil) { if(!env.preferredEntry) {
NSUserNotification *notification = [[NSUserNotification alloc] init]; NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = NSApp.applicationName; notification.title = NSApp.applicationName;
notification.userInfo = @{ MPUserNotificationTypeKey: MPUserNotificationTypeAutotypeFeedback }; notification.userInfo = @{ MPUserNotificationTypeKey: MPUserNotificationTypeAutotypeFeedback };
if(context) { if(context) {
notification.informativeText = [NSString stringWithFormat:NSLocalizedString(@"AUTOTYPE_OVERLAY_SINGLE_MATCH_FOR_%@", "Notification: Autotype found a single match for %@ (string placeholder)."), self.targetWindowTitle]; notification.informativeText = [NSString stringWithFormat:NSLocalizedString(@"AUTOTYPE_OVERLAY_SINGLE_MATCH_FOR_%@", "Notification: Autotype found a single match for %@ (string placeholder)."), env.windowTitle];
} }
else { else {
notification.informativeText = [NSString stringWithFormat:NSLocalizedString(@"AUTOTYPE_OVERLAY_NO_MATCH_FOR_%@", "Noticiation: Autotype failed to find a match for %@ (string placeholder)"), self.targetWindowTitle]; notification.informativeText = [NSString stringWithFormat:NSLocalizedString(@"AUTOTYPE_OVERLAY_NO_MATCH_FOR_%@", "Noticiation: Autotype failed to find a match for %@ (string placeholder)"), env.windowTitle];
} }
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification]; [NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
} }
[self _performAutotypeForContext:context]; [self _runAutotypeWithEnvironment:env forContext:context];
} }
- (MPAutotypeContext *)_autotypeContextForDocuments:(NSArray<MPDocument *> *)documents forWindowTitle:(NSString *)windowTitle preferredEntry:(KPKEntry *)entry { - (MPAutotypeContext *)_autotypeContextForDocuments:(NSArray<MPDocument *> *)documents withEnvironment:(MPAutotypeEnvironment *)environment {
/* /*
Query the document to generate a autotype command list for the window title Query the document to generate a autotype command list for the window title
We do not care where this came form, just get the autotype commands We do not care where this came form, just get the autotype commands
*/ */
NSMutableArray *autotypeCandidates = [[NSMutableArray alloc] init]; NSMutableArray *autotypeCandidates = [[NSMutableArray alloc] init];
for(MPDocument *document in documents) { for(MPDocument *document in documents) {
NSArray *contexts = [document autotypContextsForWindowTitle:windowTitle preferredEntry:entry]; NSArray *contexts = [document autotypContextsForWindowTitle:environment.windowTitle preferredEntry:environment.preferredEntry];
if(contexts ) { if(contexts ) {
[autotypeCandidates addObjectsFromArray:contexts]; [autotypeCandidates addObjectsFromArray:contexts];
} }
@@ -262,17 +287,22 @@ static MPAutotypeDaemon *_sharedInstance;
if(autotypeCandidates.count <= 1) { if(autotypeCandidates.count <= 1) {
return autotypeCandidates.lastObject; return autotypeCandidates.lastObject;
} }
[self _presentCandiadates:autotypeCandidates forEnvironment:environment];
[self _presentCandiadates:autotypeCandidates forWindowTitle:windowTitle];
return nil; // Nothing to do, we get called back by the window return nil; // Nothing to do, we get called back by the window
} }
- (void)_performAutotypeForContext:(MPAutotypeContext *)context { - (void)_runAutotypeWithEnvironment:(MPAutotypeEnvironment *)environment forContext:(MPAutotypeContext *)context {
if(nil == environment) {
return; // no Environment to work in
}
if(nil == context) { if(nil == context) {
return; // No context to work with return; // No context to work with
} }
__weak MPAutotypeDaemon *welf = self;
if(NO == [self _orderApplicationToFront:self.targetPID forContext:(MPAutotypeContext *)context]) { BOOL appIsFrontmost = [self _orderApplicationToFront:environment.pid completionHandler:^{
[welf _runAutotypeWithEnvironment:environment forContext:context];
}];
if(!appIsFrontmost) {
return; // We will get called back when the application is in front - hopfully return; // We will get called back when the application is in front - hopfully
} }
@@ -320,38 +350,7 @@ static MPAutotypeDaemon *_sharedInstance;
} }
} }
- (NSDictionary *)_infoDictionaryForApplication:(NSRunningApplication *)application { - (void)_presentCandiadates:(NSArray *)candidates forEnvironment:(MPAutotypeEnvironment *)environment {
NSArray *currentWindows = CFBridgingRelease(CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements, kCGNullWindowID));
NSArray *windowNumbers = [NSWindow windowNumbersWithOptions:NSWindowNumberListAllApplications];
NSUInteger minZIndex = NSNotFound;
NSDictionary *infoDict = nil;
for(NSDictionary *windowDict in currentWindows) {
NSString *windowTitle = windowDict[(NSString *)kCGWindowName];
if(windowTitle.length <= 0) {
continue;
}
NSNumber *processId = windowDict[(NSString *)kCGWindowOwnerPID];
if(processId && [processId isEqualToNumber:@(application.processIdentifier)]) {
NSNumber *number = (NSNumber *)windowDict[(NSString *)kCGWindowNumber];
NSUInteger zIndex = [windowNumbers indexOfObject:number];
if(zIndex < minZIndex) {
minZIndex = zIndex;
infoDict = @{
kMPWindowTitleKey: windowTitle,
kMPProcessIdentifierKey : processId
};
}
}
}
if(currentWindows.count > 0 && infoDict.count == 0) {
// show some information about not being able to determine any windows
NSLog(@"Unable to retrieve any window names. If you encounter this issue you might be running 10.15 and MacPass has no permission for screen recording.");
}
return infoDict;
}
- (void)_presentCandiadates:(NSArray *)candidates forWindowTitle:(NSString *)windowTitle {
if(!self.matchSelectionWindow) { if(!self.matchSelectionWindow) {
self.matchSelectionWindow = [[NSPanel alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100) self.matchSelectionWindow = [[NSPanel alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
styleMask:NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskTitled styleMask:NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskTitled
@@ -360,12 +359,7 @@ static MPAutotypeDaemon *_sharedInstance;
self.matchSelectionWindow.level = kCGAssistiveTechHighWindowLevel; self.matchSelectionWindow.level = kCGAssistiveTechHighWindowLevel;
MPAutotypeCandidateSelectionViewController *vc = [[MPAutotypeCandidateSelectionViewController alloc] init]; MPAutotypeCandidateSelectionViewController *vc = [[MPAutotypeCandidateSelectionViewController alloc] init];
vc.candidates = candidates; vc.candidates = candidates;
vc.windowTitle = windowTitle; vc.environment = environment;
if(NSRunningApplication.currentApplication.isHidden) {
vc.completionHandler = ^{
[NSRunningApplication.currentApplication hide];
};
}
self.matchSelectionWindow.collectionBehavior |= (NSWindowCollectionBehaviorFullScreenAuxiliary | self.matchSelectionWindow.collectionBehavior |= (NSWindowCollectionBehaviorFullScreenAuxiliary |
NSWindowCollectionBehaviorMoveToActiveSpace | NSWindowCollectionBehaviorMoveToActiveSpace |
NSWindowCollectionBehaviorTransient ); NSWindowCollectionBehaviorTransient );
@@ -376,24 +370,6 @@ static MPAutotypeDaemon *_sharedInstance;
[self.matchSelectionWindow makeKeyAndOrderFront:self]; [self.matchSelectionWindow makeKeyAndOrderFront:self];
} }
#pragma mark -
#pragma mark MPDocument Notifications
- (void)_didUnlockDatabase:(NSNotification *)notification {
/* Remove ourselves and call again to search matches */
[NSNotificationCenter.defaultCenter removeObserver:self name:MPDocumentDidUnlockDatabaseNotification object:nil];
NSTimeInterval now = NSDate.date.timeIntervalSinceReferenceDate;
if(now - self.userActionRequested > 30) {
NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = NSApp.applicationName;
notification.informativeText = NSLocalizedString(@"AUTOTYPE_TIMED_OUT", "Notficication: Autotype timed out");
notification.userInfo = @{ MPUserNotificationTypeKey: MPUserNotificationTypeAutotypeFeedback };
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
}
else {
[self _performAutotypeForEntry:nil];
}
}
#pragma mark - #pragma mark -
#pragma mark NSApplication Notifications #pragma mark NSApplication Notifications
- (void)_didDeactivateApplication:(NSNotification *)notification { - (void)_didDeactivateApplication:(NSNotification *)notification {
@@ -403,51 +379,25 @@ static MPAutotypeDaemon *_sharedInstance;
#pragma mark - #pragma mark -
#pragma mark Application information #pragma mark Application information
- (BOOL)_orderApplicationToFront:(pid_t)processIdentifier forContext:(MPAutotypeContext *)context { //- (BOOL)_orderApplicationToFront:(pid_t)processIdentifier inEnvironment:(MPAutotypeEnvironment *) environment {
- (BOOL)_orderApplicationToFront:(pid_t)processIdentifier completionHandler:(void (^_Nullable)(void))completionHandler {
NSRunningApplication *runingApplication = [NSRunningApplication runningApplicationWithProcessIdentifier:processIdentifier]; NSRunningApplication *runingApplication = [NSRunningApplication runningApplicationWithProcessIdentifier:processIdentifier];
NSRunningApplication *frontApplication = NSWorkspace.sharedWorkspace.frontmostApplication; NSRunningApplication *frontApplication = NSWorkspace.sharedWorkspace.frontmostApplication;
if(frontApplication.processIdentifier == processIdentifier) { if(frontApplication.processIdentifier == processIdentifier) {
return YES; return YES;
} }
/* cleanup before to make sure everything is top notch */ NSNotificationCenter * __weak nc = NSWorkspace.sharedWorkspace.notificationCenter;
if(self.applicationActivationObserver) { id __block didActivateToken = [nc addObserverForName:NSWorkspaceDidActivateApplicationNotification
[NSWorkspace.sharedWorkspace.notificationCenter removeObserver:self.applicationActivationObserver name:NSWorkspaceDidActivateApplicationNotification object:nil]; object:nil
self.applicationActivationObserver = nil; queue:NSOperationQueue.mainQueue
} usingBlock:^(NSNotification *notification) {
[nc removeObserver:didActivateToken];
self.applicationActivationObserver = [NSWorkspace.sharedWorkspace.notificationCenter addObserverForName:NSWorkspaceDidActivateApplicationNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { if(completionHandler) {
if(self.applicationActivationObserver) { completionHandler();
[NSWorkspace.sharedWorkspace.notificationCenter removeObserver:self.applicationActivationObserver name:NSWorkspaceDidActivateApplicationNotification object:nil];
} }
[self _performAutotypeForContext:context];
}]; }];
[runingApplication activateWithOptions:NSApplicationActivateIgnoringOtherApps]; [runingApplication activateWithOptions:NSApplicationActivateIgnoringOtherApps];
return NO; return NO;
} }
- (void)_updateTargeInformationForApplication:(NSRunningApplication *)application {
if(!application) {
self.targetPID = -1;
self.targetWindowTitle = @"";
}
else {
NSDictionary *frontApplicationInfoDict = [self _infoDictionaryForApplication:application];
self.targetPID = [frontApplicationInfoDict[kMPProcessIdentifierKey] intValue];
self.targetWindowTitle = frontApplicationInfoDict[kMPWindowTitleKey];
/* if we have any resolvers, let them provide the window title */
NSArray *resolvers = [MPPluginHost.sharedHost windowTitleResolverForRunningApplication:application];
for(MPPlugin<MPAutotypeWindowTitleResolverPlugin> *resolver in resolvers) {
NSString *windowTitle = [resolver windowTitleForRunningApplication:application];
if(windowTitle.length > 0) {
self.targetWindowTitle = windowTitle;
break;
}
}
}
}
@end @end

View File

@@ -0,0 +1,34 @@
//
// MPAutotypeEnvironment.h
// MacPass
//
// Created by Michael Starke on 15.01.20.
// Copyright © 2020 HicknHack Software GmbH. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class KPKEntry;
@class MPAutotypeContext;
@interface MPAutotypeEnvironment : NSObject
/**
The selected entry, if Autotype is run only for a single entry.
If autotype should search for entries, set this to nil.
*/
@property (readonly, weak, nullable) KPKEntry *preferredEntry;
@property (readonly) pid_t pid; // the PID of the target application to which the key strokes should be sent
@property (readonly, copy) NSString *windowTitle; /// The window title of the target application.
@property (readonly) BOOL hidden; /// If set to YES, MacPass was hidden when autotype was initiated
@property (readonly) BOOL isSelfTargeting; /// If MacPass should autotype to itself, YES, otherwise NO
+ (instancetype)environmentWithTargetApplication:(NSRunningApplication *)targetApplication entry:(KPKEntry * _Nullable)entry;
- (instancetype)initWithTargetApplication:(NSRunningApplication *)targetApplication entry:(KPKEntry * _Nullable)entry NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,84 @@
//
// MPAutotypeEnvironment.m
// MacPass
//
// Created by Michael Starke on 15.01.20.
// Copyright © 2020 HicknHack Software GmbH. All rights reserved.
//
#import "MPAutotypeEnvironment.h"
#import "NSRunningApplication+MPAdditions.h"
#import "MPPluginHost.h"
#import "MPPlugin.h"
@implementation MPAutotypeEnvironment
+ (instancetype)environmentWithTargetApplication:(NSRunningApplication *)targetApplication entry:(KPKEntry *)entry {
return [[MPAutotypeEnvironment alloc] initWithTargetApplication:targetApplication entry:entry];
}
- (instancetype)initWithTargetApplication:(NSRunningApplication *)targetApplication entry:(KPKEntry *)entry {
self = [super init];
if(self) {
_preferredEntry = entry;
if(!targetApplication) {
_pid = -1;
_windowTitle = @"";
}
else {
NSDictionary *frontApplicationInfoDict = targetApplication.mp_infoDictionary;
_pid = [frontApplicationInfoDict[MPProcessIdentifierKey] intValue];
_windowTitle = frontApplicationInfoDict[MPWindowTitleKey];
/* if we have any resolvers, let them provide the window title */
NSArray *resolvers = [MPPluginHost.sharedHost windowTitleResolverForRunningApplication:targetApplication];
for(MPPlugin<MPAutotypeWindowTitleResolverPlugin> *resolver in resolvers) {
NSString *windowTitle = [resolver windowTitleForRunningApplication:targetApplication];
if(windowTitle.length > 0) {
_windowTitle = windowTitle;
break;
}
}
}
_hidden = NSRunningApplication.currentApplication.isHidden;
}
return self;
}
- (BOOL)isSelfTargeting {
return NSRunningApplication.currentApplication.processIdentifier != _pid;
}
- (NSDictionary *)_infoDictionaryForApplication:(NSRunningApplication *)application {
NSArray *currentWindows = CFBridgingRelease(CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements, kCGNullWindowID));
NSArray *windowNumbers = [NSWindow windowNumbersWithOptions:NSWindowNumberListAllApplications];
NSUInteger minZIndex = NSNotFound;
NSDictionary *infoDict = nil;
for(NSDictionary *windowDict in currentWindows) {
NSString *windowTitle = windowDict[(NSString *)kCGWindowName];
if(windowTitle.length <= 0) {
continue;
}
NSNumber *processId = windowDict[(NSString *)kCGWindowOwnerPID];
if(processId && [processId isEqualToNumber:@(application.processIdentifier)]) {
NSNumber *number = (NSNumber *)windowDict[(NSString *)kCGWindowNumber];
NSUInteger zIndex = [windowNumbers indexOfObject:number];
if(zIndex < minZIndex) {
minZIndex = zIndex;
infoDict = @{
MPWindowTitleKey: windowTitle,
MPProcessIdentifierKey : processId
};
}
}
}
if(currentWindows.count > 0 && infoDict.count == 0) {
// show some information about not being able to determine any windows
NSLog(@"Unable to retrieve any window names. If you encounter this issue you might be running 10.15 and MacPass has no permission for screen recording.");
}
return infoDict;
}
@end

View File

@@ -0,0 +1,25 @@
//
// NSRunningApplication+MPAdditions.h
// MacPass
//
// Created by Michael Starke on 15.01.20.
// Copyright © 2020 HicknHack Software GmbH. All rights reserved.
//
#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
APPKIT_EXTERN NSString *const MPWindowTitleKey;
APPKIT_EXTERN NSString *const MPProcessIdentifierKey;
@interface NSRunningApplication (MPAdditions)
@property (readonly, copy) NSDictionary *mp_infoDictionary;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,50 @@
//
// NSRunningApplication+MPAdditions.m
// MacPass
//
// Created by Michael Starke on 15.01.20.
// Copyright © 2020 HicknHack Software GmbH. All rights reserved.
//
#import "NSRunningApplication+MPAdditions.h"
#import <AppKit/AppKit.h>
NSString *const MPWindowTitleKey = @"MPWindowTitleKey";
NSString *const MPProcessIdentifierKey = @"MPProcessIdentifierKey";
@implementation NSRunningApplication (MPAdditions)
- (NSDictionary *)mp_infoDictionary {
NSArray *currentWindows = CFBridgingRelease(CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements, kCGNullWindowID));
NSArray *windowNumbers = [NSWindow windowNumbersWithOptions:NSWindowNumberListAllApplications];
NSUInteger minZIndex = NSNotFound;
NSDictionary *infoDict = nil;
for(NSDictionary *windowDict in currentWindows) {
NSString *windowTitle = windowDict[(NSString *)kCGWindowName];
if(windowTitle.length <= 0) {
continue;
}
NSNumber *processId = windowDict[(NSString *)kCGWindowOwnerPID];
if(processId && [processId isEqualToNumber:@(self.processIdentifier)]) {
NSNumber *number = (NSNumber *)windowDict[(NSString *)kCGWindowNumber];
NSUInteger zIndex = [windowNumbers indexOfObject:number];
if(zIndex < minZIndex) {
minZIndex = zIndex;
infoDict = @{
MPWindowTitleKey: windowTitle,
MPProcessIdentifierKey : processId
};
}
}
}
if(currentWindows.count > 0 && infoDict.count == 0) {
// show some information about not being able to determine any windows
NSLog(@"Unable to retrieve any window names. If you encounter this issue you might be running 10.15 and MacPass has no permission for screen recording.");
}
return infoDict;
}
@end