]> git.saurik.com Git - cydia.git/blob - CyteKit/WebViewController.mm
Add delegate getters to match setDelegate setters.
[cydia.git] / CyteKit / WebViewController.mm
1 #include "CyteKit/UCPlatform.h"
2 #include "CyteKit/WebViewController.h"
3
4 #include "CyteKit/MFMailComposeViewController-MailToURL.h"
5
6 #include "iPhonePrivate.h"
7
8 #include "CyteKit/IndirectDelegate.h"
9 #include "CyteKit/Localize.h"
10 #include "CyteKit/WebViewController.h"
11 #include "CyteKit/PerlCompatibleRegEx.hpp"
12 #include "CyteKit/WebThreadLocked.hpp"
13
14 //#include <QuartzCore/CALayer.h>
15 // XXX: fix the minimum requirement
16 extern NSString * const kCAFilterNearest;
17
18 #include <WebCore/WebCoreThread.h>
19
20 #import <WebKit/WebKitErrors.h>
21 #import <WebKit/WebPreferences.h>
22
23 #include <WebKit/DOMCSSPrimitiveValue.h>
24 #include <WebKit/DOMCSSStyleDeclaration.h>
25 #include <WebKit/DOMDocument.h>
26 #include <WebKit/DOMHTMLBodyElement.h>
27 #include <WebKit/DOMRGBColor.h>
28
29 #include <dlfcn.h>
30 #include <objc/runtime.h>
31
32 #define ForSaurik 0
33 #define DefaultTimeout_ 120.0
34
35 #define ShowInternals 0
36 #define LogBrowser 0
37 #define LogMessages 0
38
39 #define lprintf(args...) fprintf(stderr, args)
40
41 // XXX: centralize these special class things to some file or mechanism?
42 static Class $MFMailComposeViewController;
43
44 float CYScrollViewDecelerationRateNormal;
45
46 @interface WebView (Apple)
47 - (void) _setLayoutInterval:(float)interval;
48 - (void) _setAllowsMessaging:(BOOL)allows;
49 @end
50
51 @implementation WebFrame (Cydia)
52
53 - (NSString *) description {
54 return [NSString stringWithFormat:@"<%s: %p, %@>", class_getName([self class]), self, [[[([self provisionalDataSource] ?: [self dataSource]) request] URL] absoluteString]];
55 }
56
57 @end
58
59 /* Indirect Delegate {{{ */
60 @implementation IndirectDelegate
61
62 - (id) delegate {
63 return delegate_;
64 }
65
66 - (void) setDelegate:(id)delegate {
67 delegate_ = delegate;
68 }
69
70 - (id) initWithDelegate:(id)delegate {
71 delegate_ = delegate;
72 return self;
73 }
74
75 - (IMP) methodForSelector:(SEL)sel {
76 if (IMP method = [super methodForSelector:sel])
77 return method;
78 fprintf(stderr, "methodForSelector:[%s] == NULL\n", sel_getName(sel));
79 return NULL;
80 }
81
82 - (BOOL) respondsToSelector:(SEL)sel {
83 if ([super respondsToSelector:sel])
84 return YES;
85
86 // XXX: WebThreadCreateNSInvocation returns nil
87
88 #if ShowInternals
89 fprintf(stderr, "[%s]R?%s\n", class_getName(self->isa), sel_getName(sel));
90 #endif
91
92 return delegate_ == nil ? NO : [delegate_ respondsToSelector:sel];
93 }
94
95 - (NSMethodSignature *) methodSignatureForSelector:(SEL)sel {
96 if (NSMethodSignature *method = [super methodSignatureForSelector:sel])
97 return method;
98
99 #if ShowInternals
100 fprintf(stderr, "[%s]S?%s\n", class_getName(self->isa), sel_getName(sel));
101 #endif
102
103 if (delegate_ != nil)
104 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
105 return sig;
106
107 // XXX: I fucking hate Apple so very very bad
108 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
109 }
110
111 - (void) forwardInvocation:(NSInvocation *)inv {
112 SEL sel = [inv selector];
113 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
114 [inv invokeWithTarget:delegate_];
115 }
116
117 @end
118 /* }}} */
119
120 @implementation CyteWebViewController
121
122 #if ShowInternals
123 #include "CyteKit/UCInternal.h"
124 #endif
125
126 + (void) _initialize {
127 [WebPreferences _setInitialDefaultTextEncodingToSystemEncoding];
128
129 dlopen("/System/Library/Frameworks/MessageUI.framework/MessageUI", RTLD_GLOBAL | RTLD_LAZY);
130 $MFMailComposeViewController = objc_getClass("MFMailComposeViewController");
131
132 if (float *_UIScrollViewDecelerationRateNormal = reinterpret_cast<float *>(dlsym(RTLD_DEFAULT, "UIScrollViewDecelerationRateNormal")))
133 CYScrollViewDecelerationRateNormal = *_UIScrollViewDecelerationRateNormal;
134 else // XXX: this actually might be fast on some older systems: we should look into this
135 CYScrollViewDecelerationRateNormal = 0.998;
136 }
137
138 - (bool) retainsNetworkActivityIndicator {
139 return true;
140 }
141
142 - (void) releaseNetworkActivityIndicator {
143 if ([loading_ count] != 0) {
144 [loading_ removeAllObjects];
145
146 if ([self retainsNetworkActivityIndicator])
147 [delegate_ releaseNetworkActivityIndicator];
148 }
149 }
150
151 - (void) dealloc {
152 #if LogBrowser
153 NSLog(@"[CyteWebViewController dealloc]");
154 #endif
155
156 [self releaseNetworkActivityIndicator];
157
158 [super dealloc];
159 }
160
161 - (NSString *) description {
162 return [NSString stringWithFormat:@"<%s: %p, %@>", class_getName([self class]), self, [[request_ URL] absoluteString]];
163 }
164
165 - (CyteWebView *) webView {
166 return (CyteWebView *) [self view];
167 }
168
169 - (NSURL *) URLWithURL:(NSURL *)url {
170 return url;
171 }
172
173 - (NSURLRequest *) requestWithURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy referrer:(NSString *)referrer {
174 NSMutableURLRequest *request([NSMutableURLRequest
175 requestWithURL:[self URLWithURL:url]
176 cachePolicy:policy
177 timeoutInterval:DefaultTimeout_
178 ]);
179
180 [request setValue:referrer forHTTPHeaderField:@"Referer"];
181
182 return request;
183 }
184
185 - (void) setRequest:(NSURLRequest *)request {
186 _assert(request_ == nil);
187 request_ = request;
188 }
189
190 - (void) setURL:(NSURL *)url {
191 [self setURL:url withReferrer:nil];
192 }
193
194 - (void) setURL:(NSURL *)url withReferrer:(NSString *)referrer {
195 [self setRequest:[self requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy referrer:referrer]];
196 }
197
198 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
199 [self loadRequest:[self requestWithURL:url cachePolicy:policy referrer:nil]];
200 }
201
202 - (void) loadURL:(NSURL *)url {
203 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
204 }
205
206 - (void) loadRequest:(NSURLRequest *)request {
207 #if LogBrowser
208 NSLog(@"loadRequest:%@", request);
209 #endif
210
211 error_ = false;
212 ready_ = true;
213
214 WebThreadLocked lock;
215 [[self webView] loadRequest:request];
216 }
217
218 - (void) reloadURLWithCache:(BOOL)cache {
219 if (request_ == nil)
220 return;
221
222 NSMutableURLRequest *request([request_ mutableCopy]);
223 [request setCachePolicy:(cache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData)];
224
225 request_ = request;
226
227 if (cache || [request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil)
228 [self loadRequest:request_];
229 else {
230 UIAlertView *alert = [[[UIAlertView alloc]
231 initWithTitle:UCLocalize("RESUBMIT_FORM")
232 message:nil
233 delegate:self
234 cancelButtonTitle:UCLocalize("CANCEL")
235 otherButtonTitles:
236 UCLocalize("SUBMIT"),
237 nil
238 ] autorelease];
239
240 [alert setContext:@"submit"];
241 [alert show];
242 }
243 }
244
245 - (void) reloadData {
246 [super reloadData];
247
248 if (ready_)
249 [self dispatchEvent:@"CydiaReloadData"];
250 else
251 [self reloadURLWithCache:YES];
252 }
253
254 - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
255 custom_ = button;
256 style_ = style;
257 function_ = function;
258
259 [self performSelectorOnMainThread:@selector(applyRightButton) withObject:nil waitUntilDone:NO];
260 }
261
262 - (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
263 custom_ = button;
264 style_ = style;
265 function_ = function;
266
267 [self performSelectorOnMainThread:@selector(applyRightButton) withObject:nil waitUntilDone:NO];
268 }
269
270 - (void) removeButton {
271 custom_ = [NSNull null];
272 [self performSelectorOnMainThread:@selector(applyRightButton) withObject:nil waitUntilDone:NO];
273 }
274
275 - (void) scrollToBottomAnimated:(NSNumber *)animated {
276 CGSize size([scroller_ contentSize]);
277 CGPoint offset([scroller_ contentOffset]);
278 CGRect frame([scroller_ frame]);
279
280 if (size.height - offset.y < frame.size.height + 20.f) {
281 CGRect rect = {{0, size.height-1}, {size.width, 1}};
282 [scroller_ scrollRectToVisible:rect animated:[animated boolValue]];
283 }
284 }
285
286 - (void) _setViewportWidth {
287 [[[self webView] _documentView] setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
288 }
289
290 - (void) setViewportWidth:(float)width {
291 width_ = width != 0 ? width : [[self class] defaultWidth];
292 [self _setViewportWidth];
293 }
294
295 - (void) _setViewportWidthOnMainThread:(NSNumber *)width {
296 [self setViewportWidth:[width floatValue]];
297 }
298
299 - (void) setViewportWidthOnMainThread:(float)width {
300 [self performSelectorOnMainThread:@selector(_setViewportWidthOnMainThread:) withObject:[NSNumber numberWithFloat:width] waitUntilDone:NO];
301 }
302
303 - (void) webViewUpdateViewSettings:(UIWebView *)view {
304 [self _setViewportWidth];
305 }
306
307 - (void) mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
308 [self dismissModalViewControllerAnimated:YES];
309 }
310
311 - (void) _setupMail:(MFMailComposeViewController *)controller {
312 }
313
314 - (void) _openMailToURL:(NSURL *)url {
315 if ($MFMailComposeViewController != nil && [$MFMailComposeViewController canSendMail]) {
316 MFMailComposeViewController *controller([[[$MFMailComposeViewController alloc] init] autorelease]);
317 [controller setMailComposeDelegate:self];
318
319 [controller setMailToURL:url];
320
321 [self _setupMail:controller];
322
323 [self presentModalViewController:controller animated:YES];
324 return;
325 }
326
327 UIApplication *app([UIApplication sharedApplication]);
328 if ([app respondsToSelector:@selector(openURL:asPanel:)])
329 [app openURL:url asPanel:YES];
330 else
331 [app openURL:url];
332 }
333
334 - (bool) _allowJavaScriptPanel {
335 return true;
336 }
337
338 - (bool) allowsNavigationAction {
339 return allowsNavigationAction_;
340 }
341
342 - (void) setAllowsNavigationAction:(bool)value {
343 allowsNavigationAction_ = value;
344 }
345
346 - (void) setAllowsNavigationActionByNumber:(NSNumber *)value {
347 [self setAllowsNavigationAction:[value boolValue]];
348 }
349
350 - (void) popViewControllerWithNumber:(NSNumber *)value {
351 UINavigationController *navigation([self navigationController]);
352 if ([navigation topViewController] == self)
353 [navigation popViewControllerAnimated:[value boolValue]];
354 }
355
356 - (void) _didFailWithError:(NSError *)error forFrame:(WebFrame *)frame {
357 NSValue *object([NSValue valueWithNonretainedObject:frame]);
358 if (![loading_ containsObject:object])
359 return;
360 [loading_ removeObject:object];
361
362 [self _didFinishLoading];
363
364 if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled)
365 return;
366
367 if ([[error domain] isEqualToString:WebKitErrorDomain] && [error code] == WebKitErrorFrameLoadInterruptedByPolicyChange) {
368 request_ = nil;
369 return;
370 }
371
372 if ([frame parentFrame] == nil) {
373 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
374 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
375 [[error localizedDescription] stringByAddingPercentEscapes]
376 ]]];
377
378 error_ = true;
379 }
380 }
381
382 - (void) pushRequest:(NSURLRequest *)request forAction:(NSDictionary *)action asPop:(bool)pop {
383 WebFrame *frame(nil);
384 if (NSDictionary *WebActionElement = [action objectForKey:@"WebActionElementKey"])
385 frame = [WebActionElement objectForKey:@"WebElementFrame"];
386 if (frame == nil)
387 frame = [[[[self webView] _documentView] webView] mainFrame];
388
389 WebDataSource *source([frame provisionalDataSource] ?: [frame dataSource]);
390 NSString *referrer([request valueForHTTPHeaderField:@"Referer"] ?: [[[source request] URL] absoluteString]);
391
392 NSURL *url([request URL]);
393
394 // XXX: filter to internal usage?
395 CyteViewController *page([delegate_ pageForURL:url forExternal:NO withReferrer:referrer]);
396
397 if (page == nil) {
398 CyteWebViewController *browser([[[class_ alloc] init] autorelease]);
399 [browser setRequest:request];
400 page = browser;
401 }
402
403 [page setDelegate:delegate_];
404
405 if (!pop) {
406 [[self navigationItem] setTitle:title_];
407
408 [[self navigationController] pushViewController:page animated:YES];
409 } else {
410 UINavigationController *navigation([[[UINavigationController alloc] initWithRootViewController:page] autorelease]);
411
412 [navigation setDelegate:delegate_];
413
414 [[page navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
415 initWithTitle:UCLocalize("CLOSE")
416 style:UIBarButtonItemStylePlain
417 target:page
418 action:@selector(close)
419 ] autorelease]];
420
421 [[self navigationController] presentModalViewController:navigation animated:YES];
422 }
423 }
424
425 // CyteWebViewDelegate {{{
426 - (void) webView:(WebView *)view addMessageToConsole:(NSDictionary *)message {
427 #if LogMessages
428 static Pcre irritating("^(?"
429 ":" "The page at .* displayed insecure content from .*\\."
430 "|" "Unsafe JavaScript attempt to access frame with URL .* from frame with URL .*\\. Domains, protocols and ports must match\\."
431 ")\\n$");
432
433 if (NSString *data = [message objectForKey:@"message"])
434 if (irritating(data))
435 return;
436
437 NSLog(@"addMessageToConsole:%@", message);
438 #endif
439 }
440
441 - (void) webView:(WebView *)view decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
442 #if LogBrowser
443 NSLog(@"decidePolicyForNavigationAction:%@ request:%@ %@ frame:%@", action, request, [request allHTTPHeaderFields], frame);
444 #endif
445
446 if ([frame parentFrame] == nil) {
447 if (!error_) {
448 NSURL *url(request == nil ? nil : [request URL]);
449
450 if (request_ != nil && ![[request_ URL] isEqual:url] && ![self allowsNavigationAction]) {
451 if (url != nil)
452 [self pushRequest:request forAction:action asPop:NO];
453 [listener ignore];
454 }
455 }
456 }
457 }
458
459 - (void) webView:(WebView *)view didDecidePolicy:(CYWebPolicyDecision)decision forNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame {
460 #if LogBrowser
461 NSLog(@"didDecidePolicy:%u forNavigationAction:%@ request:%@ frame:%@", decision, action, request, [request allHTTPHeaderFields], frame);
462 #endif
463
464 if ([frame parentFrame] == nil) {
465 switch (decision) {
466 case CYWebPolicyDecisionIgnore:
467 if ([[request_ URL] isEqual:[request URL]])
468 request_ = nil;
469 break;
470
471 case CYWebPolicyDecisionUse:
472 if (!error_)
473 request_ = request;
474 break;
475
476 default:
477 break;
478 }
479 }
480 }
481
482 - (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
483 #if LogBrowser
484 NSLog(@"decidePolicyForNewWindowAction:%@ request:%@ %@ newFrameName:%@", action, request, [request allHTTPHeaderFields], name);
485 #endif
486
487 NSURL *url([request URL]);
488 if (url == nil)
489 return;
490
491 if ([name isEqualToString:@"_open"])
492 [delegate_ openURL:url];
493 else {
494 NSString *scheme([[url scheme] lowercaseString]);
495 if ([scheme isEqualToString:@"mailto"])
496 [self _openMailToURL:url];
497 else
498 [self pushRequest:request forAction:action asPop:[name isEqualToString:@"_popup"]];
499 }
500
501 [listener ignore];
502 }
503
504 - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
505 }
506
507 - (void) webView:(WebView *)view didCommitLoadForFrame:(WebFrame *)frame {
508 #if LogBrowser
509 NSLog(@"didCommitLoadForFrame:%@", frame);
510 #endif
511
512 if ([frame parentFrame] == nil) {
513 loaded_ = true;
514 }
515 }
516
517 - (void) webView:(WebView *)view didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
518 #if LogBrowser
519 NSLog(@"didFailLoadWithError:%@ forFrame:%@", error, frame);
520 #endif
521
522 [self _didFailWithError:error forFrame:frame];
523 }
524
525 - (void) webView:(WebView *)view didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
526 #if LogBrowser
527 NSLog(@"didFailProvisionalLoadWithError:%@ forFrame:%@", error, frame);
528 #endif
529
530 [self _didFailWithError:error forFrame:frame];
531 }
532
533 - (void) webView:(WebView *)view didFinishLoadForFrame:(WebFrame *)frame {
534 NSValue *object([NSValue valueWithNonretainedObject:frame]);
535 if (![loading_ containsObject:object])
536 return;
537 [loading_ removeObject:object];
538
539 if ([frame parentFrame] == nil) {
540 if (DOMDocument *document = [frame DOMDocument])
541 if (DOMNodeList *bodies = [document getElementsByTagName:@"body"])
542 for (DOMHTMLBodyElement *body in (id) bodies) {
543 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
544
545 UIColor *uic(nil);
546
547 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
548 if ([color primitiveType] == DOM_CSS_RGBCOLOR) {
549 DOMRGBColor *rgb([color getRGBColorValue]);
550
551 float red([[rgb red] getFloatValue:DOM_CSS_NUMBER]);
552 float green([[rgb green] getFloatValue:DOM_CSS_NUMBER]);
553 float blue([[rgb blue] getFloatValue:DOM_CSS_NUMBER]);
554 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
555
556 if (red == 0xc7 && green == 0xce && blue == 0xd5)
557 uic = [UIColor pinStripeColor];
558 else if (alpha != 0)
559 uic = [UIColor
560 colorWithRed:(red / 255)
561 green:(green / 255)
562 blue:(blue / 255)
563 alpha:alpha
564 ];
565 }
566 }
567
568 [scroller_ setBackgroundColor:(uic ?: [UIColor clearColor])];
569 break;
570 }
571 }
572
573 [self _didFinishLoading];
574 }
575
576 - (void) webView:(WebView *)view didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
577 if ([frame parentFrame] != nil)
578 return;
579
580 title_ = title;
581
582 [[self navigationItem] setTitle:title_];
583 }
584
585 - (void) webView:(WebView *)view didStartProvisionalLoadForFrame:(WebFrame *)frame {
586 #if LogBrowser
587 NSLog(@"didStartProvisionalLoadForFrame:%@", frame);
588 #endif
589
590 [loading_ addObject:[NSValue valueWithNonretainedObject:frame]];
591
592 if ([frame parentFrame] == nil) {
593 title_ = nil;
594 custom_ = nil;
595 style_ = nil;
596 function_ = nil;
597
598 allowsNavigationAction_ = true;
599
600 [self setHidesNavigationBar:NO];
601 [self setScrollAlwaysBounceVertical:true];
602 [self setScrollIndicatorStyle:UIScrollViewIndicatorStyleDefault];
603
604 // XXX: do we still need to do this?
605 [[self navigationItem] setTitle:nil];
606 }
607
608 [self _didStartLoading];
609 }
610
611 - (NSURLRequest *) webView:(WebView *)view resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
612 #if LogBrowser
613 NSLog(@"resource:%@ willSendRequest:%@ redirectResponse:%@ fromDataSource:%@", identifier, request, response, source);
614 #endif
615
616 return request;
617 }
618
619 - (bool) webView:(WebView *)view shouldRunJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
620 return [self _allowJavaScriptPanel];
621 }
622
623 - (bool) webView:(WebView *)view shouldRunJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
624 return [self _allowJavaScriptPanel];
625 }
626
627 - (bool) webView:(WebView *)view shouldRunJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)text initiatedByFrame:(WebFrame *)frame {
628 return [self _allowJavaScriptPanel];
629 }
630
631 - (void) webViewClose:(WebView *)view {
632 [self close];
633 }
634 // }}}
635
636 - (void) close {
637 [[[self navigationController] parentOrPresentingViewController] dismissModalViewControllerAnimated:YES];
638 }
639
640 - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
641 NSString *context([alert context]);
642
643 if ([context isEqualToString:@"sensitive"]) {
644 switch (button) {
645 case 1:
646 sensitive_ = [NSNumber numberWithBool:YES];
647 break;
648
649 case 2:
650 sensitive_ = [NSNumber numberWithBool:NO];
651 break;
652 }
653
654 [alert dismissWithClickedButtonIndex:-1 animated:YES];
655 } else if ([context isEqualToString:@"challenge"]) {
656 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
657
658 switch (button) {
659 case 1: {
660 NSString *username([[alert textFieldAtIndex:0] text]);
661 NSString *password([[alert textFieldAtIndex:1] text]);
662
663 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
664
665 [sender useCredential:credential forAuthenticationChallenge:challenge_];
666 } break;
667
668 case 2:
669 [sender cancelAuthenticationChallenge:challenge_];
670 break;
671
672 _nodefault
673 }
674
675 challenge_ = nil;
676
677 [alert dismissWithClickedButtonIndex:-1 animated:YES];
678 } else if ([context isEqualToString:@"submit"]) {
679 if (button == [alert cancelButtonIndex]) {
680 } else if (button == [alert firstOtherButtonIndex]) {
681 if (request_ != nil) {
682 WebThreadLocked lock;
683 [[self webView] loadRequest:request_];
684 }
685 }
686
687 [alert dismissWithClickedButtonIndex:-1 animated:YES];
688 }
689 }
690
691 - (UIBarButtonItemStyle) rightButtonStyle {
692 if (style_ == nil) normal:
693 return UIBarButtonItemStylePlain;
694 else if ([style_ isEqualToString:@"Normal"])
695 return UIBarButtonItemStylePlain;
696 else if ([style_ isEqualToString:@"Highlighted"])
697 return UIBarButtonItemStyleDone;
698 else goto normal;
699 }
700
701 - (UIBarButtonItem *) customButton {
702 if (custom_ == nil)
703 return nil;
704 else if ((/*clang:*/id) custom_ == [NSNull null])
705 return (UIBarButtonItem *) [NSNull null];
706
707 return [[[UIBarButtonItem alloc]
708 initWithTitle:static_cast<NSString *>(custom_.operator NSObject *())
709 style:[self rightButtonStyle]
710 target:self
711 action:@selector(customButtonClicked)
712 ] autorelease];
713 }
714
715 - (UIBarButtonItem *) leftButton {
716 UINavigationItem *item([self navigationItem]);
717 if ([item backBarButtonItem] != nil && ![item hidesBackButton])
718 return nil;
719
720 if (UINavigationController *navigation = [self navigationController])
721 if ([[navigation parentOrPresentingViewController] modalViewController] == navigation)
722 return [[[UIBarButtonItem alloc]
723 initWithTitle:UCLocalize("CLOSE")
724 style:UIBarButtonItemStylePlain
725 target:self
726 action:@selector(close)
727 ] autorelease];
728
729 return nil;
730 }
731
732 - (void) applyLeftButton {
733 [[self navigationItem] setLeftBarButtonItem:[self leftButton]];
734 }
735
736 - (UIBarButtonItem *) rightButton {
737 return reloaditem_;
738 }
739
740 - (void) applyLoadingTitle {
741 [[self navigationItem] setTitle:UCLocalize("LOADING")];
742 }
743
744 - (void) layoutRightButton {
745 [[loadingitem_ view] addSubview:indicator_];
746 [[loadingitem_ view] bringSubviewToFront:indicator_];
747 }
748
749 - (void) applyRightButton {
750 if ([self isLoading]) {
751 [[self navigationItem] setRightBarButtonItem:loadingitem_ animated:YES];
752 [self performSelector:@selector(layoutRightButton) withObject:nil afterDelay:0];
753
754 [indicator_ startAnimating];
755 [self applyLoadingTitle];
756 } else {
757 [indicator_ stopAnimating];
758
759 UIBarButtonItem *button([self customButton]);
760 if (button == nil)
761 button = [self rightButton];
762 else if (button == (UIBarButtonItem *) [NSNull null])
763 button = nil;
764
765 [[self navigationItem] setRightBarButtonItem:button animated:YES];
766 }
767 }
768
769 - (void) didStartLoading {
770 // Overridden in subclasses.
771 }
772
773 - (void) _didStartLoading {
774 [self applyRightButton];
775
776 if ([loading_ count] != 1)
777 return;
778
779 if ([self retainsNetworkActivityIndicator])
780 [delegate_ retainNetworkActivityIndicator];
781
782 [self didStartLoading];
783 }
784
785 - (void) didFinishLoading {
786 // Overridden in subclasses.
787 }
788
789 - (void) _didFinishLoading {
790 if ([loading_ count] != 0)
791 return;
792
793 [self applyRightButton];
794 [[self navigationItem] setTitle:title_];
795
796 if ([self retainsNetworkActivityIndicator])
797 [delegate_ releaseNetworkActivityIndicator];
798
799 [self didFinishLoading];
800 }
801
802 - (bool) isLoading {
803 return [loading_ count] != 0;
804 }
805
806 - (id) initWithWidth:(float)width ofClass:(Class)_class {
807 if ((self = [super init]) != nil) {
808 width_ = width;
809 class_ = _class;
810
811 allowsNavigationAction_ = true;
812
813 loading_ = [NSMutableSet setWithCapacity:5];
814 indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
815
816 reloaditem_ = [[[UIBarButtonItem alloc]
817 initWithTitle:UCLocalize("RELOAD")
818 style:[self rightButtonStyle]
819 target:self
820 action:@selector(reloadButtonClicked)
821 ] autorelease];
822
823 loadingitem_ = [[[UIBarButtonItem alloc]
824 initWithTitle:@" "
825 style:UIBarButtonItemStylePlain
826 target:self
827 action:@selector(reloadButtonClicked)
828 ] autorelease];
829
830 indicator_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
831 [indicator_ setFrame:CGRectMake(15, 5, [indicator_ frame].size.width, [indicator_ frame].size.height)];
832 [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
833
834 [self applyLeftButton];
835 [self applyRightButton];
836 } return self;
837 }
838
839 - (NSString *) applicationNameForUserAgent {
840 return nil;
841 }
842
843 - (void) loadView {
844 CGRect bounds([[UIScreen mainScreen] applicationFrame]);
845
846 webview_ = [[[CyteWebView alloc] initWithFrame:bounds] autorelease];
847 [webview_ setDelegate:self];
848 [self setView:webview_];
849
850 if ([webview_ respondsToSelector:@selector(setDataDetectorTypes:)])
851 [webview_ setDataDetectorTypes:UIDataDetectorTypeAutomatic];
852 else
853 [webview_ setDetectsPhoneNumbers:NO];
854
855 [webview_ setScalesPageToFit:YES];
856
857 UIWebDocumentView *document([webview_ _documentView]);
858
859 // XXX: I think this improves scrolling; the hardcoded-ness sucks
860 [document setTileSize:CGSizeMake(320, 500)];
861
862 [document setBackgroundColor:[UIColor clearColor]];
863
864 // XXX: this is terribly (too?) expensive
865 [document setDrawsBackground:NO];
866
867 WebView *webview([document webView]);
868 WebPreferences *preferences([webview preferences]);
869
870 // XXX: I have no clue if I actually /want/ this modification
871 if ([webview respondsToSelector:@selector(_setLayoutInterval:)])
872 [webview _setLayoutInterval:0];
873 else if ([preferences respondsToSelector:@selector(_setLayoutInterval:)])
874 [preferences _setLayoutInterval:0];
875
876 [preferences setCacheModel:WebCacheModelDocumentBrowser];
877 [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
878
879 if ([preferences respondsToSelector:@selector(setOfflineWebApplicationCacheEnabled:)])
880 [preferences setOfflineWebApplicationCacheEnabled:YES];
881
882 if (NSString *agent = [self applicationNameForUserAgent])
883 [webview setApplicationNameForUserAgent:agent];
884
885 if ([webview respondsToSelector:@selector(setShouldUpdateWhileOffscreen:)])
886 [webview setShouldUpdateWhileOffscreen:NO];
887
888 #if LogMessages
889 if ([document respondsToSelector:@selector(setAllowsMessaging:)])
890 [document setAllowsMessaging:YES];
891 if ([webview respondsToSelector:@selector(_setAllowsMessaging:)])
892 [webview _setAllowsMessaging:YES];
893 #endif
894
895 if ([webview_ respondsToSelector:@selector(_scrollView)]) {
896 scroller_ = [webview_ _scrollView];
897
898 [scroller_ setDirectionalLockEnabled:YES];
899 [scroller_ setDecelerationRate:CYScrollViewDecelerationRateNormal];
900 [scroller_ setDelaysContentTouches:NO];
901
902 [scroller_ setCanCancelContentTouches:YES];
903 } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
904 UIScroller *scroller([webview_ _scroller]);
905 scroller_ = (UIScrollView *) scroller;
906
907 [scroller setDirectionalScrolling:YES];
908 // XXX: we might be better off /not/ setting this on older systems
909 [scroller setScrollDecelerationFactor:CYScrollViewDecelerationRateNormal]; /* 0.989324 */
910 [scroller setScrollHysteresis:0]; /* 8 */
911
912 [scroller setThumbDetectionEnabled:NO];
913
914 // use NO with UIApplicationUseLegacyEvents(YES)
915 [scroller setEventMode:YES];
916
917 // XXX: this is handled by setBounces, right?
918 //[scroller setAllowsRubberBanding:YES];
919 }
920
921 [scroller_ setFixedBackgroundPattern:YES];
922 [scroller_ setBackgroundColor:[UIColor clearColor]];
923 [scroller_ setClipsSubviews:YES];
924
925 [scroller_ setBounces:YES];
926 [scroller_ setScrollingEnabled:YES];
927 [scroller_ setShowBackgroundShadow:NO];
928
929 [self setViewportWidth:width_];
930
931 UITableView *table([[[UITableView alloc] initWithFrame:[webview_ bounds] style:UITableViewStyleGrouped] autorelease]);
932 [table setScrollsToTop:NO];
933 [webview_ insertSubview:table atIndex:0];
934
935 [table setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
936 [webview_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
937
938 ready_ = false;
939 }
940
941 - (void) releaseSubviews {
942 webview_ = nil;
943 scroller_ = nil;
944
945 [self releaseNetworkActivityIndicator];
946
947 [super releaseSubviews];
948 }
949
950 - (id) initWithWidth:(float)width {
951 return [self initWithWidth:width ofClass:[self class]];
952 }
953
954 - (id) init {
955 return [self initWithWidth:0];
956 }
957
958 - (id) initWithURL:(NSURL *)url {
959 if ((self = [self init]) != nil) {
960 [self setURL:url];
961 } return self;
962 }
963
964 - (id) initWithRequest:(NSURLRequest *)request {
965 if ((self = [self init]) != nil) {
966 [self setRequest:request];
967 } return self;
968 }
969
970 - (void) callFunction:(WebScriptObject *)function {
971 WebThreadLocked lock;
972
973 WebView *webview([[[self webView] _documentView] webView]);
974 WebFrame *frame([webview mainFrame]);
975
976 JSGlobalContextRef context([frame globalContext]);
977 JSObjectRef object([function JSObject]);
978 JSObjectCallAsFunction(context, object, NULL, 0, NULL, NULL);
979 }
980
981 - (void) reloadButtonClicked {
982 [self reloadURLWithCache:NO];
983 }
984
985 - (void) _customButtonClicked {
986 [self reloadButtonClicked];
987 }
988
989 - (void) customButtonClicked {
990 #if !AlwaysReload
991 if (function_ != nil)
992 [self callFunction:function_];
993 else
994 #endif
995 [self _customButtonClicked];
996 }
997
998 + (float) defaultWidth {
999 return 980;
1000 }
1001
1002 - (void) setNavigationBarStyle:(NSString *)name {
1003 UIBarStyle style;
1004 if ([name isEqualToString:@"Black"])
1005 style = UIBarStyleBlack;
1006 else
1007 style = UIBarStyleDefault;
1008
1009 [[[self navigationController] navigationBar] setBarStyle:style];
1010 }
1011
1012 - (void) setNavigationBarTintColor:(UIColor *)color {
1013 [[[self navigationController] navigationBar] setTintColor:color];
1014 }
1015
1016 - (void) setBadgeValue:(id)value {
1017 [[[self navigationController] tabBarItem] setBadgeValue:value];
1018 }
1019
1020 - (void) setHidesBackButton:(bool)value {
1021 [[self navigationItem] setHidesBackButton:value];
1022 [self applyLeftButton];
1023 }
1024
1025 - (void) setHidesBackButtonByNumber:(NSNumber *)value {
1026 [self setHidesBackButton:[value boolValue]];
1027 }
1028
1029 - (void) dispatchEvent:(NSString *)event {
1030 [[self webView] dispatchEvent:event];
1031 }
1032
1033 - (bool) hidesNavigationBar {
1034 return hidesNavigationBar_;
1035 }
1036
1037 - (void) _setHidesNavigationBar:(bool)value animated:(bool)animated {
1038 if (visible_)
1039 [[self navigationController] setNavigationBarHidden:(value && [self hidesNavigationBar]) animated:animated];
1040 }
1041
1042 - (void) setHidesNavigationBar:(bool)value {
1043 if (hidesNavigationBar_ != value) {
1044 hidesNavigationBar_ = value;
1045 [self _setHidesNavigationBar:YES animated:YES];
1046 }
1047 }
1048
1049 - (void) setHidesNavigationBarByNumber:(NSNumber *)value {
1050 [self setHidesNavigationBar:[value boolValue]];
1051 }
1052
1053 - (void) setScrollAlwaysBounceVertical:(bool)value {
1054 if ([webview_ respondsToSelector:@selector(_scrollView)]) {
1055 UIScrollView *scroller([webview_ _scrollView]);
1056 [scroller setAlwaysBounceVertical:value];
1057 } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
1058 //UIScroller *scroller([webview_ _scroller]);
1059 // XXX: I am sad here.
1060 } else return;
1061 }
1062
1063 - (void) setScrollAlwaysBounceVerticalNumber:(NSNumber *)value {
1064 [self setScrollAlwaysBounceVertical:[value boolValue]];
1065 }
1066
1067 - (void) setScrollIndicatorStyle:(UIScrollViewIndicatorStyle)style {
1068 if ([webview_ respondsToSelector:@selector(_scrollView)]) {
1069 UIScrollView *scroller([webview_ _scrollView]);
1070 [scroller setIndicatorStyle:style];
1071 } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
1072 UIScroller *scroller([webview_ _scroller]);
1073 [scroller setScrollerIndicatorStyle:style];
1074 } else return;
1075 }
1076
1077 - (void) setScrollIndicatorStyleWithName:(NSString *)style {
1078 UIScrollViewIndicatorStyle value;
1079
1080 if (false);
1081 else if ([style isEqualToString:@"default"])
1082 value = UIScrollViewIndicatorStyleDefault;
1083 else if ([style isEqualToString:@"black"])
1084 value = UIScrollViewIndicatorStyleBlack;
1085 else if ([style isEqualToString:@"white"])
1086 value = UIScrollViewIndicatorStyleWhite;
1087 else return;
1088
1089 [self setScrollIndicatorStyle:value];
1090 }
1091
1092 - (void) viewWillAppear:(BOOL)animated {
1093 visible_ = true;
1094
1095 if ([self hidesNavigationBar])
1096 [self _setHidesNavigationBar:YES animated:animated];
1097
1098 // XXX: why isn't this evern called automatically?
1099 [[self webView] setNeedsLayout];
1100
1101 [self dispatchEvent:@"CydiaViewWillAppear"];
1102 [super viewWillAppear:animated];
1103 }
1104
1105 - (void) viewDidAppear:(BOOL)animated {
1106 [super viewDidAppear:animated];
1107 [self dispatchEvent:@"CydiaViewDidAppear"];
1108 }
1109
1110 - (void) viewWillDisappear:(BOOL)animated {
1111 [self dispatchEvent:@"CydiaViewWillDisappear"];
1112 [super viewWillDisappear:animated];
1113
1114 if ([self hidesNavigationBar])
1115 [self _setHidesNavigationBar:NO animated:animated];
1116
1117 visible_ = false;
1118 }
1119
1120 - (void) viewDidDisappear:(BOOL)animated {
1121 [super viewDidDisappear:animated];
1122 [self dispatchEvent:@"CydiaViewDidDisappear"];
1123 }
1124
1125 @end