Skip to content

Commit e232181

Browse files
committed
Add a GitX:// protocol handler
This allows to read in arbitrary blobs from the repository. For more information, see the CallingFromWebKit.txt document in the Documentation/ directory.
1 parent b7645ec commit e232181

File tree

7 files changed

+164
-4
lines changed

7 files changed

+164
-4
lines changed

ApplicationController.m

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#import "PBRepositoryDocumentController.h"
1313
#import "PBCLIProxy.h"
1414
#import "PBServicesController.h"
15+
#import "PBGitXProtocol.h"
1516

1617
@implementation ApplicationController
1718
@synthesize cliProxy;
@@ -34,6 +35,9 @@ - (ApplicationController*)init
3435

3536
- (void)registerServices
3637
{
38+
// Register URL
39+
[NSURLProtocol registerClass:[PBGitXProtocol class]];
40+
3741
// Register the service class
3842
PBServicesController *services = [[PBServicesController alloc] init];
3943
[NSApp setServicesProvider:services];

Documentation/CallingFromWebKit.txt

+21-1
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,24 @@ async, which means that it doesn't matter if it takes a long time to run.
7878

7979
While you can have direct access to most objects, sometimes it is useful to
8080
have an async task, for example if the operation can take a long time. Use
81-
this function to keep the UI responsive.
81+
this function to keep the UI responsive.
82+
83+
Loading files from WebKit
84+
=========================
85+
86+
All views that have a subclass of PBWebController set as ResourceLoadDelegate
87+
(this happens automatically if you assign a view to your controllers) gain
88+
access to the GitX:// protocol. This protocol allows you to load in blobs from
89+
the repository. The format is as follows:
90+
91+
GitX://REVISION/path/to/file
92+
93+
Which means that revision can't have a path separator in it (so HEAD is valid,
94+
but pu/pb/fix_it is not). You also can't just pass on a blob oid. These things
95+
will probably be fixed in future revisions of the protocol.
96+
97+
What this allows you to do, for instance, is to display images that have
98+
changed. For example, if you want to show the new file from a diff, you can
99+
use something like
100+
101+
<img src="GitX://HEAD:Images/new_file.png">

GitX.xcodeproj/project.pbxproj

+6
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
F5E92A1B0E88550E00056E75 /* empty_file.png in Resources */ = {isa = PBXBuildFile; fileRef = F5E92A1A0E88550E00056E75 /* empty_file.png */; };
6969
F5E92A230E88569500056E75 /* new_file.png in Resources */ = {isa = PBXBuildFile; fileRef = F5E92A220E88569500056E75 /* new_file.png */; };
7070
F5EF8C8E0E9D4A5D0050906B /* PBWebController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5EF8C8D0E9D4A5D0050906B /* PBWebController.m */; };
71+
F5FC41F40EBCBD4300191D80 /* PBGitXProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */; };
7172
F5FE6C030EB13BC900F30D12 /* PBServicesController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FE6C020EB13BC900F30D12 /* PBServicesController.m */; };
7273
F5FF4E180E0829C20006317A /* PBGitRevList.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E170E0829C20006317A /* PBGitRevList.m */; };
7374
F5FF4E7A0E082E440006317A /* PBGitGrapher.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E790E082E440006317A /* PBGitGrapher.m */; };
@@ -199,6 +200,8 @@
199200
F5E92A220E88569500056E75 /* new_file.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = new_file.png; path = Images/new_file.png; sourceTree = "<group>"; };
200201
F5EF8C8C0E9D4A5D0050906B /* PBWebController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebController.h; sourceTree = "<group>"; };
201202
F5EF8C8D0E9D4A5D0050906B /* PBWebController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebController.m; sourceTree = "<group>"; };
203+
F5FC41F20EBCBD4300191D80 /* PBGitXProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitXProtocol.h; sourceTree = "<group>"; };
204+
F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitXProtocol.m; sourceTree = "<group>"; };
202205
F5FE6C010EB13BC900F30D12 /* PBServicesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBServicesController.h; sourceTree = "<group>"; };
203206
F5FE6C020EB13BC900F30D12 /* PBServicesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBServicesController.m; sourceTree = "<group>"; };
204207
F5FF4E160E0829C20006317A /* PBGitRevList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevList.h; sourceTree = "<group>"; };
@@ -410,6 +413,8 @@
410413
F56244080E9684B0002B6C44 /* PBUnsortableTableHeader.m */,
411414
F50A41210EBB875D00208746 /* PBNiceSplitView.h */,
412415
F50A41220EBB875D00208746 /* PBNiceSplitView.m */,
416+
F5FC41F20EBCBD4300191D80 /* PBGitXProtocol.h */,
417+
F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */,
413418
);
414419
name = Aux;
415420
sourceTree = "<group>";
@@ -643,6 +648,7 @@
643648
F5E424180EA3E4EB0046E362 /* PBWebDiffController.m in Sources */,
644649
F5FE6C030EB13BC900F30D12 /* PBServicesController.m in Sources */,
645650
F50A41230EBB875D00208746 /* PBNiceSplitView.m in Sources */,
651+
F5FC41F40EBCBD4300191D80 /* PBGitXProtocol.m in Sources */,
646652
);
647653
runOnlyForDeploymentPostprocessing = 0;
648654
};

