]>
Commit | Line | Data |
---|---|---|
bde2d79b JF |
1 | #include <BrowserView.h> |
2 | ||
04700693 JF |
3 | /* Indirect Delegate {{{ */ |
4 | @interface IndirectDelegate : NSProxy { | |
5 | _transient volatile id delegate_; | |
6 | } | |
7 | ||
8 | - (void) setDelegate:(id)delegate; | |
9 | - (id) initWithDelegate:(id)delegate; | |
10 | @end | |
11 | ||
12 | @implementation IndirectDelegate | |
13 | ||
14 | - (void) setDelegate:(id)delegate { | |
15 | delegate_ = delegate; | |
16 | } | |
17 | ||
18 | - (id) initWithDelegate:(id)delegate { | |
19 | delegate_ = delegate; | |
20 | return self; | |
21 | } | |
22 | ||
23 | - (BOOL) respondsToSelector:(SEL)sel { | |
24 | return delegate_ == nil ? FALSE : [delegate_ respondsToSelector:sel]; | |
25 | } | |
26 | ||
27 | - (NSMethodSignature *) methodSignatureForSelector:(SEL)sel { | |
28 | if (delegate_ != nil) | |
29 | if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel]) | |
30 | return sig; | |
31 | // XXX: I fucking hate Apple so very very bad | |
32 | return [NSMethodSignature signatureWithObjCTypes:"v@:"]; | |
33 | } | |
34 | ||
35 | - (void) forwardInvocation:(NSInvocation *)inv { | |
36 | SEL sel = [inv selector]; | |
37 | if (delegate_ != nil && [delegate_ respondsToSelector:sel]) | |
38 | [inv invokeWithTarget:delegate_]; | |
39 | } | |
40 | ||
41 | @end | |
42 | /* }}} */ | |
43 | ||
bde2d79b JF |
44 | @interface WebView (Cydia) |
45 | - (void) setScriptDebugDelegate:(id)delegate; | |
46 | - (void) _setFormDelegate:(id)delegate; | |
47 | - (void) _setUIKitDelegate:(id)delegate; | |
48 | - (void) setWebMailDelegate:(id)delegate; | |
49 | - (void) _setLayoutInterval:(float)interval; | |
50 | @end | |
51 | ||
45f88d41 JF |
52 | /* Web Scripting {{{ */ |
53 | @interface CydiaObject : NSObject { | |
54 | id indirect_; | |
55 | } | |
56 | ||
57 | - (id) initWithDelegate:(IndirectDelegate *)indirect; | |
58 | @end | |
59 | ||
60 | @implementation CydiaObject | |
61 | ||
62 | - (void) dealloc { | |
63 | [indirect_ release]; | |
64 | [super dealloc]; | |
65 | } | |
66 | ||
67 | - (id) initWithDelegate:(IndirectDelegate *)indirect { | |
68 | if ((self = [super init]) != nil) { | |
69 | indirect_ = [indirect retain]; | |
70 | } return self; | |
71 | } | |
72 | ||
73 | + (NSString *) webScriptNameForSelector:(SEL)selector { | |
74 | if (selector == @selector(getPackageById:)) | |
75 | return @"getPackageById"; | |
7b00c562 JF |
76 | else if (selector == @selector(setAutoPopup:)) |
77 | return @"setAutoPopup"; | |
45f88d41 JF |
78 | else if (selector == @selector(setButtonImage:withStyle:toFunction:)) |
79 | return @"setButtonImage"; | |
80 | else if (selector == @selector(setButtonTitle:withStyle:toFunction:)) | |
81 | return @"setButtonTitle"; | |
82 | else if (selector == @selector(supports:)) | |
83 | return @"supports"; | |
84 | else if (selector == @selector(du:)) | |
85 | return @"du"; | |
86 | else if (selector == @selector(statfs:)) | |
87 | return @"statfs"; | |
88 | else | |
89 | return nil; | |
90 | } | |
91 | ||
92 | + (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { | |
93 | return [self webScriptNameForSelector:selector] == nil; | |
94 | } | |
95 | ||
96 | - (BOOL) supports:(NSString *)feature { | |
97 | return [feature isEqualToString:@"window.open"]; | |
98 | } | |
99 | ||
100 | - (Package *) getPackageById:(NSString *)id { | |
101 | return [[Database sharedInstance] packageWithName:id]; | |
102 | } | |
103 | ||
104 | - (NSArray *) statfs:(NSString *)path { | |
105 | struct statfs stat; | |
106 | ||
107 | if (path == nil || statfs([path UTF8String], &stat) == -1) | |
108 | return nil; | |
109 | ||
110 | return [NSArray arrayWithObjects: | |
111 | [NSNumber numberWithUnsignedLong:stat.f_bsize], | |
112 | [NSNumber numberWithUnsignedLong:stat.f_blocks], | |
113 | [NSNumber numberWithUnsignedLong:stat.f_bfree], | |
114 | nil]; | |
115 | } | |
116 | ||
117 | - (NSNumber *) du:(NSString *)path { | |
118 | NSNumber *value(nil); | |
119 | ||
120 | int fds[2]; | |
121 | _assert(pipe(fds) != -1); | |
122 | ||
123 | pid_t pid(ExecFork()); | |
124 | if (pid == 0) { | |
125 | _assert(dup2(fds[1], 1) != -1); | |
126 | _assert(close(fds[0]) != -1); | |
127 | _assert(close(fds[1]) != -1); | |
128 | execlp("du", "du", "-s", [path UTF8String], NULL); | |
129 | exit(1); | |
130 | _assert(false); | |
131 | } | |
132 | ||
133 | _assert(close(fds[1]) != -1); | |
134 | ||
135 | if (FILE *du = fdopen(fds[0], "r")) { | |
136 | char line[1024]; | |
137 | while (fgets(line, sizeof(line), du) != NULL) { | |
138 | size_t length(strlen(line)); | |
139 | while (length != 0 && line[length - 1] == '\n') | |
140 | line[--length] = '\0'; | |
141 | if (char *tab = strchr(line, '\t')) { | |
142 | *tab = '\0'; | |
143 | value = [NSNumber numberWithUnsignedLong:strtoul(line, NULL, 0)]; | |
144 | } | |
145 | } | |
146 | ||
147 | fclose(du); | |
148 | } else _assert(close(fds[0])); | |
149 | ||
150 | int status; | |
151 | wait: | |
152 | if (waitpid(pid, &status, 0) == -1) | |
153 | if (errno == EINTR) | |
154 | goto wait; | |
155 | else _assert(false); | |
156 | ||
157 | return value; | |
158 | } | |
159 | ||
7b00c562 JF |
160 | - (void) setAutoPopup:(BOOL)popup { |
161 | [indirect_ setAutoPopup:popup]; | |
162 | } | |
163 | ||
45f88d41 JF |
164 | - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { |
165 | [indirect_ setButtonImage:button withStyle:style toFunction:function]; | |
166 | } | |
167 | ||
168 | - (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { | |
169 | [indirect_ setButtonTitle:button withStyle:style toFunction:function]; | |
170 | } | |
171 | ||
172 | @end | |
173 | /* }}} */ | |
174 | ||
bde2d79b JF |
175 | @implementation BrowserView |
176 | ||
5a5c1e4b | 177 | #if ShowInternals |
bde2d79b JF |
178 | #include "Internals.h" |
179 | #endif | |
180 | ||
181 | - (void) dealloc { | |
7b00c562 JF |
182 | #if ForSaurik |
183 | NSLog(@"[BrowserView dealloc]"); | |
184 | #endif | |
5a5c1e4b | 185 | |
bde2d79b JF |
186 | if (challenge_ != nil) |
187 | [challenge_ release]; | |
188 | ||
189 | WebView *webview = [webview_ webView]; | |
190 | [webview setFrameLoadDelegate:nil]; | |
191 | [webview setResourceLoadDelegate:nil]; | |
192 | [webview setUIDelegate:nil]; | |
193 | [webview setScriptDebugDelegate:nil]; | |
194 | [webview setPolicyDelegate:nil]; | |
195 | ||
196 | [webview setDownloadDelegate:nil]; | |
197 | ||
198 | [webview _setFormDelegate:nil]; | |
199 | [webview _setUIKitDelegate:nil]; | |
200 | [webview setWebMailDelegate:nil]; | |
201 | [webview setEditingDelegate:nil]; | |
202 | ||
203 | [webview_ setDelegate:nil]; | |
204 | [webview_ setGestureDelegate:nil]; | |
205 | ||
206 | //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | |
207 | ||
208 | [webview close]; | |
209 | ||
210 | #if RecycleWebViews | |
211 | [webview_ removeFromSuperview]; | |
212 | [Documents_ addObject:[webview_ autorelease]]; | |
213 | #else | |
214 | [webview_ release]; | |
215 | #endif | |
216 | ||
217 | [indirect_ setDelegate:nil]; | |
218 | [indirect_ release]; | |
219 | ||
45f88d41 JF |
220 | [cydia_ release]; |
221 | ||
bde2d79b JF |
222 | [scroller_ setDelegate:nil]; |
223 | ||
224 | if (button_ != nil) | |
225 | [button_ release]; | |
226 | if (style_ != nil) | |
227 | [style_ release]; | |
228 | if (function_ != nil) | |
229 | [function_ release]; | |
230 | ||
231 | [scroller_ release]; | |
232 | [indicator_ release]; | |
233 | if (confirm_ != nil) | |
234 | [confirm_ release]; | |
235 | if (title_ != nil) | |
236 | [title_ release]; | |
237 | [super dealloc]; | |
238 | } | |
239 | ||
240 | - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy { | |
241 | [self loadRequest:[NSURLRequest | |
242 | requestWithURL:url | |
243 | cachePolicy:policy | |
244 | timeoutInterval:30.0 | |
245 | ]]; | |
246 | } | |
247 | ||
248 | - (void) loadURL:(NSURL *)url { | |
249 | [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy]; | |
250 | } | |
251 | ||
252 | - (NSMutableURLRequest *) _addHeadersToRequest:(NSURLRequest *)request { | |
253 | NSMutableURLRequest *copy = [request mutableCopy]; | |
254 | ||
255 | if (Machine_ != NULL) | |
256 | [copy setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; | |
257 | if (UniqueID_ != nil) | |
258 | [copy setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"]; | |
259 | ||
260 | if (Role_ != nil) | |
261 | [copy setValue:Role_ forHTTPHeaderField:@"X-Role"]; | |
262 | ||
263 | return copy; | |
264 | } | |
265 | ||
266 | - (void) loadRequest:(NSURLRequest *)request { | |
267 | pushed_ = true; | |
f4772bd1 | 268 | error_ = false; |
bde2d79b JF |
269 | [webview_ loadRequest:request]; |
270 | } | |
271 | ||
272 | - (void) reloadURL { | |
bde2d79b JF |
273 | if (request_ == nil) |
274 | return; | |
275 | ||
276 | if ([request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil) | |
f4772bd1 | 277 | [self loadRequest:request_]; |
bde2d79b JF |
278 | else { |
279 | UIActionSheet *sheet = [[[UIActionSheet alloc] | |
280 | initWithTitle:@"Are you sure you want to submit this form again?" | |
281 | buttons:[NSArray arrayWithObjects:@"Cancel", @"Submit", nil] | |
282 | defaultButtonIndex:0 | |
283 | delegate:self | |
284 | context:@"submit" | |
285 | ] autorelease]; | |
286 | ||
287 | [sheet setNumberOfRows:1]; | |
288 | [sheet popupAlertAnimated:YES]; | |
289 | } | |
290 | } | |
291 | ||
292 | - (WebView *) webView { | |
293 | return [webview_ webView]; | |
294 | } | |
295 | ||
5a5c1e4b JF |
296 | - (UIWebDocumentView *) documentView { |
297 | return webview_; | |
298 | } | |
299 | ||
bde2d79b JF |
300 | - (void) view:(UIView *)sender didSetFrame:(CGRect)frame { |
301 | [scroller_ setContentSize:frame.size]; | |
302 | } | |
303 | ||
304 | - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old { | |
305 | [self view:sender didSetFrame:frame]; | |
306 | } | |
307 | ||
308 | - (void) pushPage:(RVPage *)page { | |
bde2d79b | 309 | [page setDelegate:delegate_]; |
2517c95f | 310 | [self setBackButtonTitle:title_]; |
5a5c1e4b | 311 | [book_ pushPage:page]; |
bde2d79b JF |
312 | } |
313 | ||
a55054a2 JF |
314 | - (void) _pushPage { |
315 | if (pushed_) | |
316 | return; | |
317 | [self autorelease]; | |
318 | pushed_ = true; | |
319 | [book_ pushPage:self]; | |
320 | } | |
321 | ||
bde2d79b | 322 | - (BOOL) getSpecial:(NSURL *)url { |
a55054a2 JF |
323 | #if ForSaurik |
324 | NSLog(@"getSpecial:%@", url); | |
325 | #endif | |
326 | ||
bde2d79b JF |
327 | NSString *href([url absoluteString]); |
328 | NSString *scheme([[url scheme] lowercaseString]); | |
329 | ||
330 | RVPage *page = nil; | |
331 | ||
332 | if ([href hasPrefix:@"apptapp://package/"]) | |
333 | page = [delegate_ pageForPackage:[href substringFromIndex:18]]; | |
334 | else if ([scheme isEqualToString:@"cydia"]) { | |
335 | page = [delegate_ pageForURL:url hasTag:NULL]; | |
336 | if (page == nil) | |
337 | return false; | |
338 | } else if (![scheme isEqualToString:@"apptapp"]) | |
339 | return false; | |
340 | ||
341 | if (page != nil) | |
342 | [self pushPage:page]; | |
343 | return true; | |
344 | } | |
345 | ||
5a5c1e4b | 346 | - (void) webViewShow:(WebView *)sender { |
5b2d5cf3 | 347 | /* XXX: this is where I cry myself to sleep */ |
5a5c1e4b JF |
348 | } |
349 | ||
7b00c562 JF |
350 | - (bool) _allowJavaScriptPanel { |
351 | return true; | |
352 | } | |
353 | ||
bde2d79b | 354 | - (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame { |
263b91b4 | 355 | if (![self _allowJavaScriptPanel]) |
7b00c562 JF |
356 | return; |
357 | ||
bde2d79b JF |
358 | UIActionSheet *sheet = [[[UIActionSheet alloc] |
359 | initWithTitle:nil | |
360 | buttons:[NSArray arrayWithObjects:@"OK", nil] | |
361 | defaultButtonIndex:0 | |
362 | delegate:self | |
363 | context:@"alert" | |
364 | ] autorelease]; | |
365 | ||
366 | [sheet setBodyText:message]; | |
367 | [sheet popupAlertAnimated:YES]; | |
368 | } | |
369 | ||
370 | - (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame { | |
7b00c562 JF |
371 | if (![self _allowJavaScriptPanel]) |
372 | return NO; | |
373 | ||
bde2d79b JF |
374 | UIActionSheet *sheet = [[[UIActionSheet alloc] |
375 | initWithTitle:nil | |
376 | buttons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil] | |
377 | defaultButtonIndex:0 | |
378 | delegate:self | |
379 | context:@"confirm" | |
380 | ] autorelease]; | |
381 | ||
382 | [sheet setNumberOfRows:1]; | |
383 | [sheet setBodyText:message]; | |
384 | [sheet popupAlertAnimated:YES]; | |
385 | ||
386 | NSRunLoop *loop([NSRunLoop currentRunLoop]); | |
387 | NSDate *future([NSDate distantFuture]); | |
388 | ||
389 | while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); | |
390 | ||
391 | NSNumber *confirm([confirm_ autorelease]); | |
392 | confirm_ = nil; | |
393 | return [confirm boolValue]; | |
394 | } | |
395 | ||
7b00c562 JF |
396 | - (void) setAutoPopup:(BOOL)popup { |
397 | popup_ = popup; | |
398 | } | |
399 | ||
bde2d79b JF |
400 | - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { |
401 | if (button_ != nil) | |
402 | [button_ autorelease]; | |
403 | button_ = button == nil ? nil : [[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:button]]] retain]; | |
404 | ||
405 | if (style_ != nil) | |
406 | [style_ autorelease]; | |
407 | style_ = style == nil ? nil : [style retain]; | |
408 | ||
409 | if (function_ != nil) | |
410 | [function_ autorelease]; | |
411 | function_ = function == nil ? nil : [function retain]; | |
412 | } | |
413 | ||
414 | - (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function { | |
415 | if (button_ != nil) | |
416 | [button_ autorelease]; | |
417 | button_ = button == nil ? nil : [button retain]; | |
418 | ||
419 | if (style_ != nil) | |
420 | [style_ autorelease]; | |
421 | style_ = style == nil ? nil : [style retain]; | |
422 | ||
423 | if (function_ != nil) | |
424 | [function_ autorelease]; | |
425 | function_ = function == nil ? nil : [function retain]; | |
426 | } | |
bde2d79b | 427 | |
04700693 JF |
428 | - (void) webViewClose:(WebView *)sender { |
429 | [book_ close]; | |
430 | } | |
431 | ||
bde2d79b | 432 | - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { |
45f88d41 | 433 | [window setValue:cydia_ forKey:@"cydia"]; |
bde2d79b JF |
434 | } |
435 | ||
436 | - (void) webView:(WebView *)sender unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame { | |
437 | NSLog(@"err:%@", error); | |
438 | } | |
439 | ||
440 | - (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener { | |
7b00c562 | 441 | #if ForSaurik |
5a5c1e4b | 442 | NSLog(@"nwa:%@", name); |
7b00c562 | 443 | #endif |
5a5c1e4b | 444 | |
bde2d79b | 445 | if (NSURL *url = [request URL]) { |
04700693 | 446 | if (name == nil) unknown: { |
04700693 JF |
447 | if (![self getSpecial:url]) { |
448 | NSString *scheme([[url scheme] lowercaseString]); | |
449 | if ([scheme isEqualToString:@"mailto"]) | |
450 | [delegate_ openMailToURL:url]; | |
451 | else goto use; | |
452 | } | |
453 | } else if ([name isEqualToString:@"_open"]) | |
bde2d79b | 454 | [delegate_ openURL:url]; |
04700693 JF |
455 | else if ([name isEqualToString:@"_popup"]) { |
456 | RVBook *book([[[RVPopUpBook alloc] initWithFrame:[delegate_ popUpBounds]] autorelease]); | |
04700693 JF |
457 | |
458 | RVPage *page([delegate_ pageForURL:url hasTag:NULL]); | |
459 | if (page == nil) { | |
7b00c562 | 460 | /* XXX: call createWebViewWithRequest instead? */ |
5a5c1e4b JF |
461 | |
462 | [self setBackButtonTitle:title_]; | |
463 | ||
04700693 JF |
464 | BrowserView *browser([[[BrowserView alloc] initWithBook:book] autorelease]); |
465 | [browser loadURL:url]; | |
466 | page = browser; | |
467 | } | |
bde2d79b | 468 | |
0a7e5478 JF |
469 | [book setDelegate:delegate_]; |
470 | [page setDelegate:delegate_]; | |
471 | ||
04700693 JF |
472 | [book setPage:page]; |
473 | [book_ pushBook:book]; | |
474 | } else goto unknown; | |
bde2d79b JF |
475 | |
476 | [listener ignore]; | |
477 | } else use: | |
478 | [listener use]; | |
479 | } | |
480 | ||
481 | - (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener { | |
482 | if ([WebView canShowMIMEType:type]) | |
483 | [listener use]; | |
484 | else { | |
485 | // XXX: handle more mime types! | |
486 | [listener ignore]; | |
487 | if (frame == [webView mainFrame]) | |
488 | [UIApp openURL:[request URL]]; | |
489 | } | |
490 | } | |
491 | ||
492 | - (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener { | |
493 | if (request == nil) ignore: { | |
494 | [listener ignore]; | |
495 | return; | |
496 | } | |
497 | ||
498 | NSURL *url([request URL]); | |
499 | ||
500 | if (url == nil) use: { | |
f4772bd1 | 501 | if (!error_ && [frame parentFrame] == nil) { |
bde2d79b JF |
502 | if (request_ != nil) |
503 | [request_ autorelease]; | |
504 | request_ = [request retain]; | |
04700693 | 505 | #if ForSaurik |
bde2d79b | 506 | NSLog(@"dpn:%@", request_); |
04700693 | 507 | #endif |
bde2d79b JF |
508 | } |
509 | ||
510 | [listener use]; | |
a55054a2 JF |
511 | /* XXX: maybe only the main frame? */ |
512 | [self _pushPage]; | |
bde2d79b JF |
513 | return; |
514 | } | |
515 | #if ForSaurik | |
516 | else NSLog(@"nav:%@:%@", url, [action description]); | |
517 | #endif | |
518 | ||
519 | const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability))); | |
520 | ||
521 | if ( | |
522 | [capability containsObject:@"com.apple.Maps"] && [url mapsURL] || | |
523 | [capability containsObject:@"com.apple.youtube"] && [url youTubeURL] | |
524 | ) { | |
525 | open: | |
526 | [UIApp openURL:url]; | |
527 | goto ignore; | |
528 | } | |
529 | ||
530 | int store(_not(int)); | |
531 | if (NSURL *itms = [url itmsURL:&store]) { | |
7b00c562 | 532 | #if ForSaurik |
bde2d79b | 533 | NSLog(@"itms#%@#%u#%@", url, store, itms); |
7b00c562 JF |
534 | #endif |
535 | ||
bde2d79b JF |
536 | if ( |
537 | store == 1 && [capability containsObject:@"com.apple.MobileStore"] || | |
538 | store == 2 && [capability containsObject:@"com.apple.AppStore"] | |
539 | ) { | |
540 | url = itms; | |
541 | goto open; | |
542 | } | |
543 | } | |
544 | ||
545 | NSString *scheme([[url scheme] lowercaseString]); | |
546 | ||
547 | if ([scheme isEqualToString:@"tel"]) { | |
548 | // XXX: intelligence | |
549 | goto open; | |
550 | } | |
551 | ||
552 | if ([scheme isEqualToString:@"mailto"]) { | |
553 | [delegate_ openMailToURL:url]; | |
554 | goto ignore; | |
555 | } | |
556 | ||
557 | if ([self getSpecial:url]) | |
558 | goto ignore; | |
559 | else if ([WebView _canHandleRequest:request]) | |
560 | goto use; | |
561 | else if ([url isSpringboardHandledURL]) | |
562 | goto open; | |
563 | else | |
564 | goto use; | |
565 | } | |
566 | ||
567 | - (void) webView:(WebView *)sender setStatusText:(NSString *)text { | |
568 | //lprintf("Status:%s\n", [text UTF8String]); | |
569 | } | |
570 | ||
bde2d79b JF |
571 | - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { |
572 | NSString *context([sheet context]); | |
573 | ||
574 | if ([context isEqualToString:@"alert"]) | |
575 | [sheet dismiss]; | |
576 | else if ([context isEqualToString:@"confirm"]) { | |
577 | switch (button) { | |
578 | case 1: | |
579 | confirm_ = [NSNumber numberWithBool:YES]; | |
580 | break; | |
581 | ||
582 | case 2: | |
583 | confirm_ = [NSNumber numberWithBool:NO]; | |
584 | break; | |
585 | } | |
586 | ||
587 | [sheet dismiss]; | |
588 | } else if ([context isEqualToString:@"challenge"]) { | |
589 | id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]); | |
590 | ||
591 | switch (button) { | |
592 | case 1: { | |
593 | NSString *username([[sheet textFieldAtIndex:0] text]); | |
594 | NSString *password([[sheet textFieldAtIndex:1] text]); | |
595 | ||
596 | NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]); | |
597 | ||
598 | [sender useCredential:credential forAuthenticationChallenge:challenge_]; | |
599 | } break; | |
600 | ||
601 | case 2: | |
602 | [sender cancelAuthenticationChallenge:challenge_]; | |
603 | break; | |
604 | ||
605 | default: | |
606 | _assert(false); | |
607 | } | |
608 | ||
609 | [challenge_ release]; | |
610 | challenge_ = nil; | |
611 | ||
612 | [sheet dismiss]; | |
613 | } else if ([context isEqualToString:@"submit"]) { | |
614 | switch (button) { | |
615 | case 1: | |
616 | break; | |
617 | ||
618 | case 2: | |
619 | if (request_ != nil) | |
620 | [webview_ loadRequest:request_]; | |
621 | break; | |
622 | ||
623 | default: | |
624 | _assert(false); | |
625 | } | |
626 | ||
627 | [sheet dismiss]; | |
628 | } | |
629 | } | |
630 | ||
631 | - (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source { | |
632 | challenge_ = [challenge retain]; | |
633 | ||
634 | NSURLProtectionSpace *space([challenge protectionSpace]); | |
635 | NSString *realm([space realm]); | |
636 | if (realm == nil) | |
637 | realm = @""; | |
638 | ||
639 | UIActionSheet *sheet = [[[UIActionSheet alloc] | |
640 | initWithTitle:realm | |
641 | buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil] | |
642 | defaultButtonIndex:0 | |
643 | delegate:self | |
644 | context:@"challenge" | |
645 | ] autorelease]; | |
646 | ||
647 | [sheet setNumberOfRows:1]; | |
648 | ||
649 | [sheet addTextFieldWithValue:@"" label:@"username"]; | |
650 | [sheet addTextFieldWithValue:@"" label:@"password"]; | |
651 | ||
652 | UITextField *username([sheet textFieldAtIndex:0]); { | |
653 | UITextInputTraits *traits([username textInputTraits]); | |
654 | [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; | |
655 | [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; | |
656 | [traits setKeyboardType:UIKeyboardTypeASCIICapable]; | |
657 | [traits setReturnKeyType:UIReturnKeyNext]; | |
658 | } | |
659 | ||
660 | UITextField *password([sheet textFieldAtIndex:1]); { | |
661 | UITextInputTraits *traits([password textInputTraits]); | |
662 | [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; | |
663 | [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; | |
664 | [traits setKeyboardType:UIKeyboardTypeASCIICapable]; | |
665 | // XXX: UIReturnKeyDone | |
666 | [traits setReturnKeyType:UIReturnKeyNext]; | |
667 | [traits setSecureTextEntry:YES]; | |
668 | } | |
669 | ||
670 | [sheet popupAlertAnimated:YES]; | |
671 | } | |
672 | ||
673 | - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source { | |
bde2d79b JF |
674 | return [self _addHeadersToRequest:request]; |
675 | } | |
676 | ||
5a5c1e4b | 677 | - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features { |
7b00c562 | 678 | //- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request userGesture:(BOOL)gesture { |
5a5c1e4b | 679 | #if ForSaurik |
7b00c562 JF |
680 | NSLog(@"cwv:%@ (%@): %@", request, title_, features == nil ? @"{}" : [features description]); |
681 | //NSLog(@"cwv:%@ (%@): %@", request, title_, gesture ? @"Yes" : @"No"); | |
5a5c1e4b | 682 | #endif |
bde2d79b | 683 | |
7b00c562 JF |
684 | NSNumber *value([features objectForKey:@"width"]); |
685 | float width(value == nil ? [BrowserView defaultWidth] : [value floatValue]); | |
686 | ||
687 | RVBook *book(!popup_ ? book_ : [[[RVPopUpBook alloc] initWithFrame:[delegate_ popUpBounds]] autorelease]); | |
688 | ||
689 | /* XXX: deal with cydia:// pages */ | |
690 | BrowserView *browser([[[BrowserView alloc] initWithBook:book forWidth:width] autorelease]); | |
691 | ||
692 | if (features == nil && popup_) { | |
693 | [book setDelegate:delegate_]; | |
694 | [browser setDelegate:delegate_]; | |
695 | ||
696 | [browser loadRequest:request]; | |
a55054a2 | 697 | |
7b00c562 JF |
698 | [book setPage:browser]; |
699 | [book_ pushBook:book]; | |
700 | } else if (request == nil) { | |
a55054a2 JF |
701 | [self setBackButtonTitle:title_]; |
702 | [browser setDelegate:delegate_]; | |
703 | [browser retain]; | |
704 | } else { | |
705 | [self pushPage:browser]; | |
706 | [browser loadRequest:request]; | |
707 | } | |
708 | ||
bde2d79b JF |
709 | return [browser webView]; |
710 | } | |
711 | ||
712 | - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request { | |
5a5c1e4b | 713 | return [self webView:sender createWebViewWithRequest:request windowFeatures:nil]; |
7b00c562 | 714 | //return [self webView:sender createWebViewWithRequest:request userGesture:YES]; |
bde2d79b JF |
715 | } |
716 | ||
717 | - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame { | |
718 | if ([frame parentFrame] != nil) | |
719 | return; | |
720 | ||
721 | title_ = [title retain]; | |
722 | [book_ reloadTitleForPage:self]; | |
723 | } | |
724 | ||
725 | - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame { | |
726 | if ([frame parentFrame] != nil) | |
727 | return; | |
728 | ||
729 | reloading_ = false; | |
730 | loading_ = true; | |
731 | [self reloadButtons]; | |
732 | ||
733 | if (title_ != nil) { | |
734 | [title_ release]; | |
735 | title_ = nil; | |
736 | } | |
737 | ||
738 | if (button_ != nil) { | |
739 | [button_ release]; | |
740 | button_ = nil; | |
741 | } | |
742 | ||
743 | if (style_ != nil) { | |
744 | [style_ release]; | |
745 | style_ = nil; | |
746 | } | |
747 | ||
748 | if (function_ != nil) { | |
749 | [function_ release]; | |
750 | function_ = nil; | |
751 | } | |
752 | ||
753 | [book_ reloadTitleForPage:self]; | |
754 | ||
755 | [scroller_ scrollPointVisibleAtTopLeft:CGPointZero]; | |
756 | ||
757 | CGRect webrect = [scroller_ bounds]; | |
758 | webrect.size.height = 0; | |
759 | [webview_ setFrame:webrect]; | |
760 | } | |
761 | ||
762 | - (void) _finishLoading { | |
763 | if (!reloading_) { | |
764 | loading_ = false; | |
765 | [self reloadButtons]; | |
766 | } | |
767 | } | |
768 | ||
769 | - (bool) _loading { | |
770 | return loading_; | |
771 | } | |
772 | ||
773 | - (void) reloadButtons { | |
774 | if ([self _loading]) | |
775 | [indicator_ startAnimation]; | |
776 | else | |
777 | [indicator_ stopAnimation]; | |
778 | [super reloadButtons]; | |
779 | } | |
780 | ||
781 | - (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame { | |
782 | return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame]; | |
783 | } | |
784 | ||
785 | - (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame { | |
786 | return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame]; | |
787 | } | |
788 | ||
789 | - (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame { | |
790 | return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame]; | |
791 | } | |
792 | ||
793 | - (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame { | |
794 | return [webview_ webView:sender didCommitLoadForFrame:frame]; | |
795 | } | |
796 | ||
797 | - (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame { | |
798 | return [webview_ webView:sender didReceiveDocTypeForFrame:frame]; | |
799 | } | |
800 | ||
801 | - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { | |
802 | if ([frame parentFrame] == nil) { | |
803 | [self _finishLoading]; | |
804 | ||
805 | if (DOMDocument *document = [frame DOMDocument]) | |
806 | if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"]) | |
807 | for (DOMHTMLBodyElement *body in bodies) { | |
808 | DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]); | |
809 | ||
810 | bool colored(false); | |
811 | ||
812 | if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) { | |
f4772bd1 JF |
813 | if ([color primitiveType] == DOM_CSS_RGBCOLOR) { |
814 | DOMRGBColor *rgb([color getRGBColorValue]); | |
815 | ||
816 | float red([[rgb red] getFloatValue:DOM_CSS_NUMBER]); | |
817 | float green([[rgb green] getFloatValue:DOM_CSS_NUMBER]); | |
818 | float blue([[rgb blue] getFloatValue:DOM_CSS_NUMBER]); | |
819 | float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]); | |
820 | ||
821 | UIColor *uic(nil); | |
822 | ||
823 | if (red == 0xc7 && green == 0xce && blue == 0xd5) | |
824 | uic = [UIColor pinStripeColor]; | |
825 | else if (alpha != 0) | |
826 | uic = [UIColor | |
827 | colorWithRed:(red / 255) | |
828 | green:(green / 255) | |
829 | blue:(blue / 255) | |
830 | alpha:alpha | |
831 | ]; | |
832 | ||
833 | if (uic != nil) { | |
834 | colored = true; | |
835 | [scroller_ setBackgroundColor:uic]; | |
836 | } | |
bde2d79b JF |
837 | } |
838 | } | |
839 | ||
840 | if (!colored) | |
841 | [scroller_ setBackgroundColor:[UIColor pinStripeColor]]; | |
842 | break; | |
843 | } | |
844 | } | |
845 | ||
846 | return [webview_ webView:sender didFinishLoadForFrame:frame]; | |
847 | } | |
848 | ||
849 | - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { | |
850 | if ([frame parentFrame] != nil) | |
851 | return; | |
2517c95f JF |
852 | if (reloading_) |
853 | return; | |
bde2d79b JF |
854 | [self _finishLoading]; |
855 | ||
856 | [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", | |
857 | [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString], | |
858 | [[error localizedDescription] stringByAddingPercentEscapes] | |
859 | ]]]; | |
f4772bd1 JF |
860 | |
861 | error_ = true; | |
bde2d79b JF |
862 | } |
863 | ||
864 | - (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary { | |
865 | #if ForSaurik | |
866 | lprintf("Console:%s\n", [[dictionary description] UTF8String]); | |
867 | #endif | |
868 | } | |
869 | ||
7b00c562 | 870 | - (id) initWithBook:(RVBook *)book forWidth:(float)width { |
bde2d79b JF |
871 | if ((self = [super initWithBook:book]) != nil) { |
872 | loading_ = false; | |
7b00c562 JF |
873 | width_ = width; |
874 | popup_ = false; | |
bde2d79b JF |
875 | |
876 | struct CGRect bounds = [self bounds]; | |
877 | ||
878 | scroller_ = [[UIScroller alloc] initWithFrame:bounds]; | |
879 | [self addSubview:scroller_]; | |
880 | ||
881 | [scroller_ setShowBackgroundShadow:NO]; | |
882 | [scroller_ setFixedBackgroundPattern:YES]; | |
883 | [scroller_ setBackgroundColor:[UIColor pinStripeColor]]; | |
884 | ||
885 | [scroller_ setScrollingEnabled:YES]; | |
886 | [scroller_ setAdjustForContentSizeChange:YES]; | |
887 | [scroller_ setClipsSubviews:YES]; | |
888 | [scroller_ setAllowsRubberBanding:YES]; | |
889 | [scroller_ setScrollDecelerationFactor:0.99]; | |
890 | [scroller_ setDelegate:self]; | |
891 | ||
892 | CGRect webrect = [scroller_ bounds]; | |
893 | webrect.size.height = 0; | |
894 | ||
895 | WebView *webview; | |
896 | ||
897 | #if RecycleWebViews | |
898 | webview_ = [Documents_ lastObject]; | |
899 | if (webview_ != nil) { | |
900 | webview_ = [webview_ retain]; | |
901 | webview = [webview_ webView]; | |
902 | [Documents_ removeLastObject]; | |
903 | [webview_ setFrame:webrect]; | |
904 | } else { | |
905 | #else | |
906 | if (true) { | |
907 | #endif | |
908 | webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect]; | |
909 | webview = [webview_ webView]; | |
910 | ||
911 | // XXX: this is terribly (too?) expensive | |
912 | //[webview_ setDrawsBackground:NO]; | |
913 | [webview setPreferencesIdentifier:@"Cydia"]; | |
914 | ||
915 | [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)]; | |
916 | ||
917 | [webview_ setAllowsMessaging:YES]; | |
918 | ||
919 | [webview_ setTilingEnabled:YES]; | |
920 | [webview_ setDrawsGrid:NO]; | |
921 | [webview_ setLogsTilingChanges:NO]; | |
922 | [webview_ setTileMinificationFilter:kCAFilterNearest]; | |
923 | [webview_ setDetectsPhoneNumbers:NO]; | |
924 | [webview_ setAutoresizes:YES]; | |
925 | ||
926 | [webview_ setMinimumScale:0.25f forDocumentTypes:0x10]; | |
7b00c562 | 927 | [webview_ setMaximumScale:5.00f forDocumentTypes:0x10]; |
bde2d79b | 928 | [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10]; |
7b00c562 | 929 | //[webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10]; |
bde2d79b JF |
930 | |
931 | [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2]; | |
932 | ||
7b00c562 | 933 | [webview_ setMinimumScale:1.00f forDocumentTypes:0x8]; |
bde2d79b JF |
934 | [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8]; |
935 | [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8]; | |
936 | ||
937 | [webview_ _setDocumentType:0x4]; | |
938 | ||
939 | [webview_ setZoomsFocusedFormControl:YES]; | |
940 | [webview_ setContentsPosition:7]; | |
941 | [webview_ setEnabledGestures:0xa]; | |
942 | [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled]; | |
943 | [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller]; | |
944 | ||
945 | [webview_ setSmoothsFonts:YES]; | |
7b00c562 | 946 | [webview_ setAllowsImageSheet:YES]; |
bde2d79b | 947 | [webview _setUsesLoaderCache:YES]; |
7b00c562 JF |
948 | |
949 | [webview setGroupName:@"CydiaGroup"]; | |
bde2d79b JF |
950 | [webview _setLayoutInterval:0]; |
951 | } | |
952 | ||
7b00c562 JF |
953 | [webview_ setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10]; |
954 | ||
bde2d79b JF |
955 | [webview_ setDelegate:self]; |
956 | [webview_ setGestureDelegate:self]; | |
957 | [scroller_ addSubview:webview_]; | |
958 | ||
959 | //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | |
960 | ||
961 | CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite]; | |
962 | indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)]; | |
963 | [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite]; | |
964 | ||
965 | Package *package([[Database sharedInstance] packageWithName:@"cydia"]); | |
966 | NSString *application = package == nil ? @"Cydia" : [NSString | |
967 | stringWithFormat:@"Cydia/%@", | |
968 | [package installed] | |
68c05606 JF |
969 | ]; |
970 | ||
7b00c562 JF |
971 | if (Product_ != nil) |
972 | application = [NSString stringWithFormat:@"%@ Version/%@", application, Product_]; | |
68c05606 | 973 | if (Build_ != nil) |
7b00c562 JF |
974 | application = [NSString stringWithFormat:@"%@ Mobile/%@", application, Build_]; |
975 | if (Safari_ != nil) | |
976 | application = [NSString stringWithFormat:@"%@ Safari/%@", application, Safari_]; | |
68c05606 JF |
977 | |
978 | /* XXX: lookup application directory? */ | |
979 | /*if (NSDictionary *safari = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) | |
980 | if (NSString *version = [safari objectForKey:@"SafariProductVersion"]) | |
981 | application = [NSString stringWithFormat:@"Version/%@ %@", version, application];*/ | |
982 | ||
983 | [webview setApplicationNameForUserAgent:application]; | |
bde2d79b JF |
984 | |
985 | indirect_ = [[IndirectDelegate alloc] initWithDelegate:self]; | |
45f88d41 | 986 | cydia_ = [[CydiaObject alloc] initWithDelegate:indirect_]; |
bde2d79b JF |
987 | |
988 | [webview setFrameLoadDelegate:self]; | |
989 | [webview setResourceLoadDelegate:indirect_]; | |
990 | [webview setUIDelegate:self]; | |
991 | [webview setScriptDebugDelegate:self]; | |
992 | [webview setPolicyDelegate:self]; | |
993 | ||
994 | [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; | |
995 | [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; | |
996 | } return self; | |
997 | } | |
998 | ||
7b00c562 JF |
999 | - (id) initWithBook:(RVBook *)book { |
1000 | return [self initWithBook:book forWidth:[[self class] defaultWidth]]; | |
1001 | } | |
1002 | ||
bde2d79b JF |
1003 | - (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event { |
1004 | [webview_ redrawScaledDocument]; | |
1005 | } | |
1006 | ||
1007 | - (void) _rightButtonClicked { | |
1008 | if (function_ == nil) { | |
1009 | reloading_ = true; | |
1010 | [self reloadURL]; | |
1011 | } else { | |
1012 | WebView *webview([webview_ webView]); | |
1013 | WebFrame *frame([webview mainFrame]); | |
1014 | ||
1015 | id _private(MSHookIvar<id>(webview, "_private")); | |
1016 | WebCore::Page *page(_private == nil ? NULL : MSHookIvar<WebCore::Page *>(_private, "page")); | |
1017 | WebCore::Settings *settings(page == NULL ? NULL : page->settings()); | |
1018 | ||
1019 | bool no; | |
1020 | if (settings == NULL) | |
1021 | no = 0; | |
1022 | else { | |
1023 | no = settings->JavaScriptCanOpenWindowsAutomatically(); | |
1024 | settings->setJavaScriptCanOpenWindowsAutomatically(true); | |
1025 | } | |
1026 | ||
1027 | [delegate_ clearFirstResponder]; | |
1028 | JSObjectRef function([function_ JSObject]); | |
1029 | JSGlobalContextRef context([frame globalContext]); | |
1030 | JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL); | |
1031 | ||
1032 | if (settings != NULL) | |
1033 | settings->setJavaScriptCanOpenWindowsAutomatically(no); | |
1034 | } | |
1035 | } | |
1036 | ||
1037 | - (id) _rightButtonTitle { | |
1038 | return button_ != nil ? button_ : @"Reload"; | |
1039 | } | |
1040 | ||
1041 | - (id) rightButtonTitle { | |
1042 | return [self _loading] ? @"" : [self _rightButtonTitle]; | |
1043 | } | |
1044 | ||
1045 | - (UINavigationButtonStyle) rightButtonStyle { | |
1046 | if (style_ == nil) normal: | |
1047 | return UINavigationButtonStyleNormal; | |
1048 | else if ([style_ isEqualToString:@"Normal"]) | |
1049 | return UINavigationButtonStyleNormal; | |
1050 | else if ([style_ isEqualToString:@"Back"]) | |
1051 | return UINavigationButtonStyleBack; | |
1052 | else if ([style_ isEqualToString:@"Highlighted"]) | |
1053 | return UINavigationButtonStyleHighlighted; | |
1054 | else if ([style_ isEqualToString:@"Destructive"]) | |
1055 | return UINavigationButtonStyleDestructive; | |
1056 | else goto normal; | |
1057 | } | |
1058 | ||
1059 | - (NSString *) title { | |
1060 | return title_ == nil ? @"Loading" : title_; | |
1061 | } | |
1062 | ||
1063 | - (NSString *) backButtonTitle { | |
1064 | return @"Browser"; | |
1065 | } | |
1066 | ||
1067 | - (void) setPageActive:(BOOL)active { | |
1068 | if (!active) | |
1069 | [indicator_ removeFromSuperview]; | |
1070 | else | |
1071 | [[book_ navigationBar] addSubview:indicator_]; | |
1072 | } | |
1073 | ||
1074 | - (void) resetViewAnimated:(BOOL)animated { | |
1075 | } | |
1076 | ||
1077 | - (void) setPushed:(bool)pushed { | |
1078 | pushed_ = pushed; | |
1079 | } | |
1080 | ||
7b00c562 JF |
1081 | + (float) defaultWidth { |
1082 | return 980; | |
1083 | } | |
1084 | ||
bde2d79b | 1085 | @end |