From f890e0daa7ca701b8c0db6c34177132c2bf6e8e9 Mon Sep 17 00:00:00 2001 From: "Hoa V. DINH" Date: Wed, 27 Mar 2013 08:46:25 -0700 Subject: Fixed cancellation of IMAP operations --- src/ui/mac/MCOCIDURLProtocol.h | 31 ++++ src/ui/mac/MCOCIDURLProtocol.mm | 148 ++++++++++++++++++ src/ui/mac/MCOMessageView.h | 49 ++++++ src/ui/mac/MCOMessageView.mm | 335 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 563 insertions(+) create mode 100644 src/ui/mac/MCOCIDURLProtocol.h create mode 100644 src/ui/mac/MCOCIDURLProtocol.mm create mode 100644 src/ui/mac/MCOMessageView.h create mode 100644 src/ui/mac/MCOMessageView.mm (limited to 'src/ui') diff --git a/src/ui/mac/MCOCIDURLProtocol.h b/src/ui/mac/MCOCIDURLProtocol.h new file mode 100644 index 00000000..cf0b5335 --- /dev/null +++ b/src/ui/mac/MCOCIDURLProtocol.h @@ -0,0 +1,31 @@ +// +// MCTCIDURLProtocol.h +// testUI +// +// Created by DINH Viêt Hoà on 1/22/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#import + +#include + +@interface MCOCIDURLProtocol : NSURLProtocol + ++ (void) registerProtocol; + ++ (BOOL) isCID:(NSURL *)url; ++ (BOOL) isXMailcoreImage:(NSURL *)url; + +#ifdef __cplusplus ++ (void) startLoadingWithMessage:(MCOAbstractMessage *)message + partUniqueID:(NSString *)partUniqueID + data:(NSData *)data + request:(NSMutableURLRequest *)request; + ++ (void) partDownloadedMessage:(MCOAbstractMessage *)message + partUniqueID:(NSString *)partUniqueID + data:(NSData *)data; +#endif + +@end diff --git a/src/ui/mac/MCOCIDURLProtocol.mm b/src/ui/mac/MCOCIDURLProtocol.mm new file mode 100644 index 00000000..83730c9d --- /dev/null +++ b/src/ui/mac/MCOCIDURLProtocol.mm @@ -0,0 +1,148 @@ +// +// MCTCIDURLProtocol.m +// testUI +// +// Created by DINH Viêt Hoà on 1/22/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#import "MCOCIDURLProtocol.h" + +#import "MCOMessageView.h" + +#define MCOCIDURLProtocolDownloadedNotification @"MCOCIDURLProtocolDownloadedNotification" + +@implementation MCOCIDURLProtocol + ++ (void) registerProtocol +{ + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + [NSURLProtocol registerClass:[MCOCIDURLProtocol class]]; + }); +} + ++ (BOOL) canInitWithRequest:(NSURLRequest *)theRequest +{ + if ([self isCID:[theRequest URL]]) + return YES; + if ([self isXMailcoreImage:[theRequest URL]]) + return YES; + return NO; +} + ++ (BOOL) isCID:(NSURL *)url +{ + NSString *theScheme = [url scheme]; + if ([theScheme caseInsensitiveCompare:@"cid"] == NSOrderedSame) + return YES; + return NO; +} + ++ (BOOL) isXMailcoreImage:(NSURL *)url +{ + NSString *theScheme = [url scheme]; + if ([theScheme caseInsensitiveCompare:@"x-mailcore-image"] == NSOrderedSame) + return YES; + return NO; +} + ++ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request +{ + return request; +} + +- (id) initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id < NSURLProtocolClient >)client +{ + self = [super initWithRequest:request cachedResponse:cachedResponse client:client]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_downloaded:) name:MCOCIDURLProtocolDownloadedNotification object:nil]; + return self; +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (NSString *) _partUniqueID +{ + return [NSURLProtocol propertyForKey:@"PartUniqueID" inRequest:[self request]]; +} + +- (NSData *) _data +{ + return [NSURLProtocol propertyForKey:@"Data" inRequest:[self request]]; +} + +- (MCOAbstractMessage *) _message +{ + return (MCOAbstractMessage *) [NSURLProtocol propertyForKey:@"Message" inRequest:[self request]]; +} + +- (void) startLoading +{ + //NSLog(@"waiting for %p %@", self, [self _partUniqueID]); + if ([self _data] != NULL) { + [[self class] partDownloadedMessage:[self _message] partUniqueID:[self _partUniqueID] data:[self _data]]; + } +} + +- (void) _downloaded:(NSNotification *)notification +{ + NSDictionary * userInfo = [notification userInfo]; + + //NSLog(@"downloaded?"); + NSString * notifPartID = [userInfo objectForKey:@"PartUniqueID"]; + MCOAbstractMessage * notifMessage = [userInfo objectForKey:@"Message"]; + if (notifMessage != [self _message]) { + return; + } + if (![[self _partUniqueID] isEqualToString:notifPartID]) { + return; + } + + //NSLog(@"downloaded2 %p %@", self, notifPartID); + + NSData * data = [userInfo objectForKey:@"Data"]; + NSURLResponse * response = [[NSURLResponse alloc] initWithURL:[[self request] URL] MIMEType:@"application/data" + expectedContentLength:[data length] textEncodingName:nil]; + [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; + [[self client] URLProtocol:self didLoadData:data]; + [[self client] URLProtocolDidFinishLoading:self]; + [response release]; +} + +- (void) stopLoading +{ +} + ++ (void) startLoadingWithMessage:(MCOAbstractMessage *)message + partUniqueID:(NSString *)partUniqueID + data:(NSData *)data + request:(NSMutableURLRequest *)request +{ + [NSURLProtocol setProperty:message + forKey:@"Message" inRequest:request]; + if (data != NULL) { + [NSURLProtocol setProperty:data forKey:@"Data" inRequest:request]; + } + [NSURLProtocol setProperty:partUniqueID forKey:@"PartUniqueID" inRequest:request]; +} + ++ (void) partDownloadedMessage:(MCOAbstractMessage *)message + partUniqueID:(NSString *)partUniqueID + data:(NSData *)data +{ + NSMutableDictionary * userInfo = [[NSMutableDictionary alloc] init]; + [userInfo setObject:message forKey:@"Message"]; + [userInfo setObject:partUniqueID forKey:@"PartUniqueID"]; + if (data != NULL) { + [userInfo setObject:data forKey:@"Data"]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:MCOCIDURLProtocolDownloadedNotification object:nil userInfo:userInfo]; + [userInfo release]; + //NSLog(@"downloaded %p %@", self, partUniqueID); +} + +@end diff --git a/src/ui/mac/MCOMessageView.h b/src/ui/mac/MCOMessageView.h new file mode 100644 index 00000000..c8ffe241 --- /dev/null +++ b/src/ui/mac/MCOMessageView.h @@ -0,0 +1,49 @@ +// +// MCOMessageView.h +// testUI +// +// Created by DINH Viêt Hoà on 1/19/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#import +#include + +@protocol MCOMessageViewDelegate; + +@interface MCOMessageView : NSView + +@property (nonatomic, copy) NSString * folder; +@property (nonatomic, strong) MCOAbstractMessage * message; + +@property (nonatomic, assign) id delegate; + +@property (nonatomic, assign) BOOL prefetchIMAPImagesEnabled; +@property (nonatomic, assign) BOOL prefetchIMAPAttachmentsEnabled; + +@end + +@protocol MCOMessageViewDelegate + +@optional +- (NSData *) MCOMessageView:(MCOMessageView *)view dataForPartWithUniqueID:(NSString *)partUniqueID; +- (void) MCOMessageView:(MCOMessageView *)view fetchDataForPartWithUniqueID:(NSString *)partUniqueID + downloadedFinished:(void (^)(NSError * error))downloadFinished; + +- (NSString *) MCOMessageView_templateForMainHeader:(MCOMessageView *)view; +- (NSString *) MCOMessageView_templateForImage:(MCOMessageView *)view; +- (NSString *) MCOMessageView_templateForAttachment:(MCOMessageView *)view; +- (NSString *) MCOMessageView_templateForMessage:(MCOMessageView *)view; +- (NSString *) MCOMessageView_templateForEmbeddedMessage:(MCOMessageView *)view; +- (NSString *) MCOMessageView_templateForEmbeddedMessageHeader:(MCOMessageView *)view; +- (NSString *) MCOMessageView_templateForAttachmentSeparator:(MCOMessageView *)view; + +- (NSDictionary *) MCOMessageView:(MCOMessageView *)view templateValuesForPartWithUniqueID:(NSString *)uniqueID; +- (NSDictionary *) MCOMessageView:(MCOMessageView *)view templateValuesForHeader:(MCOMessageHeader *)header; +- (BOOL) MCOMessageView:(MCOMessageView *)view canPreviewPart:(MCOAbstractPart *)part; + +- (NSString *) MCOMessageView:(MCOMessageView *)view filteredHTMLForPart:(NSString *)html; +- (NSString *) MCOMessageView:(MCOMessageView *)view filteredHTMLForMessage:(NSString *)html; +- (NSData *) MCOMessageView:(MCOMessageView *)view previewForData:(NSData *)data isHTMLInlineImage:(BOOL)isHTMLInlineImage; + +@end diff --git a/src/ui/mac/MCOMessageView.mm b/src/ui/mac/MCOMessageView.mm new file mode 100644 index 00000000..4e3b908c --- /dev/null +++ b/src/ui/mac/MCOMessageView.mm @@ -0,0 +1,335 @@ +// +// MCOMessageView.m +// testUI +// +// Created by DINH Viêt Hoà on 1/19/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#import "MCOMessageView.h" + +#import + +#import "MCOCIDURLProtocol.h" + +@interface MCOMessageView () + +@end + +@implementation MCOMessageView { + WebView * _webView; + NSString * _folder; + MCOAbstractMessage * _message; + id _delegate; + BOOL _prefetchIMAPImagesEnabled; + BOOL _prefetchIMAPAttachmentsEnabled; +} + +@synthesize folder = _folder; +@synthesize delegate = _delegate; +@synthesize prefetchIMAPImagesEnabled = _prefetchIMAPImagesEnabled; +@synthesize prefetchIMAPAttachmentsEnabled = _prefetchIMAPAttachmentsEnabled; + ++ (void) initialize +{ + [MCOCIDURLProtocol registerProtocol]; +} + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + + _webView = [[WebView alloc] initWithFrame:[self bounds]]; + [_webView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable]; + [_webView setResourceLoadDelegate:self]; + [self addSubview:_webView]; + + return self; +} + +- (void) dealloc +{ + [_message release]; + [_folder release]; + [_webView release]; + + [super dealloc]; +} + +- (void) setMessage:(MCOAbstractMessage *)message +{ + [_message release]; + _message = [message retain]; + + [_webView stopLoading:nil]; + [self _refresh]; +} + +- (MCOAbstractMessage *) message +{ + return _message; +} + +- (void) _refresh +{ + NSString * content; + + if (_message == nil) { + content = nil; + } + else { + if ([_message isKindOfClass:[MCOIMAPMessage class]]) { + content = [(MCOIMAPMessage *) _message htmlRenderingWithFolder:_folder delegate:self]; + } + else if ([_message isKindOfClass:[MCOMessageBuilder class]]) { + content = [(MCOMessageBuilder *) _message htmlRenderingWithDelegate:self]; + } + else if ([_message isKindOfClass:[MCOMessageParser class]]) { + content = [(MCOMessageParser *) _message htmlRenderingWithDelegate:self]; + } + else { + content = nil; + MCAssert(0); + } + } + [[_webView mainFrame] loadHTMLString:content baseURL:nil]; +} + +- (MCOAbstractPart *) _partForCIDURL:(NSURL *)url +{ + return [_message partForContentID:[url resourceSpecifier]]; +} + +- (MCOAbstractPart *) _partForUniqueID:(NSString *)partUniqueID +{ + return [_message partForUniqueID:partUniqueID]; +} + +- (NSData *) _dataForIMAPPart:(MCOIMAPPart *)part folder:(NSString *)folder +{ + NSData * data; + NSString * partUniqueID = [part uniqueID]; + data = [[self delegate] MCOMessageView:self dataForPartWithUniqueID:partUniqueID]; + if (data == NULL) { + [[self delegate] MCOMessageView:self fetchDataForPartWithUniqueID:partUniqueID downloadedFinished:^(NSError * error) { + [self _refresh]; + }]; + } + return data; +} + +- (NSURLRequest *)webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(id)dataSource +{ + MCOAbstractPart * part = NULL; + + if ([MCOCIDURLProtocol isCID:[request URL]]) { + part = [self _partForCIDURL:[request URL]]; + } + else if ([MCOCIDURLProtocol isXMailcoreImage:[ request URL]]) { + NSString * specifier = [[request URL] resourceSpecifier]; + NSString * partUniqueID = specifier; + part = [self _partForUniqueID:partUniqueID]; + } + + if (part != NULL) { + if ([_message isKindOfClass:[MCOIMAPMessage class]]) { + NSMutableURLRequest * mutableRequest = [request mutableCopy]; + NSString * partUniqueID = [part uniqueID]; + NSData * data = [[self delegate] MCOMessageView:self dataForPartWithUniqueID:partUniqueID]; + if (data == NULL) { + [[self delegate] MCOMessageView:self fetchDataForPartWithUniqueID:partUniqueID downloadedFinished:^(NSError * error) { + NSData * downloadedData = [[self delegate] MCOMessageView:self dataForPartWithUniqueID:partUniqueID]; + NSData * previewData = [[self delegate] MCOMessageView:self previewForData:downloadedData isHTMLInlineImage:[MCOCIDURLProtocol isCID:[request URL]]]; + [MCOCIDURLProtocol partDownloadedMessage:_message partUniqueID:partUniqueID data:previewData]; + }]; + } + [MCOCIDURLProtocol startLoadingWithMessage:_message + partUniqueID:partUniqueID + data:data + request:mutableRequest]; + + return [mutableRequest autorelease]; + } + else if ([_message isKindOfClass:[MCOMessageParser class]]) { + NSMutableURLRequest * mutableRequest = [request mutableCopy]; + NSString * partUniqueID = [part uniqueID]; + NSData * data = [(MCOAttachment *) part data]; + NSData * previewData = [[self delegate] MCOMessageView:self previewForData:data isHTMLInlineImage:[MCOCIDURLProtocol isCID:[request URL]]]; + [MCOCIDURLProtocol startLoadingWithMessage:_message + partUniqueID:partUniqueID + data:previewData + request:mutableRequest]; + + return [mutableRequest autorelease]; + } + else { + return request; + } + } + else { + return request; + } +} + +- (BOOL) MCOAbstractMessage:(MCOAbstractMessage *)msg canPreviewPart:(MCOAbstractPart *)part +{ + static NSMutableSet * supportedImageMimeTypes = NULL; + if (supportedImageMimeTypes == NULL) { + supportedImageMimeTypes = [[NSMutableSet alloc] init]; + [supportedImageMimeTypes addObject:@"image/png"]; + [supportedImageMimeTypes addObject:@"image/gif"]; + [supportedImageMimeTypes addObject:@"image/jpg"]; + [supportedImageMimeTypes addObject:@"image/jpeg"]; + } + static NSMutableSet * supportedImageExtension = NULL; + if (supportedImageExtension == NULL) { + supportedImageExtension = [[NSMutableSet alloc] init]; + [supportedImageExtension addObject:@"png"]; + [supportedImageExtension addObject:@"gif"]; + [supportedImageExtension addObject:@"jpg"]; + [supportedImageExtension addObject:@"jpeg"]; + } + + if ([supportedImageMimeTypes containsObject:[[part mimeType] lowercaseString]]) + return YES; + + NSString * ext = nil; + if ([part filename] != nil) { + if ([[part filename] pathExtension] != nil) { + ext = [[[part filename] pathExtension] lowercaseString]; + } + } + if (ext != nil) { + if ([supportedImageExtension containsObject:ext]) + return YES; + } + + if (![[self delegate] respondsToSelector:@selector(MCOMessageView:canPreviewPart:)]) { + return false; + } + return [[self delegate] MCOMessageView:self canPreviewPart:part]; +} + +- (NSDictionary *) MCOAbstractMessage:(MCOAbstractMessage *)msg templateValuesForHeader:(MCOMessageHeader *)header +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView:templateValuesForHeader:)]) { + return nil; + } + return [[self delegate] MCOMessageView:self templateValuesForHeader:header]; +} + +- (NSDictionary *) MCOAbstractMessage:(MCOAbstractMessage *)msg templateValuesForPart:(MCOAbstractPart *)part +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView:templateValuesForPartWithUniqueID:)]) { + return nil; + } + return [[self delegate] MCOMessageView:self templateValuesForPartWithUniqueID:[part uniqueID]]; +} + +- (NSString *) MCOAbstractMessage_templateForMainHeader:(MCOAbstractMessage *)msg +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView_templateForMainHeader:)]) { + return nil; + } + return [[self delegate] MCOMessageView_templateForMainHeader:self]; +} + +- (NSString *) MCOAbstractMessage_templateForImage:(MCOAbstractMessage *)msg +{ + NSString * templateString; + if ([[self delegate] respondsToSelector:@selector(MCOMessageView_templateForImage:)]) { + templateString = [[self delegate] MCOMessageView_templateForImage:self]; + } + else { + templateString = @""; + } + templateString = [NSString stringWithFormat:@"
%@
", templateString]; + return templateString; +} + +- (NSString *) MCOAbstractMessage_templateForAttachment:(MCOAbstractMessage *)msg +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView_templateForAttachment:)]) { + return NULL; + } + NSString * templateString = [[self delegate] MCOMessageView_templateForAttachment:self]; + templateString = [NSString stringWithFormat:@"
%@
", templateString]; + return templateString; +} + +- (NSString *) MCOAbstractMessage_templateForMessage:(MCOAbstractMessage *)msg +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView_templateForMessage:)]) { + return NULL; + } + return [[self delegate] MCOMessageView_templateForMessage:self]; +} + +- (NSString *) MCOAbstractMessage_templateForEmbeddedMessage:(MCOAbstractMessage *)msg +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView_templateForEmbeddedMessage:)]) { + return NULL; + } + return [[self delegate] MCOMessageView_templateForEmbeddedMessage:self]; +} + +- (NSString *) MCOAbstractMessage_templateForEmbeddedMessageHeader:(MCOAbstractMessage *)msg +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView_templateForEmbeddedMessageHeader:)]) { + return NULL; + } + return [[self delegate] MCOMessageView_templateForEmbeddedMessageHeader:self]; +} + +- (NSString *) MCOAbstractMessage_templateForAttachmentSeparator:(MCOAbstractMessage *)msg +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView_templateForAttachmentSeparator:)]) { + return NULL; + } + return [[self delegate] MCOMessageView_templateForAttachmentSeparator:self]; +} + +- (NSString *) MCOAbstractMessage:(MCOAbstractMessage *)msg filterHTMLForPart:(NSString *)html +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView:filteredHTMLForPart:)]) { + return html; + } + return [[self delegate] MCOMessageView:self filteredHTMLForPart:html]; +} + +- (NSString *) MCOAbstractMessage:(MCOAbstractMessage *)msg filterHTMLForMessage:(NSString *)html +{ + if (![[self delegate] respondsToSelector:@selector(MCOMessageView:filteredHTMLForMessage:)]) { + return html; + } + return [[self delegate] MCOMessageView:self filteredHTMLForMessage:html]; +} + +- (NSData *) MCOAbstractMessage:(MCOAbstractMessage *)msg dataForIMAPPart:(MCOIMAPPart *)part folder:(NSString *)folder +{ + return [self _dataForIMAPPart:part folder:folder]; +} + +- (void) MCOAbstractMessage:(MCOAbstractMessage *)msg prefetchAttachmentIMAPPart:(MCOIMAPPart *)part folder:(NSString *)folder +{ + if (!_prefetchIMAPAttachmentsEnabled) + return; + + NSString * partUniqueID = [part uniqueID]; + [[self delegate] MCOMessageView:self fetchDataForPartWithUniqueID:partUniqueID downloadedFinished:^(NSError * error) { + // do nothing + }]; +} + +- (void) MCOAbstractMessage:(MCOAbstractMessage *)msg prefetchImageIMAPPart:(MCOIMAPPart *)part folder:(NSString *)folder +{ + if (!_prefetchIMAPImagesEnabled) + return; + + NSString * partUniqueID = [part uniqueID]; + [[self delegate] MCOMessageView:self fetchDataForPartWithUniqueID:partUniqueID downloadedFinished:^(NSError * error) { + // do nothing + }]; +} + +@end -- cgit v1.2.3