diff --git a/ApplicationController.m b/ApplicationController.m index 94cca1a..bd1f1e9 100644 --- a/ApplicationController.m +++ b/ApplicationController.m @@ -12,6 +12,7 @@ #import "PBRepositoryDocumentController.h" #import "PBCLIProxy.h" #import "PBServicesController.h" +#import "PBGitXProtocol.h" @implementation ApplicationController @synthesize cliProxy; @@ -34,6 +35,9 @@ - (void)registerServices { + // Register URL + [NSURLProtocol registerClass:[PBGitXProtocol class]]; + // Register the service class PBServicesController *services = [[PBServicesController alloc] init]; [NSApp setServicesProvider:services]; diff --git a/Documentation/CallingFromWebKit.txt b/Documentation/CallingFromWebKit.txt index 4124705..a1a863b 100644 --- a/Documentation/CallingFromWebKit.txt +++ b/Documentation/CallingFromWebKit.txt @@ -78,4 +78,24 @@ async, which means that it doesn't matter if it takes a long time to run. While you can have direct access to most objects, sometimes it is useful to have an async task, for example if the operation can take a long time. Use -this function to keep the UI responsive. \ No newline at end of file +this function to keep the UI responsive. + +Loading files from WebKit +========================= + +All views that have a subclass of PBWebController set as ResourceLoadDelegate +(this happens automatically if you assign a view to your controllers) gain +access to the GitX:// protocol. This protocol allows you to load in blobs from +the repository. The format is as follows: + + GitX://REVISION/path/to/file + +Which means that revision can't have a path separator in it (so HEAD is valid, +but pu/pb/fix_it is not). You also can't just pass on a blob oid. These things +will probably be fixed in future revisions of the protocol. + +What this allows you to do, for instance, is to display images that have +changed. For example, if you want to show the new file from a diff, you can +use something like + + \ No newline at end of file diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 4c3d722..15de761 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ F5E92A1B0E88550E00056E75 /* empty_file.png in Resources */ = {isa = PBXBuildFile; fileRef = F5E92A1A0E88550E00056E75 /* empty_file.png */; }; F5E92A230E88569500056E75 /* new_file.png in Resources */ = {isa = PBXBuildFile; fileRef = F5E92A220E88569500056E75 /* new_file.png */; }; F5EF8C8E0E9D4A5D0050906B /* PBWebController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5EF8C8D0E9D4A5D0050906B /* PBWebController.m */; }; + F5FC41F40EBCBD4300191D80 /* PBGitXProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */; }; F5FE6C030EB13BC900F30D12 /* PBServicesController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FE6C020EB13BC900F30D12 /* PBServicesController.m */; }; F5FF4E180E0829C20006317A /* PBGitRevList.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E170E0829C20006317A /* PBGitRevList.m */; }; F5FF4E7A0E082E440006317A /* PBGitGrapher.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E790E082E440006317A /* PBGitGrapher.m */; }; @@ -199,6 +200,8 @@ F5E92A220E88569500056E75 /* new_file.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = new_file.png; path = Images/new_file.png; sourceTree = ""; }; F5EF8C8C0E9D4A5D0050906B /* PBWebController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebController.h; sourceTree = ""; }; F5EF8C8D0E9D4A5D0050906B /* PBWebController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebController.m; sourceTree = ""; }; + F5FC41F20EBCBD4300191D80 /* PBGitXProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitXProtocol.h; sourceTree = ""; }; + F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitXProtocol.m; sourceTree = ""; }; F5FE6C010EB13BC900F30D12 /* PBServicesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBServicesController.h; sourceTree = ""; }; F5FE6C020EB13BC900F30D12 /* PBServicesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBServicesController.m; sourceTree = ""; }; F5FF4E160E0829C20006317A /* PBGitRevList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevList.h; sourceTree = ""; }; @@ -410,6 +413,8 @@ F56244080E9684B0002B6C44 /* PBUnsortableTableHeader.m */, F50A41210EBB875D00208746 /* PBNiceSplitView.h */, F50A41220EBB875D00208746 /* PBNiceSplitView.m */, + F5FC41F20EBCBD4300191D80 /* PBGitXProtocol.h */, + F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */, ); name = Aux; sourceTree = ""; @@ -643,6 +648,7 @@ F5E424180EA3E4EB0046E362 /* PBWebDiffController.m in Sources */, F5FE6C030EB13BC900F30D12 /* PBServicesController.m in Sources */, F50A41230EBB875D00208746 /* PBNiceSplitView.m in Sources */, + F5FC41F40EBCBD4300191D80 /* PBGitXProtocol.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/PBGitXProtocol.h b/PBGitXProtocol.h new file mode 100644 index 0000000..c4790c3 --- /dev/null +++ b/PBGitXProtocol.h @@ -0,0 +1,24 @@ +// +// PBGitXProtocol.h +// GitX +// +// Created by Pieter de Bie on 01-11-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import +#import "PBGitRepository.h" + +@interface PBGitXProtocol : NSURLProtocol { + NSFileHandle *handle; +} +@end + +@interface NSURLRequest (PBGitXProtocol) +@property (readonly) PBGitRepository *repository; +@end + +@interface NSMutableURLRequest (PBGitXProtocol) +@property (retain) PBGitRepository *repository; +@end + diff --git a/PBGitXProtocol.m b/PBGitXProtocol.m new file mode 100644 index 0000000..dcffeea --- /dev/null +++ b/PBGitXProtocol.m @@ -0,0 +1,79 @@ +// +// PBGitXProtocol.m +// GitX +// +// Created by Pieter de Bie on 01-11-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import "PBGitXProtocol.h" + + +@implementation PBGitXProtocol + ++ (BOOL) canInitWithRequest:(NSURLRequest *)request +{ + return [[[request URL] scheme] isEqualToString:@"GitX"]; +} + ++ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request +{ + return request; +} + +-(void)startLoading +{ + NSURL *url = [[self request] URL]; + PBGitRepository *repo = [[self request] repository]; + + if(!repo) { + [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:nil]]; + return; + } + + NSString *specifier = [NSString stringWithFormat:@"%@:%@", [url host], [[url path] substringFromIndex:1]]; + handle = [repo handleInWorkDirForArguments:[NSArray arrayWithObjects:@"cat-file", @"blob", specifier, nil]]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishFileLoad:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; + [handle readToEndOfFileInBackgroundAndNotify]; + + NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[[self request] URL] + MIMEType:nil + expectedContentLength:-1 + textEncodingName:nil]; + + [[self client] URLProtocol:self + didReceiveResponse:response + cacheStoragePolicy:NSURLCacheStorageNotAllowed]; +} + +- (void) didFinishFileLoad:(NSNotification *)notification +{ + NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem]; + [[self client] URLProtocol:self didLoadData:data]; + [[self client] URLProtocolDidFinishLoading:self]; +} + +- (void) stopLoading +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +@end + +@implementation NSURLRequest (PBGitXProtocol) + +- (PBGitRepository *) repository +{ + return [NSURLProtocol propertyForKey:@"PBGitRepository" inRequest:self]; +} +@end + +@implementation NSMutableURLRequest (PBGitXProtocol) +@dynamic repository; + +- (void) setRepository:(PBGitRepository *)repository +{ + [NSURLProtocol setProperty:repository forKey:@"PBGitRepository" inRequest:self]; +} + +@end diff --git a/PBWebController.h b/PBWebController.h index 3aae774..5921a11 100644 --- a/PBWebController.h +++ b/PBWebController.h @@ -16,9 +16,13 @@ // For async git reading NSMapTable *callbacks; + + // For the repository access + IBOutlet id repository; } @property (retain) NSString *startFile; +@property (retain) id repository; - (WebScriptObject *) script; @end diff --git a/PBWebController.m b/PBWebController.m index ab53e80..c8a4aab 100644 --- a/PBWebController.m +++ b/PBWebController.m @@ -8,11 +8,13 @@ #import "PBWebController.h" #import "PBGitRepository.h" +#import "PBGitXProtocol.h" + #include @implementation PBWebController -@synthesize startFile; +@synthesize startFile, repository; - (void) awakeFromNib { @@ -25,6 +27,7 @@ finishedLoading = NO; [view setUIDelegate:self]; [view setFrameLoadDelegate:self]; + [view setResourceLoadDelegate:self]; [[view mainFrame] loadRequest:request]; } @@ -50,6 +53,26 @@ NSLog(@"Error from webkit: %@", dictionary); } +- (NSURLRequest *)webView:(WebView *)sender + resource:(id)identifier + willSendRequest:(NSURLRequest *)request + redirectResponse:(NSURLResponse *)redirectResponse + fromDataSource:(WebDataSource *)dataSource +{ + if (!self.repository) + return request; + + // TODO: Change this to canInitWithRequest + if ([[[request URL] scheme] isEqualToString:@"GitX"]) { + NSMutableURLRequest *newRequest = [request mutableCopy]; + [newRequest setRepository:self.repository]; + return newRequest; + } + + return request; +} + + + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector { return NO; @@ -81,7 +104,7 @@ #pragma mark Using async function from JS -- (void) runCommand:(WebScriptObject *)arguments inRepository:(PBGitRepository *)repository callBack:(WebScriptObject *)callBack +- (void) runCommand:(WebScriptObject *)arguments inRepository:(PBGitRepository *)repo callBack:(WebScriptObject *)callBack { // The JS bridge does not handle JS Arrays, even though the docs say it does. So, we convert it ourselves. int length = [[arguments valueForKey:@"length"] intValue]; @@ -90,7 +113,7 @@ for (i = 0; i < length; i++) [realArguments addObject:[arguments webScriptValueAtIndex:i]]; - NSFileHandle *handle = [repository handleInWorkDirForArguments:realArguments]; + NSFileHandle *handle = [repo handleInWorkDirForArguments:realArguments]; [callbacks setObject:callBack forKey:handle]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(JSRunCommandDone:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; [handle readToEndOfFileInBackgroundAndNotify];