1 #include <UICaboodle/BrowserView.h>
2 #include <UICaboodle/UCLocalize.h>
4 #import <QuartzCore/CALayer.h>
5 // XXX: fix the minimum requirement
6 extern NSString * const kCAFilterNearest;
8 #include <WebCore/WebCoreThread.h>
9 #include <WebKit/WebPreferences-WebPrivate.h>
11 #include "substrate.h"
17 static CFArrayRef (*$GSSystemCopyCapability)(CFStringRef);
18 static CFArrayRef (*$GSSystemGetCapability)(CFStringRef);
19 static Class $UIFormAssistant;
20 static Class $UIWebBrowserView;
22 @interface NSString (UIKit)
23 - (NSString *) stringByAddingPercentEscapes;
26 /* Indirect Delegate {{{ */
27 @interface IndirectDelegate : NSObject {
28 _transient volatile id delegate_;
31 - (void) setDelegate:(id)delegate;
32 - (id) initWithDelegate:(id)delegate;
35 @implementation IndirectDelegate
37 - (void) setDelegate:(id)delegate {
41 - (id) initWithDelegate:(id)delegate {
46 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
48 return [delegate_ webView:sender didClearWindowObject:window forFrame:frame];
51 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
53 return [delegate_ webView:sender didCommitLoadForFrame:frame];
56 - (void) webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
58 return [delegate_ webView:sender didFailLoadWithError:error forFrame:frame];
61 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
63 return [delegate_ webView:sender didFailProvisionalLoadWithError:error forFrame:frame];
66 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
68 return [delegate_ webView:sender didFinishLoadForFrame:frame];
71 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
73 return [delegate_ webView:sender didReceiveTitle:title forFrame:frame];
76 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
78 return [delegate_ webView:sender didStartProvisionalLoadForFrame:frame];
81 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
83 return [delegate_ webView:sender resource:identifier didReceiveAuthenticationChallenge:challenge fromDataSource:source];
86 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
88 return [delegate_ webView:sender resource:identifier willSendRequest:request redirectResponse:redirectResponse fromDataSource:source];
92 - (IMP) methodForSelector:(SEL)sel {
93 if (IMP method = [super methodForSelector:sel])
95 fprintf(stderr, "methodForSelector:[%s] == NULL\n", sel_getName(sel));
99 - (BOOL) respondsToSelector:(SEL)sel {
100 if ([super respondsToSelector:sel])
102 // XXX: WebThreadCreateNSInvocation returns nil
103 //fprintf(stderr, "[%s]R?%s\n", class_getName(self->isa), sel_getName(sel));
104 return delegate_ == nil ? NO : [delegate_ respondsToSelector:sel];
107 - (NSMethodSignature *) methodSignatureForSelector:(SEL)sel {
108 if (NSMethodSignature *method = [super methodSignatureForSelector:sel])
110 //fprintf(stderr, "[%s]S?%s\n", class_getName(self->isa), sel_getName(sel));
111 if (delegate_ != nil)
112 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
114 // XXX: I fucking hate Apple so very very bad
115 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
118 - (void) forwardInvocation:(NSInvocation *)inv {
119 SEL sel = [inv selector];
120 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
121 [inv invokeWithTarget:delegate_];
127 @interface WebView (UICaboodle)
128 - (void) setScriptDebugDelegate:(id)delegate;
129 - (void) _setFormDelegate:(id)delegate;
130 - (void) _setUIKitDelegate:(id)delegate;
131 - (void) setWebMailDelegate:(id)delegate;
132 - (void) _setLayoutInterval:(float)interval;
135 @implementation WebScriptObject (UICaboodle)
138 id length([self valueForKey:@"length"]);
139 if ([length respondsToSelector:@selector(intValue)])
140 return [length intValue];
145 - (id) objectAtIndex:(unsigned)index {
146 return [self webScriptValueAtIndex:index];
151 @interface BrowserView : UIView {
153 UIWebDocumentView *documentView;
155 @property (nonatomic, retain) UIWebDocumentView *documentView;
158 @implementation BrowserView
160 @synthesize documentView;
163 [documentView release];
167 - (void)layoutSubviews {
168 [super layoutSubviews];
169 if ([documentView respondsToSelector:@selector(setMinimumSize:)])
170 [documentView setMinimumSize:documentView.bounds.size];
175 #define ShowInternals 0
178 #define lprintf(args...) fprintf(stderr, args)
180 @implementation BrowserController
183 #include "UICaboodle/UCInternal.h"
186 + (void) _initialize {
187 //[WebView enableWebThread];
189 WebPreferences *preferences([WebPreferences standardPreferences]);
190 [preferences setCacheModel:WebCacheModelDocumentBrowser];
191 [preferences setOfflineWebApplicationCacheEnabled:YES];
193 [WebPreferences _setInitialDefaultTextEncodingToSystemEncoding];
195 $GSSystemCopyCapability = reinterpret_cast<CFArrayRef (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemCopyCapability"));
196 $GSSystemGetCapability = reinterpret_cast<CFArrayRef (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemGetCapability"));
197 $UIFormAssistant = objc_getClass("UIFormAssistant");
199 $UIWebBrowserView = objc_getClass("UIWebBrowserView");
200 if ($UIWebBrowserView == nil) {
202 $UIWebBrowserView = objc_getClass("UIWebDocumentView");
210 NSLog(@"[BrowserController dealloc]");
213 if (challenge_ != nil)
214 [challenge_ release];
218 WebView *webview = [document_ webView];
219 [webview setFrameLoadDelegate:nil];
220 [webview setResourceLoadDelegate:nil];
221 [webview setUIDelegate:nil];
222 [webview setScriptDebugDelegate:nil];
223 [webview setPolicyDelegate:nil];
225 /* XXX: these are set by UIWebDocumentView
226 [webview setDownloadDelegate:nil];
227 [webview _setFormDelegate:nil];
228 [webview _setUIKitDelegate:nil];
229 [webview setEditingDelegate:nil];*/
231 /* XXX: no one sets this, ever
232 [webview setWebMailDelegate:nil];*/
234 [document_ setDelegate:nil];
235 [document_ setGestureDelegate:nil];
237 if ([document_ respondsToSelector:@selector(setFormEditingDelegate:)])
238 [document_ setFormEditingDelegate:nil];
240 [document_ setInteractionDelegate:nil];
242 [indirect_ setDelegate:nil];
244 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
249 [document_ removeFromSuperview];
250 [Documents_ addObject:[document_ autorelease]];
259 [scroller_ setDelegate:nil];
265 if (function_ != nil)
275 [indicator_ release];
278 if (sensitive_ != nil)
279 [sensitive_ release];
282 if (reloaditem_ != nil)
283 [reloaditem_ release];
284 if (loadingitem_ != nil)
285 [loadingitem_ release];
290 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
291 [self loadRequest:[NSURLRequest
294 timeoutInterval:120.0
298 - (void) loadURL:(NSURL *)url {
299 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
302 - (void) loadRequest:(NSURLRequest *)request {
307 [document_ loadRequest:request];
315 if ([request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil)
316 [self loadRequest:request_];
318 UIAlertView *alert = [[[UIAlertView alloc]
319 initWithTitle:UCLocalize("RESUBMIT_FORM")
322 cancelButtonTitle:UCLocalize("CANCEL")
323 otherButtonTitles:UCLocalize("SUBMIT"), nil
325 [alert setContext:@"submit"];
330 - (WebView *) webView {
331 return [document_ webView];
334 - (UIWebDocumentView *) documentView {
338 /* XXX: WebThreadLock? */
339 - (void) _fixScroller:(CGRect)bounds {
342 if (!editing_ || $UIFormAssistant == nil)
345 UIFormAssistant *assistant([$UIFormAssistant sharedFormAssistant]);
346 CGRect peripheral([assistant peripheralFrame]);
348 NSLog(@"per:%f", peripheral.size.height);
350 extra = peripheral.size.height;
353 CGRect subrect([scroller_ frame]);
354 subrect.size.height -= extra;
356 if ([scroller_ respondsToSelector:@selector(setScrollerIndicatorSubrect:)])
357 [scroller_ setScrollerIndicatorSubrect:subrect];
359 [document_ setValue:[NSValue valueWithSize:NSMakeSize(subrect.size.width, subrect.size.height)] forGestureAttribute:UIGestureAttributeVisibleSize];
362 size.height += extra;
363 [scroller_ setContentSize:size];
365 if ([scroller_ respondsToSelector:@selector(releaseRubberBandIfNecessary)])
366 [scroller_ releaseRubberBandIfNecessary];
369 - (void) fixScroller {
370 CGRect bounds([document_ documentBounds]);
372 NSLog(@"_fs:(%f,%f+%f,%f)", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
374 [self _fixScroller:bounds];
377 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
380 NSLog(@"dsf:(%f,%f+%f,%f)", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
382 [self _fixScroller:frame];
385 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
386 [self view:sender didSetFrame:frame];
389 - (void) pushPage:(UCViewController *)page {
390 [page setDelegate:delegate_];
391 [[self navigationItem] setTitle:title_];
392 [[self navigationController] pushViewController:page animated:YES];
398 // WTR: [self autorelease];
400 [[self navigationController] pushViewController:self animated:YES];
403 - (void) swapPage:(UCViewController *)page {
404 [page setDelegate:delegate_];
405 if (pushed_) [[self navigationController] popViewControllerAnimated:NO];
407 [[self navigationController] pushViewController:page animated:NO];
410 - (BOOL) getSpecial:(NSURL *)url swap:(BOOL)swap {
412 NSLog(@"getSpecial:%@", url);
415 if (UCViewController *page = [delegate_ pageForURL:url hasTag:NULL]) {
417 [self swapPage:page];
419 [self pushPage:page];
426 - (void) formAssistant:(id)sender didBeginEditingFormNode:(id)node {
429 - (void) formAssistant:(id)sender didEndEditingFormNode:(id)node {
433 - (void) webViewShow:(WebView *)sender {
434 /* XXX: this is where I cry myself to sleep */
437 - (bool) _allowJavaScriptPanel {
441 - (bool) allowSensitiveRequests {
442 return [self _allowJavaScriptPanel];
445 - (void) _promptForSensitive:(NSMutableArray *)array {
446 NSString *name([array objectAtIndex:0]);
448 UIAlertView *alert = [[[UIAlertView alloc]
452 cancelButtonTitle:UCLocalize("NO")
453 otherButtonTitles:UCLocalize("YES"), nil
456 NSString *host(@"XXX");
458 [alert setContext:@"sensitive"];
459 [alert setMessage:[NSString stringWithFormat:@"The website at %@ is requesting your phone's %@. This is almost certainly for product licensing purposes. Will you allow this?", host, name]];
462 NSRunLoop *loop([NSRunLoop currentRunLoop]);
463 NSDate *future([NSDate distantFuture]);
465 while (sensitive_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
467 NSNumber *sensitive([sensitive_ autorelease]);
471 [array replaceObjectAtIndex:0 withObject:sensitive];
474 - (bool) promptForSensitive:(NSString *)name {
475 if (![self allowSensitiveRequests])
478 NSMutableArray *array([NSMutableArray arrayWithCapacity:1]);
479 [array addObject:name];
481 [self performSelectorOnMainThread:@selector(_promptForSensitive:) withObject:array waitUntilDone:YES];
482 return [[array lastObject] boolValue];
485 - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
486 if (![self _allowJavaScriptPanel])
490 UIAlertView *alert = [[[UIAlertView alloc]
494 cancelButtonTitle:UCLocalize("OK")
495 otherButtonTitles:nil
497 [alert setContext:@"alert"];
501 - (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
502 if (![self _allowJavaScriptPanel])
506 UIAlertView *alert = [[[UIAlertView alloc]
510 cancelButtonTitle:UCLocalize("CANCEL")
511 otherButtonTitles:UCLocalize("OK"), nil
514 [alert setContext:@"confirm"];
517 NSRunLoop *loop([NSRunLoop currentRunLoop]);
518 NSDate *future([NSDate distantFuture]);
520 while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
522 NSNumber *confirm([confirm_ autorelease]);
526 return [confirm boolValue];
529 - (void) setAutoPopup:(BOOL)popup {
533 - (void) setSpecial:(id)function {
535 [special_ autorelease];
536 special_ = function == nil ? nil : [function retain];
539 - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
541 [button_ autorelease];
542 button_ = button == nil ? nil : [[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:button]]] retain];
545 [style_ autorelease];
546 style_ = style == nil ? nil : [style retain];
548 if (function_ != nil)
549 [function_ autorelease];
550 function_ = function == nil ? nil : [function retain];
552 [self applyRightButton];
555 - (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
557 [button_ autorelease];
558 button_ = button == nil ? nil : [button retain];
561 [style_ autorelease];
562 style_ = style == nil ? nil : [style retain];
564 if (function_ != nil)
565 [function_ autorelease];
566 function_ = function == nil ? nil : [function retain];
568 [self applyRightButton];
571 - (void) setFinishHook:(id)function {
573 [finish_ autorelease];
574 finish_ = function == nil ? nil : [function retain];
577 - (void) setPopupHook:(id)function {
579 [closer_ autorelease];
580 closer_ = function == nil ? nil : [function retain];
583 - (void) _openMailToURL:(NSURL *)url {
584 [UIApp openURL:url];// asPanel:YES];
587 - (void) webView:(WebView *)sender willBeginEditingFormElement:(id)element {
591 - (void) webView:(WebView *)sender didBeginEditingFormElement:(id)element {
595 - (void) webViewDidEndEditingFormElements:(WebView *)sender {
600 - (void) webViewClose:(WebView *)sender {
605 [[self navigationController] dismissModalViewControllerAnimated:YES];
608 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
611 - (void) webView:(WebView *)sender unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame {
612 NSLog(@"err:%@", error);
615 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
617 NSLog(@"nwa:%@", name);
620 if (NSURL *url = [request URL]) {
621 if (name == nil) unknown: {
622 if (![self getSpecial:url swap:NO]) {
623 NSString *scheme([[url scheme] lowercaseString]);
624 if ([scheme isEqualToString:@"mailto"])
625 [self _openMailToURL:url];
628 } else if ([name isEqualToString:@"_open"])
629 [delegate_ openURL:url];
630 else if ([name isEqualToString:@"_popup"]) {
631 NSString *scheme([[url scheme] lowercaseString]);
632 if ([scheme isEqualToString:@"mailto"])
633 [self _openMailToURL:url];
635 UCNavigationController *navigation([[[UCNavigationController alloc] init] autorelease]);
636 [navigation setHook:indirect_];
638 UCViewController *page([delegate_ pageForURL:url hasTag:NULL]);
640 /* XXX: call createWebViewWithRequest instead? */
642 BrowserController *browser([[[class_ alloc] init] autorelease]);
643 [browser loadURL:url];
647 [navigation setDelegate:delegate_];
648 [page setDelegate:delegate_];
650 [navigation setViewControllers:[NSArray arrayWithObject:page]];
651 UIBarButtonItem *closeItem = [[UIBarButtonItem alloc]
652 initWithTitle:UCLocalize("CLOSE")
653 style:UIBarButtonItemStylePlain
655 action:@selector(close)
657 [[page navigationItem] setLeftBarButtonItem:closeItem];
660 [[self navigationController] presentModalViewController:navigation animated:YES];
669 - (void) webView:(WebView *)sender decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
670 if ([WebView canShowMIMEType:type])
673 // XXX: handle more mime types!
676 WebView *webview([document_ webView]);
677 if (frame == [webview mainFrame])
678 [UIApp openURL:[request URL]];
682 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
683 if (request == nil) ignore: {
688 NSURL *url([request URL]);
689 NSString *host([url host]);
691 if (url == nil) use: {
692 if (!error_ && [frame parentFrame] == nil) {
694 [request_ autorelease];
695 request_ = [request retain];
697 NSLog(@"dpn:%@", request_);
703 WebView *webview([document_ webView]);
704 if (frame == [webview mainFrame])
709 else NSLog(@"nav:%@:%@", url, [action description]);
712 const NSArray *capability;
714 if ($GSSystemCopyCapability != NULL) {
715 capability = reinterpret_cast<const NSArray *>((*$GSSystemCopyCapability)(kGSDisplayIdentifiersCapability));
716 capability = [capability autorelease];
717 } else if ($GSSystemGetCapability != NULL) {
718 capability = reinterpret_cast<const NSArray *>((*$GSSystemGetCapability)(kGSDisplayIdentifiersCapability));
724 if (capability != nil && (
725 [url isGoogleMapsURL] && [capability containsObject:@"com.apple.Maps"] && (open = [url mapsURL]) != nil||
726 [host hasSuffix:@"youtube.com"] && [capability containsObject:@"com.apple.youtube"] && (open = [url youTubeURL]) != nil ||
727 [url respondsToSelector:@selector(phobosURL)] && (open = [url phobosURL]) != nil
735 int store(_not(int));
736 if (NSURL *itms = [url itmsURL:&store]) {
738 NSLog(@"itms#%@#%u#%@", url, store, itms);
741 if (capability != nil && (
742 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
743 store == 2 && [capability containsObject:@"com.apple.AppStore"]
750 NSString *scheme([[url scheme] lowercaseString]);
752 if ([scheme isEqualToString:@"tel"]) {
757 if ([scheme isEqualToString:@"mailto"]) {
758 [self _openMailToURL:url];
762 if ([self getSpecial:url swap:YES])
764 else if ([WebView _canHandleRequest:request])
766 else if ([url isSpringboardHandledURL])
772 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
773 //lprintf("Status:%s\n", [text UTF8String]);
776 - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
777 NSString *context([alert context]);
779 if ([context isEqualToString:@"alert"]) {
781 [alert dismissWithClickedButtonIndex:-1 animated:YES];
782 } else if ([context isEqualToString:@"confirm"]) {
785 confirm_ = [NSNumber numberWithBool:YES];
789 confirm_ = [NSNumber numberWithBool:NO];
793 [alert dismissWithClickedButtonIndex:-1 animated:YES];
794 } else if ([context isEqualToString:@"sensitive"]) {
797 sensitive_ = [NSNumber numberWithBool:YES];
801 sensitive_ = [NSNumber numberWithBool:NO];
805 [alert dismissWithClickedButtonIndex:-1 animated:YES];
806 } else if ([context isEqualToString:@"challenge"]) {
807 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
811 NSString *username([[alert textFieldAtIndex:0] text]);
812 NSString *password([[alert textFieldAtIndex:1] text]);
814 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
816 [sender useCredential:credential forAuthenticationChallenge:challenge_];
820 [sender cancelAuthenticationChallenge:challenge_];
826 [challenge_ release];
829 [alert dismissWithClickedButtonIndex:-1 animated:YES];
830 } else if ([context isEqualToString:@"submit"]) {
836 if (request_ != nil) {
838 [document_ loadRequest:request_];
846 [alert dismissWithClickedButtonIndex:-1 animated:YES];
850 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
851 challenge_ = [challenge retain];
853 NSURLProtectionSpace *space([challenge protectionSpace]);
854 NSString *realm([space realm]);
858 UIAlertView *alert = [[[UIAlertView alloc]
862 cancelButtonTitle:UCLocalize("CANCEL")
863 otherButtonTitles:UCLocalize("LOGIN"), nil
866 [alert setContext:@"challenge"];
867 [alert setNumberOfRows:1];
869 [alert addTextFieldWithValue:@"" label:UCLocalize("USERNAME")];
870 [alert addTextFieldWithValue:@"" label:UCLocalize("PASSWORD")];
872 UITextField *username([alert textFieldAtIndex:0]); {
873 UITextInputTraits *traits([username textInputTraits]);
874 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
875 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
876 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
877 [traits setReturnKeyType:UIReturnKeyNext];
880 UITextField *password([alert textFieldAtIndex:1]); {
881 UITextInputTraits *traits([password textInputTraits]);
882 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
883 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
884 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
885 // XXX: UIReturnKeyDone
886 [traits setReturnKeyType:UIReturnKeyNext];
887 [traits setSecureTextEntry:YES];
893 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
897 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
898 //- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request userGesture:(BOOL)gesture {
900 NSLog(@"cwv:%@ (%@): %@", request, title_, features == nil ? @"{}" : [features description]);
901 //NSLog(@"cwv:%@ (%@): %@", request, title_, gesture ? @"Yes" : @"No");
904 NSNumber *value([features objectForKey:@"width"]);
905 float width(value == nil ? 0 : [value floatValue]);
907 UCNavigationController *navigation(!popup_ ? [self navigationController] : [[[UCNavigationController alloc] init] autorelease]);
909 /* XXX: deal with cydia:// pages */
910 BrowserController *browser([[[class_ alloc] initWithWidth:width] autorelease]);
912 if (features != nil && popup_) {
913 [navigation setDelegate:delegate_];
914 [navigation setHook:indirect_];
915 [browser setDelegate:delegate_];
917 [browser loadRequest:request];
919 [navigation setViewControllers:[NSArray arrayWithObject:browser]];
920 UIBarButtonItem *closeItem = [[UIBarButtonItem alloc]
921 initWithTitle:UCLocalize("CLOSE")
922 style:UIBarButtonItemStylePlain
924 action:@selector(close)
926 [[browser navigationItem] setLeftBarButtonItem:closeItem];
929 [[self navigationController] presentModalViewController:navigation animated:YES];
930 } /*else if (request == nil) {
931 [[self navigationItem] setTitle:title_];
932 [browser setDelegate:delegate_];
935 [self pushPage:browser];
936 [browser loadRequest:request];
939 return [browser webView];
942 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
943 return [self webView:sender createWebViewWithRequest:request windowFeatures:nil];
944 //return [self webView:sender createWebViewWithRequest:request userGesture:YES];
947 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
948 if ([frame parentFrame] != nil)
951 title_ = [title retain];
952 [[self navigationItem] setTitle:title_];
955 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
956 /*if ([loading_ count] == 0)
958 [loading_ addObject:[NSValue valueWithNonretainedObject:frame]];
960 if ([frame parentFrame] == nil) {
961 [document_ resignFirstResponder];
970 if (button_ != nil) {
980 if (function_ != nil) {
985 if (finish_ != nil) {
990 if (closer_ != nil) {
995 if (special_ != nil) {
1000 [[self navigationItem] setTitle:title_];
1003 CGRect webrect = [scroller_ bounds];
1004 webrect.size.height = 1;
1005 [document_ setFrame:webrect];
1008 if ([scroller_ respondsToSelector:@selector(scrollPointVisibleAtTopLeft:)])
1009 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
1011 [scroller_ scrollRectToVisible:CGRectZero animated:NO];
1013 if ([scroller_ respondsToSelector:@selector(setZoomScale:duration:)])
1014 [scroller_ setZoomScale:1 duration:0];
1015 else if ([scroller_ respondsToSelector:@selector(_setZoomScale:duration:)])
1016 [scroller_ _setZoomScale:1 duration:0];
1017 /*else if ([scroller_ respondsToSelector:@selector(setZoomScale:animated:)])
1018 [scroller_ setZoomScale:1 animated:NO];*/
1021 CGRect webrect = [scroller_ bounds];
1022 webrect.size.height = 0;
1023 [document_ setFrame:webrect];
1027 [self _startLoading];
1030 - (UINavigationButtonStyle) rightButtonStyle {
1031 if (style_ == nil) normal:
1032 return UINavigationButtonStyleNormal;
1033 else if ([style_ isEqualToString:@"Normal"])
1034 return UINavigationButtonStyleNormal;
1035 else if ([style_ isEqualToString:@"Back"])
1036 return UINavigationButtonStyleBack;
1037 else if ([style_ isEqualToString:@"Highlighted"])
1038 return UINavigationButtonStyleHighlighted;
1039 else if ([style_ isEqualToString:@"Destructive"])
1040 return UINavigationButtonStyleDestructive;
1044 - (UIBarButtonItem *) customButton {
1045 UIBarButtonItem *customItem = [[UIBarButtonItem alloc]
1046 initWithTitle:button_
1047 style:[self rightButtonStyle]
1049 action:@selector(customButtonClicked)
1052 return [customItem autorelease];
1055 - (void) applyLoadingTitle {
1056 [[self navigationItem] setTitle:UCLocalize("LOADING")];
1059 - (void) applyRightButton {
1060 if ([self isLoading]) {
1061 [[self navigationItem] setRightBarButtonItem:loadingitem_ animated:YES];
1062 [[loadingitem_ view] addSubview:indicator_];
1063 [self applyLoadingTitle];
1064 } else if (button_) {
1065 [[self navigationItem] setRightBarButtonItem:[self customButton] animated:YES];
1067 [[self navigationItem] setRightBarButtonItem:reloaditem_ animated:YES];
1071 - (void) _startLoading {
1072 [self applyRightButton];
1075 - (void) _finishLoading {
1076 size_t count([loading_ count]);
1078 [self autorelease];*/
1079 if (reloading_ || count != 0)
1082 [self callFunction:finish_];
1084 [self applyRightButton];
1085 if (![self isLoading]) [[self navigationItem] setTitle:title_];
1088 - (bool) isLoading {
1089 return [loading_ count] != 0;
1092 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
1093 return [document_ webView:sender shouldScrollToPoint:point forFrame:frame];
1096 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
1097 return [document_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
1100 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
1101 return [document_ webView:sender needsScrollNotifications:notifications forFrame:frame];
1104 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
1106 return [document_ webView:sender didCommitLoadForFrame:frame];
1109 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
1110 return [document_ webView:sender didReceiveDocTypeForFrame:frame];
1113 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
1114 [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]];
1115 [self _finishLoading];
1117 if ([frame parentFrame] == nil) {
1118 if (DOMDocument *document = [frame DOMDocument])
1119 if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
1120 for (DOMHTMLBodyElement *body in bodies) {
1121 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
1123 bool colored(false);
1125 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
1126 if ([color primitiveType] == DOM_CSS_RGBCOLOR) {
1127 DOMRGBColor *rgb([color getRGBColorValue]);
1129 float red([[rgb red] getFloatValue:DOM_CSS_NUMBER]);
1130 float green([[rgb green] getFloatValue:DOM_CSS_NUMBER]);
1131 float blue([[rgb blue] getFloatValue:DOM_CSS_NUMBER]);
1132 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
1136 if (red == 0xc7 && green == 0xce && blue == 0xd5)
1137 uic = [UIColor pinStripeColor];
1138 else if (alpha != 0)
1140 colorWithRed:(red / 255)
1148 [scroller_ setBackgroundColor:uic];
1154 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
1159 return [document_ webView:sender didFinishLoadForFrame:frame];
1162 - (void) _didFailWithError:(NSError *)error forFrame:(WebFrame *)frame {
1164 /*if ([frame parentFrame] == nil)
1165 [self autorelease];*/
1167 [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]];
1168 [self _finishLoading];
1173 if ([frame parentFrame] == nil) {
1174 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
1175 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
1176 [[error localizedDescription] stringByAddingPercentEscapes]
1183 - (void) webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
1184 [self _didFailWithError:error forFrame:frame];
1185 if ([document_ respondsToSelector:@selector(webView:didFailLoadWithError:forFrame:)])
1186 [document_ webView:sender didFailLoadWithError:error forFrame:frame];
1189 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
1190 [self _didFailWithError:error forFrame:frame];
1193 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
1194 #if LogBrowser || ForSaurik
1195 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
1199 - (void) webView:(WebView *)sender didReceiveMessage:(NSDictionary *)dictionary {
1200 #if LogBrowser || ForSaurik
1201 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
1203 if ([document_ respondsToSelector:@selector(webView:didReceiveMessage:)])
1204 [document_ webView:sender didReceiveMessage:dictionary];
1207 - (void) webView:(id)sender willCloseFrame:(id)frame {
1208 if ([document_ respondsToSelector:@selector(webView:willCloseFrame:)])
1209 [document_ webView:sender willCloseFrame:frame];
1212 - (void) webView:(id)sender didFinishDocumentLoadForFrame:(id)frame {
1213 if ([document_ respondsToSelector:@selector(webView:didFinishDocumentLoadForFrame:)])
1214 [document_ webView:sender didFinishDocumentLoadForFrame:frame];
1217 - (void) webView:(id)sender didFirstLayoutInFrame:(id)frame {
1218 if ([document_ respondsToSelector:@selector(webView:didFirstLayoutInFrame:)])
1219 [document_ webView:sender didFirstLayoutInFrame:frame];
1222 - (void) webViewFormEditedStatusHasChanged:(id)changed {
1223 if ([document_ respondsToSelector:@selector(webViewFormEditedStatusHasChanged:)])
1224 [document_ webViewFormEditedStatusHasChanged:changed];
1227 - (void) webView:(id)sender formStateDidFocusNode:(id)formState {
1228 if ([document_ respondsToSelector:@selector(webView:formStateDidFocusNode:)])
1229 [document_ webView:sender formStateDidFocusNode:formState];
1232 - (void) webView:(id)sender formStateDidBlurNode:(id)formState {
1233 if ([document_ respondsToSelector:@selector(webView:formStateDidBlurNode:)])
1234 [document_ webView:sender formStateDidBlurNode:formState];
1237 /* XXX: fix this stupid include file
1238 - (void) webView:(WebView *)sender frame:(WebFrame *)frame exceededDatabaseQuotaForSecurityOrigin:(WebSecurityOrigin *)origin database:(NSString *)database {
1239 [origin setQuota:0x500000];
1242 - (void) webViewDidLayout:(id)sender {
1243 [document_ webViewDidLayout:sender];
1246 - (void) webView:(id)sender didFirstVisuallyNonEmptyLayoutInFrame:(id)frame {
1247 [document_ webView:sender didFirstVisuallyNonEmptyLayoutInFrame:frame];
1250 - (void) webView:(id)sender saveStateToHistoryItem:(id)item forFrame:(id)frame {
1251 [document_ webView:sender saveStateToHistoryItem:item forFrame:frame];
1254 - (void) webView:(id)sender restoreStateFromHistoryItem:(id)item forFrame:(id)frame force:(BOOL)force {
1255 [document_ webView:sender restoreStateFromHistoryItem:item forFrame:frame force:force];
1258 - (void) webView:(id)sender attachRootLayer:(id)layer {
1259 [document_ webView:sender attachRootLayer:layer];
1262 - (id) webView:(id)sender plugInViewWithArguments:(id)arguments fromPlugInPackage:(id)package {
1263 return [document_ webView:sender plugInViewWithArguments:arguments fromPlugInPackage:package];
1266 - (void) webView:(id)sender willShowFullScreenForPlugInView:(id)view {
1267 [document_ webView:sender willShowFullScreenForPlugInView:view];
1270 - (void) webView:(id)sender didHideFullScreenForPlugInView:(id)view {
1271 [document_ webView:sender didHideFullScreenForPlugInView:view];
1274 - (void) webView:(id)sender willAddPlugInView:(id)view {
1275 [document_ webView:sender willAddPlugInView:view];
1278 - (void) webView:(id)sender didObserveDeferredContentChange:(int)change forFrame:(id)frame {
1279 [document_ webView:sender didObserveDeferredContentChange:change forFrame:frame];
1282 - (void) webViewDidPreventDefaultForEvent:(id)sender {
1283 [document_ webViewDidPreventDefaultForEvent:sender];
1286 - (void) _setTileDrawingEnabled:(BOOL)enabled {
1287 //[document_ setTileDrawingEnabled:enabled];
1290 - (void) setViewportWidth:(float)width {
1291 width_ = width != 0 ? width : [[self class] defaultWidth];
1292 [document_ setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
1295 - (void) willStartGesturesInView:(UIView *)view forEvent:(GSEventRef)event {
1296 [self _setTileDrawingEnabled:NO];
1299 - (void) didFinishGesturesInView:(UIView *)view forEvent:(GSEventRef)event {
1300 [self _setTileDrawingEnabled:YES];
1301 [document_ redrawScaledDocument];
1304 - (void) scrollerWillStartDragging:(UIScroller *)scroller {
1305 [self _setTileDrawingEnabled:NO];
1308 - (void) scrollerDidEndDragging:(UIScroller *)scroller willSmoothScroll:(BOOL)smooth {
1309 [self _setTileDrawingEnabled:YES];
1312 - (void) scrollerDidEndDragging:(UIScroller *)scroller {
1313 [self _setTileDrawingEnabled:YES];
1316 - (id) initWithWidth:(float)width ofClass:(Class)_class {
1317 if ((self = [super init]) != nil) {
1319 loading_ = [[NSMutableSet alloc] initWithCapacity:3];
1322 BrowserView *actualView = [[BrowserView alloc] initWithFrame:CGRectZero];
1323 [self setView:actualView];
1325 struct CGRect bounds = [[self view] bounds];
1327 scroller_ = [[objc_getClass(Wildcat_ ? "UIScrollView" : "UIScroller") alloc] initWithFrame:bounds];
1328 [[self view] addSubview:scroller_];
1330 [scroller_ setFixedBackgroundPattern:YES];
1331 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
1333 [scroller_ setScrollingEnabled:YES];
1334 [scroller_ setClipsSubviews:YES];
1337 [scroller_ setAllowsRubberBanding:YES];
1339 [scroller_ setDelegate:self];
1340 [scroller_ setBounces:YES];
1343 [scroller_ setScrollHysteresis:8];
1344 [scroller_ setThumbDetectionEnabled:NO];
1345 [scroller_ setDirectionalScrolling:YES];
1346 //[scroller_ setScrollDecelerationFactor:0.99]; /* 0.989324 */
1347 [scroller_ setEventMode:YES];
1351 UIScrollView *scroller((UIScrollView *)scroller_);
1352 //[scroller setDirectionalLockEnabled:NO];
1353 [scroller setDelaysContentTouches:NO];
1354 //[scroller setScrollsToTop:NO];
1355 //[scroller setCanCancelContentTouches:NO];
1358 [scroller_ setShowBackgroundShadow:NO]; /* YES */
1359 //[scroller_ setAllowsRubberBanding:YES]; /* Vertical */
1362 [scroller_ setAdjustForContentSizeChange:YES]; /* NO */
1364 CGRect webrect = [scroller_ bounds];
1365 webrect.size.height = 0;
1372 document_ = [Documents_ lastObject];
1373 if (document_ != nil) {
1374 document_ = [document_ retain];
1375 webview = [document_ webView];
1376 [Documents_ removeLastObject];
1377 [document_ setFrame:webrect];
1382 document_ = [[$UIWebBrowserView alloc] initWithFrame:webrect];
1383 webview = [document_ webView];
1385 // XXX: this is terribly (too?) expensive
1386 //[document_ setDrawsBackground:NO];
1387 [webview setPreferencesIdentifier:@"Cydia"];
1389 [document_ setTileSize:CGSizeMake(webrect.size.width, 500)];
1391 if ([document_ respondsToSelector:@selector(enableReachability)])
1392 [document_ enableReachability];
1393 if ([document_ respondsToSelector:@selector(setAllowsMessaging:)])
1394 [document_ setAllowsMessaging:YES];
1395 if ([document_ respondsToSelector:@selector(useSelectionAssistantWithMode:)])
1396 [document_ useSelectionAssistantWithMode:0];
1398 [document_ setTilingEnabled:YES];
1399 [document_ setDrawsGrid:NO];
1400 [document_ setLogsTilingChanges:NO];
1401 [document_ setTileMinificationFilter:kCAFilterNearest];
1403 if ([document_ respondsToSelector:@selector(setDataDetectorTypes:)])
1404 /* XXX: abstractify */
1405 [document_ setDataDetectorTypes:0x80000000];
1407 [document_ setDetectsPhoneNumbers:NO];
1409 [document_ setAutoresizes:YES];
1411 [document_ setMinimumScale:0.25f forDocumentTypes:0x10];
1412 [document_ setMaximumScale:5.00f forDocumentTypes:0x10];
1413 [document_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10];
1414 //[document_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
1416 [document_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2];
1418 [document_ setMinimumScale:1.00f forDocumentTypes:0x8];
1419 [document_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8];
1420 [document_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8];
1422 [document_ _setDocumentType:0x4];
1424 if ([document_ respondsToSelector:@selector(setZoomsFocusedFormControl:)])
1425 [document_ setZoomsFocusedFormControl:YES];
1426 [document_ setContentsPosition:7];
1427 [document_ setEnabledGestures:0xa];
1428 [document_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
1429 [document_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
1431 [document_ setSmoothsFonts:YES];
1432 [document_ setAllowsImageSheet:YES];
1433 [webview _setUsesLoaderCache:YES];
1435 [webview setGroupName:@"CydiaGroup"];
1437 WebPreferences *preferences([webview preferences]);
1439 if ([webview respondsToSelector:@selector(_setLayoutInterval:)])
1440 [webview _setLayoutInterval:0];
1442 [preferences _setLayoutInterval:0];
1445 actualView.documentView = document_;
1446 [actualView release];
1448 [self setViewportWidth:width];
1450 [document_ setDelegate:self];
1451 [document_ setGestureDelegate:self];
1453 if ([document_ respondsToSelector:@selector(setFormEditingDelegate:)])
1454 [document_ setFormEditingDelegate:self];
1456 [document_ setInteractionDelegate:self];
1458 [scroller_ addSubview:document_];
1460 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
1462 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
1464 [webview setFrameLoadDelegate:indirect_];
1465 [webview setPolicyDelegate:indirect_];
1466 [webview setResourceLoadDelegate:indirect_];
1467 [webview setUIDelegate:indirect_];
1469 /* XXX: do not turn this on under penalty of extreme pain */
1470 [webview setScriptDebugDelegate:nil];
1474 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
1475 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(15, 5, indsize.width, indsize.height)];
1476 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
1477 [indicator_ startAnimation];
1479 reloaditem_ = [[UIBarButtonItem alloc]
1480 initWithTitle:UCLocalize("RELOAD")
1481 style:[self rightButtonStyle]
1483 action:@selector(reloadButtonClicked)
1486 loadingitem_ = [[UIBarButtonItem alloc]
1488 style:UIBarButtonItemStylePlain
1490 action:@selector(reloadButtonClicked)
1492 [[loadingitem_ view] addSubview:indicator_];
1494 [scroller_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
1495 [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
1496 [document_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
1498 /*UIWebView *test([[[UIWebView alloc] initWithFrame:[[self view] bounds]] autorelease]);
1499 [test loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.saurik.com/"]]];
1500 [[self view] addSubview:test];*/
1504 - (id) initWithWidth:(float)width {
1505 return [self initWithWidth:width ofClass:[self class]];
1509 return [self initWithWidth:0];
1512 - (NSString *) stringByEvaluatingJavaScriptFromString:(NSString *)script {
1514 WebView *webview([document_ webView]);
1515 NSString *string([webview stringByEvaluatingJavaScriptFromString:script]);
1520 - (void) callFunction:(WebScriptObject *)function {
1523 WebView *webview([document_ webView]);
1524 WebFrame *frame([webview mainFrame]);
1526 id _private(MSHookIvar<id>(webview, "_private"));
1527 WebCore::Page *page(_private == nil ? NULL : MSHookIvar<WebCore::Page *>(_private, "page"));
1528 WebCore::Settings *settings(page == NULL ? NULL : page->settings());
1531 if (settings == NULL)
1534 no = settings->JavaScriptCanOpenWindowsAutomatically();
1535 settings->setJavaScriptCanOpenWindowsAutomatically(true);
1538 if (UIWindow *window = [[self view] window])
1539 if (UIResponder *responder = [window firstResponder])
1540 [responder resignFirstResponder];
1542 JSObjectRef object([function JSObject]);
1543 JSGlobalContextRef context([frame globalContext]);
1544 JSObjectCallAsFunction(context, object, NULL, 0, NULL, NULL);
1546 if (settings != NULL)
1547 settings->setJavaScriptCanOpenWindowsAutomatically(no);
1552 - (void) didDismissModalViewController {
1554 [self callFunction:closer_];
1557 - (void) reloadButtonClicked {
1562 - (void) customButtonClicked {
1564 if (function_ != nil)
1565 [self callFunction:function_];
1568 [self reloadButtonClicked];
1571 - (void) setPageActive:(BOOL)active {
1573 [indicator_ removeFromSuperview];
1575 [[[[self navigationItem] rightBarButtonItem] view] addSubview:indicator_];
1578 - (void) resetViewAnimated:(BOOL)animated {
1581 - (void) setPushed:(bool)pushed {
1585 + (float) defaultWidth {