// XXX: remove
#import <MessageUI/MailComposeController.h>
-#import <WebCore/WebScriptObject.h>
-//#include <WebCore/DOMHTML.h>
+#include <WebKit/DOMCSSPrimitiveValue.h>
+#include <WebKit/DOMCSSStyleDeclaration.h>
+#include <WebKit/DOMDocument.h>
+#include <WebKit/DOMHTMLBodyElement.h>
+#include <WebKit/DOMNodeList.h>
+#include <WebKit/DOMRGBColor.h>
#include <WebKit/WebFrame.h>
#include <WebKit/WebPolicyDelegate.h>
-#include <WebKit/WebView.h>
+#include <WebKit/WebScriptObject.h>
+#import <WebKit/WebView.h>
#import <WebKit/WebView-WebPrivate.h>
#include <sstream>
#define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
+void NSLogPoint(const char *fix, const CGPoint &point) {
+ NSLog(@"%s(%g,%g)", fix, point.x, point.y);
+}
+
void NSLogRect(const char *fix, const CGRect &rect) {
NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
}
#define lprintf(args...) fprintf(stderr, args)
#define ForRelease 0
-#define ForSaurik 1 && !ForRelease
+#define ForSaurik (1 && !ForRelease)
#define RecycleWebViews 0
-#define AlwaysReload 1 && !ForRelease
+#define AlwaysReload (1 && !ForRelease)
/* Radix Sort {{{ */
@interface NSMutableArray (Radix)
@end
/* }}} */
+/* Apple Bug Fixes {{{ */
+@implementation UIWebDocumentView (Cydia)
+
+- (void) _setScrollerOffset:(CGPoint)offset {
+ UIScroller *scroller([self _scroller]);
+
+ CGSize size([scroller contentSize]);
+ CGSize bounds([scroller bounds].size);
+
+ CGPoint max;
+ max.x = size.width - bounds.width;
+ max.y = size.height - bounds.height;
+
+ // wtf Apple?!
+ if (max.x < 0)
+ max.x = 0;
+ if (max.y < 0)
+ max.y = 0;
+
+ offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
+ offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
+
+ [scroller setOffset:offset];
+}
+
+@end
+/* }}} */
+
typedef enum {
kUIControlEventMouseDown = 1 << 0,
kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
} UIControlEventMasks;
+NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
+ size_t length([self length] - state->state);
+ if (length <= 0)
+ return 0;
+ else if (length > count)
+ length = count;
+ for (size_t i(0); i != length; ++i)
+ objects[i] = [self item:state->state++];
+ state->itemsPtr = objects;
+ state->mutationsPtr = (unsigned long *) self;
+ return length;
+}
+
@interface NSString (UIKit)
- (NSString *) stringByAddingPercentEscapes;
- (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
static NSDictionary *SectionMap_;
static NSMutableDictionary *Metadata_;
+static NSMutableDictionary *Indices_;
static _transient NSMutableDictionary *Settings_;
static _transient NSString *Role_;
static _transient NSMutableDictionary *Packages_;
- (Source *) source;
- (NSString *) role;
+- (NSString *) rating;
- (BOOL) matches:(NSString *)text;
}
+ (NSArray *) _attributeKeys {
- return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
+ return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
}
- (NSArray *) attributeKeys {
return role_;
}
+- (NSString *) rating {
+ if (NSString *pattern = [Indices_ objectForKey:@"Rating"])
+ return [pattern stringByReplacingOccurrencesOfString:@"%@" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ else
+ return nil;
+}
+
- (BOOL) matches:(NSString *)text {
if (text == nil)
return NO;
}
- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
- NSString *context = [sheet context];
+ NSString *context([sheet context]);
- if ([context isEqualToString:@"remove"])
+ if ([context isEqualToString:@"remove"]) {
switch (button) {
case 1:
[self cancel];
default:
_assert(false);
}
- else if ([context isEqualToString:@"unable"])
- [self cancel];
- [sheet dismiss];
+ [sheet dismiss];
+ } else if ([context isEqualToString:@"unable"]) {
+ [self cancel];
+ [sheet dismiss];
+ } else
+ [super alertSheet:sheet buttonClicked:button];
}
- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
}
- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
- NSString *context = [sheet context];
+ NSString *context([sheet context]);
+
if ([context isEqualToString:@"conffile"]) {
FILE *input = [database_ input];
default:
_assert(false);
}
- }
- [sheet dismiss];
+ [sheet dismiss];
+ }
}
- (void) closeButtonPushed {
}
- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
- int count = [buttons_ count];
- _assert(count != 0);
- _assert(button <= count + 1);
+ NSString *context([sheet context]);
- if (count != button - 1)
- [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
+ if ([context isEqualToString:@"modify"]) {
+ int count = [buttons_ count];
+ _assert(count != 0);
+ _assert(button <= count + 1);
- [sheet dismiss];
+ if (count != button - 1)
+ [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
+
+ [sheet dismiss];
+ } else
+ [super alertSheet:sheet buttonClicked:button];
}
- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
- [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
return [super webView:sender didFinishLoadForFrame:frame];
}
buttons:buttons
defaultButtonIndex:2
delegate:self
- context:@"manage"
+ context:@"modify"
] autorelease]];
}
}
}
- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
- NSString *context = [sheet context];
- if ([context isEqualToString:@"source"])
+ NSString *context([sheet context]);
+
+ if ([context isEqualToString:@"source"]) {
switch (button) {
case 1: {
NSString *href = [[sheet textField] text];
_assert(false);
}
- [sheet dismiss];
+ [sheet dismiss];
+ } else if ([context isEqualToString:@"trivial"])
+ [sheet dismiss];
+ else if ([context isEqualToString:@"urlerror"])
+ [sheet dismiss];
}
- (id) initWithBook:(RVBook *)book database:(Database *)database {
context:@"source"
] autorelease];
+ [sheet setNumberOfRows:1];
+
[sheet addTextFieldWithValue:@"http://" label:@""];
UITextInputTraits *traits = [[sheet textField] textInputTraits];
- [traits setAutocapitalizationType:0];
- [traits setKeyboardType:3];
- [traits setAutocorrectionType:1];
+ [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+ [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
+ [traits setKeyboardType:UIKeyboardTypeURL];
+ // XXX: UIReturnKeyDone
+ [traits setReturnKeyType:UIReturnKeyNext];
[sheet popupAlertAnimated:YES];
}
@implementation HomeView
- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
- [sheet dismiss];
+ NSString *context([sheet context]);
+
+ if ([context isEqualToString:@"about"])
+ [sheet dismiss];
+ else
+ [super alertSheet:sheet buttonClicked:button];
}
- (void) _leftButtonClicked {
return @"Settings";
}
+#if !AlwaysReload
- (NSString *) _rightButtonTitle {
return nil;
}
+#endif
@end
/* }}} */
/* }}} */
/* Browser Implementation {{{ */
@implementation BrowserView
+#include "internals.h"
- (void) dealloc {
+ if (challenge_ != nil)
+ [challenge_ release];
+
WebView *webview = [webview_ webView];
[webview setFrameLoadDelegate:nil];
[webview setResourceLoadDelegate:nil];
[scroller_ setDelegate:nil];
+ [background_ release];
[scroller_ release];
[urls_ release];
[indicator_ release];
return true;
}
+- (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
+ UIActionSheet *sheet = [[[UIActionSheet alloc]
+ initWithTitle:@"JavaScript Alert"
+ buttons:[NSArray arrayWithObjects:@"OK", nil]
+ defaultButtonIndex:0
+ delegate:self
+ context:@"alert"
+ ] autorelease];
+
+ [sheet setBodyText:message];
+ [sheet popupAlertAnimated:YES];
+}
+
- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
[window setValue:delegate_ forKey:@"cydia"];
}
-- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
+- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
if (NSURL *url = [request URL]) {
+ NSLog(@"win:%@:%@", url, [action description]);
if (![self getSpecial:url]) {
NSString *scheme([[url scheme] lowercaseString]);
if ([scheme isEqualToString:@"mailto"])
[listener use];
return;
}
+ else NSLog(@"nav:%@:%@", url, [action description]);
const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
[book_ pushPage:self];
}
-- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+ NSString *context([sheet context]);
+
+ if ([context isEqualToString:@"alert"])
+ [sheet dismiss];
+ else if ([context isEqualToString:@"challenge"]) {
+ id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
+
+ switch (button) {
+ case 1: {
+ NSString *username([[sheet textFieldAtIndex:0] text]);
+ NSString *password([[sheet textFieldAtIndex:1] text]);
+
+ NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
+
+ [sender useCredential:credential forAuthenticationChallenge:challenge_];
+ } break;
+
+ case 2:
+ [sender cancelAuthenticationChallenge:challenge_];
+ break;
+
+ default:
+ _assert(false);
+ }
+
+ [challenge_ release];
+ challenge_ = nil;
+
+ [sheet dismiss];
+ }
+}
+
+- (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
+ challenge_ = [challenge retain];
+
+ NSURLProtectionSpace *space([challenge protectionSpace]);
+ NSString *realm([space realm]);
+ if (realm == nil)
+ realm = @"";
+
+ UIActionSheet *sheet = [[[UIActionSheet alloc]
+ initWithTitle:realm
+ buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
+ defaultButtonIndex:0
+ delegate:self
+ context:@"challenge"
+ ] autorelease];
+
+ [sheet setNumberOfRows:1];
+
+ [sheet addTextFieldWithValue:@"" label:@"username"];
+ [sheet addTextFieldWithValue:@"" label:@"password"];
+
+ UITextField *username([sheet textFieldAtIndex:0]); {
+ UITextInputTraits *traits([username textInputTraits]);
+ [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+ [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
+ [traits setKeyboardType:UIKeyboardTypeASCIICapable];
+ [traits setReturnKeyType:UIReturnKeyNext];
+ }
+
+ UITextField *password([sheet textFieldAtIndex:1]); {
+ UITextInputTraits *traits([password textInputTraits]);
+ [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+ [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
+ [traits setKeyboardType:UIKeyboardTypeASCIICapable];
+ // XXX: UIReturnKeyDone
+ [traits setReturnKeyType:UIReturnKeyNext];
+ [traits setSecureTextEntry:YES];
+ }
+
+ [sheet popupAlertAnimated:YES];
+}
+
+- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
NSURL *url = [request URL];
if ([self getSpecial:url])
return nil;
return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
}
+- (void) _clearBackground {
+ [background_ setBackgroundColor:[UIColor pinStripeColor]];
+ [background_ setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
+ [scroller_ setShowBackgroundShadow:NO];
+}
+
- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
- if ([frame parentFrame] == nil)
+ if ([frame parentFrame] == nil) {
[self _finishLoading];
+
+ [self _clearBackground];
+
+ if (DOMDocument *document = [frame DOMDocument])
+ if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
+ for (DOMHTMLBodyElement *body in bodies) {
+ DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
+
+ bool colored(false);
+
+ if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
+ DOMRGBColor *rgb([color getRGBColorValue]);
+
+ float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
+ if (alpha != 0) {
+ colored = true;
+
+ [background_ setBackgroundColor:[UIColor
+ colorWithRed:([[rgb red] getFloatValue:DOM_CSS_NUMBER] / 255)
+ green:([[rgb green] getFloatValue:DOM_CSS_NUMBER] / 255)
+ blue:([[rgb blue] getFloatValue:DOM_CSS_NUMBER] / 255)
+ alpha:alpha
+ ]];
+ }
+ }
+
+ if (DOMCSSPrimitiveValue *image = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-image"])) {
+ NSString *src([image getStringValue]);
+ if ([src isEqualToString:@"none"])
+ goto none;
+ NSLog(@"img:%@", [image getStringValue]);
+ } else none: if (colored)
+ [background_ setImage:nil];
+
+ break;
+ }
+ }
+
return [webview_ webView:sender didFinishLoadForFrame:frame];
}
struct CGRect bounds = [self bounds];
- UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
- [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
- [self addSubview:pinstripe];
+ background_ = [[UIImageView alloc] initWithFrame:bounds];
+ [self _clearBackground];
+ [self addSubview:background_];
scroller_ = [[UIScroller alloc] initWithFrame:bounds];
[self addSubview:scroller_];
#endif
webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
webview = [webview_ webView];
+ [webview_ setDrawsBackground:NO];
[webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
[webview_ setZoomsFocusedFormControl:YES];
[webview_ setContentsPosition:7];
[webview_ setEnabledGestures:0xa];
- [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
- [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
+ [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
+ [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
[webview_ setSmoothsFonts:YES];
urls_ = [[NSMutableArray alloc] initWithCapacity:16];
[self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+ [background_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
[scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
- [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
} return self;
}
NSData *data(UIImagePNGRepresentation(icon));
+ NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
+ [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ [client URLProtocol:self didLoadData:data];
+ [client URLProtocolDidFinishLoading:self];
+ } else if ([command isEqualToString:@"source-icon"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ NSString *source(Simplify(path));
+
+ UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
+ if (icon == nil)
+ icon = [UIImage applicationImageNamed:@"unknown.png"];
+
+ NSData *data(UIImagePNGRepresentation(icon));
+
NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
[client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[client URLProtocol:self didLoadData:data];
[field_ setPaddingTop:5];
- UITextInputTraits *traits = [field_ textInputTraits];
- [traits setAutocapitalizationType:0];
- [traits setAutocorrectionType:1];
- [traits setReturnKeyType:6];
+ UITextInputTraits *traits([field_ textInputTraits]);
+ [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+ [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
+ [traits setReturnKeyType:UIReturnKeySearch];
CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
}
- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
- NSString *context = [sheet context];
- if ([context isEqualToString:@"fixhalf"])
+ NSString *context([sheet context]);
+
+ if ([context isEqualToString:@"fixhalf"]) {
switch (button) {
case 1:
@synchronized (self) {
default:
_assert(false);
}
- else if ([context isEqualToString:@"role"]) {
+
+ [sheet dismiss];
+ } else if ([context isEqualToString:@"role"]) {
switch (button) {
case 1: Role_ = @"User"; break;
case 2: Role_ = @"Hacker"; break;
[self updateData];
else
[self finish];
- } else if ([context isEqualToString:@"upgrade"])
+
+ [sheet dismiss];
+ } else if ([context isEqualToString:@"upgrade"]) {
switch (button) {
case 1:
@synchronized (self) {
_assert(false);
}
- [sheet dismiss];
+ [sheet dismiss];
+ }
}
- (void) reorganize { _pooled
[self setIdleTimerDisabled:YES];
hud_ = [self addProgressHUD];
- [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
+ [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
[self setStatusBarShowsProgress:YES];
}*/
int main(int argc, char *argv[]) { _pooled
+ class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
+
bool substrate(false);
if (argc != 0) {
setuid(0);
setgid(0);
-#if 0 /* XXX: this costs 1.4s of startup performance */
+#if 1 /* XXX: this costs 1.4s of startup performance */
if (unlink("/var/cache/apt/pkgcache.bin") == -1)
_assert(errno == ENOENT);
if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
/*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
+ if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
+ Indices_ = [[NSMutableDictionary alloc] init];
+
if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
else {