]> git.saurik.com Git - cydia.git/blobdiff - CyteKit/WebViewController.mm
Use two rows for Removing Essentials buttons.
[cydia.git] / CyteKit / WebViewController.mm
index 860955e1b51ca5114a6e9c96e07a7b0805917fce..9c85b29703b54a222dc48e52b9a155c2940ab5b3 100644 (file)
@@ -1,8 +1,11 @@
 #include "CyteKit/UCPlatform.h"
+#include "CyteKit/WebViewController.h"
+
+#include "CyteKit/MFMailComposeViewController-MailToURL.h"
 
-#include <UIKit/UIKit.h>
 #include "iPhonePrivate.h"
 
+#include "CyteKit/IndirectDelegate.h"
 #include "CyteKit/Localize.h"
 #include "CyteKit/WebViewController.h"
 #include "CyteKit/PerlCompatibleRegEx.hpp"
@@ -14,7 +17,8 @@ extern NSString * const kCAFilterNearest;
 
 #include <WebCore/WebCoreThread.h>
 
-#include <WebKit/WebPreferences.h>
+#import <WebKit/WebKitErrors.h>
+#import <WebKit/WebPreferences.h>
 
 #include <WebKit/DOMCSSPrimitiveValue.h>
 #include <WebKit/DOMCSSStyleDeclaration.h>
@@ -22,6 +26,9 @@ extern NSString * const kCAFilterNearest;
 #include <WebKit/DOMHTMLBodyElement.h>
 #include <WebKit/DOMRGBColor.h>
 
+#include <dlfcn.h>
+#include <objc/runtime.h>
+
 #define ForSaurik 0
 #define DefaultTimeout_ 120.0
 
@@ -31,13 +38,8 @@ extern NSString * const kCAFilterNearest;
 
 #define lprintf(args...) fprintf(stderr, args)
 
-template <typename Type_>
-static inline void CYRelease(Type_ &value) {
-    if (value != nil) {
-        [value release];
-        value = nil;
-    }
-}
+// XXX: centralize these special class things to some file or mechanism?
+static Class $MFMailComposeViewController;
 
 float CYScrollViewDecelerationRateNormal;
 
@@ -46,21 +48,15 @@ float CYScrollViewDecelerationRateNormal;
 - (void) _setAllowsMessaging:(BOOL)allows;
 @end
 
-@interface WebPreferences (Apple)
-+ (void) _setInitialDefaultTextEncodingToSystemEncoding;
-- (void) _setLayoutInterval:(NSInteger)interval;
-- (void) setOfflineWebApplicationCacheEnabled:(BOOL)enabled;
-@end
+@implementation WebFrame (Cydia)
 