PBGitXProtocol.h

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// PBGitXProtocol.h
3+
// GitX
4+
//
5+
// Created by Pieter de Bie on 01-11-08.
6+
// Copyright 2008 Pieter de Bie. All rights reserved.
7+
//
8+
9+
#import <Cocoa/Cocoa.h>
10+
#import "PBGitRepository.h"
11+
12+
@interface PBGitXProtocol : NSURLProtocol {
13+
NSFileHandle *handle;
14+
}
15+
@end
16+
17+
@interface NSURLRequest (PBGitXProtocol)
18+
@property (readonly) PBGitRepository *repository;
19+
@end
20+
21+
@interface NSMutableURLRequest (PBGitXProtocol)
22+
@property (retain) PBGitRepository *repository;
23+
@end
24+

PBGitXProtocol.m

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// PBGitXProtocol.m
3+
// GitX
4+
//
5+
// Created by Pieter de Bie on 01-11-08.
6+
// Copyright 2008 Pieter de Bie. All rights reserved.
7+
//
8+
9+
#import "PBGitXProtocol.h"
10+
11+
12+
@implementation PBGitXProtocol
13+
14+
+ (BOOL) canInitWithRequest:(NSURLRequest *)request
15+
{
16+
return [[[request URL] scheme] isEqualToString:@"GitX"];
17+
}
18+
19+
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
20+
{
21+
return request;
22+
}
23+
24+
-(void)startLoading
25+
{
26+
NSURL *url = [[self request] URL];
27+
PBGitRepository *repo = [[self request] repository];
28+
29+
if(!repo) {
30+
[[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:nil]];
31+
return;
32+
}
33+
34+
NSString *specifier = [NSString stringWithFormat:@"%@:%@", [url host], [[url path] substringFromIndex:1]];
35+
handle = [repo handleInWorkDirForArguments:[NSArray arrayWithObjects:@"cat-file", @"blob", specifier, nil]];
36+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishFileLoad:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
37+
[handle readToEndOfFileInBackgroundAndNotify];
38+
39+
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[[self request] URL]
40+
MIMEType:nil
41+
expectedContentLength:-1
42+
textEncodingName:nil];
43+
44+
[[self client] URLProtocol:self
45+
didReceiveResponse:response
46+
cacheStoragePolicy:NSURLCacheStorageNotAllowed];
47+
}
48+
49+
- (void) didFinishFileLoad:(NSNotification *)notification
50+
{
51+
NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem];
52+
[[self client] URLProtocol:self didLoadData:data];
53+
[[self client] URLProtocolDidFinishLoading:self];
54+
}
55+
56+
- (void) stopLoading
57+
{
58+
[[NSNotificationCenter defaultCenter] removeObserver:self];
59+
}
60+
61+
@end
62+
63+
@implementation NSURLRequest (PBGitXProtocol)
64+
65+
- (PBGitRepository *) repository
66+
{
67+
return [NSURLProtocol propertyForKey:@"PBGitRepository" inRequest:self];
68+
}
69+
@end
70+
71+
@implementation NSMutableURLRequest (PBGitXProtocol)
72+
@dynamic repository;
73+
74+
- (void) setRepository:(PBGitRepository *)repository
75+
{
76+
[NSURLProtocol setProperty:repository forKey:@"PBGitRepository" inRequest:self];
77+
}
78+
79+
@end

