X-Git-Url: https://git.saurik.com/cydia.git/blobdiff_plain/2517c95f6a439390cb5d94a841781999e71accad..18096c78c51f7dee48bb9e9312fe733d9d30e320:/UICaboodle/BrowserView.m diff --git a/UICaboodle/BrowserView.m b/UICaboodle/BrowserView.m index 1fd14221..cb3709c2 100644 --- a/UICaboodle/BrowserView.m +++ b/UICaboodle/BrowserView.m @@ -1,7 +1,9 @@ #include +#include + /* Indirect Delegate {{{ */ -@interface IndirectDelegate : NSProxy { +@interface IndirectDelegate : NSObject { _transient volatile id delegate_; } @@ -20,11 +22,71 @@ return self; } +- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didClearWindowObject:window forFrame:frame]; +} + +- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didCommitLoadForFrame:frame]; +} + +- (void) webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didFailLoadWithError:error forFrame:frame]; +} + +- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didFailProvisionalLoadWithError:error forFrame:frame]; +} + +- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didFinishLoadForFrame:frame]; +} + +- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didReceiveTitle:title forFrame:frame]; +} + +- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame { + if (delegate_ != nil) + return [delegate_ webView:sender didStartProvisionalLoadForFrame:frame]; +} + +- (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source { + if (delegate_ != nil) + return [delegate_ webView:sender resource:identifier didReceiveAuthenticationChallenge:challenge fromDataSource:source]; +} + +- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source { + if (delegate_ != nil) + return [delegate_ webView:sender resource:identifier willSendRequest:request redirectResponse:redirectResponse fromDataSource:source]; + return nil; +} + +- (IMP) methodForSelector:(SEL)sel { + if (IMP method = [super methodForSelector:sel]) + return method; + fprintf(stderr, "methodForSelector:[%s] == NULL\n", sel_getName(sel)); + return NULL; +} + - (BOOL) respondsToSelector:(SEL)sel { - return delegate_ == nil ? FALSE : [delegate_ respondsToSelector:sel]; + if ([super respondsToSelector:sel]) + return YES; + // XXX: WebThreadCreateNSInvocation returns nil + //fprintf(stderr, "[%s]R?%s\n", class_getName(self->isa), sel_getName(sel)); + return delegate_ == nil ? NO : [delegate_ respondsToSelector:sel]; } - (NSMethodSignature *) methodSignatureForSelector:(SEL)sel { + if (NSMethodSignature *method = [super methodSignatureForSelector:sel]) + return method; + //fprintf(stderr, "[%s]S?%s\n", class_getName(self->isa), sel_getName(sel)); if (delegate_ != nil) if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel]) return sig; @@ -49,6 +111,29 @@ - (void) _setLayoutInterval:(float)interval; @end +@interface WebScriptObject (Cydia) + +- (unsigned) count; +- (id) objectAtIndex:(unsigned)index; + +@end + +@implementation WebScriptObject (Cydia) + +- (unsigned) count { + id length([self valueForKey:@"length"]); + if ([length respondsToSelector:@selector(intValue)]) + return [length intValue]; + else + return 0; +} + +- (id) objectAtIndex:(unsigned)index { + return [self webScriptValueAtIndex:index]; +} + +@end + /* Web Scripting {{{ */ @interface CydiaObject : NSObject { id indirect_; @@ -70,15 +155,47 @@ } return self; } ++ (NSArray *) _attributeKeys { + return [NSArray arrayWithObjects:@"device", nil]; +} + +- (NSArray *) attributeKeys { + return [[self class] _attributeKeys]; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char *)name { + return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; +} + +- (NSString *) device { + return [[UIDevice currentDevice] uniqueIdentifier]; +} + + (NSString *) webScriptNameForSelector:(SEL)selector { - if (selector == @selector(getPackageById:)) + if (selector == @selector(close)) + return @"close"; + else if (selector == @selector(getPackageById:)) return @"getPackageById"; + else if (selector == @selector(setAutoPopup:)) + return @"setAutoPopup"; else if (selector == @selector(setButtonImage:withStyle:toFunction:)) return @"setButtonImage"; else if (selector == @selector(setButtonTitle:withStyle:toFunction:)) return @"setButtonTitle"; + else if (selector == @selector(setFinishHook:)) + return @"setFinishHook"; + else if (selector == @selector(setPopupHook:)) + return @"setPopupHook"; + else if (selector == @selector(setSpecial:)) + return @"setSpecial"; + else if (selector == @selector(setViewportWidth:)) + return @"setViewportWidth"; else if (selector == @selector(supports:)) return @"supports"; + else if (selector == @selector(stringWithFormat:arguments:)) + return @"format"; + else if (selector == @selector(localizedStringForKey:value:table:)) + return @"localize"; else if (selector == @selector(du:)) return @"du"; else if (selector == @selector(statfs:)) @@ -123,7 +240,8 @@ _assert(dup2(fds[1], 1) != -1); _assert(close(fds[0]) != -1); _assert(close(fds[1]) != -1); - execlp("du", "du", "-s", [path UTF8String], NULL); + /* XXX: this should probably not use du */ + execl("/usr/libexec/cydia/du", "du", "-s", [path UTF8String], NULL); exit(1); _assert(false); } @@ -155,6 +273,14 @@ return value; } +- (void) close { + [indirect_ close]; +} + +- (void) setAutoPopup:(BOOL)popup { + [indirect_ setAutoPopup:popup]; +} + - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { [indirect_ setButtonImage:button withStyle:style toFunction:function]; } @@ -163,19 +289,56 @@ [indirect_ setButtonTitle:button withStyle:style toFunction:function]; } +- (void) setSpecial:(id)function { + [indirect_ setSpecial:function]; +} + +- (void) setFinishHook:(id)function { + [indirect_ setFinishHook:function]; +} + +- (void) setPopupHook:(id)function { + [indirect_ setPopupHook:function]; +} + +- (void) setViewportWidth:(float)width { + [indirect_ setViewportWidth:width]; +} + +- (NSString *) stringWithFormat:(NSString *)format arguments:(WebScriptObject *)arguments { + //NSLog(@"SWF:\"%@\" A:%@", format, [arguments description]); + unsigned count([arguments count]); + id values[count]; + for (unsigned i(0); i != count; ++i) + values[i] = [arguments objectAtIndex:i]; + return [[[NSString alloc] initWithFormat:format arguments:reinterpret_cast(values)] autorelease]; +} + +- (NSString *) localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)table { + if (reinterpret_cast(table) == [WebUndefined undefined]) + table = nil; + return [[NSBundle mainBundle] localizedStringForKey:key value:value table:table]; +} + @end /* }}} */ @implementation BrowserView -#if ForSaurik +#if ShowInternals #include "Internals.h" #endif - (void) dealloc { +#if LogBrowser + NSLog(@"[BrowserView dealloc]"); +#endif + if (challenge_ != nil) [challenge_ release]; + WebThreadLock(); + WebView *webview = [webview_ webView]; [webview setFrameLoadDelegate:nil]; [webview setResourceLoadDelegate:nil]; @@ -185,13 +348,20 @@ [webview setDownloadDelegate:nil]; + /* XXX: these are set by UIWebDocumentView [webview _setFormDelegate:nil]; [webview _setUIKitDelegate:nil]; - [webview setWebMailDelegate:nil]; - [webview setEditingDelegate:nil]; + [webview setEditingDelegate:nil];*/ + + /* XXX: no one sets this, ever + [webview setWebMailDelegate:nil];*/ [webview_ setDelegate:nil]; [webview_ setGestureDelegate:nil]; + [webview_ setFormEditingDelegate:nil]; + [webview_ setInteractionDelegate:nil]; + + [indirect_ setDelegate:nil]; //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; @@ -204,9 +374,10 @@ [webview_ release]; #endif - [indirect_ setDelegate:nil]; [indirect_ release]; + WebThreadUnlock(); + [cydia_ release]; [scroller_ setDelegate:nil]; @@ -217,6 +388,12 @@ [style_ release]; if (function_ != nil) [function_ release]; + if (finish_ != nil) + [finish_ release]; + if (closer_ != nil) + [closer_ release]; + if (special_ != nil) + [special_ release]; [scroller_ release]; [indicator_ release]; @@ -256,11 +433,13 @@ - (void) loadRequest:(NSURLRequest *)request { pushed_ = true; error_ = false; + + WebThreadLock(); [webview_ loadRequest:request]; + WebThreadUnlock(); } - (void) reloadURL { - NSLog(@"rlu:%@", request_); if (request_ == nil) return; @@ -268,8 +447,8 @@ [self loadRequest:request_]; else { UIActionSheet *sheet = [[[UIActionSheet alloc] - initWithTitle:@"Are you sure you want to submit this form again?" - buttons:[NSArray arrayWithObjects:@"Cancel", @"Submit", nil] + initWithTitle:CYLocalize("RESUBMIT_FORM") + buttons:[NSArray arrayWithObjects:CYLocalize("CANCEL"), CYLocalize("SUBMIT"), nil] defaultButtonIndex:0 delegate:self context:@"submit" @@ -284,8 +463,52 @@ return [webview_ webView]; } +- (UIWebDocumentView *) documentView { + return webview_; +} + +/* XXX: WebThreadLock? */ +- (void) _fixScroller:(CGRect)bounds { + float extra; + if (!editing_) + extra = 0; + else { + UIFormAssistant *assistant([UIFormAssistant sharedFormAssistant]); + CGRect peripheral([assistant peripheralFrame]); +#if LogBrowser + NSLog(@"per:%f", peripheral.size.height); +#endif + extra = peripheral.size.height; + } + + CGRect subrect([scroller_ frame]); + subrect.size.height -= extra; + [scroller_ setScrollerIndicatorSubrect:subrect]; + + NSSize visible(NSMakeSize(subrect.size.width, subrect.size.height)); + [webview_ setValue:[NSValue valueWithSize:visible] forGestureAttribute:UIGestureAttributeVisibleSize]; + + CGSize size(size_); + size.height += extra; + [scroller_ setContentSize:size]; + + [scroller_ releaseRubberBandIfNecessary]; +} + +- (void) fixScroller { + CGRect bounds([webview_ documentBounds]); +#if TrackResize + NSLog(@"_fs:(%f,%f+%f,%f)", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); +#endif + [self _fixScroller:bounds]; +} + - (void) view:(UIView *)sender didSetFrame:(CGRect)frame { - [scroller_ setContentSize:frame.size]; + size_ = frame.size; +#if TrackResize + NSLog(@"dsf:(%f,%f+%f,%f)", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); +#endif + [self _fixScroller:frame]; } - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old { @@ -294,11 +517,31 @@ - (void) pushPage:(RVPage *)page { [page setDelegate:delegate_]; - [book_ pushPage:page]; [self setBackButtonTitle:title_]; + [book_ pushPage:page]; +} + +- (void) _pushPage { + if (pushed_) + return; + // WTR: [self autorelease]; + pushed_ = true; + [book_ pushPage:self]; } -- (BOOL) getSpecial:(NSURL *)url { +- (void) swapPage:(RVPage *)page { + [page setDelegate:delegate_]; + if (pushed_) + [book_ swapPage:page]; + else + [book_ pushPage:page]; +} + +- (BOOL) getSpecial:(NSURL *)url swap:(BOOL)swap { +#if LogBrowser + NSLog(@"getSpecial:%@", url); +#endif + NSString *href([url absoluteString]); NSString *scheme([[url scheme] lowercaseString]); @@ -314,14 +557,29 @@ return false; if (page != nil) - [self pushPage:page]; + if (swap) + [self swapPage:page]; + else + [self pushPage:page]; + return true; +} + +- (void) webViewShow:(WebView *)sender { + /* XXX: this is where I cry myself to sleep */ +} + +- (bool) _allowJavaScriptPanel { return true; } - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame { + if (![self _allowJavaScriptPanel]) + return; + [self retain]; + UIActionSheet *sheet = [[[UIActionSheet alloc] initWithTitle:nil - buttons:[NSArray arrayWithObjects:@"OK", nil] + buttons:[NSArray arrayWithObjects:CYLocalize("OK"), nil] defaultButtonIndex:0 delegate:self context:@"alert" @@ -332,11 +590,15 @@ } - (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame { + if (![self _allowJavaScriptPanel]) + return NO; + [self retain]; + UIActionSheet *sheet = [[[UIActionSheet alloc] initWithTitle:nil - buttons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil] + buttons:[NSArray arrayWithObjects:CYLocalize("OK"), CYLocalize("Cancel"), nil] defaultButtonIndex:0 - delegate:self + delegate:indirect_ context:@"confirm" ] autorelease]; @@ -351,9 +613,21 @@ NSNumber *confirm([confirm_ autorelease]); confirm_ = nil; + + [self autorelease]; return [confirm boolValue]; } +- (void) setAutoPopup:(BOOL)popup { + popup_ = popup; +} + +- (void) setSpecial:(id)function { + if (special_ != nil) + [special_ autorelease]; + special_ = function == nil ? nil : [function retain]; +} + - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { if (button_ != nil) [button_ autorelease]; @@ -366,6 +640,8 @@ if (function_ != nil) [function_ autorelease]; function_ = function == nil ? nil : [function retain]; + + [self reloadButtons]; } - (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { @@ -380,12 +656,43 @@ if (function_ != nil) [function_ autorelease]; function_ = function == nil ? nil : [function retain]; + + [self reloadButtons]; +} + +- (void) setFinishHook:(id)function { + if (finish_ != nil) + [finish_ autorelease]; + finish_ = function == nil ? nil : [function retain]; +} + +- (void) setPopupHook:(id)function { + if (closer_ != nil) + [closer_ autorelease]; + closer_ = function == nil ? nil : [function retain]; +} + +- (void) webView:(WebView *)sender willBeginEditingFormElement:(id)element { + editing_ = true; +} + +- (void) webView:(WebView *)sender didBeginEditingFormElement:(id)element { + [self fixScroller]; +} + +- (void) webViewDidEndEditingFormElements:(WebView *)sender { + editing_ = false; + [self fixScroller]; } - (void) webViewClose:(WebView *)sender { [book_ close]; } +- (void) close { + [book_ close]; +} + - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { [window setValue:cydia_ forKey:@"cydia"]; } @@ -395,10 +702,13 @@ } - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id)listener { +#if LogBrowser + NSLog(@"nwa:%@", name); +#endif + if (NSURL *url = [request URL]) { if (name == nil) unknown: { - NSLog(@"win:%@:%@", url, [action description]); - if (![self getSpecial:url]) { + if (![self getSpecial:url swap:NO]) { NSString *scheme([[url scheme] lowercaseString]); if ([scheme isEqualToString:@"mailto"]) [delegate_ openMailToURL:url]; @@ -407,20 +717,30 @@ } else if ([name isEqualToString:@"_open"]) [delegate_ openURL:url]; else if ([name isEqualToString:@"_popup"]) { - RVBook *book([[[RVPopUpBook alloc] initWithFrame:[delegate_ popUpBounds]] autorelease]); - - RVPage *page([delegate_ pageForURL:url hasTag:NULL]); - if (page == nil) { - BrowserView *browser([[[BrowserView alloc] initWithBook:book] autorelease]); - [browser loadURL:url]; - page = browser; - } + NSString *scheme([[url scheme] lowercaseString]); + if ([scheme isEqualToString:@"mailto"]) + [delegate_ openMailToURL:url]; + else { + RVBook *book([[[RVPopUpBook alloc] initWithFrame:[delegate_ popUpBounds]] autorelease]); + [book setHook:indirect_]; + + RVPage *page([delegate_ pageForURL:url hasTag:NULL]); + if (page == nil) { + /* XXX: call createWebViewWithRequest instead? */ + + [self setBackButtonTitle:title_]; + + BrowserView *browser([[[BrowserView alloc] initWithBook:book] autorelease]); + [browser loadURL:url]; + page = browser; + } - [book setDelegate:delegate_]; - [page setDelegate:delegate_]; + [book setDelegate:delegate_]; + [page setDelegate:delegate_]; - [book setPage:page]; - [book_ pushBook:book]; + [book setPage:page]; + [book_ pushBook:book]; + } } else goto unknown; [listener ignore]; @@ -428,13 +748,15 @@ [listener use]; } -- (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { +- (void) webView:(WebView *)sender decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { if ([WebView canShowMIMEType:type]) [listener use]; else { // XXX: handle more mime types! [listener ignore]; - if (frame == [webView mainFrame]) + + WebView *webview([webview_ webView]); + if (frame == [webview mainFrame]) [UIApp openURL:[request URL]]; } } @@ -452,24 +774,34 @@ if (request_ != nil) [request_ autorelease]; request_ = [request retain]; -#if ForSaurik +#if LogBrowser NSLog(@"dpn:%@", request_); #endif } [listener use]; + + WebView *webview([webview_ webView]); + if (frame == [webview mainFrame]) + [self _pushPage]; return; } -#if ForSaurik +#if LogBrowser else NSLog(@"nav:%@:%@", url, [action description]); #endif - const NSArray *capability(reinterpret_cast(GSSystemGetCapability(kGSDisplayIdentifiersCapability))); + const NSArray *capability; - if ( +#if 0 // XXX:3:GSSystemCopyCapability + capability = reinterpret_cast(GSSystemGetCapability(kGSDisplayIdentifiersCapability)); +#else + capability = nil; +#endif + + if (capability != nil && ( [capability containsObject:@"com.apple.Maps"] && [url mapsURL] || [capability containsObject:@"com.apple.youtube"] && [url youTubeURL] - ) { + )) { open: [UIApp openURL:url]; goto ignore; @@ -477,11 +809,14 @@ int store(_not(int)); if (NSURL *itms = [url itmsURL:&store]) { +#if LogBrowser NSLog(@"itms#%@#%u#%@", url, store, itms); - if ( +#endif + + if (capability != nil && ( store == 1 && [capability containsObject:@"com.apple.MobileStore"] || store == 2 && [capability containsObject:@"com.apple.AppStore"] - ) { + )) { url = itms; goto open; } @@ -499,7 +834,7 @@ goto ignore; } - if ([self getSpecial:url]) + if ([self getSpecial:url swap:YES]) goto ignore; else if ([WebView _canHandleRequest:request]) goto use; @@ -513,19 +848,13 @@ //lprintf("Status:%s\n", [text UTF8String]); } -- (void) _pushPage { - if (pushed_) - return; - pushed_ = true; - [book_ pushPage:self]; -} - - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { NSString *context([sheet context]); - if ([context isEqualToString:@"alert"]) + if ([context isEqualToString:@"alert"]) { + [self autorelease]; [sheet dismiss]; - else if ([context isEqualToString:@"confirm"]) { + } else if ([context isEqualToString:@"confirm"]) { switch (button) { case 1: confirm_ = [NSNumber numberWithBool:YES]; @@ -568,8 +897,11 @@ break; case 2: - if (request_ != nil) + if (request_ != nil) { + WebThreadLock(); [webview_ loadRequest:request_]; + WebThreadUnlock(); + } break; default: @@ -590,7 +922,7 @@ UIActionSheet *sheet = [[[UIActionSheet alloc] initWithTitle:realm - buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil] + buttons:[NSArray arrayWithObjects:CYLocalize("LOGIN"), CYLocalize("CANCEL"), nil] defaultButtonIndex:0 delegate:self context:@"challenge" @@ -598,8 +930,8 @@ [sheet setNumberOfRows:1]; - [sheet addTextFieldWithValue:@"" label:@"username"]; - [sheet addTextFieldWithValue:@"" label:@"password"]; + [sheet addTextFieldWithValue:@"" label:CYLocalize("USERNAME")]; + [sheet addTextFieldWithValue:@"" label:CYLocalize("PASSWORD")]; UITextField *username([sheet textFieldAtIndex:0]); { UITextInputTraits *traits([username textInputTraits]); @@ -623,33 +955,48 @@ } - (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; - [self _pushPage]; return [self _addHeadersToRequest:request]; } -- (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed { - [self setBackButtonTitle:title_]; +- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features { +//- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request userGesture:(BOOL)gesture { +#if LogBrowser + NSLog(@"cwv:%@ (%@): %@", request, title_, features == nil ? @"{}" : [features description]); + //NSLog(@"cwv:%@ (%@): %@", request, title_, gesture ? @"Yes" : @"No"); +#endif + + NSNumber *value([features objectForKey:@"width"]); + float width(value == nil ? 0 : [value floatValue]); - BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease]; - [browser setDelegate:delegate_]; + RVBook *book(!popup_ ? book_ : [[[RVPopUpBook alloc] initWithFrame:[delegate_ popUpBounds]] autorelease]); - if (pushed) { + /* XXX: deal with cydia:// pages */ + BrowserView *browser([[[BrowserView alloc] initWithBook:book forWidth:width] autorelease]); + + if (features != nil && popup_) { + [book setDelegate:delegate_]; + [book setHook:indirect_]; + [browser setDelegate:delegate_]; + + [browser loadRequest:request]; + + [book setPage:browser]; + [book_ pushBook:book]; + } else if (request == nil) { + [self setBackButtonTitle:title_]; + [browser setDelegate:delegate_]; + [browser retain]; + } else { + [self pushPage:browser]; [browser loadRequest:request]; - [book_ pushPage:browser]; } return [browser webView]; } - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request { - return [self _createWebViewWithRequest:request pushed:(request != nil)]; -} - -- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features { - return [self _createWebViewWithRequest:request pushed:YES]; + return [self webView:sender createWebViewWithRequest:request windowFeatures:nil]; + //return [self webView:sender createWebViewWithRequest:request userGesture:YES]; } - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame { @@ -661,55 +1008,86 @@ } - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame { - if ([frame parentFrame] != nil) - return; + if ([loading_ count] == 0) + [self retain]; + [loading_ addObject:[NSValue valueWithNonretainedObject:frame]]; - reloading_ = false; - loading_ = true; - [self reloadButtons]; + if ([frame parentFrame] == nil) { + [webview_ resignFirstResponder]; - if (title_ != nil) { - [title_ release]; - title_ = nil; - } + reloading_ = false; - if (button_ != nil) { - [button_ release]; - button_ = nil; - } + if (title_ != nil) { + [title_ release]; + title_ = nil; + } - if (style_ != nil) { - [style_ release]; - style_ = nil; - } + if (button_ != nil) { + [button_ release]; + button_ = nil; + } - if (function_ != nil) { - [function_ release]; - function_ = nil; - } + if (style_ != nil) { + [style_ release]; + style_ = nil; + } - [book_ reloadTitleForPage:self]; + if (function_ != nil) { + [function_ release]; + function_ = nil; + } + + if (finish_ != nil) { + [finish_ release]; + finish_ = nil; + } - [scroller_ scrollPointVisibleAtTopLeft:CGPointZero]; + if (closer_ != nil) { + [closer_ release]; + closer_ = nil; + } + + if (special_ != nil) { + [special_ release]; + special_ = nil; + } + + [book_ reloadTitleForPage:self]; + + [scroller_ scrollPointVisibleAtTopLeft:CGPointZero]; + + if ([scroller_ respondsToSelector:@selector(setZoomScale:duration:)]) + [scroller_ setZoomScale:1 duration:0]; + else if ([scroller_ respondsToSelector:@selector(_setZoomScale:duration:)]) + [scroller_ _setZoomScale:1 duration:0]; + /*else if ([scroller_ respondsToSelector:@selector(setZoomScale:animated:)]) + [scroller_ setZoomScale:1 animated:NO];*/ + + CGRect webrect = [scroller_ bounds]; + webrect.size.height = 0; + [webview_ setFrame:webrect]; + } - CGRect webrect = [scroller_ bounds]; - webrect.size.height = 0; - [webview_ setFrame:webrect]; + [self reloadButtons]; } - (void) _finishLoading { - if (!reloading_) { - loading_ = false; - [self reloadButtons]; - } + size_t count([loading_ count]); + if (count == 0) + [self autorelease]; + if (reloading_ || count != 0) + return; + if (finish_ != nil) + [self callFunction:finish_]; + [self reloadButtons]; } -- (bool) _loading { - return loading_; +- (bool) isLoading { + return [loading_ count] != 0; } - (void) reloadButtons { - if ([self _loading]) + if ([self isLoading]) [indicator_ startAnimation]; else [indicator_ stopAnimation]; @@ -729,6 +1107,7 @@ } - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame { + [self _pushPage]; return [webview_ webView:sender didCommitLoadForFrame:frame]; } @@ -737,9 +1116,10 @@ } - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { - if ([frame parentFrame] == nil) { - [self _finishLoading]; + [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]]; + [self _finishLoading]; + if ([frame parentFrame] == nil) { if (DOMDocument *document = [frame DOMDocument]) if (DOMNodeList *bodies = [document getElementsByTagName:@"body"]) for (DOMHTMLBodyElement *body in bodies) { @@ -784,52 +1164,110 @@ return [webview_ webView:sender didFinishLoadForFrame:frame]; } -- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { - if ([frame parentFrame] != nil) - return; +- (void) _didFailWithError:(NSError *)error forFrame:(WebFrame *)frame { + if ([frame parentFrame] == nil) + [self autorelease]; + + [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]]; + [self _finishLoading]; + if (reloading_) return; - [self _finishLoading]; - [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", - [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString], - [[error localizedDescription] stringByAddingPercentEscapes] - ]]]; + if ([frame parentFrame] == nil) { + [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", + [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString], + [[error localizedDescription] stringByAddingPercentEscapes] + ]]]; - error_ = true; + error_ = true; + } +} + +- (void) webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + [self _didFailWithError:error forFrame:frame]; +} + +- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { + [self _didFailWithError:error forFrame:frame]; } - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary { -#if ForSaurik +#if LogBrowser || ForSaurik lprintf("Console:%s\n", [[dictionary description] UTF8String]); #endif } -- (id) initWithBook:(RVBook *)book { +/* XXX: fix this stupid include file +- (void) webView:(WebView *)sender frame:(WebFrame *)frame exceededDatabaseQuotaForSecurityOrigin:(WebSecurityOrigin *)origin database:(NSString *)database { + [origin setQuota:0x500000]; +}*/ + +- (void) _setTileDrawingEnabled:(BOOL)enabled { + //[webview_ setTileDrawingEnabled:enabled]; +} + +- (void) setViewportWidth:(float)width { + width_ = width ? width != 0 : [[self class] defaultWidth]; + [webview_ setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10]; +} + +- (void) willStartGesturesInView:(UIView *)view forEvent:(GSEventRef)event { + [self _setTileDrawingEnabled:NO]; +} + +- (void) didFinishGesturesInView:(UIView *)view forEvent:(GSEventRef)event { + [self _setTileDrawingEnabled:YES]; + [webview_ redrawScaledDocument]; +} + +- (void) scrollerWillStartDragging:(UIScroller *)scroller { + [self _setTileDrawingEnabled:NO]; +} + +- (void) scrollerDidEndDragging:(UIScroller *)scroller willSmoothScroll:(BOOL)smooth { + [self _setTileDrawingEnabled:YES]; +} + +- (void) scrollerDidEndDragging:(UIScroller *)scroller { + [self _setTileDrawingEnabled:YES]; +} + +- (id) initWithBook:(RVBook *)book forWidth:(float)width { if ((self = [super initWithBook:book]) != nil) { - loading_ = false; + loading_ = [[NSMutableSet alloc] initWithCapacity:3]; + popup_ = false; struct CGRect bounds = [self bounds]; scroller_ = [[UIScroller alloc] initWithFrame:bounds]; [self addSubview:scroller_]; - [scroller_ setShowBackgroundShadow:NO]; [scroller_ setFixedBackgroundPattern:YES]; [scroller_ setBackgroundColor:[UIColor pinStripeColor]]; [scroller_ setScrollingEnabled:YES]; - [scroller_ setAdjustForContentSizeChange:YES]; [scroller_ setClipsSubviews:YES]; [scroller_ setAllowsRubberBanding:YES]; - [scroller_ setScrollDecelerationFactor:0.99]; + [scroller_ setDelegate:self]; + [scroller_ setBounces:YES]; + [scroller_ setScrollHysteresis:8]; + [scroller_ setThumbDetectionEnabled:NO]; + [scroller_ setDirectionalScrolling:YES]; + [scroller_ setScrollDecelerationFactor:0.99]; /* 0.989324 */ + [scroller_ setEventMode:YES]; + [scroller_ setShowBackgroundShadow:NO]; /* YES */ + [scroller_ setAllowsRubberBanding:YES]; /* Vertical */ + [scroller_ setAdjustForContentSizeChange:YES]; /* NO */ CGRect webrect = [scroller_ bounds]; webrect.size.height = 0; WebView *webview; + WebThreadLock(); + #if RecycleWebViews webview_ = [Documents_ lastObject]; if (webview_ != nil) { @@ -856,114 +1294,158 @@ [webview_ setDrawsGrid:NO]; [webview_ setLogsTilingChanges:NO]; [webview_ setTileMinificationFilter:kCAFilterNearest]; - [webview_ setDetectsPhoneNumbers:NO]; + if ([webview_ respondsToSelector:@selector(setDataDetectorTypes:)]) + /* XXX: abstractify */ + [webview_ setDataDetectorTypes:0x80000000]; + else + [webview_ setDetectsPhoneNumbers:NO]; [webview_ setAutoresizes:YES]; [webview_ setMinimumScale:0.25f forDocumentTypes:0x10]; + [webview_ setMaximumScale:5.00f forDocumentTypes:0x10]; [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10]; - [webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10]; + //[webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10]; [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2]; - [webview_ setMinimumScale:1.0f forDocumentTypes:0x8]; + [webview_ setMinimumScale:1.00f forDocumentTypes:0x8]; [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8]; [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8]; [webview_ _setDocumentType:0x4]; - [webview_ setZoomsFocusedFormControl:YES]; + if ([webview_ respondsToSelector:@selector(UIWebDocumentView:)]) + [webview_ setZoomsFocusedFormControl:YES]; [webview_ setContentsPosition:7]; [webview_ setEnabledGestures:0xa]; [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled]; [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller]; [webview_ setSmoothsFonts:YES]; - + [webview_ setAllowsImageSheet:YES]; [webview _setUsesLoaderCache:YES]; - [webview setGroupName:@"Cydia"]; - [webview _setLayoutInterval:0]; + + [webview setGroupName:@"CydiaGroup"]; + if ([webview respondsToSelector:@selector(_setLayoutInterval:)]) + [webview _setLayoutInterval:0]; } + [self setViewportWidth:width]; + [webview_ setDelegate:self]; [webview_ setGestureDelegate:self]; + [webview_ setFormEditingDelegate:self]; + [webview_ setInteractionDelegate:self]; + [scroller_ addSubview:webview_]; //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite]; - indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)]; - [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite]; - Package *package([[Database sharedInstance] packageWithName:@"cydia"]); NSString *application = package == nil ? @"Cydia" : [NSString stringWithFormat:@"Cydia/%@", [package installed] ]; + if (Product_ != nil) + application = [NSString stringWithFormat:@"%@ Version/%@", application, Product_]; if (Build_ != nil) - application = [NSString stringWithFormat:@"Mobile/%@ %@", Build_, application]; - - /* XXX: lookup application directory? */ - /*if (NSDictionary *safari = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) - if (NSString *version = [safari objectForKey:@"SafariProductVersion"]) - application = [NSString stringWithFormat:@"Version/%@ %@", version, application];*/ + application = [NSString stringWithFormat:@"%@ Mobile/%@", application, Build_]; + if (Safari_ != nil) + application = [NSString stringWithFormat:@"%@ Safari/%@", application, Safari_]; [webview setApplicationNameForUserAgent:application]; indirect_ = [[IndirectDelegate alloc] initWithDelegate:self]; cydia_ = [[CydiaObject alloc] initWithDelegate:indirect_]; - [webview setFrameLoadDelegate:self]; + [webview setFrameLoadDelegate:indirect_]; [webview setResourceLoadDelegate:indirect_]; - [webview setUIDelegate:self]; - [webview setScriptDebugDelegate:self]; - [webview setPolicyDelegate:self]; + [webview setUIDelegate:indirect_]; + [webview setScriptDebugDelegate:indirect_]; + [webview setPolicyDelegate:indirect_]; + + WebThreadUnlock(); + + CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite]; + indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)]; + [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite]; [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; + + /*UIWebView *test([[[UIWebView alloc] initWithFrame:[self bounds]] autorelease]); + [test loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.saurik.com/"]]]; + [self addSubview:test];*/ } return self; } -- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event { - [webview_ redrawScaledDocument]; +- (id) initWithBook:(RVBook *)book { + return [self initWithBook:book forWidth:0]; } -- (void) _rightButtonClicked { - if (function_ == nil) { - reloading_ = true; - [self reloadURL]; - } else { - WebView *webview([webview_ webView]); - WebFrame *frame([webview mainFrame]); - - id _private(MSHookIvar(webview, "_private")); - WebCore::Page *page(_private == nil ? NULL : MSHookIvar(_private, "page")); - WebCore::Settings *settings(page == NULL ? NULL : page->settings()); - - bool no; - if (settings == NULL) - no = 0; - else { - no = settings->JavaScriptCanOpenWindowsAutomatically(); - settings->setJavaScriptCanOpenWindowsAutomatically(true); - } +- (NSString *) stringByEvaluatingJavaScriptFromString:(NSString *)script { + WebThreadLock(); + WebView *webview([webview_ webView]); + NSString *string([webview stringByEvaluatingJavaScriptFromString:script]); + WebThreadUnlock(); + return string; +} - [delegate_ clearFirstResponder]; - JSObjectRef function([function_ JSObject]); - JSGlobalContextRef context([frame globalContext]); - JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL); +- (void) callFunction:(WebScriptObject *)function { + WebThreadLock(); - if (settings != NULL) - settings->setJavaScriptCanOpenWindowsAutomatically(no); + WebView *webview([webview_ webView]); + WebFrame *frame([webview mainFrame]); + + id _private(MSHookIvar(webview, "_private")); + WebCore::Page *page(_private == nil ? NULL : MSHookIvar(_private, "page")); + WebCore::Settings *settings(page == NULL ? NULL : page->settings()); + + bool no; + if (settings == NULL) + no = 0; + else { + no = settings->JavaScriptCanOpenWindowsAutomatically(); + settings->setJavaScriptCanOpenWindowsAutomatically(true); } + + [delegate_ clearFirstResponder]; + JSObjectRef object([function JSObject]); + JSGlobalContextRef context([frame globalContext]); + JSObjectCallAsFunction(context, object, NULL, 0, NULL, NULL); + + if (settings != NULL) + settings->setJavaScriptCanOpenWindowsAutomatically(no); + + WebThreadUnlock(); +} + +- (void) didCloseBook:(RVBook *)book { + if (closer_ != nil) + [self callFunction:closer_]; +} + +- (void) __rightButtonClicked { + reloading_ = true; + [self reloadURL]; +} + +- (void) _rightButtonClicked { +#if !AlwaysReload + if (function_ != nil) + [self callFunction:function_]; + else +#endif + [self __rightButtonClicked]; } - (id) _rightButtonTitle { - return button_ != nil ? button_ : @"Reload"; + return CYLocalize("RELOAD"); } - (id) rightButtonTitle { - return [self _loading] ? @"" : [self _rightButtonTitle]; + return [self isLoading] ? @"" : button_ != nil ? button_ : [self _rightButtonTitle]; } - (UINavigationButtonStyle) rightButtonStyle { @@ -981,11 +1463,11 @@ } - (NSString *) title { - return title_ == nil ? @"Loading" : title_; + return title_ == nil ? CYLocalize("LOADING") : title_; } - (NSString *) backButtonTitle { - return @"Browser"; + return CYLocalize("BROWSER"); } - (void) setPageActive:(BOOL)active { @@ -1002,4 +1484,8 @@ pushed_ = pushed; } ++ (float) defaultWidth { + return 980; +} + @end