|
9 | 9 |
|
10 | 10 | #import <Foundation/Foundation.h> |
11 | 11 | #import <UIKit/UIKit.h> |
| 12 | +#import <sys/sysctl.h> |
12 | 13 | #import "BNCDeviceInfo.h" |
13 | 14 | #import "BNCPreferenceHelper.h" |
14 | 15 | #import "BNCSystemObserver.h" |
@@ -83,27 +84,107 @@ - (id)init { |
83 | 84 |
|
84 | 85 | } |
85 | 86 |
|
| 87 | + self.browserUserAgent = [self.class userAgentString]; |
| 88 | + return self; |
| 89 | +} |
| 90 | + |
| 91 | ++ (NSString*) systemBuildVersion { |
| 92 | + int mib[2] = { CTL_KERN, KERN_OSVERSION }; |
| 93 | + u_int namelen = sizeof(mib) / sizeof(mib[0]); |
| 94 | + |
| 95 | + // Get the size for the buffer -- |
| 96 | + |
| 97 | + size_t bufferSize = 0; |
| 98 | + sysctl(mib, namelen, NULL, &bufferSize, NULL, 0); |
| 99 | + if (bufferSize <= 0) return nil; |
| 100 | + |
| 101 | + u_char buildBuffer[bufferSize]; |
| 102 | + int result = sysctl(mib, namelen, buildBuffer, &bufferSize, NULL, 0); |
| 103 | + |
| 104 | + NSString *version = nil; |
| 105 | + if (result >= 0) { |
| 106 | + version = [[NSString alloc] |
| 107 | + initWithBytes:buildBuffer |
| 108 | + length:bufferSize-1 |
| 109 | + encoding:NSUTF8StringEncoding]; |
| 110 | + } |
| 111 | + return version; |
| 112 | +} |
| 113 | + |
| 114 | + |
| 115 | ++ (NSString*) userAgentString { |
| 116 | + |
86 | 117 | static NSString* browserUserAgentString = nil; |
| 118 | + void (^setBrowserUserAgent)() = ^() { |
| 119 | + if (!browserUserAgentString) { |
| 120 | + browserUserAgentString = |
| 121 | + [[[UIWebView alloc] |
| 122 | + initWithFrame:CGRectZero] |
| 123 | + stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; |
| 124 | + BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper]; |
| 125 | + preferences.browserUserAgentString = browserUserAgentString; |
| 126 | + preferences.lastSystemBuildVersion = self.systemBuildVersion; |
| 127 | + //NSLog(@"[Branch] userAgentString: '%@'.", browserUserAgentString); |
| 128 | + } |
| 129 | + }; |
| 130 | + |
| 131 | + // We only get the string once per app run: |
| 132 | + |
| 133 | + if (browserUserAgentString) |
| 134 | + return browserUserAgentString; |
| 135 | + |
| 136 | + // Did we cache it? |
| 137 | + |
| 138 | + BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper]; |
| 139 | + if (preferences.browserUserAgentString && |
| 140 | + preferences.lastSystemBuildVersion && |
| 141 | + [preferences.lastSystemBuildVersion isEqualToString:self.systemBuildVersion]) { |
| 142 | + browserUserAgentString = [preferences.browserUserAgentString copy]; |
| 143 | + return browserUserAgentString; |
| 144 | + } |
87 | 145 |
|
88 | | - void (^setUpBrowserUserAgent)() = ^() { |
89 | | - browserUserAgentString = |
90 | | - [[[UIWebView alloc] |
91 | | - initWithFrame:CGRectZero] |
92 | | - stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; |
93 | | - self.browserUserAgent = browserUserAgentString; |
94 | | - }; |
95 | | - |
96 | | - @synchronized (self.class) { |
97 | | - if (browserUserAgentString) { |
98 | | - self.browserUserAgent = browserUserAgentString; |
99 | | - } else if (NSThread.isMainThread) { |
100 | | - setUpBrowserUserAgent(); |
101 | | - } else { |
102 | | - dispatch_sync(dispatch_get_main_queue(), setUpBrowserUserAgent); |
103 | | - } |
| 146 | + // Make sure this executes on the main thread. |
| 147 | + // Uses an implied lock through dispatch_queues: This can deadlock if mis-used! |
| 148 | + |
| 149 | + if (NSThread.isMainThread) { |
| 150 | + setBrowserUserAgent(); |
| 151 | + return browserUserAgentString; |
| 152 | + } |
| 153 | + |
| 154 | + // Different case for iOS 7.0: |
| 155 | + if ([UIDevice currentDevice].systemVersion.floatValue < 8.0) { |
| 156 | + dispatch_sync(dispatch_get_main_queue(), ^ { |
| 157 | + setBrowserUserAgent(); |
| 158 | + }); |
| 159 | + return browserUserAgentString; |
104 | 160 | } |
105 | 161 |
|
106 | | - return self; |
| 162 | + // Wait and yield to prevent deadlock: |
| 163 | + |
| 164 | + int retries = 10; |
| 165 | + int64_t timeoutDelta = (dispatch_time_t)((long double)NSEC_PER_SEC * (long double)0.100); |
| 166 | + while (!browserUserAgentString && retries > 0) { |
| 167 | + |
| 168 | + dispatch_block_t agentBlock = dispatch_block_create_with_qos_class( |
| 169 | + DISPATCH_BLOCK_DETACHED | DISPATCH_BLOCK_ENFORCE_QOS_CLASS, |
| 170 | + QOS_CLASS_USER_INTERACTIVE, |
| 171 | + 0, ^ { |
| 172 | + //NSLog(@"Will userAgent."); |
| 173 | + setBrowserUserAgent(); |
| 174 | + //NSLog(@"Did userAgent."); |
| 175 | + }); |
| 176 | + dispatch_async(dispatch_get_main_queue(), agentBlock); |
| 177 | + |
| 178 | + dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutDelta); |
| 179 | + #if defined(BNCTesting) |
| 180 | + long result = dispatch_block_wait(agentBlock, timeoutTime); |
| 181 | + NSLog(@"Wait result: %ld.", result); |
| 182 | + #else |
| 183 | + dispatch_block_wait(agentBlock, timeoutTime); |
| 184 | + #endif |
| 185 | + retries--; |
| 186 | + } |
| 187 | + return browserUserAgentString; |
107 | 188 | } |
108 | 189 |
|
109 | 190 | @end |
0 commit comments