]> git.saurik.com Git - cydia.git/blame - CyteKit/WebViewController.mm
Trial move some key global variables into CyteKit.
[cydia.git] / CyteKit / WebViewController.mm
CommitLineData
819a0ab1 1#include "CyteKit/UCPlatform.h"
c21004b9 2
cfc530e6 3#include "CyteKit/IndirectDelegate.h"
d458596e 4#include "CyteKit/Localize.h"
63755c48 5#include "CyteKit/MFMailComposeViewController-MailToURL.h"
b4fd1197 6#include "CyteKit/RegEx.hpp"
4bcafc01 7#include "CyteKit/WebThreadLocked.hpp"
63755c48
JF
8#include "CyteKit/WebViewController.h"
9
10#include "iPhonePrivate.h"
f8c9fd4c 11#include <Menes/ObjectHandle.h>
43f3d7f6 12
c21004b9 13//#include <QuartzCore/CALayer.h>
43f3d7f6
JF
14// XXX: fix the minimum requirement
15extern NSString * const kCAFilterNearest;
22f8bed9 16
caa427d1 17#include <WebCore/WebCoreThread.h>
c21004b9 18
bf7c998c
JF
19#include <dlfcn.h>
20#include <objc/runtime.h>
21
a3d01a76
JF
22#include "Substrate.hpp"
23
adcb0422 24#define ForSaurik 0
eb09425a 25#define DefaultTimeout_ 120.0
bfc87a4d 26
0815487b
JF
27#define ShowInternals 0
28#define LogBrowser 0
72bdb258 29#define LogMessages 0
0815487b
JF
30
31#define lprintf(args...) fprintf(stderr, args)
32
7c80833f
JF
33JSValueRef (*$JSObjectCallAsFunction)(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef *);
34
2b2a4e33
JF
35// XXX: centralize these special class things to some file or mechanism?
36static Class $MFMailComposeViewController;
37
9c1605e2
JF
38float CYScrollViewDecelerationRateNormal;
39
aa1e1906
JF
40@interface WebFrame (Cydia)
41- (void) cydia$updateHeight;
42@end
43
dffe1eb1
JF
44@implementation WebFrame (Cydia)
45
46- (NSString *) description {
47 return [NSString stringWithFormat:@"<%s: %p, %@>", class_getName([self class]), self, [[[([self provisionalDataSource] ?: [self dataSource]) request] URL] absoluteString]];
48}
49
aa1e1906
JF
50- (void) cydia$updateHeight {
51 [[[self frameElement] style]
52 setProperty:@"height"
53 value:[NSString stringWithFormat:@"%dpx",
54 [[[self DOMDocument] body] scrollHeight]]
55 priority:nil];
56}
57
dffe1eb1
JF
58@end
59
aa5d0de7 60/* Indirect Delegate {{{ */
aa5d0de7
JF
61@implementation IndirectDelegate
62
353dda5b
JF
63- (id) delegate {
64 return delegate_;
65}
66
aa5d0de7
JF
67- (void) setDelegate:(id)delegate {
68 delegate_ = delegate;
69}
70
71- (id) initWithDelegate:(id)delegate {
72 delegate_ = delegate;
73 return self;
74}
75
caa427d1
JF
76- (IMP) methodForSelector:(SEL)sel {
77 if (IMP method = [super methodForSelector:sel])
78 return method;
79 fprintf(stderr, "methodForSelector:[%s] == NULL\n", sel_getName(sel));
80 return NULL;
81}
82
aa5d0de7 83- (BOOL) respondsToSelector:(SEL)sel {
caa427d1
JF
84 if ([super respondsToSelector:sel])
85 return YES;
0815487b 86
caa427d1 87 // XXX: WebThreadCreateNSInvocation returns nil
0815487b
JF
88
89#if ShowInternals
c66799b9 90 fprintf(stderr, "[%s]R?%s\n", class_getName(object_getClass(self)), sel_getName(sel));
0815487b
JF
91#endif
92
caa427d1 93 return delegate_ == nil ? NO : [delegate_ respondsToSelector:sel];
aa5d0de7
JF
94}
95
96- (NSMethodSignature *) methodSignatureForSelector:(SEL)sel {
caa427d1
JF
97 if (NSMethodSignature *method = [super methodSignatureForSelector:sel])
98 return method;
0815487b
JF
99
100#if ShowInternals
c66799b9 101 fprintf(stderr, "[%s]S?%s\n", class_getName(object_getClass(self)), sel_getName(sel));
0815487b
JF
102#endif
103
aa5d0de7
JF
104 if (delegate_ != nil)
105 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
106 return sig;
0815487b 107
aa5d0de7
JF
108 // XXX: I fucking hate Apple so very very bad
109 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
110}
111
112- (void) forwardInvocation:(NSInvocation *)inv {
113 SEL sel = [inv selector];
114 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
115 [inv invokeWithTarget:delegate_];
116}
117
118@end
119/* }}} */
120
f8c9fd4c
JF
121@implementation CyteWebViewController {
122 _H<CyteWebView, 1> webview_;
123 _transient UIScrollView *scroller_;
124
125 _H<UIActivityIndicatorView> indicator_;
126 _H<IndirectDelegate, 1> indirect_;
127 _H<NSURLAuthenticationChallenge> challenge_;
128
129 bool error_;
130 _H<NSURLRequest> request_;
131 bool ready_;
132
133 _transient NSNumber *sensitive_;
134 _H<NSURL> appstore_;
135
136 _H<NSString> title_;
137 _H<NSMutableSet> loading_;
138
139 _H<NSMutableSet> registered_;
140 _H<NSTimer> timer_;
141
142 // XXX: NSString * or UIImage *
143 _H<NSObject> custom_;
144 _H<NSString> style_;
145
146 _H<WebScriptObject> function_;
147
148 float width_;
149 Class class_;
150
151 _H<UIBarButtonItem> reloaditem_;
152 _H<UIBarButtonItem> loadingitem_;
153
154 bool visible_;
155 bool hidesNavigationBar_;
156 bool allowsNavigationAction_;
157}
2634b249
JF
158
159#if ShowInternals
819a0ab1 160#include "CyteKit/UCInternal.h"
22f8bed9
JF
161#endif
162
2634b249
JF
163+ (void) _initialize {
164 [WebPreferences _setInitialDefaultTextEncodingToSystemEncoding];
9c1605e2 165
7c80833f
JF
166 void *js(NULL);
167 if (js == NULL)
168 js = dlopen("/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore", RTLD_GLOBAL | RTLD_LAZY);
169 if (js == NULL)
170 js = dlopen("/System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore", RTLD_GLOBAL | RTLD_LAZY);
171 if (js != NULL)
172 $JSObjectCallAsFunction = reinterpret_cast<JSValueRef (*)(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef *)>(dlsym(js, "JSObjectCallAsFunction"));
173
2b2a4e33
JF
174 dlopen("/System/Library/Frameworks/MessageUI.framework/MessageUI", RTLD_GLOBAL | RTLD_LAZY);
175 $MFMailComposeViewController = objc_getClass("MFMailComposeViewController");
176
09e03d23 177 if (CGFloat *_UIScrollViewDecelerationRateNormal = reinterpret_cast<CGFloat *>(dlsym(RTLD_DEFAULT, "UIScrollViewDecelerationRateNormal")))
9c1605e2
JF
178 CYScrollViewDecelerationRateNormal = *_UIScrollViewDecelerationRateNormal;
179 else // XXX: this actually might be fast on some older systems: we should look into this
180 CYScrollViewDecelerationRateNormal = 0.998;
2634b249
JF
181}
182
2713be8e
JF
183- (bool) retainsNetworkActivityIndicator {
184 return true;
185}
186
8b74eaff
JF
187- (void) releaseNetworkActivityIndicator {
188 if ([loading_ count] != 0) {
189 [loading_ removeAllObjects];
190
191 if ([self retainsNetworkActivityIndicator])
f8c9fd4c 192 [self.delegate releaseNetworkActivityIndicator];
8b74eaff
JF
193 }
194}
195
2634b249
JF
196- (void) dealloc {
197#if LogBrowser
09e89a8a 198 NSLog(@"[CyteWebViewController dealloc]");
2634b249
JF
199#endif
200
8b74eaff 201 [self releaseNetworkActivityIndicator];
bc11cf5b 202
22f8bed9
JF
203 [super dealloc];
204}
205
e3e0246d
JF
206- (NSString *) description {
207 return [NSString stringWithFormat:@"<%s: %p, %@>", class_getName([self class]), self, [[request_ URL] absoluteString]];
208}
209
6f837280
JF
210- (CyteWebView *) webView {
211 return (CyteWebView *) [self view];
212}
213
f8c9fd4c
JF
214- (CyteWebViewController *) indirect {
215 return (CyteWebViewController *) (IndirectDelegate *) indirect_;
216}
217
dd48f2e6
JF
218- (NSURL *) URLWithURL:(NSURL *)url {
219 return url;
220}
eb09425a 221
f050e4d9
JF
222- (NSURLRequest *) requestWithURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy referrer:(NSString *)referrer {
223 NSMutableURLRequest *request([NSMutableURLRequest
dd48f2e6
JF
224 requestWithURL:[self URLWithURL:url]
225 cachePolicy:policy
eb09425a 226 timeoutInterval:DefaultTimeout_
f050e4d9
JF
227 ]);
228
229 [request setValue:referrer forHTTPHeaderField:@"Referer"];
230
231 return request;
eb09425a
JF
232}
233
e8eda555 234- (void) setRequest:(NSURLRequest *)request {
dd48f2e6 235 _assert(request_ == nil);
e8eda555
JF
236 request_ = request;
237}
238
f8c9fd4c
JF
239- (NSURLRequest *) request {
240 return request_;
241}
242
e8eda555 243- (void) setURL:(NSURL *)url {
f050e4d9
JF
244 [self setURL:url withReferrer:nil];
245}
246
247- (void) setURL:(NSURL *)url withReferrer:(NSString *)referrer {
248 [self setRequest:[self requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy referrer:referrer]];
dd48f2e6
JF
249}
250
22f8bed9 251- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
f050e4d9 252 [self loadRequest:[self requestWithURL:url cachePolicy:policy referrer:nil]];
22f8bed9
JF
253}
254
255- (void) loadURL:(NSURL *)url {
256 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
257}
258
22f8bed9 259- (void) loadRequest:(NSURLRequest *)request {
0352f238
JF
260#if LogBrowser
261 NSLog(@"loadRequest:%@", request);
262#endif
263
fe468f45 264 error_ = false;
d9fc1d37 265 ready_ = true;
caa427d1 266
0893a034 267 WebThreadLocked lock;
6f837280 268 [[self webView] loadRequest:request];
22f8bed9
JF
269}
270
b13b8664 271- (void) reloadURLWithCache:(BOOL)cache {
22f8bed9
JF
272 if (request_ == nil)
273 return;
274
b13b8664
JF
275 NSMutableURLRequest *request([request_ mutableCopy]);
276 [request setCachePolicy:(cache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData)];
277
a374f380 278 request_ = request;
b13b8664 279
d69dbfc5 280 if (cache || [request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil)
fe468f45 281 [self loadRequest:request_];
22f8bed9 282 else {
79ed082a 283 UIAlertView *alert = [[[UIAlertView alloc]
43f3d7f6 284 initWithTitle:UCLocalize("RESUBMIT_FORM")
79ed082a 285 message:nil
22f8bed9 286 delegate:self
79ed082a 287 cancelButtonTitle:UCLocalize("CANCEL")
1aa29546
JF
288 otherButtonTitles:
289 UCLocalize("SUBMIT"),
290 nil
22f8bed9 291 ] autorelease];
2634b249 292
79ed082a
GP
293 [alert setContext:@"submit"];
294 [alert show];
22f8bed9
JF
295 }
296}
297
eb09425a
JF
298- (void) reloadData {
299 [super reloadData];
d9fc1d37
JF
300
301 if (ready_)
302 [self dispatchEvent:@"CydiaReloadData"];
303 else
304 [self reloadURLWithCache:YES];
eb09425a
JF
305}
306
22f8bed9 307- (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
46d3a5cf
JF
308 custom_ = button;
309 style_ = style;
310 function_ = function;
12b59862 311
70a9ff4e 312 [self performSelectorOnMainThread:@selector(applyRightButton) withObject:nil waitUntilDone:NO];
22f8bed9
JF
313}
314
315- (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
46d3a5cf
JF
316 custom_ = button;
317 style_ = style;
318 function_ = function;
22f8bed9 319
ed5566c7
JF
320 [self performSelectorOnMainThread:@selector(applyRightButton) withObject:nil waitUntilDone:NO];
321}
12b59862 322
ed5566c7
JF
323- (void) removeButton {
324 custom_ = [NSNull null];
70a9ff4e 325 [self performSelectorOnMainThread:@selector(applyRightButton) withObject:nil waitUntilDone:NO];
22f8bed9 326}
22f8bed9 327
8e3b68d4
JF
328- (void) scrollToBottomAnimated:(NSNumber *)animated {
329 CGSize size([scroller_ contentSize]);
330 CGPoint offset([scroller_ contentOffset]);
331 CGRect frame([scroller_ frame]);
332
333 if (size.height - offset.y < frame.size.height + 20.f) {
334 CGRect rect = {{0, size.height-1}, {size.width, 1}};
335 [scroller_ scrollRectToVisible:rect animated:[animated boolValue]];
336 }
337}
338
7e37a676 339- (void) _setViewportWidth {
6f837280 340 [[[self webView] _documentView] setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
7e37a676
JF
341}
342
2634b249
JF
343- (void) setViewportWidth:(float)width {
344 width_ = width != 0 ? width : [[self class] defaultWidth];
7e37a676
JF
345 [self _setViewportWidth];
346}
347
8dbdaafa
JF
348- (void) _setViewportWidthOnMainThread:(NSNumber *)width {
349 [self setViewportWidth:[width floatValue]];
350}
351
352- (void) setViewportWidthOnMainThread:(float)width {
353 [self performSelectorOnMainThread:@selector(_setViewportWidthOnMainThread:) withObject:[NSNumber numberWithFloat:width] waitUntilDone:NO];
354}
355
7e37a676
JF
356- (void) webViewUpdateViewSettings:(UIWebView *)view {
357 [self _setViewportWidth];
22f8bed9
JF
358}
359
2b2a4e33
JF
360- (void) mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
361 [self dismissModalViewControllerAnimated:YES];
362}
363
22485d93
JF
364- (void) _setupMail:(MFMailComposeViewController *)controller {
365}
366
2634b249 367- (void) _openMailToURL:(NSURL *)url {
2b2a4e33
JF
368 if ($MFMailComposeViewController != nil && [$MFMailComposeViewController canSendMail]) {
369 MFMailComposeViewController *controller([[[$MFMailComposeViewController alloc] init] autorelease]);
370 [controller setMailComposeDelegate:self];
371
372 [controller setMailToURL:url];
373
22485d93
JF
374 [self _setupMail:controller];
375
2b2a4e33
JF
376 [self presentModalViewController:controller animated:YES];
377 return;
378 }
379
8ea72491
JF
380 UIApplication *app([UIApplication sharedApplication]);
381 if ([app respondsToSelector:@selector(openURL:asPanel:)])
382 [app openURL:url asPanel:YES];
383 else
384 [app openURL:url];
22f8bed9
JF
385}
386
2634b249
JF
387- (bool) _allowJavaScriptPanel {
388 return true;
22f8bed9
JF
389}
390
8366df5e
JF
391- (bool) allowsNavigationAction {
392 return allowsNavigationAction_;
393}
394
395- (void) setAllowsNavigationAction:(bool)value {
396 allowsNavigationAction_ = value;
397}
398
399- (void) setAllowsNavigationActionByNumber:(NSNumber *)value {
400 [self setAllowsNavigationAction:[value boolValue]];
52498c7e
JF
401}
402
8d497e2a
JF
403- (void) popViewControllerWithNumber:(NSNumber *)value {
404 UINavigationController *navigation([self navigationController]);
405 if ([navigation topViewController] == self)
406 [navigation popViewControllerAnimated:[value boolValue]];
407}
408
2634b249 409- (void) _didFailWithError:(NSError *)error forFrame:(WebFrame *)frame {
8b74eaff
JF
410 NSValue *object([NSValue valueWithNonretainedObject:frame]);
411 if (![loading_ containsObject:object])
412 return;
413 [loading_ removeObject:object];
414
2634b249 415 [self _didFinishLoading];
22f8bed9 416
46b423a7 417 if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled)
22f8bed9
JF
418 return;
419
fe2d3954 420 if ([[error domain] isEqualToString:WebKitErrorDomain] && [error code] == WebKitErrorFrameLoadInterruptedByPolicyChange) {
5bb0da03 421 request_ = nil;
fe2d3954
JF
422 return;
423 }
424
caa427d1 425 if ([frame parentFrame] == nil) {
2634b249
JF
426 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
427 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
428 [[error localizedDescription] stringByAddingPercentEscapes]
429 ]]];
22f8bed9 430
2634b249
JF
431 error_ = true;
432 }
433}
caa427d1 434
f050e4d9
JF
435- (void) pushRequest:(NSURLRequest *)request forAction:(NSDictionary *)action asPop:(bool)pop {
436 WebFrame *frame(nil);
437 if (NSDictionary *WebActionElement = [action objectForKey:@"WebActionElementKey"])
438 frame = [WebActionElement objectForKey:@"WebElementFrame"];
439 if (frame == nil)
440 frame = [[[[self webView] _documentView] webView] mainFrame];
441
442 WebDataSource *source([frame provisionalDataSource] ?: [frame dataSource]);
443 NSString *referrer([request valueForHTTPHeaderField:@"Referer"] ?: [[[source request] URL] absoluteString]);
444
2634b249 445 NSURL *url([request URL]);
a5938ea5 446
028dbd1c 447 // XXX: filter to internal usage?
f8c9fd4c 448 CyteViewController *page([self.delegate pageForURL:url forExternal:NO withReferrer:referrer]);
2fad210a 449
2634b249 450 if (page == nil) {
09e89a8a 451 CyteWebViewController *browser([[[class_ alloc] init] autorelease]);
0620c393 452 [browser setRequest:request];
2634b249 453 page = browser;
bc11cf5b 454 }
ce041f4f 455
f8c9fd4c
JF
456 [page setDelegate:self.delegate];
457 [page setPageColor:self.pageColor];
b5e7eebb 458
52498c7e 459 if (!pop) {
2634b249 460 [[self navigationItem] setTitle:title_];
b5e7eebb 461
2634b249
JF
462 [[self navigationController] pushViewController:page animated:YES];
463 } else {
2e26757e 464 UINavigationController *navigation([[[UINavigationController alloc] initWithRootViewController:page] autorelease]);
22f8bed9 465
f8c9fd4c 466 [navigation setDelegate:self.delegate];
22f8bed9 467
2634b249
JF
468 [[page navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
469 initWithTitle:UCLocalize("CLOSE")
470 style:UIBarButtonItemStylePlain
471 target:page
472 action:@selector(close)
473 ] autorelease]];
474
45e66037 475 [[self navigationController] presentModalViewController:navigation animated:YES];
2634b249 476 }
52498c7e
JF
477}
478
1ee69bb4 479// CyteWebViewDelegate {{{
52498c7e
JF
480- (void) webView:(WebView *)view addMessageToConsole:(NSDictionary *)message {
481#if LogMessages
b4fd1197 482 static RegEx irritating("(?"
c8f62968
JF
483 ":" "The page at .* displayed insecure content from .*\\."
484 "|" "Unsafe JavaScript attempt to access frame with URL .* from frame with URL .*\\. Domains, protocols and ports must match\\."
b4fd1197 485 ")\\n");
c8f62968 486
c81b955f
JF
487 if (NSString *data = [message objectForKey:@"message"])
488 if (irritating(data))
489 return;
490
52498c7e
JF
491 NSLog(@"addMessageToConsole:%@", message);
492#endif
493}
494
495- (void) webView:(WebView *)view decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
496#if LogBrowser
f050e4d9 497 NSLog(@"decidePolicyForNavigationAction:%@ request:%@ %@ frame:%@", action, request, [request allHTTPHeaderFields], frame);
52498c7e
JF
498#endif
499
1002f4d9 500 NSURL *url(request == nil ? nil : [request URL]);
c623466b
JF
501 NSString *scheme([[url scheme] lowercaseString]);
502 NSString *absolute([[url absoluteString] lowercaseString]);
503
504 if (
505 [scheme isEqualToString:@"itms"] ||
506 [scheme isEqualToString:@"itmss"] ||
507 [scheme isEqualToString:@"itms-apps"] ||
508 [scheme isEqualToString:@"itms-appss"] ||
509 [absolute hasPrefix:@"http://itunes.apple.com/"] ||
510 [absolute hasPrefix:@"https://itunes.apple.com/"] ||
511 false) {
1002f4d9
JF
512 appstore_ = url;
513
514 UIAlertView *alert = [[[UIAlertView alloc]
515 initWithTitle:UCLocalize("APP_STORE_REDIRECT")
516 message:nil
517 delegate:self
518 cancelButtonTitle:UCLocalize("CANCEL")
519 otherButtonTitles:
520 UCLocalize("ALLOW"),
521 nil
522 ] autorelease];
523
524 [alert setContext:@"itmsappss"];
525 [alert show];
526
527 [listener ignore];
528 return;
529 }
530
52498c7e
JF
531 if ([frame parentFrame] == nil) {
532 if (!error_) {
d323285e 533 if (request_ != nil && ![[request_ URL] isEqual:url] && ![self allowsNavigationAction]) {
8366df5e 534 if (url != nil)
f050e4d9 535 [self pushRequest:request forAction:action asPop:NO];
52498c7e
JF
536 [listener ignore];
537 }
538 }
539 }
540}
541
d323285e 542- (void) webView:(WebView *)view didDecidePolicy:(CYWebPolicyDecision)decision forNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame {
77d4e15b 543#if LogBrowser
c66799b9 544 NSLog(@"didDecidePolicy:%u forNavigationAction:%@ request:%@ %@ frame:%@", decision, action, request, [request allHTTPHeaderFields], frame);
77d4e15b
JF
545#endif
546
547 if ([frame parentFrame] == nil) {
548 switch (decision) {
549 case CYWebPolicyDecisionIgnore:
550 if ([[request_ URL] isEqual:[request URL]])
551 request_ = nil;
552 break;
553
554 case CYWebPolicyDecisionUse:
555 if (!error_)
556 request_ = request;
557 break;
558
559 default:
560 break;
561 }
562 }
d323285e
JF
563}
564
f050e4d9 565- (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
52498c7e 566#if LogBrowser
f050e4d9 567 NSLog(@"decidePolicyForNewWindowAction:%@ request:%@ %@ newFrameName:%@", action, request, [request allHTTPHeaderFields], name);
52498c7e
JF
568#endif
569
570 NSURL *url([request URL]);
571 if (url == nil)
572 return;
573
f050e4d9 574 if ([name isEqualToString:@"_open"])
f8c9fd4c 575 [self.delegate openURL:url];
52498c7e
JF
576 else {
577 NSString *scheme([[url scheme] lowercaseString]);
578 if ([scheme isEqualToString:@"mailto"])
579 [self _openMailToURL:url];
580 else
f050e4d9 581 [self pushRequest:request forAction:action asPop:[name isEqualToString:@"_popup"]];
52498c7e 582 }
22f8bed9 583
2634b249 584 [listener ignore];
22f8bed9
JF
585}
586
2634b249 587- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
c215c5ed
JF
588#if LogBrowser
589 NSLog(@"didClearWindowObject:%@ forFrame:%@", window, frame);
590#endif
22f8bed9
JF
591}
592
64ffa332
JF
593- (void) webView:(WebView *)view didCommitLoadForFrame:(WebFrame *)frame {
594#if LogBrowser
595 NSLog(@"didCommitLoadForFrame:%@", frame);
596#endif
597
598 if ([frame parentFrame] == nil) {
599 }
600}
601
2634b249
JF
602- (void) webView:(WebView *)view didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
603#if LogBrowser
604 NSLog(@"didFailLoadWithError:%@ forFrame:%@", error, frame);
605#endif
606
607 [self _didFailWithError:error forFrame:frame];
22f8bed9
JF
608}
609
2634b249
JF
610- (void) webView:(WebView *)view didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
611#if LogBrowser
612 NSLog(@"didFailProvisionalLoadWithError:%@ forFrame:%@", error, frame);
613#endif
614
615 [self _didFailWithError:error forFrame:frame];
22f8bed9
JF
616}
617
2634b249 618- (void) webView:(WebView *)view didFinishLoadForFrame:(WebFrame *)frame {
8b74eaff
JF
619 NSValue *object([NSValue valueWithNonretainedObject:frame]);
620 if (![loading_ containsObject:object])
621 return;
622 [loading_ removeObject:object];
22f8bed9 623
caa427d1 624 if ([frame parentFrame] == nil) {
22f8bed9 625 if (DOMDocument *document = [frame DOMDocument])
4cc9e99a 626 if (DOMNodeList *bodies = [document getElementsByTagName:@"body"])
96f3833b 627 for (DOMHTMLBodyElement *body in (id) bodies) {
22f8bed9
JF
628 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
629
65ea9562 630 UIColor *uic(nil);
22f8bed9
JF
631
632 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
fe468f45
JF
633 if ([color primitiveType] == DOM_CSS_RGBCOLOR) {
634 DOMRGBColor *rgb([color getRGBColorValue]);
635
636 float red([[rgb red] getFloatValue:DOM_CSS_NUMBER]);
637 float green([[rgb green] getFloatValue:DOM_CSS_NUMBER]);
638 float blue([[rgb blue] getFloatValue:DOM_CSS_NUMBER]);
639 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
640
d32f96be
JF
641 if (alpha == 1)
642 uic = [UIColor
643 colorWithRed:(red / 255)
644 green:(green / 255)
645 blue:(blue / 255)
646 alpha:alpha
647 ];
22f8bed9
JF
648 }
649 }
650
77259cab 651 [super setPageColor:uic];
f8c9fd4c 652 [scroller_ setBackgroundColor:self.pageColor];
22f8bed9
JF
653 break;
654 }
655 }
656
2634b249 657 [self _didFinishLoading];
22f8bed9
JF
658}
659
2634b249
JF
660- (void) webView:(WebView *)view didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
661 if ([frame parentFrame] != nil)
662 return;
caa427d1 663
7b33d201 664 title_ = title;
3931b718 665
2634b249
JF
666 [[self navigationItem] setTitle:title_];
667}
22f8bed9 668
2634b249 669- (void) webView:(WebView *)view didStartProvisionalLoadForFrame:(WebFrame *)frame {
a86042fa
JF
670#if LogBrowser
671 NSLog(@"didStartProvisionalLoadForFrame:%@", frame);
672#endif
673
2634b249 674 [loading_ addObject:[NSValue valueWithNonretainedObject:frame]];
7592e053 675
caa427d1 676 if ([frame parentFrame] == nil) {
7b33d201 677 title_ = nil;
46d3a5cf
JF
678 custom_ = nil;
679 style_ = nil;
680 function_ = nil;
2634b249 681
aa1e1906
JF
682 [registered_ removeAllObjects];
683 timer_ = nil;
684
622a9912
JF
685 allowsNavigationAction_ = true;
686
5cdfcd6f 687 [self setHidesNavigationBar:NO];
db698f42 688 [self setScrollAlwaysBounceVertical:true];
4886cc81 689 [self setScrollIndicatorStyle:UIScrollViewIndicatorStyleDefault];
5cdfcd6f 690
2634b249
JF
691 // XXX: do we still need to do this?
692 [[self navigationItem] setTitle:nil];
caa427d1 693 }
fe468f45 694
2634b249 695 [self _didStartLoading];
caa427d1
JF
696}
697
754456f5
JF
698- (void) webView:(WebView *)view resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
699 challenge_ = [challenge retain];
700
701 NSURLProtectionSpace *space([challenge protectionSpace]);
702 NSString *realm([space realm]);
703 if (realm == nil)
704 realm = @"";
705
706 UIAlertView *alert = [[[UIAlertView alloc]
707 initWithTitle:realm
708 message:nil
709 delegate:self
710 cancelButtonTitle:UCLocalize("CANCEL")
711 otherButtonTitles:UCLocalize("LOGIN"), nil
712 ] autorelease];
713
714 [alert setContext:@"challenge"];
715 [alert setNumberOfRows:1];
716
717 [alert addTextFieldWithValue:@"" label:UCLocalize("USERNAME")];
718 [alert addTextFieldWithValue:@"" label:UCLocalize("PASSWORD")];
719
720 UITextField *username([alert textFieldAtIndex:0]); {
63755c48 721 NSObject<UITextInputTraits> *traits([username textInputTraits]);
754456f5
JF
722 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
723 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
724 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
725 [traits setReturnKeyType:UIReturnKeyNext];
726 }
727
728 UITextField *password([alert textFieldAtIndex:1]); {
63755c48 729 NSObject<UITextInputTraits> *traits([password textInputTraits]);
754456f5
JF
730 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
731 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
732 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
733 // XXX: UIReturnKeyDone
734 [traits setReturnKeyType:UIReturnKeyNext];
735 [traits setSecureTextEntry:YES];
736 }
737
738 [alert show];
739}
740
2634b249 741- (NSURLRequest *) webView:(WebView *)view resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
0352f238
JF
742#if LogBrowser
743 NSLog(@"resource:%@ willSendRequest:%@ redirectResponse:%@ fromDataSource:%@", identifier, request, response, source);
744#endif
745
2634b249 746 return request;
22f8bed9
JF
747}
748
92de61aa
JF
749- (NSURLRequest *) webThreadWebView:(WebView *)view resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
750#if LogBrowser
751 NSLog(@"resource:%@ willSendRequest:%@ redirectResponse:%@ fromDataSource:%@", identifier, request, response, source);
752#endif
753
754 return request;
755}
756
2634b249
JF
757- (bool) webView:(WebView *)view shouldRunJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
758 return [self _allowJavaScriptPanel];
22f8bed9
JF
759}
760
2634b249
JF
761- (bool) webView:(WebView *)view shouldRunJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
762 return [self _allowJavaScriptPanel];
600d005d
JF
763}
764
2634b249
JF
765- (bool) webView:(WebView *)view shouldRunJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)text initiatedByFrame:(WebFrame *)frame {
766 return [self _allowJavaScriptPanel];
600d005d
JF
767}
768
2634b249
JF
769- (void) webViewClose:(WebView *)view {
770 [self close];
600d005d 771}
2634b249 772// }}}
600d005d 773
2634b249 774- (void) close {
19f2d77f 775 [[[self navigationController] parentOrPresentingViewController] dismissModalViewControllerAnimated:YES];
600d005d
JF
776}
777
2634b249
JF
778- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
779 NSString *context([alert context]);
600d005d 780
2634b249
JF
781 if ([context isEqualToString:@"sensitive"]) {
782 switch (button) {
783 case 1:
784 sensitive_ = [NSNumber numberWithBool:YES];
785 break;
600d005d 786
2634b249
JF
787 case 2:
788 sensitive_ = [NSNumber numberWithBool:NO];
789 break;
790 }
600d005d 791
2634b249
JF
792 [alert dismissWithClickedButtonIndex:-1 animated:YES];
793 } else if ([context isEqualToString:@"challenge"]) {
794 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
caa427d1 795
754456f5
JF
796 if (button == [alert cancelButtonIndex])
797 [sender cancelAuthenticationChallenge:challenge_];
798 else if (button == [alert firstOtherButtonIndex]) {
799 NSString *username([[alert textFieldAtIndex:0] text]);
800 NSString *password([[alert textFieldAtIndex:1] text]);
600d005d 801
754456f5 802 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
600d005d 803
754456f5 804 [sender useCredential:credential forAuthenticationChallenge:challenge_];
2634b249 805 }
600d005d 806
2634b249 807 challenge_ = nil;
600d005d 808
1002f4d9
JF
809 [alert dismissWithClickedButtonIndex:-1 animated:YES];
810 } else if ([context isEqualToString:@"itmsappss"]) {
811 if (button == [alert cancelButtonIndex]) {
812 } else if (button == [alert firstOtherButtonIndex]) {
f8c9fd4c 813 [self.delegate openURL:appstore_];
1002f4d9
JF
814 }
815
2634b249
JF
816 [alert dismissWithClickedButtonIndex:-1 animated:YES];
817 } else if ([context isEqualToString:@"submit"]) {
75b95256
JF
818 if (button == [alert cancelButtonIndex]) {
819 } else if (button == [alert firstOtherButtonIndex]) {
820 if (request_ != nil) {
0893a034 821 WebThreadLocked lock;
6f837280 822 [[self webView] loadRequest:request_];
75b95256 823 }
2634b249 824 }
600d005d 825
2634b249
JF
826 [alert dismissWithClickedButtonIndex:-1 animated:YES];
827 }
600d005d
JF
828}
829
2634b249
JF
830- (UIBarButtonItemStyle) rightButtonStyle {
831 if (style_ == nil) normal:
832 return UIBarButtonItemStylePlain;
833 else if ([style_ isEqualToString:@"Normal"])
834 return UIBarButtonItemStylePlain;
835 else if ([style_ isEqualToString:@"Highlighted"])
836 return UIBarButtonItemStyleDone;
837 else goto normal;
600d005d
JF
838}
839
2634b249 840- (UIBarButtonItem *) customButton {
29756674 841 if (custom_ == nil)
f274bb0c 842 return [self rightButton];
4cc9e99a 843 else if ((/*clang:*/id) custom_ == [NSNull null])
f274bb0c 844 return nil;
29756674
JF
845
846 return [[[UIBarButtonItem alloc]
ed5566c7 847 initWithTitle:static_cast<NSString *>(custom_.operator NSObject *())
2634b249
JF
848 style:[self rightButtonStyle]
849 target:self
850 action:@selector(customButtonClicked)
851 ] autorelease];
caa427d1
JF
852}
853
e6124cb6
JF
854- (UIBarButtonItem *) leftButton {
855 UINavigationItem *item([self navigationItem]);
856 if ([item backBarButtonItem] != nil && ![item hidesBackButton])
857 return nil;
858
859 if (UINavigationController *navigation = [self navigationController])
19f2d77f 860 if ([[navigation parentOrPresentingViewController] modalViewController] == navigation)
e6124cb6
JF
861 return [[[UIBarButtonItem alloc]
862 initWithTitle:UCLocalize("CLOSE")
863 style:UIBarButtonItemStylePlain
864 target:self
865 action:@selector(close)
866 ] autorelease];
867
868 return nil;
869}
870
871- (void) applyLeftButton {
872 [[self navigationItem] setLeftBarButtonItem:[self leftButton]];
873}
874
2634b249
JF
875- (UIBarButtonItem *) rightButton {
876 return reloaditem_;
eb35c522
JF
877}
878
2634b249
JF
879- (void) applyLoadingTitle {
880 [[self navigationItem] setTitle:UCLocalize("LOADING")];
caa427d1
JF
881}
882
c83a464d
JF
883- (void) layoutRightButton {
884 [[loadingitem_ view] addSubview:indicator_];
885 [[loadingitem_ view] bringSubviewToFront:indicator_];
886}
887
2634b249
JF
888- (void) applyRightButton {
889 if ([self isLoading]) {
890 [[self navigationItem] setRightBarButtonItem:loadingitem_ animated:YES];
c83a464d 891 [self performSelector:@selector(layoutRightButton) withObject:nil afterDelay:0];
8d603a7d
JF
892
893 [indicator_ startAnimating];
2634b249 894 [self applyLoadingTitle];
2634b249 895 } else {
8d603a7d 896 [indicator_ stopAnimating];
f274bb0c 897 [[self navigationItem] setRightBarButtonItem:[self customButton] animated:YES];
2634b249 898 }
caa427d1
JF
899}
900
df30fbee
GP
901- (void) didStartLoading {
902 // Overridden in subclasses.
903}
904
2634b249
JF
905- (void) _didStartLoading {
906 [self applyRightButton];
54043703
JF
907
908 if ([loading_ count] != 1)
909 return;
df30fbee 910
2713be8e 911 if ([self retainsNetworkActivityIndicator])
f8c9fd4c 912 [self.delegate retainNetworkActivityIndicator];
2713be8e 913
df30fbee
GP
914 [self didStartLoading];
915}
916
917- (void) didFinishLoading {
918 // Overridden in subclasses.
caa427d1
JF
919}
920
2634b249
JF
921- (void) _didFinishLoading {
922 if ([loading_ count] != 0)
923 return;
924
925 [self applyRightButton];
df30fbee 926 [[self navigationItem] setTitle:title_];
2634b249 927
2713be8e 928 if ([self retainsNetworkActivityIndicator])
f8c9fd4c 929 [self.delegate releaseNetworkActivityIndicator];
2713be8e 930
df30fbee 931 [self didFinishLoading];
caa427d1
JF
932}
933
2634b249
JF
934- (bool) isLoading {
935 return [loading_ count] != 0;
caa427d1
JF
936}
937
b5e7eebb
GP
938- (id) initWithWidth:(float)width ofClass:(Class)_class {
939 if ((self = [super init]) != nil) {
6f837280
JF
940 width_ = width;
941 class_ = _class;
942
77259cab 943 [super setPageColor:nil];
5738f070 944
df08be87
JF
945 allowsNavigationAction_ = true;
946
7b33d201 947 loading_ = [NSMutableSet setWithCapacity:5];
aa1e1906 948 registered_ = [NSMutableSet setWithCapacity:5];
7b33d201 949 indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
caa427d1 950
6f837280
JF
951 reloaditem_ = [[[UIBarButtonItem alloc]
952 initWithTitle:UCLocalize("RELOAD")
953 style:[self rightButtonStyle]
954 target:self
955 action:@selector(reloadButtonClicked)
956 ] autorelease];
65ea9562 957
6f837280 958 loadingitem_ = [[[UIBarButtonItem alloc]
5328a9f0 959 initWithTitle:(kCFCoreFoundationVersionNumber >= 800 ? @" " : @" ")
6f837280
JF
960 style:UIBarButtonItemStylePlain
961 target:self
f274bb0c 962 action:@selector(customButtonClicked)
6f837280 963 ] autorelease];
22f8bed9 964
5328a9f0
JF
965 UIActivityIndicatorViewStyle style;
966 float left;
967 if (kCFCoreFoundationVersionNumber >= 800) {
968 style = UIActivityIndicatorViewStyleGray;
969 left = 7;
970 } else {
971 style = UIActivityIndicatorViewStyleWhite;
972 left = 15;
973 }
974
975 indicator_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:style] autorelease];
976 [indicator_ setFrame:CGRectMake(left, 5, [indicator_ frame].size.width, [indicator_ frame].size.height)];
6f837280 977 [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
22f8bed9 978
6f837280
JF
979 [self applyLeftButton];
980 [self applyRightButton];
981 } return self;
982}
22f8bed9 983
c6cf66c7
JF
984- (NSString *) applicationNameForUserAgent {
985 return nil;
986}
987
6f837280
JF
988- (void) loadView {
989 CGRect bounds([[UIScreen mainScreen] applicationFrame]);
ea173384 990
6f837280
JF
991 webview_ = [[[CyteWebView alloc] initWithFrame:bounds] autorelease];
992 [webview_ setDelegate:self];
993 [self setView:webview_];
ea173384 994
6f837280
JF
995 if ([webview_ respondsToSelector:@selector(setDataDetectorTypes:)])
996 [webview_ setDataDetectorTypes:UIDataDetectorTypeAutomatic];
997 else
998 [webview_ setDetectsPhoneNumbers:NO];
14f17703 999
6f837280 1000 [webview_ setScalesPageToFit:YES];
ea173384 1001
6f837280 1002 UIWebDocumentView *document([webview_ _documentView]);
22f8bed9 1003
6f837280
JF
1004 // XXX: I think this improves scrolling; the hardcoded-ness sucks
1005 [document setTileSize:CGSizeMake(320, 500)];
22f8bed9 1006
6f837280
JF
1007 WebView *webview([document webView]);
1008 WebPreferences *preferences([webview preferences]);
72bdb258 1009
6f837280
JF
1010 // XXX: I have no clue if I actually /want/ this modification
1011 if ([webview respondsToSelector:@selector(_setLayoutInterval:)])
1012 [webview _setLayoutInterval:0];
1013 else if ([preferences respondsToSelector:@selector(_setLayoutInterval:)])
1014 [preferences _setLayoutInterval:0];
22f8bed9 1015
6f837280 1016 [preferences setCacheModel:WebCacheModelDocumentBrowser];
d331bfc3 1017 [preferences setJavaScriptCanOpenWindowsAutomatically:NO];
64cc10e2
JF
1018
1019 if ([preferences respondsToSelector:@selector(setOfflineWebApplicationCacheEnabled:)])
1020 [preferences setOfflineWebApplicationCacheEnabled:YES];
22f8bed9 1021
c6cf66c7
JF
1022 if (NSString *agent = [self applicationNameForUserAgent])
1023 [webview setApplicationNameForUserAgent:agent];
1024
6f837280
JF
1025 if ([webview respondsToSelector:@selector(setShouldUpdateWhileOffscreen:)])
1026 [webview setShouldUpdateWhileOffscreen:NO];
22f8bed9 1027
6f837280
JF
1028#if LogMessages
1029 if ([document respondsToSelector:@selector(setAllowsMessaging:)])
1030 [document setAllowsMessaging:YES];
1031 if ([webview respondsToSelector:@selector(_setAllowsMessaging:)])
1032 [webview _setAllowsMessaging:YES];
1033#endif
3e9c9e85 1034
6f837280
JF
1035 if ([webview_ respondsToSelector:@selector(_scrollView)]) {
1036 scroller_ = [webview_ _scrollView];
ea173384 1037
6f837280
JF
1038 [scroller_ setDirectionalLockEnabled:YES];
1039 [scroller_ setDecelerationRate:CYScrollViewDecelerationRateNormal];
1040 [scroller_ setDelaysContentTouches:NO];
ea173384 1041
6f837280
JF
1042 [scroller_ setCanCancelContentTouches:YES];
1043 } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
1044 UIScroller *scroller([webview_ _scroller]);
1045 scroller_ = (UIScrollView *) scroller;
bc11cf5b 1046
6f837280
JF
1047 [scroller setDirectionalScrolling:YES];
1048 // XXX: we might be better off /not/ setting this on older systems
1049 [scroller setScrollDecelerationFactor:CYScrollViewDecelerationRateNormal]; /* 0.989324 */
1050 [scroller setScrollHysteresis:0]; /* 8 */
caa427d1 1051
6f837280 1052 [scroller setThumbDetectionEnabled:NO];
caa427d1 1053
6f837280
JF
1054 // use NO with UIApplicationUseLegacyEvents(YES)
1055 [scroller setEventMode:YES];
bc11cf5b 1056
6f837280
JF
1057 // XXX: this is handled by setBounces, right?
1058 //[scroller setAllowsRubberBanding:YES];
1059 }
bc11cf5b 1060
d32f96be 1061 [webview_ setOpaque:NO];
69b1d328 1062 [webview_ setBackgroundColor:nil];
d32f96be 1063
6f837280 1064 [scroller_ setFixedBackgroundPattern:YES];
f8c9fd4c 1065 [scroller_ setBackgroundColor:self.pageColor];
6f837280 1066 [scroller_ setClipsSubviews:YES];
2634b249 1067
6f837280
JF
1068 [scroller_ setBounces:YES];
1069 [scroller_ setScrollingEnabled:YES];
1070 [scroller_ setShowBackgroundShadow:NO];
22f8bed9 1071
6f837280 1072 [self setViewportWidth:width_];
65ea9562 1073
494216be
JF
1074 if ([[UIColor groupTableViewBackgroundColor] isEqual:[UIColor clearColor]]) {
1075 UITableView *table([[[UITableView alloc] initWithFrame:[webview_ bounds] style:UITableViewStyleGrouped] autorelease]);
1076 [table setScrollsToTop:NO];
1077 [webview_ insertSubview:table atIndex:0];
1078 [table setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
1079 }
1080
6f837280 1081 [webview_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
47a40da6
JF
1082
1083 ready_ = false;
6f837280
JF
1084}
1085
1086- (void) releaseSubviews {
1087 webview_ = nil;
1088 scroller_ = nil;
7be3eb32 1089
8b74eaff
JF
1090 [self releaseNetworkActivityIndicator];
1091
7be3eb32 1092 [super releaseSubviews];
22f8bed9
JF
1093}
1094
b5e7eebb
GP
1095- (id) initWithWidth:(float)width {
1096 return [self initWithWidth:width ofClass:[self class]];
245cce8a
JF
1097}
1098
b5e7eebb
GP
1099- (id) init {
1100 return [self initWithWidth:0];
3e9c9e85
JF
1101}
1102
eb09425a
JF
1103- (id) initWithURL:(NSURL *)url {
1104 if ((self = [self init]) != nil) {
1105 [self setURL:url];
1106 } return self;
1107}
1108
68bd7c60
JF
1109- (id) initWithRequest:(NSURLRequest *)request {
1110 if ((self = [self init]) != nil) {
1111 [self setRequest:request];
1112 } return self;
1113}
1114
d331bfc3
JF
1115+ (void) _lockJavaScript:(WebPreferences *)preferences {
1116 WebThreadLocked lock;
1117 [preferences setJavaScriptCanOpenWindowsAutomatically:NO];
1118}
1119
12b59862 1120- (void) callFunction:(WebScriptObject *)function {
0893a034 1121 WebThreadLocked lock;
caa427d1 1122
6f837280 1123 WebView *webview([[[self webView] _documentView] webView]);
d331bfc3 1124 WebPreferences *preferences([webview preferences]);
c21004b9 1125
d331bfc3
JF
1126 [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
1127 if ([webview respondsToSelector:@selector(_preferencesChanged:)])
1128 [webview _preferencesChanged:preferences];
1129 else
1130 [webview _preferencesChangedNotification:[NSNotification notificationWithName:@"" object:preferences]];
1131
1132 WebFrame *frame([webview mainFrame]);
12b59862 1133 JSGlobalContextRef context([frame globalContext]);
d331bfc3 1134
9dbfe708 1135 JSObjectRef object([function JSObject]);
7c80833f
JF
1136 if ($JSObjectCallAsFunction != NULL)
1137 ($JSObjectCallAsFunction)(context, object, NULL, 0, NULL, NULL);
d331bfc3
JF
1138
1139 // XXX: the JavaScript code submits a form, which seems to happen asynchronously
1140 NSObject *target([CyteWebViewController class]);
1141 [NSObject cancelPreviousPerformRequestsWithTarget:target selector:@selector(_lockJavaScript:) object:preferences];
1142 [target performSelector:@selector(_lockJavaScript:) withObject:preferences afterDelay:1];
12b59862
JF
1143}
1144
ce041f4f 1145- (void) reloadButtonClicked {
d69dbfc5 1146 [self reloadURLWithCache:NO];
12b59862
JF
1147}
1148
719d6c2f
DH
1149- (void) _customButtonClicked {
1150 [self reloadButtonClicked];
1151}
1152
ce041f4f 1153- (void) customButtonClicked {
caa427d1
JF
1154#if !AlwaysReload
1155 if (function_ != nil)
12b59862 1156 [self callFunction:function_];
caa427d1
JF
1157 else
1158#endif
719d6c2f 1159 [self _customButtonClicked];
22f8bed9
JF
1160}
1161
3e9c9e85
JF
1162+ (float) defaultWidth {
1163 return 980;
1164}
1165
82406217
JF
1166- (void) setNavigationBarStyle:(NSString *)name {
1167 UIBarStyle style;
1168 if ([name isEqualToString:@"Black"])
1169 style = UIBarStyleBlack;
1170 else
1171 style = UIBarStyleDefault;
1172
1173 [[[self navigationController] navigationBar] setBarStyle:style];
1174}
1175
00984204
JF
1176- (void) setNavigationBarTintColor:(UIColor *)color {
1177 [[[self navigationController] navigationBar] setTintColor:color];
1178}
1179
c31c825d
JF
1180- (void) setBadgeValue:(id)value {
1181 [[[self navigationController] tabBarItem] setBadgeValue:value];
1182}
1183
b8a5d89d
JF
1184- (void) setHidesBackButton:(bool)value {
1185 [[self navigationItem] setHidesBackButton:value];
e6124cb6 1186 [self applyLeftButton];
b8a5d89d
JF
1187}
1188
1189- (void) setHidesBackButtonByNumber:(NSNumber *)value {
1190 [self setHidesBackButton:[value boolValue]];
1191}
1192
f196b921 1193- (void) dispatchEvent:(NSString *)event {
6f837280 1194 [[self webView] dispatchEvent:event];
f196b921
JF
1195}
1196
5cdfcd6f
JF
1197- (bool) hidesNavigationBar {
1198 return hidesNavigationBar_;
1199}
1200
1201- (void) _setHidesNavigationBar:(bool)value animated:(bool)animated {
1202 if (visible_)
1203 [[self navigationController] setNavigationBarHidden:(value && [self hidesNavigationBar]) animated:animated];
1204}
1205
1206- (void) setHidesNavigationBar:(bool)value {
1207 if (hidesNavigationBar_ != value) {
1208 hidesNavigationBar_ = value;
1209 [self _setHidesNavigationBar:YES animated:YES];
1210 }
1211}
1212
1213- (void) setHidesNavigationBarByNumber:(NSNumber *)value {
1214 [self setHidesNavigationBar:[value boolValue]];
1215}
1216
db698f42
JF
1217- (void) setScrollAlwaysBounceVertical:(bool)value {
1218 if ([webview_ respondsToSelector:@selector(_scrollView)]) {
1219 UIScrollView *scroller([webview_ _scrollView]);
1220 [scroller setAlwaysBounceVertical:value];
1221 } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
1222 //UIScroller *scroller([webview_ _scroller]);
1223 // XXX: I am sad here.
1224 } else return;
1225}
1226
1227- (void) setScrollAlwaysBounceVerticalNumber:(NSNumber *)value {
1228 [self setScrollAlwaysBounceVertical:[value boolValue]];
1229}
1230
4886cc81
JF
1231- (void) setScrollIndicatorStyle:(UIScrollViewIndicatorStyle)style {
1232 if ([webview_ respondsToSelector:@selector(_scrollView)]) {
1233 UIScrollView *scroller([webview_ _scrollView]);
1234 [scroller setIndicatorStyle:style];
1235 } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
1236 UIScroller *scroller([webview_ _scroller]);
1237 [scroller setScrollerIndicatorStyle:style];
1238 } else return;
1239}
1240
1241- (void) setScrollIndicatorStyleWithName:(NSString *)style {
1242 UIScrollViewIndicatorStyle value;
1243
1244 if (false);
1245 else if ([style isEqualToString:@"default"])
1246 value = UIScrollViewIndicatorStyleDefault;
1247 else if ([style isEqualToString:@"black"])
1248 value = UIScrollViewIndicatorStyleBlack;
1249 else if ([style isEqualToString:@"white"])
1250 value = UIScrollViewIndicatorStyleWhite;
1251 else return;
1252
1253 [self setScrollIndicatorStyle:value];
1254}
1255
f196b921 1256- (void) viewWillAppear:(BOOL)animated {
5cdfcd6f
JF
1257 visible_ = true;
1258
1259 if ([self hidesNavigationBar])
1260 [self _setHidesNavigationBar:YES animated:animated];
1261
3cf38067
JF
1262 // XXX: why isn't this evern called automatically?
1263 [[self webView] setNeedsLayout];
1264
f196b921
JF
1265 [self dispatchEvent:@"CydiaViewWillAppear"];
1266 [super viewWillAppear:animated];
1267}
1268
1269- (void) viewDidAppear:(BOOL)animated {
f196b921 1270 [super viewDidAppear:animated];
5cdfcd6f 1271 [self dispatchEvent:@"CydiaViewDidAppear"];
f196b921
JF
1272}
1273
1274- (void) viewWillDisappear:(BOOL)animated {
1275 [self dispatchEvent:@"CydiaViewWillDisappear"];
1276 [super viewWillDisappear:animated];
5cdfcd6f
JF
1277
1278 if ([self hidesNavigationBar])
1279 [self _setHidesNavigationBar:NO animated:animated];
1280
1281 visible_ = false;
f196b921
JF
1282}
1283
1284- (void) viewDidDisappear:(BOOL)animated {
f196b921 1285 [super viewDidDisappear:animated];
5cdfcd6f 1286 [self dispatchEvent:@"CydiaViewDidDisappear"];
f196b921
JF
1287}
1288
aa1e1906
JF
1289- (void) updateHeights:(NSTimer *)timer {
1290 for (WebFrame *frame in (id) registered_)
1291 [frame cydia$updateHeight];
1292}
1293
1294- (void) registerFrame:(WebFrame *)frame {
1295 [registered_ addObject:frame];
1296
1297 if (timer_ == nil)
1298 timer_ = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(updateHeights:) userInfo:nil repeats:YES];
1299}
1300
a5938ea5 1301@end
a3d01a76
JF
1302
1303MSClassHook(WAKWindow)
1304
1305static CGSize $WAKWindow$screenSize(WAKWindow *self, SEL _cmd) {
1306 CGSize size([[UIScreen mainScreen] bounds].size);
1307 /*if ([$WAKWindow respondsToSelector:@selector(hasLandscapeOrientation)])
1308 if ([$WAKWindow hasLandscapeOrientation])
1309 std::swap(size.width, size.height);*/
1310 return size;
1311}
1312
1313static struct WAKWindow$screenSize { WAKWindow$screenSize() {
1314 if ($WAKWindow != NULL)
1315 if (Method method = class_getInstanceMethod($WAKWindow, @selector(screenSize)))
1316 method_setImplementation(method, (IMP) &$WAKWindow$screenSize);
1317} } WAKWindow$screenSize;;
1318
1319MSClassHook(NSUserDefaults)
1320
1321MSHook(id, NSUserDefaults$objectForKey$, NSUserDefaults *self, SEL _cmd, NSString *key) {
1322 if ([key respondsToSelector:@selector(isEqualToString:)] && [key isEqualToString:@"WebKitLocalStorageDatabasePathPreferenceKey"])
1323 return [NSString stringWithFormat:@"%@/%@/%@", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject, NSBundle.mainBundle.bundleIdentifier, @"LocalStorage"];
1324 return _NSUserDefaults$objectForKey$(self, _cmd, key);
1325}
1326
1327CYHook(NSUserDefaults, objectForKey$, objectForKey:)