-/* Indirect Delegate {{{ */
-@interface IndirectDelegate : NSObject {
-    _transient volatile id delegate_;
+- (NSString *) description {
+    return [NSString stringWithFormat:@"<%s: %p, %@>", class_getName([self class]), self, [[[([self provisionalDataSource] ?: [self dataSource]) request] URL] absoluteString]];
 }
 
-- (void) setDelegate:(id)delegate;
-- (id) initWithDelegate:(id)delegate;
 @end
 
+/* Indirect Delegate {{{ */
 @implementation IndirectDelegate
 
 - (void) setDelegate:(id)delegate {
@@ -126,59 +122,77 @@ float CYScrollViewDecelerationRateNormal;
 + (void) _initialize {
     [WebPreferences _setInitialDefaultTextEncodingToSystemEncoding];
 
+    dlopen("/System/Library/Frameworks/MessageUI.framework/MessageUI", RTLD_GLOBAL | RTLD_LAZY);
+    $MFMailComposeViewController = objc_getClass("MFMailComposeViewController");
+
     if (float *_UIScrollViewDecelerationRateNormal = reinterpret_cast<float *>(dlsym(RTLD_DEFAULT, "UIScrollViewDecelerationRateNormal")))
         CYScrollViewDecelerationRateNormal = *_UIScrollViewDecelerationRateNormal;
     else // XXX: this actually might be fast on some older systems: we should look into this
         CYScrollViewDecelerationRateNormal = 0.998;
 }
 
+- (bool) retainsNetworkActivityIndicator {
+    return true;
+}
+
+- (void) releaseNetworkActivityIndicator {
+    if ([loading_ count] != 0) {
+        [loading_ removeAllObjects];
+
+        if ([self retainsNetworkActivityIndicator])
+            [delegate_ releaseNetworkActivityIndicator];
+    }
+}
+
 - (void) dealloc {
 #if LogBrowser
     NSLog(@"[CyteWebViewController dealloc]");
 #endif
 
-    [webview_ setDelegate:nil];
-
-    [indirect_ setDelegate:nil];
-    [indirect_ release];
-
-    if (challenge_ != nil)
-        [challenge_ release];
+    [self releaseNetworkActivityIndicator];
 
-    if (title_ != nil)
-        [title_ release];
-
-    if ([loading_ count] != 0)
-        [delegate_ releaseNetworkActivityIndicator];
-    [loading_ release];
-
-    [reloaditem_ release];
-    [loadingitem_ release];
+    [super dealloc];
+}
 
-    [indicator_ release];
+- (NSString *) description {
+    return [NSString stringWithFormat:@"<%s: %p, %@>", class_getName([self class]), self, [[request_ URL] absoluteString]];
+}
 
-    [super dealloc];
+- (CyteWebView *) webView {
+    return (CyteWebView *) [self view];
 }
 
 - (NSURL *) URLWithURL:(NSURL *)url {
     return url;
 }
 
-- (NSURLRequest *) requestWithURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
-    return [NSURLRequest
+- (NSURLRequest *) requestWithURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy referrer:(NSString *)referrer {
+    NSMutableURLRequest *request([NSMutableURLRequest
         requestWithURL:[self URLWithURL:url]
         cachePolicy:policy
         timeoutInterval:DefaultTimeout_
-    ];
+    ]);
+
+    [request setValue:referrer forHTTPHeaderField:@"Referer"];
+
+    return request;
 }
 
-- (void) setURL:(NSURL *)url {
+- (void) setRequest:(NSURLRequest *)request {
     _assert(request_ == nil);
-    request_ = [self requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
+    request_ = request;
+}
+
+- (void) setURL:(NSURL *)url {
+    [self setURL:url withReferrer:nil];
+}
+
+- (void) setURL:(NSURL *)url withReferrer:(NSString *)referrer {
+    [self setRequest:[self requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy referrer:referrer]];
 }
 
 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
-    [self loadRequest:[self requestWithURL:url cachePolicy:policy]];
+    [self loadRequest:[self requestWithURL:url cachePolicy:policy referrer:nil]];
 }
 
 - (void) loadURL:(NSURL *)url {
@@ -191,9 +205,10 @@ float CYScrollViewDecelerationRateNormal;
 #endif
 
     error_ = false;
+    ready_ = true;
 
     WebThreadLocked lock;
-    [webview_ loadRequest:request];
+    [[self webView] loadRequest:request];
 }
 
 - (void) reloadURLWithCache:(BOOL)cache {
@@ -229,7 +244,11 @@ float CYScrollViewDecelerationRateNormal;
 
 - (void) reloadData {
     [super reloadData];
-    [self reloadURLWithCache:YES];
+
+    if (ready_)
+        [self dispatchEvent:@"CydiaReloadData"];
+    else
+        [self reloadURLWithCache:YES];
 }
 
 - (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
@@ -265,7 +284,7 @@ float CYScrollViewDecelerationRateNormal;
 }
 
 - (void) _setViewportWidth {
-    [[webview_ _documentView] setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
+    [[[self webView] _documentView] setViewportSize:CGSizeMake(width_, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
 }
 
 - (void) setViewportWidth:(float)width {
@@ -285,8 +304,31 @@ float CYScrollViewDecelerationRateNormal;
     [self _setViewportWidth];
 }
 
+- (void) mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
+    [self dismissModalViewControllerAnimated:YES];
+}
+
+- (void) _setupMail:(MFMailComposeViewController *)controller {
+}
+
 - (void) _openMailToURL:(NSURL *)url {
-    [[UIApplication sharedApplication] openURL:url];// asPanel:YES];
+    if ($MFMailComposeViewController != nil && [$MFMailComposeViewController canSendMail]) {
+        MFMailComposeViewController *controller([[[$MFMailComposeViewController alloc] init] autorelease]);
+        [controller setMailComposeDelegate:self];
+
+        [controller setMailToURL:url];
+
+        [self _setupMail:controller];
+
+        [self presentModalViewController:controller animated:YES];
+        return;
+    }
+
+    UIApplication *app([UIApplication sharedApplication]);
+    if ([app respondsToSelector:@selector(openURL:asPanel:)])
+        [app openURL:url asPanel:YES];
+    else
+        [app openURL:url];
 }
 
 - (bool) _allowJavaScriptPanel {
@@ -312,12 +354,21 @@ float CYScrollViewDecelerationRateNormal;
 }
 
 - (void) _didFailWithError:(NSError *)error forFrame:(WebFrame *)frame {
-    [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]];
+    NSValue *object([NSValue valueWithNonretainedObject:frame]);
+    if (![loading_ containsObject:object])
+        return;
+    [loading_ removeObject:object];
+
     [self _didFinishLoading];
 
-    if ([error code] == NSURLErrorCancelled)
+    if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled)
         return;
 
+    if ([[error domain] isEqualToString:WebKitErrorDomain] && [error code] == WebKitErrorFrameLoadInterruptedByPolicyChange) {
+        request_ = nil;
+        return;
+    }
+
     if ([frame parentFrame] == nil) {
         [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
             [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
@@ -328,15 +379,24 @@ float CYScrollViewDecelerationRateNormal;
     }
 }
 
-- (void) pushRequest:(NSURLRequest *)request asPop:(bool)pop {
+- (void) pushRequest:(NSURLRequest *)request forAction:(NSDictionary *)action asPop:(bool)pop {
+    WebFrame *frame(nil);
+    if (NSDictionary *WebActionElement = [action objectForKey:@"WebActionElementKey"])
+        frame = [WebActionElement objectForKey:@"WebElementFrame"];
+    if (frame == nil)
+        frame = [[[[self webView] _documentView] webView] mainFrame];
+
+    WebDataSource *source([frame provisionalDataSource] ?: [frame dataSource]);
+    NSString *referrer([request valueForHTTPHeaderField:@"Referer"] ?: [[[source request] URL] absoluteString]);
+
     NSURL *url([request URL]);
 
     // XXX: filter to internal usage?
-    CYViewController *page([delegate_ pageForURL:url forExternal:NO]);
+    CyteViewController *page([delegate_ pageForURL:url forExternal:NO withReferrer:referrer]);
 
     if (page == nil) {
         CyteWebViewController *browser([[[class_ alloc] init] autorelease]);
-        [browser loadRequest:request];
+        [browser setRequest:request];
         page = browser;
     }
 
@@ -359,15 +419,17 @@ float CYScrollViewDecelerationRateNormal;
         ] autorelease]];
 
         [[self navigationController] presentModalViewController:navigation animated:YES];
-
-        [delegate_ unloadData];
     }
 }
 
 // CyteWebViewDelegate {{{
 - (void) webView:(WebView *)view addMessageToConsole:(NSDictionary *)message {
 #if LogMessages
-    static Pcre irritating("^(?:The page at .* displayed insecure content from .*\\.|Unsafe JavaScript attempt to access frame with URL .* from frame with URL .*\\. Domains, protocols and ports must match\\.)\\n$");
+    static Pcre irritating("^(?"
+        ":" "The page at .* displayed insecure content from .*\\."
+        "|" "Unsafe JavaScript attempt to access frame with URL .* from frame with URL .*\\. Domains, protocols and ports must match\\."
+    ")\\n$");
+
     if (NSString *data = [message objectForKey:@"message"])
         if (irritating(data))
             return;
@@ -378,41 +440,46 @@ float CYScrollViewDecelerationRateNormal;
 
 - (void) webView:(WebView *)view decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
 #if LogBrowser
-    NSLog(@"decidePolicyForNavigationAction:%@ request:%@ frame:%@", action, request, frame);
+    NSLog(@"decidePolicyForNavigationAction:%@ request:%@ %@ frame:%@", action, request, [request allHTTPHeaderFields], frame);
 #endif
 
     if ([frame parentFrame] == nil) {
         if (!error_) {
             NSURL *url(request == nil ? nil : [request URL]);
 
-            if (request_ == nil || [self allowsNavigationAction] || [[request_ URL] isEqual:url])
-                request_ = request;
-            else {
+            if (request_ != nil && ![[request_ URL] isEqual:url] && ![self allowsNavigationAction]) {
                 if (url != nil)
-                    [self pushRequest:request asPop:NO];
+                    [self pushRequest:request forAction:action asPop:NO];
                 [listener ignore];
             }
         }
     }
 }
 
-- (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
+- (void) webView:(WebView *)view didDecidePolicy:(CYWebPolicyDecision)decision forNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame {
+    if ([frame parentFrame] == nil)
+        if (decision == CYWebPolicyDecisionUse)
+            if (!error_)
+                request_ = request;
+}
+
+- (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
 #if LogBrowser
-    NSLog(@"decidePolicyForNewWindowAction:%@ request:%@ newFrameName:%@", action, request, frame);
+    NSLog(@"decidePolicyForNewWindowAction:%@ request:%@ %@ newFrameName:%@", action, request, [request allHTTPHeaderFields], name);
 #endif
 
     NSURL *url([request URL]);
     if (url == nil)
         return;
 
-    if ([frame isEqualToString:@"_open"])
+    if ([name isEqualToString:@"_open"])
         [delegate_ openURL:url];
     else {
         NSString *scheme([[url scheme] lowercaseString]);
         if ([scheme isEqualToString:@"mailto"])
             [self _openMailToURL:url];
         else
-            [self pushRequest:request asPop:[frame isEqualToString:@"_popup"]];
+            [self pushRequest:request forAction:action asPop:[name isEqualToString:@"_popup"]];
     }
 
     [listener ignore];
@@ -421,6 +488,16 @@ float CYScrollViewDecelerationRateNormal;
 - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
 }
 
+- (void) webView:(WebView *)view didCommitLoadForFrame:(WebFrame *)frame {
+#if LogBrowser
+    NSLog(@"didCommitLoadForFrame:%@", frame);
+#endif
+
+    if ([frame parentFrame] == nil) {
+        loaded_ = true;
+    }
+}
+
 - (void) webView:(WebView *)view didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
 #if LogBrowser
     NSLog(@"didFailLoadWithError:%@ forFrame:%@", error, frame);
@@ -438,7 +515,10 @@ float CYScrollViewDecelerationRateNormal;
 }
 
 - (void) webView:(WebView *)view didFinishLoadForFrame:(WebFrame *)frame {
-    [loading_ removeObject:[NSValue valueWithNonretainedObject:frame]];
+    NSValue *object([NSValue valueWithNonretainedObject:frame]);
+    if (![loading_ containsObject:object])
+        return;
+    [loading_ removeObject:object];
 
     if ([frame parentFrame] == nil) {
         if (DOMDocument *document = [frame DOMDocument])
@@ -481,23 +561,29 @@ float CYScrollViewDecelerationRateNormal;
     if ([frame parentFrame] != nil)
         return;
 
-    if (title_ != nil)
-        [title_ autorelease];
-    title_ = [title retain];
+    title_ = title;
 
     [[self navigationItem] setTitle:title_];
 }
 
 - (void) webView:(WebView *)view didStartProvisionalLoadForFrame:(WebFrame *)frame {
+#if LogBrowser
+    NSLog(@"didStartProvisionalLoadForFrame:%@", frame);
+#endif
+
     [loading_ addObject:[NSValue valueWithNonretainedObject:frame]];
 
     if ([frame parentFrame] == nil) {
-        CYRelease(title_);
+        title_ = nil;
         custom_ = nil;
         style_ = nil;
         function_ = nil;
 
+        allowsNavigationAction_ = true;
+
         [self setHidesNavigationBar:NO];
+        [self setScrollAlwaysBounceVertical:true];
+        [self setScrollIndicatorStyle:UIScrollViewIndicatorStyleDefault];
 
         // XXX: do we still need to do this?
         [[self navigationItem] setTitle:nil];
@@ -532,7 +618,7 @@ float CYScrollViewDecelerationRateNormal;
 // }}}
 
 - (void) close {
-    [[self navigationController] dismissModalViewControllerAnimated:YES];
+    [[[self navigationController] parentViewController] dismissModalViewControllerAnimated:YES];
 }
 
 - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
@@ -570,7 +656,6 @@ float CYScrollViewDecelerationRateNormal;
             _nodefault
         }
 
-        [challenge_ release];
         challenge_ = nil;
 
         [alert dismissWithClickedButtonIndex:-1 animated:YES];
@@ -579,7 +664,7 @@ float CYScrollViewDecelerationRateNormal;
         } else if (button == [alert firstOtherButtonIndex]) {
             if (request_ != nil) {
                 WebThreadLocked lock;
-                [webview_ loadRequest:request_];
+                [[self webView] loadRequest:request_];
             }
         }
 
@@ -598,7 +683,12 @@ float CYScrollViewDecelerationRateNormal;
 }
 
 - (UIBarButtonItem *) customButton {
-    return custom_ == [NSNull null] ? nil : [[[UIBarButtonItem alloc]
+    if (custom_ == nil)
+        return nil;
+    else if (custom_ == [NSNull null])
+        return (UIBarButtonItem *) [NSNull null];
+
+    return [[[UIBarButtonItem alloc]
         initWithTitle:static_cast<NSString *>(custom_.operator NSObject *())
         style:[self rightButtonStyle]
         target:self
@@ -606,6 +696,27 @@ float CYScrollViewDecelerationRateNormal;
     ] autorelease];
 }
 
+- (UIBarButtonItem *) leftButton {
+    UINavigationItem *item([self navigationItem]);
+    if ([item backBarButtonItem] != nil && ![item hidesBackButton])
+        return nil;
+
+    if (UINavigationController *navigation = [self navigationController])
+        if ([[navigation parentViewController] modalViewController] == navigation)
+            return [[[UIBarButtonItem alloc]
+                initWithTitle:UCLocalize("CLOSE")
+                style:UIBarButtonItemStylePlain
+                target:self
+                action:@selector(close)
+            ] autorelease];
+
+    return nil;
+}
+
+- (void) applyLeftButton {
+    [[self navigationItem] setLeftBarButtonItem:[self leftButton]];
+}
+
 - (UIBarButtonItem *) rightButton {
     return reloaditem_;
 }
@@ -629,9 +740,13 @@ float CYScrollViewDecelerationRateNormal;
     } else {
         [indicator_ stopAnimating];
 
-        [[self navigationItem] setRightBarButtonItem:(
-            custom_ != nil ? [self customButton] : [self rightButton]
-        ) animated:YES];
+        UIBarButtonItem *button([self customButton]);
+        if (button == nil)
+            button = [self rightButton];
+        else if (button == (UIBarButtonItem *) [NSNull null])
+            button = nil;
+
+        [[self navigationItem] setRightBarButtonItem:button animated:YES];
     }
 }
 
@@ -645,7 +760,9 @@ float CYScrollViewDecelerationRateNormal;
     if ([loading_ count] != 1)
         return;
 
-    [delegate_ retainNetworkActivityIndicator];
+    if ([self retainsNetworkActivityIndicator])
+        [delegate_ retainNetworkActivityIndicator];
+
     [self didStartLoading];
 }
 
@@ -660,7 +777,9 @@ float CYScrollViewDecelerationRateNormal;
     [self applyRightButton];
     [[self navigationItem] setTitle:title_];
 
-    [delegate_ releaseNetworkActivityIndicator];
+    if ([self retainsNetworkActivityIndicator])
+        [delegate_ releaseNetworkActivityIndicator];
+
     [self didFinishLoading];
 }
 
@@ -670,115 +789,144 @@ float CYScrollViewDecelerationRateNormal;
 
 - (id) initWithWidth:(float)width ofClass:(Class)_class {
     if ((self = [super init]) != nil) {
+        width_ = width;
+        class_ = _class;
+
         allowsNavigationAction_ = true;
 
-        class_ = _class;
-        loading_ = [[NSMutableSet alloc] initWithCapacity:5];
+        loading_ = [NSMutableSet setWithCapacity:5];
+        indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
 
-        indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
+        reloaditem_ = [[[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("RELOAD")
+            style:[self rightButtonStyle]
+            target:self
+            action:@selector(reloadButtonClicked)
+        ] autorelease];
 
-        CGRect bounds([[self view] bounds]);
+        loadingitem_ = [[[UIBarButtonItem alloc]
+            initWithTitle:@" "
+            style:UIBarButtonItemStylePlain
+            target:self
+            action:@selector(reloadButtonClicked)
+        ] autorelease];
 
-        webview_ = [[[CyteWebView alloc] initWithFrame:bounds] autorelease];
-        [webview_ setDelegate:self];
-        [self setView:webview_];
+        indicator_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
+        [indicator_ setFrame:CGRectMake(15, 5, [indicator_ frame].size.width, [indicator_ frame].size.height)];
+        [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
 
-        if ([webview_ respondsToSelector:@selector(setDataDetectorTypes:)])
-            [webview_ setDataDetectorTypes:UIDataDetectorTypeAutomatic];
-        else
-            [webview_ setDetectsPhoneNumbers:NO];
+        [self applyLeftButton];
+        [self applyRightButton];
+    } return self;
+}
 
-        [webview_ setScalesPageToFit:YES];
+- (NSString *) applicationNameForUserAgent {
+    return nil;
+}
 
-        UIWebDocumentView *document([webview_ _documentView]);
+- (void) loadView {
+    CGRect bounds([[UIScreen mainScreen] applicationFrame]);
 
-        // XXX: I think this improves scrolling; the hardcoded-ness sucks
-        [document setTileSize:CGSizeMake(320, 500)];
+    webview_ = [[[CyteWebView alloc] initWithFrame:bounds] autorelease];
+    [webview_ setDelegate:self];
+    [self setView:webview_];
 
-        [document setBackgroundColor:[UIColor clearColor]];
+    if ([webview_ respondsToSelector:@selector(setDataDetectorTypes:)])
+        [webview_ setDataDetectorTypes:UIDataDetectorTypeAutomatic];
+    else
+        [webview_ setDetectsPhoneNumbers:NO];
 
-        // XXX: this is terribly (too?) expensive
-        [document setDrawsBackground:NO];
+    [webview_ setScalesPageToFit:YES];
 
-        WebView *webview([document webView]);
-        WebPreferences *preferences([webview preferences]);
+    UIWebDocumentView *document([webview_ _documentView]);
 
-        // XXX: I have no clue if I actually /want/ this modification
-        if ([webview respondsToSelector:@selector(_setLayoutInterval:)])
-            [webview _setLayoutInterval:0];
-        else if ([preferences respondsToSelector:@selector(_setLayoutInterval:)])
-            [preferences _setLayoutInterval:0];
+    // XXX: I think this improves scrolling; the hardcoded-ness sucks
+    [document setTileSize:CGSizeMake(320, 500)];
 
-        [preferences setCacheModel:WebCacheModelDocumentBrowser];
-        [preferences setOfflineWebApplicationCacheEnabled:YES];
+    [document setBackgroundColor:[UIColor clearColor]];
+
+    // XXX: this is terribly (too?) expensive
+    [document setDrawsBackground:NO];
+
+    WebView *webview([document webView]);
+    WebPreferences *preferences([webview preferences]);
+
+    // XXX: I have no clue if I actually /want/ this modification
+    if ([webview respondsToSelector:@selector(_setLayoutInterval:)])
+        [webview _setLayoutInterval:0];
+    else if ([preferences respondsToSelector:@selector(_setLayoutInterval:)])
+        [preferences _setLayoutInterval:0];
+
+    [preferences setCacheModel:WebCacheModelDocumentBrowser];
+    [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
+    [preferences setOfflineWebApplicationCacheEnabled:YES];
+
+    if (NSString *agent = [self applicationNameForUserAgent])
+        [webview setApplicationNameForUserAgent:agent];
+
+    if ([webview respondsToSelector:@selector(setShouldUpdateWhileOffscreen:)])
+        [webview setShouldUpdateWhileOffscreen:NO];
 
 #if LogMessages
-        if ([document respondsToSelector:@selector(setAllowsMessaging:)])
-            [document setAllowsMessaging:YES];
-        if ([webview respondsToSelector:@selector(_setAllowsMessaging:)])
-            [webview _setAllowsMessaging:YES];
+    if ([document respondsToSelector:@selector(setAllowsMessaging:)])
+        [document setAllowsMessaging:YES];
+    if ([webview respondsToSelector:@selector(_setAllowsMessaging:)])
+        [webview _setAllowsMessaging:YES];
 #endif
 
-        if ([webview_ respondsToSelector:@selector(_scrollView)]) {
-            scroller_ = [webview_ _scrollView];
+    if ([webview_ respondsToSelector:@selector(_scrollView)]) {
+        scroller_ = [webview_ _scrollView];
 
-            [scroller_ setDirectionalLockEnabled:YES];
-            [scroller_ setDecelerationRate:CYScrollViewDecelerationRateNormal];
-            [scroller_ setDelaysContentTouches:NO];
+        [scroller_ setDirectionalLockEnabled:YES];
+        [scroller_ setDecelerationRate:CYScrollViewDecelerationRateNormal];
+        [scroller_ setDelaysContentTouches:NO];
 
-            [scroller_ setCanCancelContentTouches:YES];
-        } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
-            UIScroller *scroller([webview_ _scroller]);
-            scroller_ = (UIScrollView *) scroller;
+        [scroller_ setCanCancelContentTouches:YES];
+    } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
+        UIScroller *scroller([webview_ _scroller]);
+        scroller_ = (UIScrollView *) scroller;
 
-            [scroller setDirectionalScrolling:YES];
-            // XXX: we might be better off /not/ setting this on older systems
-            [scroller setScrollDecelerationFactor:CYScrollViewDecelerationRateNormal]; /* 0.989324 */
-            [scroller setScrollHysteresis:0]; /* 8 */
+        [scroller setDirectionalScrolling:YES];
+        // XXX: we might be better off /not/ setting this on older systems
+        [scroller setScrollDecelerationFactor:CYScrollViewDecelerationRateNormal]; /* 0.989324 */
+        [scroller setScrollHysteresis:0]; /* 8 */
 
-            [scroller setThumbDetectionEnabled:NO];
+        [scroller setThumbDetectionEnabled:NO];
 
-            // use NO with UIApplicationUseLegacyEvents(YES)
-            [scroller setEventMode:YES];
+        // use NO with UIApplicationUseLegacyEvents(YES)
+        [scroller setEventMode:YES];
 
-            // XXX: this is handled by setBounces, right?
-            //[scroller setAllowsRubberBanding:YES];
-        }
+        // XXX: this is handled by setBounces, right?
+        //[scroller setAllowsRubberBanding:YES];
+    }
 
-        [scroller_ setFixedBackgroundPattern:YES];
-        [scroller_ setBackgroundColor:[UIColor clearColor]];
-        [scroller_ setClipsSubviews:YES];
+    [scroller_ setFixedBackgroundPattern:YES];
+    [scroller_ setBackgroundColor:[UIColor clearColor]];
+    [scroller_ setClipsSubviews:YES];
 
-        [scroller_ setBounces:YES];
-        [scroller_ setScrollingEnabled:YES];
-        [scroller_ setShowBackgroundShadow:NO];
+    [scroller_ setBounces:YES];
+    [scroller_ setScrollingEnabled:YES];
+    [scroller_ setShowBackgroundShadow:NO];
 
-        [self setViewportWidth:width];
+    [self setViewportWidth:width_];
 
-        reloaditem_ = [[UIBarButtonItem alloc]
-            initWithTitle:UCLocalize("RELOAD")
-            style:[self rightButtonStyle]
-            target:self
-            action:@selector(reloadButtonClicked)
-        ];
+    UITableView *table([[[UITableView alloc] initWithFrame:[webview_ bounds] style:UITableViewStyleGrouped] autorelease]);
+    [table setScrollsToTop:NO];
+    [webview_ insertSubview:table atIndex:0];
 
-        loadingitem_ = [[UIBarButtonItem alloc]
-            initWithTitle:@" "
-            style:UIBarButtonItemStylePlain
-            target:self
-            action:@selector(reloadButtonClicked)
-        ];
+    [table setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
+    [webview_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
 
-        indicator_ = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; 
-        [indicator_ setFrame:CGRectMake(15, 5, [indicator_ frame].size.width, [indicator_ frame].size.height)];
+    ready_ = false;
+}
 
-        UITableView *table([[[UITableView alloc] initWithFrame:bounds style:UITableViewStyleGrouped] autorelease]);
-        [webview_ insertSubview:table atIndex:0];
+- (void) releaseSubviews {
+    webview_ = nil;
+    scroller_ = nil;
 
-        [table setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
-        [webview_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
-        [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
-    } return self;
+    [self releaseNetworkActivityIndicator];
+
+    [super releaseSubviews];
 }
 
 - (id) initWithWidth:(float)width {
@@ -795,40 +943,21 @@ float CYScrollViewDecelerationRateNormal;
     } return self;
 }
 
+- (id) initWithRequest:(NSURLRequest *)request {
+    if ((self = [self init]) != nil) {
+        [self setRequest:request];
+    } return self;
+}
+
 - (void) callFunction:(WebScriptObject *)function {
     WebThreadLocked lock;
 
-    WebView *webview([[webview_ _documentView] webView]);
+    WebView *webview([[[self webView] _documentView] webView]);
     WebFrame *frame([webview mainFrame]);
-    WebPreferences *preferences([webview preferences]);
-
-    bool maybe([preferences javaScriptCanOpenWindowsAutomatically]);
-    [preferences setJavaScriptCanOpenWindowsAutomatically:NO];
 
-    /*id _private(MSHookIvar<id>(webview, "_private"));
-    WebCore::Page *page(_private == nil ? NULL : MSHookIvar<WebCore::Page *>(_private, "page"));
-    WebCore::Settings *settings(page == NULL ? NULL : page->settings());
-
-    bool no;
-    if (settings == NULL)
-        no = 0;
-    else {
-        no = settings->JavaScriptCanOpenWindowsAutomatically();
-        settings->setJavaScriptCanOpenWindowsAutomatically(true);
-    }*/
-
-    if (UIWindow *window = [[self view] window])
-        if (UIResponder *responder = [window firstResponder])
-            [responder resignFirstResponder];
-
-    JSObjectRef object([function JSObject]);
     JSGlobalContextRef context([frame globalContext]);
+    JSObjectRef object([function JSObject]);
     JSObjectCallAsFunction(context, object, NULL, 0, NULL, NULL);
-
-    /*if (settings != NULL)
-        settings->setJavaScriptCanOpenWindowsAutomatically(no);*/
-
-    [preferences setJavaScriptCanOpenWindowsAutomatically:maybe];
 }
 
 - (void) reloadButtonClicked {
@@ -872,6 +1001,7 @@ float CYScrollViewDecelerationRateNormal;
 
 - (void) setHidesBackButton:(bool)value {
     [[self navigationItem] setHidesBackButton:value];
+    [self applyLeftButton];
 }
 
 - (void) setHidesBackButtonByNumber:(NSNumber *)value {
@@ -879,7 +1009,7 @@ float CYScrollViewDecelerationRateNormal;
 }
 
 - (void) dispatchEvent:(NSString *)event {
-    [webview_ dispatchEvent:event];
+    [[self webView] dispatchEvent:event];
 }
 
 - (bool) hidesNavigationBar {
@@ -902,12 +1032,54 @@ float CYScrollViewDecelerationRateNormal;
     [self setHidesNavigationBar:[value boolValue]];
 }
 
+- (void) setScrollAlwaysBounceVertical:(bool)value {
+    if ([webview_ respondsToSelector:@selector(_scrollView)]) {
+        UIScrollView *scroller([webview_ _scrollView]);
+        [scroller setAlwaysBounceVertical:value];
+    } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
+        //UIScroller *scroller([webview_ _scroller]);
+        // XXX: I am sad here.
+    } else return;
+}
+
+- (void) setScrollAlwaysBounceVerticalNumber:(NSNumber *)value {
+    [self setScrollAlwaysBounceVertical:[value boolValue]];
+}
+
+- (void) setScrollIndicatorStyle:(UIScrollViewIndicatorStyle)style {
+    if ([webview_ respondsToSelector:@selector(_scrollView)]) {
+        UIScrollView *scroller([webview_ _scrollView]);
+        [scroller setIndicatorStyle:style];
+    } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
+        UIScroller *scroller([webview_ _scroller]);
+        [scroller setScrollerIndicatorStyle:style];
+    } else return;
+}
+
+- (void) setScrollIndicatorStyleWithName:(NSString *)style {
+    UIScrollViewIndicatorStyle value;
+
+    if (false);
+    else if ([style isEqualToString:@"default"])
+        value = UIScrollViewIndicatorStyleDefault;
+    else if ([style isEqualToString:@"black"])
+        value = UIScrollViewIndicatorStyleBlack;
+    else if ([style isEqualToString:@"white"])
+        value = UIScrollViewIndicatorStyleWhite;
+    else return;
+
+    [self setScrollIndicatorStyle:value];
+}
+
 - (void) viewWillAppear:(BOOL)animated {
     visible_ = true;
 
     if ([self hidesNavigationBar])
         [self _setHidesNavigationBar:YES animated:animated];
 
+    // XXX: why isn't this evern called automatically?
+    [[self webView] setNeedsLayout];
+
     [self dispatchEvent:@"CydiaViewWillAppear"];
     [super viewWillAppear:animated];
 }