1 /* Cydget - open-source AwayView plugin multiplexer
2 * Copyright (C) 2009-2014 Jay Freeman (saurik)
5 /* GNU General Public License, Version 3 {{{ */
7 * Cydia is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published
9 * by the Free Software Foundation, either version 3 of the License,
10 * or (at your option) any later version.
12 * Cydia is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Cydia. If not, see <http://www.gnu.org/licenses/>.
22 #include <CydiaSubstrate/CydiaSubstrate.h>
23 #include <UIKit/UIKit.h>
27 #include <SpringBoardUI/SBAwayViewPluginController.h>
29 #include <WebKit/WebFrame.h>
30 #include <WebKit/WebView.h>
31 #include <WebKit/WebPreferences-WebPrivate.h>
33 #include <WebCycript.h>
36 #include <unicode/uregex.h>
41 _disused static unsigned trace_;
43 #define _trace() do { \
44 NSLog(@"_trace(%u)@%s:%u[%s](%p)\n", \
45 trace_++, __FILE__, __LINE__, __FUNCTION__, pthread_self() \
49 typedef uint16_t UChar;
51 @interface TPBottomLockBar
52 - (CGFloat) defaultHeight;
55 @interface UIScroller : UIView
56 - (void) setDirectionalScrolling:(BOOL)directional;
57 - (void) setScrollDecelerationFactor:(CGFloat)factor;
58 - (void) setScrollHysteresis:(CGFloat)hysteresis;
59 - (void) setThumbDetectionEnabled:(BOOL)enabled;
62 @interface UIWebDocumentView : UIView
63 - (CGRect) documentBounds;
64 - (void) enableReachability;
65 - (void) loadRequest:(NSURLRequest *)request;
66 - (void) redrawScaledDocument;
67 - (void) setAllowsImageSheet:(BOOL)allows;
68 - (void) setAllowsMessaging:(BOOL)allows;
69 - (void) setAutoresizes:(BOOL)autoresizes;
70 - (void) setContentsPosition:(NSInteger)position;
71 - (void) setDrawsBackground:(BOOL)draws;
72 - (void) _setDocumentType:(NSInteger)type;
73 - (void) setDrawsGrid:(BOOL)draws;
74 - (void) setInitialScale:(float)scale forDocumentTypes:(NSInteger)types;
75 - (void) setLogsTilingChanges:(BOOL)logs;
76 - (void) setMinimumScale:(float)scale forDocumentTypes:(NSInteger)types;
77 - (void) setMinimumSize:(CGSize)size;
78 - (void) setMaximumScale:(float)scale forDocumentTypes:(NSInteger)tpyes;
79 - (void) setSmoothsFonts:(BOOL)smooths;
80 - (void) setTileMinificationFilter:(NSString *)filter;
81 - (void) setTileSize:(CGSize)size;
82 - (void) setTilingEnabled:(BOOL)enabled;
83 - (void) setViewportSize:(CGSize)size forDocumentTypes:(NSInteger)types;
84 - (void) setZoomsFocusedFormControl:(BOOL)zooms;
85 - (void) useSelectionAssistantWithMode:(NSInteger)mode;
86 - (WebView *) webView;
89 @interface UIView (Apple)
90 - (UIScroller *) _scroller;
91 - (void) setClipsSubviews:(BOOL)clips;
92 - (void) setEnabledGestures:(NSInteger)gestures;
93 - (void) setFixedBackgroundPattern:(BOOL)fixed;
94 - (void) setGestureDelegate:(id)delegate;
95 - (void) setNeedsDisplayOnBoundsChange:(BOOL)needs;
96 - (void) setValue:(NSValue *)value forGestureAttribute:(NSInteger)attribute;
97 - (void) setZoomScale:(float)scale duration:(double)duration;
98 - (void) _setZoomScale:(float)scale duration:(double)duration;
101 @interface SBLockScreenView : UIView
102 - (BOOL) mediaControlsHidden;
105 @interface SBLockScreenNotificationListController : NSObject
106 - (BOOL) hasAnyContent;
109 @interface SBLockScreenViewController : UIViewController
110 - (SBLockScreenView *) lockScreenView;
111 - (BOOL) isShowingMediaControls;
112 - (void) _setMediaControlsVisible:(BOOL)visible;
113 - (SBLockScreenNotificationListController *) _notificationController;
116 @interface SBLockScreenManager : NSObject
117 + (SBLockScreenManager *) sharedInstance;
118 - (SBLockScreenViewController *) lockScreenViewController;
121 @protocol CydgetController
122 - (NSDictionary *) currentConfiguration;
123 - (NSString *) currentPath;
126 static Class $CydgetController(objc_getClass("CydgetController"));
128 MSClassHook(SBLockScreenManager)
131 /* ICU Regular Expression {{{ */
134 URegularExpression *regex_;
137 RegEx(const char *regex) {
139 UErrorCode status(U_ZERO_ERROR);
140 regex_ = uregex_openC(regex, 0, &error, &status);
141 if (U_FAILURE(status))
142 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"*** RegEx(): [%u] %s", error.offset, u_errorName(status)] userInfo:nil];
146 uregex_close(regex_);
149 bool operator ()(NSString *string) {
150 return operator ()(reinterpret_cast<const uint16_t *>([string cStringUsingEncoding:NSUTF16StringEncoding]), [string length]);
153 bool operator ()(const UChar *data, size_t size) {
154 UErrorCode status(U_ZERO_ERROR);
155 uregex_setText(regex_, data, size, &status);
156 if (!U_SUCCESS(status))
158 bool matches(uregex_matches(regex_, 0, &status));
159 if (!U_SUCCESS(status))
166 /* Perl-Compatible RegEx {{{ */
176 RegEx(const char *regex) :
181 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
184 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"*** RegEx(): [%u] %s", offset, error] userInfo:nil];
186 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
187 matches_ = new int[(capture_ + 1) * 3];
195 bool operator ()(NSString *data) {
196 // XXX: length is for characters, not for bytes
197 return operator ()([data UTF8String], [data length]);
200 bool operator ()(const char *data, size_t size) {
202 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
208 static float CYScrollViewDecelerationRateNormal;
210 @interface UIScrollView (Apple)
211 - (void) setDecelerationRate:(CGFloat)value;
212 - (void) setScrollingEnabled:(BOOL)enabled;
213 - (void) setShowBackgroundShadow:(BOOL)show;
216 @interface UIWebView (Apple)
217 - (UIWebDocumentView *) _documentView;
218 - (void) setDataDetectorTypes:(NSInteger)types;
219 - (void) _setDrawInWebThread:(BOOL)draw;
220 - (UIScrollView *) _scrollView;
221 - (UIScroller *) _scroller;
222 - (void) webView:(WebView *)view addMessageToConsole:(NSDictionary *)message;
223 - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame;
226 @interface WebView (Apple)
227 - (void) _setLayoutInterval:(float)interval;
228 - (void) _setAllowsMessaging:(BOOL)allows;
229 - (void) setShouldUpdateWhileOffscreen:(BOOL)update;
232 @protocol CydgetWebViewDelegate <UIWebViewDelegate>
233 - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame;
236 @class UIWebViewWebViewDelegate;
238 @interface UIWebView (WebCycript)
239 - (void) updateStyles;
242 @interface CydgetWebView : UIWebView {
247 @implementation CydgetWebView
249 - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
250 NSObject<CydgetWebViewDelegate> *delegate((NSObject<CydgetWebViewDelegate> *) [self delegate]);
251 if ([delegate respondsToSelector:@selector(webView:didClearWindowObject:forFrame:)])
252 [delegate webView:view didClearWindowObject:window forFrame:frame];
253 if ([UIWebView instancesRespondToSelector:@selector(webView:didClearWindowObject:forFrame:)])
254 [super webView:view didClearWindowObject:window forFrame:frame];
257 - (void) webView:(WebView *)view addMessageToConsole:(NSDictionary *)message {
258 NSLog(@"addMessageToConsole:%@", message);
260 if ([UIWebView instancesRespondToSelector:@selector(webView:addMessageToConsole:)])
261 [super webView:view addMessageToConsole:message];
266 @interface WebCydgetLockScreenView : UIView <UIWebViewDelegate> {
267 CydgetWebView *webview_;
268 UIScrollView *scroller_;
272 - (void) updateStyles;
276 @implementation WebCydgetLockScreenView
278 //#include "UICaboodle/UCInternal.h"
281 [[NSNotificationCenter defaultCenter] removeObserver:self];
282 [webview_ setDelegate:nil];
287 - (void) loadRequest:(NSURLRequest *)request {
288 [webview_ loadRequest:request];
291 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
292 [self loadRequest:[NSURLRequest
299 - (void) loadURL:(NSURL *)url {
300 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
303 - (id) initWithURL:(NSURL *)url {
304 CGRect frame = [[UIScreen mainScreen] bounds];
305 if (kCFCoreFoundationVersionNumber < 800)
306 frame.size.height -= 20; //[[[$SBStatusBarController sharedStatusBarController] statusBarView] frame].size.height;
308 if ((self = [super initWithFrame:frame]) != nil) {
309 CGRect bounds([self bounds]);
310 if (kCFCoreFoundationVersionNumber < 800)
311 bounds.size.height -= [objc_getClass("TPBottomLockBar") defaultHeight];
313 webview_ = [[CydgetWebView alloc] initWithFrame:bounds];
314 [webview_ setDelegate:self];
315 [self addSubview:webview_];
317 if ([webview_ respondsToSelector:@selector(setDataDetectorTypes:)])
318 [webview_ setDataDetectorTypes:0x80000000];
320 [webview_ setDetectsPhoneNumbers:NO];
322 [webview_ setScalesPageToFit:YES];
324 if (kCFCoreFoundationVersionNumber < 478.61)
325 if ([webview_ respondsToSelector:@selector(_setDrawInWebThread:)])
326 [webview_ _setDrawInWebThread:NO];
328 UIWebDocumentView *document([webview_ _documentView]);
329 WebView *webview([document webView]);
330 WebPreferences *preferences([webview preferences]);
332 [document setTileSize:CGSizeMake(bounds.size.width, 500)];
334 [document setBackgroundColor:[UIColor clearColor]];
335 [document setDrawsBackground:NO];
337 [webview setPreferencesIdentifier:@"WebCycript"];
339 if ([webview respondsToSelector:@selector(_setLayoutInterval:)])
340 [webview _setLayoutInterval:0];
342 [preferences _setLayoutInterval:0];
344 [preferences setCacheModel:WebCacheModelDocumentViewer];
345 [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
346 [preferences setOfflineWebApplicationCacheEnabled:YES];
348 if ([webview respondsToSelector:@selector(setShouldUpdateWhileOffscreen:)])
349 [webview setShouldUpdateWhileOffscreen:NO];
351 if ([document respondsToSelector:@selector(setAllowsMessaging:)])
352 [document setAllowsMessaging:YES];
353 if ([webview respondsToSelector:@selector(_setAllowsMessaging:)])
354 [webview _setAllowsMessaging:YES];
356 if ([webview_ respondsToSelector:@selector(_scrollView)]) {
357 scroller_ = [webview_ _scrollView];
359 [scroller_ setDirectionalLockEnabled:YES];
360 [scroller_ setDecelerationRate:CYScrollViewDecelerationRateNormal];
361 [scroller_ setDelaysContentTouches:NO];
363 [scroller_ setCanCancelContentTouches:YES];
365 [scroller_ setAlwaysBounceVertical:NO];
366 } else if ([webview_ respondsToSelector:@selector(_scroller)]) {
367 UIScroller *scroller([webview_ _scroller]);
368 scroller_ = (UIScrollView *) scroller;
370 [scroller setDirectionalScrolling:YES];
371 [scroller setScrollDecelerationFactor:CYScrollViewDecelerationRateNormal]; /* 0.989324 */
372 [scroller setScrollHysteresis:0]; /* 8 */
374 [scroller setThumbDetectionEnabled:NO];
377 [webview_ setOpaque:NO];
378 [webview_ setBackgroundColor:[UIColor clearColor]];
380 [scroller_ setFixedBackgroundPattern:YES];
381 [scroller_ setBackgroundColor:[UIColor clearColor]];
382 [scroller_ setClipsSubviews:NO];
384 [scroller_ setBounces:YES];
385 [scroller_ setShowBackgroundShadow:NO]; /* YES */
387 [self setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
388 [webview_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
390 NSDictionary *configuration([$CydgetController currentConfiguration]);
391 cycript_ = [configuration objectForKey:@"CycriptURLs"];
393 [scroller_ setScrollingEnabled:[[configuration objectForKey:@"Scrollable"] boolValue]];
397 [[NSNotificationCenter defaultCenter]
399 selector:@selector(mediaControlsDidSomething:)
400 name:@"SBLockScreenViewControllerMediaControlsDidShow"
404 [[NSNotificationCenter defaultCenter]
406 selector:@selector(mediaControlsDidSomething:)
407 name:@"SBLockScreenViewControllerMediaControlsDidHide"
413 - (void) mediaControlsDidSomething:(NSNotification *)notification {
417 - (void) webView:(WebView *)webview didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
419 if (NSString *href = [[[[frame dataSource] request] URL] absoluteString])
420 if (RegEx([cycript_ UTF8String])(href))
421 WebCycriptSetupView(webview);
424 - (void) updateStyles {
425 [webview_ updateStyles];
430 @interface WebCycriptLockScreenController : SBAwayViewPluginController {
431 NSDictionary *configuration_;
435 WebCydgetLockScreenView *background_;
436 WebCydgetLockScreenView *foreground_;
441 static BOOL CYHaveMediaControls() {
442 SBLockScreenView *view([[[$SBLockScreenManager sharedInstance] lockScreenViewController] lockScreenView]);
443 return view != nil && ![view mediaControlsHidden];
444 //return [[[$SBLockScreenManager sharedInstance] lockScreenViewController] isShowingMediaControls];
447 static BOOL CYHaveNotificationList() {
448 SBLockScreenNotificationListController *controller([[[$SBLockScreenManager sharedInstance] lockScreenViewController] _notificationController]);
449 return controller != nil && [controller hasAnyContent];
452 static void $UIWebViewWebViewDelegate$webView$addMessageToConsole$(UIWebViewWebViewDelegate *self, SEL sel, WebView *view, NSDictionary *message) {
453 UIWebView *uiWebView(MSHookIvar<UIWebView *>(self, "uiWebView"));
454 if ([uiWebView respondsToSelector:@selector(webView:addMessageToConsole:)])
455 [uiWebView webView:view addMessageToConsole:message];
458 static void $UIWebViewWebViewDelegate$webView$didClearWindowObject$forFrame$(UIWebViewWebViewDelegate *self, SEL sel, WebView *view, WebScriptObject *window, WebFrame *frame) {
459 UIWebView *uiWebView(MSHookIvar<UIWebView *>(self, "uiWebView"));
460 if ([uiWebView respondsToSelector:@selector(webView:didClearWindowObject:forFrame:)])
461 [uiWebView webView:view didClearWindowObject:window forFrame:frame];
465 if (Class $UIWebViewWebViewDelegate = objc_getClass("UIWebViewWebViewDelegate")) {
466 class_addMethod($UIWebViewWebViewDelegate, @selector(webView:addMessageToConsole:), (IMP) &$UIWebViewWebViewDelegate$webView$addMessageToConsole$, "v16@0:4@8@12");
467 class_addMethod($UIWebViewWebViewDelegate, @selector(webView:didClearWindowObject:forFrame:), (IMP) &$UIWebViewWebViewDelegate$webView$didClearWindowObject$forFrame$, "v20@0:4@8@12@16");
470 if (CGFloat *_UIScrollViewDecelerationRateNormal = reinterpret_cast<CGFloat *>(dlsym(RTLD_DEFAULT, "UIScrollViewDecelerationRateNormal")))
471 CYScrollViewDecelerationRateNormal = *_UIScrollViewDecelerationRateNormal;
472 else // XXX: this actually might be fast on some older systems: we should look into this
473 CYScrollViewDecelerationRateNormal = 0.998;
475 if (kCFCoreFoundationVersionNumber >= 800) {
476 WebCycriptRegisterStyle("-cydget-media-controls", &CYHaveMediaControls);
477 WebCycriptRegisterStyle("-cydget-notification-list", &CYHaveNotificationList);
481 @implementation WebCycriptLockScreenController
483 + (id) rootViewController {
484 return [[[self alloc] init] autorelease];
488 [configuration_ release];
489 [background_ release];
490 [foreground_ release];
495 if ((self = [super init]) != nil) {
496 configuration_ = [[$CydgetController currentConfiguration] retain];
497 legacy_ = [configuration_ objectForKey:@"Background"] == nil;
498 media_ = [[configuration_ objectForKey:@"MediaControls"] boolValue];
499 items_ = [[configuration_ objectForKey:@"NotificationList"] boolValue];
504 NSURL *base([NSURL fileURLWithPath:[$CydgetController currentPath]]);
506 if (NSString *background = [configuration_ objectForKey:@"Background"])
507 background_ = [[WebCydgetLockScreenView alloc] initWithURL:[NSURL URLWithString:background relativeToURL:base]];
509 if (NSString *homepage = [configuration_ objectForKey:@"Homepage"]) {
510 foreground_ = [[WebCydgetLockScreenView alloc] initWithURL:[NSURL URLWithString:homepage relativeToURL:base]];
511 [self setView:foreground_];
516 [background_ removeFromSuperview];
517 [background_ release];
519 [foreground_ removeFromSuperview];
520 [foreground_ release];
525 - (void) updateStyles {
526 [foreground_ updateStyles];
527 [background_ updateStyles];
530 - (UIView *) backgroundView {
534 - (BOOL) showAwayItems {
538 - (BOOL) updateHidden {
539 if (foreground_ == nil)
541 bool media(CYHaveMediaControls());
542 [foreground_ setHidden:media];
546 - (BOOL) showDateView {
547 if (kCFCoreFoundationVersionNumber < 800)
550 if (!legacy_ && foreground_ == nil)
552 if (!items_ && CYHaveNotificationList())
557 - (BOOL) allowsLockScreenMediaControls {
558 if (kCFCoreFoundationVersionNumber < 800)
563 /*- (BOOL) showHeaderView {
567 // 0: view is rendered above head
568 // 1: view moves as one with head
569 // 2: view moves up and down only
570 // 3: view simply never does move
571 - (NSUInteger) presentationStyle {
580 - (NSUInteger) overlayStyle {
581 return legacy_ ? 1 : 4;
584 // 1: blur -> view -> list
585 // 2: view -> blur -> list
586 // 3: view. unblur below?!
587 // 4: blur -> list -> view
588 - (NSUInteger) notificationBehavior {
589 return items_ ? 1 : 2;
592 - (BOOL) viewWantsFullscreenLayout {
593 return kCFCoreFoundationVersionNumber >= 800;
596 /*- (BOOL) viewWantsOverlayLayout {
597 return kCFCoreFoundationVersionNumber >= 800;
600 - (BOOL) shouldDisableOnUnlock {
604 - (BOOL) canBeAlwaysFullscreen {
608 /*- (BOOL) wantsSwipeGestureRecognizer {
612 - (BOOL) handleGesture:(int)arg1 fingerCount:(NSUInteger)fingers {
617 // - (void) lockScreenMediaControlsShown:(BOOL)shown;
619 - (BOOL) handleMenuButtonDoubleTap {
620 if (kCFCoreFoundationVersionNumber >= 800) {
621 SBLockScreenViewController *controller([[$SBLockScreenManager sharedInstance] lockScreenViewController]);
622 [controller _setMediaControlsVisible:![controller isShowingMediaControls]];
625 return [super handleMenuButtonDoubleTap];