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 #define ShowInternals 0
154 #define lprintf(args...) fprintf(stderr, args)
156 @implementation BrowserView
159 #include "UICaboodle/UCInternal.h"
162 + (void) _initialize {
163 //[WebView enableWebThread];
165 WebPreferences *preferences([WebPreferences standardPreferences]);
166 [preferences setCacheModel:WebCacheModelDocumentBrowser];
167 [preferences setOfflineWebApplicationCacheEnabled:YES];
169 [WebPreferences _setInitialDefaultTextEncodingToSystemEncoding];
171 $GSSystemCopyCapability = reinterpret_cast<CFArrayRef (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemCopyCapability"));
172 $GSSystemGetCapability = reinterpret_cast<CFArrayRef (*)(CFStringRef)>(dlsym(RTLD_DEFAULT, "GSSystemGetCapability"));
173 $UIFormAssistant = objc_getClass("UIFormAssistant");
175 $UIWebBrowserView = objc_getClass("UIWebBrowserView");
176 if ($UIWebBrowserView == nil) {
178 $UIWebBrowserView = objc_getClass("UIWebDocumentView");
186 NSLog(@"[BrowserView dealloc]");
189 if (challenge_ != nil)
190 [challenge_ release];
194 WebView *webview = [document_ webView];
195 [webview setFrameLoadDelegate:nil];
196 [webview setResourceLoadDelegate:nil];
197 [webview setUIDelegate:nil];
198 [webview setScriptDebugDelegate:nil];
199 [webview setPolicyDelegate:nil];
201 /* XXX: these are set by UIWebDocumentView
202 [webview setDownloadDelegate:nil];
203 [webview _setFormDelegate:nil];
204 [webview _setUIKitDelegate:nil];
205 [webview setEditingDelegate:nil];*/
207 /* XXX: no one sets this, ever
208 [webview setWebMailDelegate:nil];*/
210 [document_ setDelegate:nil];
211 [document_ setGestureDelegate:nil];
213 if ([document_ respondsToSelector:@selector(setFormEditingDelegate:)])
214 [document_ setFormEditingDelegate:nil];
216 [document_ setInteractionDelegate:nil];
218 [indirect_ setDelegate:nil];
220 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
225 [document_ removeFromSuperview];
226 [Documents_ addObject:[document_ autorelease]];
235 [scroller_ setDelegate:nil];
241 if (function_ != nil)
251 [indicator_ release];
254 if (sensitive_ != nil)
255 [sensitive_ release];
261 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
262 [self loadRequest:[NSURLRequest
265 timeoutInterval:120.0
269 - (void) loadURL:(NSURL *)url {
270 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
273 - (void) loadRequest:(NSURLRequest *)request {
278 [document_ loadRequest:request];
286 if ([request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil)
287 [self loadRequest:request_];
289 UIActionSheet *sheet = [[[UIActionSheet alloc]
290 initWithTitle:UCLocalize("RESUBMIT_FORM")
291 buttons:[NSArray arrayWithObjects:UCLocalize("CANCEL"), UCLocalize("SUBMIT"), nil]
297 [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
299 [sheet setNumberOfRows:1];
300 [sheet popupAlertAnimated:YES];
304 - (WebView *) webView {
305 return [document_ webView];
308 - (UIWebDocumentView *) documentView {
312 /* XXX: WebThreadLock? */
313 - (void) _fixScroller:(CGRect)bounds {
316 if (!editing_ || $UIFormAssistant == nil)
319 UIFormAssistant *assistant([$UIFormAssistant sharedFormAssistant]);
320 CGRect peripheral([assistant peripheralFrame]);
322 NSLog(@"per:%f", peripheral.size.height);
324 extra = peripheral.size.height;
327 CGRect subrect([scroller_ frame]);
328 subrect.size.height -= extra;
330 if ([scroller_ respondsToSelector:@selector(setScrollerIndicatorSubrect:)])
331 [scroller_ setScrollerIndicatorSubrect:subrect];
333 [document_ setValue:[NSValue valueWithSize:NSMakeSize(subrect.size.width, subrect.size.height)] forGestureAttribute:UIGestureAttributeVisibleSize];
336 size.height += extra;
337 [scroller_ setContentSize:size];
339 if ([scroller_ respondsToSelector:@selector(releaseRubberBandIfNecessary)])
340 [scroller_ releaseRubberBandIfNecessary];
343 - (void) fixScroller {
344 CGRect bounds([document_ documentBounds]);
346 NSLog(@"_fs:(%f,%f+%f,%f)", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
348 [self _fixScroller:bounds];
351 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
354 NSLog(@"dsf:(%f,%f+%f,%f)", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
356 [self _fixScroller:frame];
359 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
360 [self view:sender didSetFrame:frame];
363 - (void) pushPage:(UCViewController *)page {
364 [page setDelegate:delegate_];
365 [[self navigationItem] setTitle:title_];
366 [[self navigationController] pushViewController:page animated:YES];
372 // WTR: [self autorelease];
374 [[self navigationController] pushViewController:self animated:YES];
377 - (void) swapPage:(UCViewController *)page {
378 [page setDelegate:delegate_];
379 if (pushed_) [[self navigationController] popViewControllerAnimated:NO];
381 [[self navigationController] pushViewController:page animated:NO];
384 - (BOOL) getSpecial:(NSURL *)url swap:(BOOL)swap {
386 NSLog(@"getSpecial:%@", url);
389 if (UCViewController *page = [delegate_ pageForURL:url hasTag:NULL]) {
391 [self swapPage:page];
393 [self pushPage:page];
400 - (void) formAssistant:(id)sender didBeginEditingFormNode:(id)node {
403 - (void) formAssistant:(id)sender didEndEditingFormNode:(id)node {
407 - (void) webViewShow:(WebView *)sender {
408 /* XXX: this is where I cry myself to sleep */
411 - (bool) _allowJavaScriptPanel {
415 - (bool) allowSensitiveRequests {
416 return [self _allowJavaScriptPanel];
419 - (void) _promptForSensitive:(NSMutableArray *)array {
420 NSString *name([array objectAtIndex:0]);
422 UIActionSheet *sheet = [[[UIActionSheet alloc]
424 buttons:[NSArray arrayWithObjects:UCLocalize("YES"), UCLocalize("NO"), nil]
430 [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
432 NSString *host(@"XXX");
434 [sheet setNumberOfRows:1];
435 [sheet setBodyText:[NSString stringWithFormat:@"The website at %@ is requesting your phone's %@. This is almost certainly for product licensing purposes. Will you allow this?", host, name]];
436 [sheet popupAlertAnimated:YES];
438 NSRunLoop *loop([NSRunLoop currentRunLoop]);
439 NSDate *future([NSDate distantFuture]);
441 while (sensitive_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
443 NSNumber *sensitive([sensitive_ autorelease]);
447 [array replaceObjectAtIndex:0 withObject:sensitive];
450 - (bool) promptForSensitive:(NSString *)name {
451 if (![self allowSensitiveRequests])
454 NSMutableArray *array([NSMutableArray arrayWithCapacity:1]);
455 [array addObject:name];
457 [self performSelectorOnMainThread:@selector(_promptForSensitive:) withObject:array waitUntilDone:YES];
458 return [[array lastObject] boolValue];
461 - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
462 if (![self _allowJavaScriptPanel])
466 UIActionSheet *sheet = [[[UIActionSheet alloc]
468 buttons:[NSArray arrayWithObjects:UCLocalize("OK"), nil]
474 [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
476 [sheet setBodyText:message];
477 [sheet popupAlertAnimated:YES];
480 - (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
481 if (![self _allowJavaScriptPanel])
485 UIActionSheet *sheet = [[[UIActionSheet alloc]
487 buttons:[NSArray arrayWithObjects:UCLocalize("OK"), UCLocalize("CANCEL"), nil]
493 [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
495 [sheet setNumberOfRows:1];
496 [sheet setBodyText:message];
497 [sheet popupAlertAnimated:YES];
499 NSRunLoop *loop([NSRunLoop currentRunLoop]);
500 NSDate *future([NSDate distantFuture]);
502 while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
504 NSNumber *confirm([confirm_ autorelease]);
508 return [confirm boolValue];
511 - (void) setAutoPopup:(BOOL)popup {
515 - (void) setSpecial:(id)function {
517 [special_ autorelease];
518 special_ = function == nil ? nil : [function retain];
521 - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
523 [button_ autorelease];
524 button_ = button == nil ? nil : [[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:button]]] retain];
527 [style_ autorelease];
528 style_ = style == nil ? nil : [style retain];
530 if (function_ != nil)
531 [function_ autorelease];
532 function_ = function == nil ? nil : [function retain];
534 [self reloadButtons];
537 - (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
539 [button_ autorelease];
540 button_ = button == nil ? nil : [button retain];
543 [style_ autorelease];
544 style_ = style == nil ? nil : [style retain];
546 if (function_ != nil)
547 [function_ autorelease];
548 function_ = function == nil ? nil : [function retain];
550 [self reloadButtons];
553 - (void) setFinishHook:(id)function {
555 [finish_ autorelease];
556 finish_ = function == nil ? nil : [function retain];
559 - (void) setPopupHook:(id)function {
561 [closer_ autorelease];
562 closer_ = function == nil ? nil : [function retain];
565 - (void) _openMailToURL:(NSURL *)url {
566 [UIApp openURL:url];// asPanel:YES];
569 - (void) webView:(WebView *)sender willBeginEditingFormElement:(id)element {
573 - (void) webView:(WebView *)sender didBeginEditingFormElement:(id)element {
577 - (void) webViewDidEndEditingFormElements:(WebView *)sender {
582 - (void) webViewClose:(WebView *)sender {
587 [[self navigationController] dismissModalViewControllerAnimated:YES];
590 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
593 - (void) webView:(WebView *)sender unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame {
594 NSLog(@"err:%@", error);
597 - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
599 NSLog(@"nwa:%@", name);
602 if (NSURL *url = [request URL]) {
603 if (name == nil) unknown: {
604 if (![self getSpecial:url swap:NO]) {
605 NSString *scheme([[url scheme] lowercaseString]);
606 if ([scheme isEqualToString:@"mailto"])
607 [self _openMailToURL:url];
610 } else if ([name isEqualToString:@"_open"])
611 [delegate_ openURL:url];
612 else if ([name isEqualToString:@"_popup"]) {
613 NSString *scheme([[url scheme] lowercaseString]);
614 if ([scheme isEqualToString:@"mailto"])
615 [self _openMailToURL:url];
617 UCNavigationController *navigation([[[UCNavigationController alloc] init] autorelease]);
618 [navigation setHook:indirect_];
620 UCViewController *page([delegate_ pageForURL:url hasTag:NULL]);
622 /* XXX: call createWebViewWithRequest instead? */
624 BrowserView *browser([[[class_ alloc] init] autorelease]);
625 [browser loadURL:url];
629 [navigation setDelegate:delegate_];
630 [page setDelegate:delegate_];
632 [navigation setViewControllers:[NSArray arrayWithObject:page]];
633 UIBarButtonItem *closeItem = [[UIBarButtonItem alloc]
634 initWithTitle:UCLocalize("CLOSE")
635 style:UIBarButtonItemStylePlain
637 action:@selector(close)
639 [[page navigationItem] setLeftBarButtonItem:closeItem];
642 [[self navigationController] presentModalViewController:navigation animated:YES];
651 - (void) webView:(WebView *)sender decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
652 if ([WebView canShowMIMEType:type])
655 // XXX: handle more mime types!
658 WebView *webview([document_ webView]);
659 if (frame == [webview mainFrame])
660 [UIApp openURL:[request URL]];
664 - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
665 if (request == nil) ignore: {
670 NSURL *url([request URL]);
671 NSString *host([url host]);
673 if (url == nil) use: {
674 if (!error_ && [frame parentFrame] == nil) {
676 [request_ autorelease];
677 request_ = [request retain];
679 NSLog(@"dpn:%@", request_);
685 WebView *webview([document_ webView]);
686 if (frame == [webview mainFrame])
691 else NSLog(@"nav:%@:%@", url, [action description]);
694 const NSArray *capability;
696 if ($GSSystemCopyCapability != NULL) {
697 capability = reinterpret_cast<const NSArray *>((*$GSSystemCopyCapability)(kGSDisplayIdentifiersCapability));
698 capability = [capability autorelease];
699 } else if ($GSSystemGetCapability != NULL) {
700 capability = reinterpret_cast<const NSArray *>((*$GSSystemGetCapability)(kGSDisplayIdentifiersCapability));
706 if (capability != nil && (
707 [url isGoogleMapsURL] && [capability containsObject:@"com.apple.Maps"] && (open = [url mapsURL]) != nil||
708 [host hasSuffix:@"youtube.com"] && [capability containsObject:@"com.apple.youtube"] && (open = [url youTubeURL]) != nil ||
709 [url respondsToSelector:@selector(phobosURL)] && (open = [url phobosURL]) != nil
717 int store(_not(int));
718 if (NSURL *itms = [url itmsURL:&store]) {
720 NSLog(@"itms#%@#%u#%@", url, store, itms);
723 if (capability != nil && (
724 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
725 store == 2 && [capability containsObject:@"com.apple.AppStore"]
732 NSString *scheme([[url scheme] lowercaseString]);
734 if ([scheme isEqualToString:@"tel"]) {
739 if ([scheme isEqualToString:@"mailto"]) {
740 [self _openMailToURL:url];
744 if ([self getSpecial:url swap:YES])
746 else if ([WebView _canHandleRequest:request])
748 else if ([url isSpringboardHandledURL])
754 - (void) webView:(WebView *)sender setStatusText:(NSString *)text {
755 //lprintf("Status:%s\n", [text UTF8String]);
758 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
759 NSString *context([sheet context]);
761 if ([context isEqualToString:@"alert"]) {
764 } else if ([context isEqualToString:@"confirm"]) {
767 confirm_ = [NSNumber numberWithBool:YES];
771 confirm_ = [NSNumber numberWithBool:NO];
776 } else if ([context isEqualToString:@"sensitive"]) {
779 sensitive_ = [NSNumber numberWithBool:YES];
783 sensitive_ = [NSNumber numberWithBool:NO];
788 } else if ([context isEqualToString:@"challenge"]) {
789 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
793 NSString *username([[sheet textFieldAtIndex:0] text]);
794 NSString *password([[sheet textFieldAtIndex:1] text]);
796 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
798 [sender useCredential:credential forAuthenticationChallenge:challenge_];
802 [sender cancelAuthenticationChallenge:challenge_];
808 [challenge_ release];
812 } else if ([context isEqualToString:@"submit"]) {
818 if (request_ != nil) {
820 [document_ loadRequest:request_];
832 - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
833 challenge_ = [challenge retain];
835 NSURLProtectionSpace *space([challenge protectionSpace]);
836 NSString *realm([space realm]);
840 UIActionSheet *sheet = [[[UIActionSheet alloc]
842 buttons:[NSArray arrayWithObjects:UCLocalize("LOGIN"), UCLocalize("CANCEL"), nil]
848 [sheet setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
850 [sheet setNumberOfRows:1];
852 [sheet addTextFieldWithValue:@"" label:UCLocalize("USERNAME")];
853 [sheet addTextFieldWithValue:@"" label:UCLocalize("PASSWORD")];
855 UITextField *username([sheet textFieldAtIndex:0]); {
856 UITextInputTraits *traits([username textInputTraits]);
857 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
858 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
859 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
860 [traits setReturnKeyType:UIReturnKeyNext];
863 UITextField *password([sheet textFieldAtIndex:1]); {
864 UITextInputTraits *traits([password textInputTraits]);
865 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
866 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
867 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
868 // XXX: UIReturnKeyDone
869 [traits setReturnKeyType:UIReturnKeyNext];
870 [traits setSecureTextEntry:YES];
873 [sheet popupAlertAnimated:YES];
876 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
880 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
881 //- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request userGesture:(BOOL)gesture {
883 NSLog(@"cwv:%@ (%@): %@", request, title_, features == nil ? @"{}" : [features description]);
884 //NSLog(@"cwv:%@ (%@): %@", request, title_, gesture ? @"Yes" : @"No");
887 NSNumber *value([features objectForKey:@"width"]);
888 float width(value == nil ? 0 : [value floatValue]);
890 UCNavigationController *navigation(!popup_ ? [self navigationController] : [[[UCNavigationController alloc] init] autorelease]);
892 /* XXX: deal with cydia:// pages */
893 BrowserView *browser([[[class_ alloc] initWithWidth:width] autorelease]);
895 if (features != nil && popup_) {
896 [navigation setDelegate:delegate_];
897 [navigation setHook:indirect_];
898 [browser setDelegate:delegate_];
900 [browser loadRequest:request];
902 [navigation setViewControllers:[NSArray arrayWithObject:browser]];
903 UIBarButtonItem *closeItem = [[UIBarButtonItem alloc]
904 initWithTitle:UCLocalize("CLOSE")
905 style:UIBarButtonItemStylePlain
907 action:@selector(close)
909 [[browser navigationItem] setLeftBarButtonItem:closeItem];
912 [[self navigationController] presentModalViewController:navigation animated:YES];
913 } /*else if (request == nil) {
914 [[self navigationItem] setTitle:title_];
915 [browser setDelegate:delegate_];
918 [self pushPage:browser];
919 [browser loadRequest:request];
922 return [browser webView];
925 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
926 return [self webView:sender createWebViewWithRequest:request windowFeatures:nil];
927 //return [self webView:sender createWebViewWithRequest:request userGesture:YES];
930 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
931 if ([frame parentFrame] != nil)
934 title_ = [title retain];
935 [[self navigationItem] setTitle:title_];
938 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
939 /*if ([loading_ count] == 0)
941 [loading_ addObject:[NSValue valueWithNonretainedObject:frame]];
943 if ([frame parentFrame] == nil) {
944 [document_ resignFirstResponder];
953 if (button_ != nil) {
963 if (function_ != nil) {
968 if (finish_ != nil) {
973 if (closer_ != nil) {
978 if (special_ != nil) {
983 [[self navigationItem] setTitle:title_];
986 CGRect webrect = [scroller_ bounds];
987 webrect.size.height = 1;
988 [document_ setFrame:webrect];
991 if ([scroller_ respondsToSelector:@selector(scrollPointVisibleAtTopLeft:)])
992 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
994 [scroller_ scrollRectToVisible:CGRectZero animated:NO];
996 if ([scroller_ respondsToSelector:@selector(setZoomScale:duration:)])
997 [scroller_ setZoomScale:1 duration:0];
998 else if ([scroller_ respondsToSelector:@selector(_setZoomScale:duration:)])
999 [scroller_ _setZoomScale:1 duration:0];
1000 /*else if ([scroller_ respondsToSelector:@selector(setZoomScale:animated:)])
1001 [scroller_ setZoomScale:1 animated:NO];*/
1004 CGRect webrect = [scroller_ bounds];
1005 webrect.size.height = 0;
1006 [document_ setFrame:webrect];
1010 [self reloadButtons];
1013 - (void) didFinishLoading { }
1015 - (void) _finishLoading {
1016 size_t count([loading_ count]);
1018 [self autorelease];*/
1019 if (reloading_ || count != 0)
1022 [self callFunction:finish_];
1024 [self reloadButtons];
1027 - (bool) isLoading {
1028 return [loading_ count] != 0;
1031 - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
1032 return [document_ webView:sender shouldScrollToPoint:point forFrame:frame];
1035 - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
1036 return [document_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
1039 - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
1040 return [document_ webView:sender needsScrollNotifications:notifications forFrame:frame];
1043 - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
1045 return [document_ webView:sender didCommitLoadForFrame:frame];
1048 - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
1049 return [document_ webView:sender didReceiveDocTypeForFrame:frame];
1052 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
1053 [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]];
1054 [self _finishLoading];
1056 if ([frame parentFrame] == nil) {
1057 if (DOMDocument *document = [frame DOMDocument])
1058 if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
1059 for (DOMHTMLBodyElement *body in bodies) {
1060 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
1062 bool colored(false);
1064 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
1065 if ([color primitiveType] == DOM_CSS_RGBCOLOR) {
1066 DOMRGBColor *rgb([color getRGBColorValue]);
1068 float red([[rgb red] getFloatValue:DOM_CSS_NUMBER]);
1069 float green([[rgb green] getFloatValue:DOM_CSS_NUMBER]);
1070 float blue([[rgb blue] getFloatValue:DOM_CSS_NUMBER]);
1071 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
1075 if (red == 0xc7 && green == 0xce && blue == 0xd5)
1076 uic = [UIColor pinStripeColor];
1077 else if (alpha != 0)
1079 colorWithRed:(red / 255)
1087 [scroller_ setBackgroundColor:uic];
1093 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
1098 return [document_ webView:sender didFinishLoadForFrame:frame];
1101 - (void) _didFailWithError:(NSError *)error forFrame:(WebFrame *)frame {
1103 /*if ([frame parentFrame] == nil)
1104 [self autorelease];*/
1106 [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]];
1107 [self _finishLoading];
1112 if ([frame parentFrame] == nil) {
1113 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
1114 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
1115 [[error localizedDescription] stringByAddingPercentEscapes]
1122 - (void) webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
1123 [self _didFailWithError:error forFrame:frame];
1124 if ([document_ respondsToSelector:@selector(webView:didFailLoadWithError:forFrame:)])
1125 [document_ webView:sender didFailLoadWithError:error forFrame:frame];
1128 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
1129 [self _didFailWithError:error forFrame:frame];
1132 - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
1133 #if LogBrowser || ForSaurik
1134 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
1138 - (void) webView:(WebView *)sender didReceiveMessage:(NSDictionary *)dictionary {
1139 #if LogBrowser || ForSaurik
1140 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
1142 if ([document_ respondsToSelector:@selector(webView:didReceiveMessage:)])
1143 [document_ webView:sender didReceiveMessage:dictionary];
1146 - (void) webView:(id)sender willCloseFrame:(id)frame {
1147 if ([document_ respondsToSelector:@selector(webView:willCloseFrame:)])
1148 [document_ webView:sender willCloseFrame:frame];
1151 - (void) webView:(id)sender didFinishDocumentLoadForFrame:(id)frame {
1152 if ([document_ respondsToSelector:@selector(webView:didFinishDocumentLoadForFrame:)])
1153 [document_ webView:sender didFinishDocumentLoadForFrame:frame];
1156 - (void) webView:(id)sender didFirstLayoutInFrame:(id)frame {
1157 if ([document_ respondsToSelector:@selector(webView:didFirstLayoutInFrame:)])
1158 [document_ webView:sender didFirstLayoutInFrame:frame];
1161 - (void) webViewFormEditedStatusHasChanged:(id)changed {
1162 if ([document_ respondsToSelector:@selector(webViewFormEditedStatusHasChanged:)])
1163 [document_ webViewFormEditedStatusHasChanged:changed];
1166 - (void) webView:(id)sender formStateDidFocusNode:(id)formState {
1167 if ([document_ respondsToSelector:@selector(webView:formStateDidFocusNode:)])
1168 [document_ webView:sender formStateDidFocusNode:formState];
1171 - (void) webView:(id)sender formStateDidBlurNode:(id)formState {
1172 if ([document_ respondsToSelector:@selector(webView:formStateDidBlurNode:)])
1173 [document_ webView:sender formStateDidBlurNode:formState];
1176 /* XXX: fix this stupid include file
1177 - (void) webView:(WebView *)sender frame:(WebFrame *)frame exceededDatabaseQuotaForSecurityOrigin:(WebSecurityOrigin *)origin database:(NSString *)database {
1178 [origin setQuota:0x500000];
1181 - (void) webViewDidLayout:(id)sender {
1182 [document_ webViewDidLayout:sender];
1185 - (void) webView:(id)sender didFirstVisuallyNonEmptyLayoutInFrame:(id)frame {
1186 [document_ webView:sender didFirstVisuallyNonEmptyLayoutInFrame:frame];
1189 - (void) webView:(id)sender saveStateToHistoryItem:(id)item forFrame:(id)frame {
1190 [document_ webView:sender saveStateToHistoryItem:item forFrame:frame];
1193 - (void) webView:(id)sender restoreStateFromHistoryItem:(id)item forFrame:(id)frame force:(BOOL)force {
1194 [document_ webView:sender restoreStateFromHistoryItem:item forFrame:frame force:force];
1197 - (void) webView:(id)sender attachRootLayer:(id)layer {
1198 [document_ webView:sender attachRootLayer:layer];
1201 - (id) webView:(id)sender plugInViewWithArguments:(id)arguments fromPlugInPackage:(id)package {
1202 return [document_ webView:sender plugInViewWithArguments:arguments fromPlugInPackage:package];
1205 - (void) webView:(id)sender willShowFullScreenForPlugInView:(id)view {
1206 [document_ webView:sender willShowFullScreenForPlugInView:view];
1209 - (void) webView:(id)sender didHideFullScreenForPlugInView:(id)view {
1210 [document_ webView:sender didHideFullScreenForPlugInView:view];
1213 - (void) webView:(id)sender willAddPlugInView:(id)view {
1214 [document_ webView:sender willAddPlugInView:view];
1217 - (void) webView:(id)sender didObserveDeferredContentChange:(int)change forFrame:(id)frame {
1218 [document_ webView:sender didObserveDeferredContentChange:change forFrame:frame];
1221 - (void) webViewDidPreventDefaultForEvent:(id)sender {
1222 [document_ webViewDidPreventDefaultForEvent:sender];
1225 - (void) _setTileDrawingEnabled:(BOOL)enabled {
1226 //[document_ setTileDrawingEnabled:enabled];
1229 - (void) setViewportWidth:(float)width {
1230 width_ = width != 0 ? width : [[self class] defaultWidth];
1231 [document_ setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
1234 - (void) willStartGesturesInView:(UIView *)view forEvent:(GSEventRef)event {
1235 [self _setTileDrawingEnabled:NO];
1238 - (void) didFinishGesturesInView:(UIView *)view forEvent:(GSEventRef)event {
1239 [self _setTileDrawingEnabled:YES];
1240 [document_ redrawScaledDocument];
1243 - (void) scrollerWillStartDragging:(UIScroller *)scroller {
1244 [self _setTileDrawingEnabled:NO];
1247 - (void) scrollerDidEndDragging:(UIScroller *)scroller willSmoothScroll:(BOOL)smooth {
1248 [self _setTileDrawingEnabled:YES];
1251 - (void) scrollerDidEndDragging:(UIScroller *)scroller {
1252 [self _setTileDrawingEnabled:YES];
1255 - (id) initWithWidth:(float)width ofClass:(Class)_class {
1256 if ((self = [super init]) != nil) {
1258 loading_ = [[NSMutableSet alloc] initWithCapacity:3];
1261 struct CGRect bounds = [[self view] bounds];
1263 scroller_ = [[objc_getClass(Wildcat_ ? "UIScrollView" : "UIScroller") alloc] initWithFrame:bounds];
1264 [[self view] addSubview:scroller_];
1266 [scroller_ setFixedBackgroundPattern:YES];
1267 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
1269 [scroller_ setScrollingEnabled:YES];
1270 [scroller_ setClipsSubviews:YES];
1273 [scroller_ setAllowsRubberBanding:YES];
1275 [scroller_ setDelegate:self];
1276 [scroller_ setBounces:YES];
1279 [scroller_ setScrollHysteresis:8];
1280 [scroller_ setThumbDetectionEnabled:NO];
1281 [scroller_ setDirectionalScrolling:YES];
1282 //[scroller_ setScrollDecelerationFactor:0.99]; /* 0.989324 */
1283 [scroller_ setEventMode:YES];
1287 UIScrollView *scroller((UIScrollView *)scroller_);
1288 //[scroller setDirectionalLockEnabled:NO];
1289 [scroller setDelaysContentTouches:NO];
1290 //[scroller setScrollsToTop:NO];
1291 //[scroller setCanCancelContentTouches:NO];
1294 [scroller_ setShowBackgroundShadow:NO]; /* YES */
1295 //[scroller_ setAllowsRubberBanding:YES]; /* Vertical */
1298 [scroller_ setAdjustForContentSizeChange:YES]; /* NO */
1300 CGRect webrect = [scroller_ bounds];
1301 webrect.size.height = 0;
1308 document_ = [Documents_ lastObject];
1309 if (document_ != nil) {
1310 document_ = [document_ retain];
1311 webview = [document_ webView];
1312 [Documents_ removeLastObject];
1313 [document_ setFrame:webrect];
1318 document_ = [[$UIWebBrowserView alloc] initWithFrame:webrect];
1319 webview = [document_ webView];
1321 // XXX: this is terribly (too?) expensive
1322 //[document_ setDrawsBackground:NO];
1323 [webview setPreferencesIdentifier:@"Cydia"];
1325 [document_ setTileSize:CGSizeMake(webrect.size.width, 500)];
1327 if ([document_ respondsToSelector:@selector(enableReachability)])
1328 [document_ enableReachability];
1329 if ([document_ respondsToSelector:@selector(setAllowsMessaging:)])
1330 [document_ setAllowsMessaging:YES];
1331 if ([document_ respondsToSelector:@selector(useSelectionAssistantWithMode:)])
1332 [document_ useSelectionAssistantWithMode:0];
1334 [document_ setTilingEnabled:YES];
1335 [document_ setDrawsGrid:NO];
1336 [document_ setLogsTilingChanges:NO];
1337 [document_ setTileMinificationFilter:kCAFilterNearest];
1339 if ([document_ respondsToSelector:@selector(setDataDetectorTypes:)])
1340 /* XXX: abstractify */
1341 [document_ setDataDetectorTypes:0x80000000];
1343 [document_ setDetectsPhoneNumbers:NO];
1345 [document_ setAutoresizes:YES];
1347 [document_ setMinimumScale:0.25f forDocumentTypes:0x10];
1348 [document_ setMaximumScale:5.00f forDocumentTypes:0x10];
1349 [document_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10];
1350 //[document_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
1352 [document_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2];
1354 [document_ setMinimumScale:1.00f forDocumentTypes:0x8];
1355 [document_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8];
1356 [document_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8];
1358 [document_ _setDocumentType:0x4];
1360 if ([document_ respondsToSelector:@selector(setZoomsFocusedFormControl:)])
1361 [document_ setZoomsFocusedFormControl:YES];
1362 [document_ setContentsPosition:7];
1363 [document_ setEnabledGestures:0xa];
1364 [document_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
1365 [document_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
1367 [document_ setSmoothsFonts:YES];
1368 [document_ setAllowsImageSheet:YES];
1369 [webview _setUsesLoaderCache:YES];
1371 [webview setGroupName:@"CydiaGroup"];
1373 WebPreferences *preferences([webview preferences]);
1375 if ([webview respondsToSelector:@selector(_setLayoutInterval:)])
1376 [webview _setLayoutInterval:0];
1378 [preferences _setLayoutInterval:0];
1381 [self setViewportWidth:width];
1383 [document_ setDelegate:self];
1384 [document_ setGestureDelegate:self];
1386 if ([document_ respondsToSelector:@selector(setFormEditingDelegate:)])
1387 [document_ setFormEditingDelegate:self];
1389 [document_ setInteractionDelegate:self];
1391 [scroller_ addSubview:document_];
1393 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
1395 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
1397 [webview setFrameLoadDelegate:indirect_];
1398 [webview setPolicyDelegate:indirect_];
1399 [webview setResourceLoadDelegate:indirect_];
1400 [webview setUIDelegate:indirect_];
1402 /* XXX: do not turn this on under penalty of extreme pain */
1403 [webview setScriptDebugDelegate:nil];
1407 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
1408 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(15, 5, indsize.width, indsize.height)];
1409 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
1410 [indicator_ startAnimation];
1412 [scroller_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
1413 [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
1414 [document_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
1416 /*UIWebView *test([[[UIWebView alloc] initWithFrame:[[self view] bounds]] autorelease]);
1417 [test loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.saurik.com/"]]];
1418 [[self view] addSubview:test];*/
1422 - (id) initWithWidth:(float)width {
1423 return [self initWithWidth:width ofClass:[self class]];
1427 return [self initWithWidth:0];
1430 - (NSString *) stringByEvaluatingJavaScriptFromString:(NSString *)script {
1432 WebView *webview([document_ webView]);
1433 NSString *string([webview stringByEvaluatingJavaScriptFromString:script]);
1438 - (void) callFunction:(WebScriptObject *)function {
1441 WebView *webview([document_ webView]);
1442 WebFrame *frame([webview mainFrame]);
1444 id _private(MSHookIvar<id>(webview, "_private"));
1445 WebCore::Page *page(_private == nil ? NULL : MSHookIvar<WebCore::Page *>(_private, "page"));
1446 WebCore::Settings *settings(page == NULL ? NULL : page->settings());
1449 if (settings == NULL)
1452 no = settings->JavaScriptCanOpenWindowsAutomatically();
1453 settings->setJavaScriptCanOpenWindowsAutomatically(true);
1456 if (UIWindow *window = [[self view] window])
1457 if (UIResponder *responder = [window firstResponder])
1458 [responder resignFirstResponder];
1460 JSObjectRef object([function JSObject]);
1461 JSGlobalContextRef context([frame globalContext]);
1462 JSObjectCallAsFunction(context, object, NULL, 0, NULL, NULL);
1464 if (settings != NULL)
1465 settings->setJavaScriptCanOpenWindowsAutomatically(no);
1470 - (void) didDismissModalViewController {
1472 [self callFunction:closer_];
1475 - (void) __rightButtonClicked {
1480 - (void) _rightButtonClicked {
1482 if (function_ != nil)
1483 [self callFunction:function_];
1486 [self __rightButtonClicked];
1489 - (UINavigationButtonStyle) rightButtonStyle {
1490 if (style_ == nil) normal:
1491 return UINavigationButtonStyleNormal;
1492 else if ([style_ isEqualToString:@"Normal"])
1493 return UINavigationButtonStyleNormal;
1494 else if ([style_ isEqualToString:@"Back"])
1495 return UINavigationButtonStyleBack;
1496 else if ([style_ isEqualToString:@"Highlighted"])
1497 return UINavigationButtonStyleHighlighted;
1498 else if ([style_ isEqualToString:@"Destructive"])
1499 return UINavigationButtonStyleDestructive;
1503 - (void) reloadButtons {
1504 if ([self isLoading]) {
1505 UIBarButtonItem *reloadItem = [[UIBarButtonItem alloc]
1507 style:UIBarButtonItemStylePlain
1509 action:@selector(_rightButtonClicked)
1511 [[self navigationItem] setRightBarButtonItem:reloadItem];
1512 [[reloadItem view] addSubview:indicator_];
1513 [[self navigationItem] setTitle:UCLocalize("LOADING")];
1514 [reloadItem release];
1516 UIBarButtonItem *reloadItem = [[UIBarButtonItem alloc]
1517 initWithTitle:button_ ?: UCLocalize("RELOAD")
1518 style:[self rightButtonStyle]
1520 action:@selector(_rightButtonClicked)
1522 [[self navigationItem] setRightBarButtonItem:reloadItem animated:YES];
1523 [[self navigationItem] setTitle:title_];
1524 [reloadItem release];
1526 if (function_ == nil) [self didFinishLoading];
1530 - (void) setPageActive:(BOOL)active {
1532 [indicator_ removeFromSuperview];
1534 [[[[self navigationItem] rightBarButtonItem] view] addSubview:indicator_];
1537 - (void) resetViewAnimated:(BOOL)animated {
1540 - (void) setPushed:(bool)pushed {
1544 + (float) defaultWidth {