PBWebController.h

+4
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616

1717
// For async git reading
1818
NSMapTable *callbacks;
19+
20+
// For the repository access
21+
IBOutlet id repository;
1922
}
2023

2124
@property (retain) NSString *startFile;
25+
@property (retain) id repository;
2226

2327
- (WebScriptObject *) script;
2428
@end

PBWebController.m

+26-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88

99
#import "PBWebController.h"
1010
#import "PBGitRepository.h"
11+
#import "PBGitXProtocol.h"
12+
1113
#include <SystemConfiguration/SCNetworkReachability.h>
1214

1315
@implementation PBWebController
1416

15-
@synthesize startFile;
17+
@synthesize startFile, repository;
1618

1719
- (void) awakeFromNib
1820
{
@@ -25,6 +27,7 @@ - (void) awakeFromNib
2527
finishedLoading = NO;
2628
[view setUIDelegate:self];
2729
[view setFrameLoadDelegate:self];
30+
[view setResourceLoadDelegate:self];
2831
[[view mainFrame] loadRequest:request];
2932
}
3033

@@ -50,6 +53,26 @@ - (void)webView:(WebView *)webView addMessageToConsole:(NSDictionary *)dictionar
5053
NSLog(@"Error from webkit: %@", dictionary);
5154
}
5255

56+
- (NSURLRequest *)webView:(WebView *)sender
57+
resource:(id)identifier
58+
willSendRequest:(NSURLRequest *)request
59+
redirectResponse:(NSURLResponse *)redirectResponse
60+
fromDataSource:(WebDataSource *)dataSource
61+
{
62+
if (!self.repository)
63+
return request;
64+
65+
// TODO: Change this to canInitWithRequest
66+
if ([[[request URL] scheme] isEqualToString:@"GitX"]) {
67+
NSMutableURLRequest *newRequest = [request mutableCopy];
68+
[newRequest setRepository:self.repository];
69+
return newRequest;
70+
}
71+
72+
return request;
73+
}
74+
75+
5376
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
5477
{
5578
return NO;
@@ -81,7 +104,7 @@ - (BOOL) isReachable:(NSString *)hostname
81104

82105
#pragma mark Using async function from JS
83106

84-
- (void) runCommand:(WebScriptObject *)arguments inRepository:(PBGitRepository *)repository callBack:(WebScriptObject *)callBack
107+
- (void) runCommand:(WebScriptObject *)arguments inRepository:(PBGitRepository *)repo callBack:(WebScriptObject *)callBack
85108
{
86109
// The JS bridge does not handle JS Arrays, even though the docs say it does. So, we convert it ourselves.
87110
int length = [[arguments valueForKey:@"length"] intValue];
@@ -90,7 +113,7 @@ - (void) runCommand:(WebScriptObject *)arguments inRepository:(PBGitRepository *
90113
for (i = 0; i < length; i++)
91114
[realArguments addObject:[arguments webScriptValueAtIndex:i]];
92115

93-
NSFileHandle *handle = [repository handleInWorkDirForArguments:realArguments];
116+
NSFileHandle *handle = [repo handleInWorkDirForArguments:realArguments];
94117
[callbacks setObject:callBack forKey:handle];
95118
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(JSRunCommandDone:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
96119
[handle readToEndOfFileInBackgroundAndNotify];

0 commit comments

Comments
 (0)