| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/chrome/app/safe_mode/safe_mode_view_controller.h" | 5 #import "ios/chrome/app/safe_mode/safe_mode_view_controller.h" |
| 6 | 6 |
| 7 #import <QuartzCore/QuartzCore.h> | 7 #import <QuartzCore/QuartzCore.h> |
| 8 | 8 |
| 9 #include "base/strings/sys_string_conversions.h" | 9 #include "base/strings/sys_string_conversions.h" |
| 10 #import "ios/chrome/app/safe_mode_crashing_modules_config.h" | 10 #import "ios/chrome/app/safe_mode_crashing_modules_config.h" |
| 11 #import "ios/chrome/app/safe_mode_util.h" | 11 #import "ios/chrome/app/safe_mode_util.h" |
| 12 #include "ios/chrome/browser/crash_report/breakpad_helper.h" | 12 #include "ios/chrome/browser/crash_report/breakpad_helper.h" |
| 13 #import "ios/chrome/browser/ui/fancy_ui/primary_action_button.h" | 13 #import "ios/chrome/browser/ui/fancy_ui/primary_action_button.h" |
| 14 #include "ios/chrome/browser/ui/ui_util.h" | 14 #include "ios/chrome/browser/ui/ui_util.h" |
| 15 #import "ios/chrome/browser/ui/uikit_ui_util.h" | 15 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 16 #include "ios/chrome/grit/ios_chromium_strings.h" | 16 #include "ios/chrome/grit/ios_chromium_strings.h" |
| 17 #import "ui/gfx/ios/NSString+CrStringDrawing.h" | 17 #import "ui/gfx/ios/NSString+CrStringDrawing.h" |
| 18 | 18 |
| 19 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 20 #error "This file requires ARC support." |
| 21 #endif |
| 22 |
| 19 namespace { | 23 namespace { |
| 20 const CGFloat kVerticalSpacing = 20; | 24 const CGFloat kVerticalSpacing = 20; |
| 21 const CGFloat kUploadProgressSpacing = 5; | 25 const CGFloat kUploadProgressSpacing = 5; |
| 22 const NSTimeInterval kUploadPumpInterval = 0.1; | 26 const NSTimeInterval kUploadPumpInterval = 0.1; |
| 23 const NSTimeInterval kUploadTotalTime = 5; | 27 const NSTimeInterval kUploadTotalTime = 5; |
| 24 } // anonymous namespace | 28 } // anonymous namespace |
| 25 | 29 |
| 26 @interface SafeModeViewController () | 30 @interface SafeModeViewController () |
| 27 // Returns |YES| if any third-party modifications are detected. | 31 // Returns |YES| if any third-party modifications are detected. |
| 28 + (BOOL)detectedThirdPartyMods; | 32 + (BOOL)detectedThirdPartyMods; |
| 29 // Returns |YES| if there are crash reports to upload. | 33 // Returns |YES| if there are crash reports to upload. |
| 30 + (BOOL)hasReportToUpload; | 34 + (BOOL)hasReportToUpload; |
| 31 // Returns a message explaining which, if any, 3rd party modules were detected | 35 // Returns a message explaining which, if any, 3rd party modules were detected |
| 32 // that may cause Chrome to crash. | 36 // that may cause Chrome to crash. |
| 33 - (NSString*)startupCrashModuleText; | 37 - (NSString*)startupCrashModuleText; |
| 34 // Starts timer to update progress bar for crash report upload. | 38 // Starts timer to update progress bar for crash report upload. |
| 35 - (void)startUploadProgress; | 39 - (void)startUploadProgress; |
| 36 // Updates progress bar for crash report upload. | 40 // Updates progress bar for crash report upload. |
| 37 - (void)pumpUploadProgress; | 41 - (void)pumpUploadProgress; |
| 38 // Called when user taps on "Resume Chrome" button. Restores the default | 42 // Called when user taps on "Resume Chrome" button. Restores the default |
| 39 // Breakpad configuration and notifies the delegate to attempt to start the | 43 // Breakpad configuration and notifies the delegate to attempt to start the |
| 40 // browser. | 44 // browser. |
| 41 - (void)startBrowserFromSafeMode; | 45 - (void)startBrowserFromSafeMode; |
| 42 @end | 46 @end |
| 43 | 47 |
| 44 @implementation SafeModeViewController | 48 @implementation SafeModeViewController { |
| 49 __weak id<SafeModeViewControllerDelegate> delegate_; |
| 50 UIView* innerView_; |
| 51 PrimaryActionButton* startButton_; |
| 52 UILabel* uploadDescription_; |
| 53 UIProgressView* uploadProgress_; |
| 54 NSDate* uploadStartTime_; |
| 55 NSTimer* uploadTimer_; |
| 56 } |
| 45 | 57 |
| 46 - (id)initWithDelegate:(id<SafeModeViewControllerDelegate>)delegate { | 58 - (id)initWithDelegate:(id<SafeModeViewControllerDelegate>)delegate { |
| 47 self = [super init]; | 59 self = [super init]; |
| 48 if (self) { | 60 if (self) { |
| 49 delegate_.reset(delegate); | 61 delegate_ = delegate; |
| 50 } | 62 } |
| 51 return self; | 63 return self; |
| 52 } | 64 } |
| 53 | 65 |
| 54 + (BOOL)hasSuggestions { | 66 + (BOOL)hasSuggestions { |
| 55 if ([SafeModeViewController detectedThirdPartyMods]) | 67 if ([SafeModeViewController detectedThirdPartyMods]) |
| 56 return YES; | 68 return YES; |
| 57 return [SafeModeViewController hasReportToUpload]; | 69 return [SafeModeViewController hasReportToUpload]; |
| 58 } | 70 } |
| 59 | 71 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 CGRect mainBounds = [[UIScreen mainScreen] bounds]; | 144 CGRect mainBounds = [[UIScreen mainScreen] bounds]; |
| 133 // SafeModeViewController only supports portrait orientation (see | 145 // SafeModeViewController only supports portrait orientation (see |
| 134 // implementation of supportedInterfaceOrientations: below) but if the app is | 146 // implementation of supportedInterfaceOrientations: below) but if the app is |
| 135 // launched from landscape mode (e.g. iPad or iPhone 6+) then the mainScreen's | 147 // launched from landscape mode (e.g. iPad or iPhone 6+) then the mainScreen's |
| 136 // bounds will still be landscape at this point. Swap the height and width | 148 // bounds will still be landscape at this point. Swap the height and width |
| 137 // here so that the dimensions will be correct once the app rotates to | 149 // here so that the dimensions will be correct once the app rotates to |
| 138 // portrait. | 150 // portrait. |
| 139 if (IsLandscape()) { | 151 if (IsLandscape()) { |
| 140 mainBounds.size = CGSizeMake(mainBounds.size.height, mainBounds.size.width); | 152 mainBounds.size = CGSizeMake(mainBounds.size.height, mainBounds.size.width); |
| 141 } | 153 } |
| 142 base::scoped_nsobject<UIScrollView> scrollView( | 154 UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:mainBounds]; |
| 143 [[UIScrollView alloc] initWithFrame:mainBounds]); | |
| 144 self.view = scrollView; | 155 self.view = scrollView; |
| 145 [self.view setBackgroundColor:[UIColor colorWithWhite:0.902 alpha:1.0]]; | 156 [self.view setBackgroundColor:[UIColor colorWithWhite:0.902 alpha:1.0]]; |
| 146 const CGFloat kIPadInset = | 157 const CGFloat kIPadInset = |
| 147 (mainBounds.size.width - kIPadWidth - kHorizontalSpacing) / 2; | 158 (mainBounds.size.width - kIPadWidth - kHorizontalSpacing) / 2; |
| 148 const CGFloat widthInset = IsIPadIdiom() ? kIPadInset : kHorizontalSpacing; | 159 const CGFloat widthInset = IsIPadIdiom() ? kIPadInset : kHorizontalSpacing; |
| 149 innerView_.reset([[UIView alloc] | 160 innerView_ = [[UIView alloc] |
| 150 initWithFrame:CGRectInset(mainBounds, widthInset, kVerticalSpacing * 2)]); | 161 initWithFrame:CGRectInset(mainBounds, widthInset, kVerticalSpacing * 2)]; |
| 151 [innerView_ setBackgroundColor:[UIColor whiteColor]]; | 162 [innerView_ setBackgroundColor:[UIColor whiteColor]]; |
| 152 [innerView_ layer].cornerRadius = 3; | 163 [innerView_ layer].cornerRadius = 3; |
| 153 [innerView_ layer].borderWidth = 1; | 164 [innerView_ layer].borderWidth = 1; |
| 154 [innerView_ layer].borderColor = | 165 [innerView_ layer].borderColor = |
| 155 [UIColor colorWithWhite:0.851 alpha:1.0].CGColor; | 166 [UIColor colorWithWhite:0.851 alpha:1.0].CGColor; |
| 156 [innerView_ layer].masksToBounds = YES; | 167 [innerView_ layer].masksToBounds = YES; |
| 157 [scrollView addSubview:innerView_]; | 168 [scrollView addSubview:innerView_]; |
| 158 | 169 |
| 159 UIImage* fatalImage = [UIImage imageNamed:@"fatal_error.png"]; | 170 UIImage* fatalImage = [UIImage imageNamed:@"fatal_error.png"]; |
| 160 base::scoped_nsobject<UIImageView> imageView( | 171 UIImageView* imageView = [[UIImageView alloc] initWithImage:fatalImage]; |
| 161 [[UIImageView alloc] initWithImage:fatalImage]); | |
| 162 // Shift the image down a bit. | 172 // Shift the image down a bit. |
| 163 CGRect imageFrame = [imageView frame]; | 173 CGRect imageFrame = [imageView frame]; |
| 164 imageFrame.origin.y = kVerticalSpacing; | 174 imageFrame.origin.y = kVerticalSpacing; |
| 165 [imageView setFrame:imageFrame]; | 175 [imageView setFrame:imageFrame]; |
| 166 [self centerView:imageView afterView:nil]; | 176 [self centerView:imageView afterView:nil]; |
| 167 [innerView_ addSubview:imageView]; | 177 [innerView_ addSubview:imageView]; |
| 168 | 178 |
| 169 base::scoped_nsobject<UILabel> awSnap([[UILabel alloc] init]); | 179 UILabel* awSnap = [[UILabel alloc] init]; |
| 170 [awSnap setText:NSLocalizedString(@"IDS_IOS_SAFE_MODE_AW_SNAP", @"")]; | 180 [awSnap setText:NSLocalizedString(@"IDS_IOS_SAFE_MODE_AW_SNAP", @"")]; |
| 171 [awSnap setBackgroundColor:[UIColor clearColor]]; | 181 [awSnap setBackgroundColor:[UIColor clearColor]]; |
| 172 [awSnap setTextColor:[UIColor blackColor]]; | 182 [awSnap setTextColor:[UIColor blackColor]]; |
| 173 [awSnap setFont:[UIFont boldSystemFontOfSize:21]]; | 183 [awSnap setFont:[UIFont boldSystemFontOfSize:21]]; |
| 174 [awSnap sizeToFit]; | 184 [awSnap sizeToFit]; |
| 175 [self centerView:awSnap afterView:imageView]; | 185 [self centerView:awSnap afterView:imageView]; |
| 176 [innerView_ addSubview:awSnap]; | 186 [innerView_ addSubview:awSnap]; |
| 177 | 187 |
| 178 base::scoped_nsobject<UILabel> description([[UILabel alloc] init]); | 188 UILabel* description = [[UILabel alloc] init]; |
| 179 [description setText:[self startupCrashModuleText]]; | 189 [description setText:[self startupCrashModuleText]]; |
| 180 [description setBackgroundColor:[UIColor clearColor]]; | 190 [description setBackgroundColor:[UIColor clearColor]]; |
| 181 [description setTextColor:[UIColor colorWithWhite:0.31 alpha:1.0]]; | 191 [description setTextColor:[UIColor colorWithWhite:0.31 alpha:1.0]]; |
| 182 [description setTextAlignment:NSTextAlignmentCenter]; | 192 [description setTextAlignment:NSTextAlignmentCenter]; |
| 183 [description setNumberOfLines:0]; | 193 [description setNumberOfLines:0]; |
| 184 [description setLineBreakMode:NSLineBreakByWordWrapping]; | 194 [description setLineBreakMode:NSLineBreakByWordWrapping]; |
| 185 CGRect frame = [description frame]; | 195 CGRect frame = [description frame]; |
| 186 frame.size.width = IsIPadIdiom() ? kIPadWidth : kIPhoneWidth; | 196 frame.size.width = IsIPadIdiom() ? kIPadWidth : kIPhoneWidth; |
| 187 CGSize maxSize = CGSizeMake(frame.size.width, 999999.0f); | 197 CGSize maxSize = CGSizeMake(frame.size.width, 999999.0f); |
| 188 frame.size.height = | 198 frame.size.height = |
| 189 [[description text] cr_boundingSizeWithSize:maxSize | 199 [[description text] cr_boundingSizeWithSize:maxSize |
| 190 font:[description font]] | 200 font:[description font]] |
| 191 .height; | 201 .height; |
| 192 [description setFrame:frame]; | 202 [description setFrame:frame]; |
| 193 [self centerView:description afterView:awSnap]; | 203 [self centerView:description afterView:awSnap]; |
| 194 [innerView_ addSubview:description]; | 204 [innerView_ addSubview:description]; |
| 195 | 205 |
| 196 startButton_.reset([[PrimaryActionButton alloc] init]); | 206 startButton_ = [[PrimaryActionButton alloc] init]; |
| 197 NSString* startText = | 207 NSString* startText = |
| 198 NSLocalizedString(@"IDS_IOS_SAFE_MODE_RELOAD_CHROME", @""); | 208 NSLocalizedString(@"IDS_IOS_SAFE_MODE_RELOAD_CHROME", @""); |
| 199 [startButton_ setTitle:startText forState:UIControlStateNormal]; | 209 [startButton_ setTitle:startText forState:UIControlStateNormal]; |
| 200 [startButton_ titleLabel].textAlignment = NSTextAlignmentCenter; | 210 [startButton_ titleLabel].textAlignment = NSTextAlignmentCenter; |
| 201 [startButton_ titleLabel].lineBreakMode = NSLineBreakByWordWrapping; | 211 [startButton_ titleLabel].lineBreakMode = NSLineBreakByWordWrapping; |
| 202 frame = [startButton_ frame]; | 212 frame = [startButton_ frame]; |
| 203 frame.size.width = IsIPadIdiom() ? kIPadWidth : kIPhoneWidth; | 213 frame.size.width = IsIPadIdiom() ? kIPadWidth : kIPhoneWidth; |
| 204 maxSize = CGSizeMake(frame.size.width, 999999.0f); | 214 maxSize = CGSizeMake(frame.size.width, 999999.0f); |
| 205 const CGFloat kButtonBuffer = kVerticalSpacing / 2; | 215 const CGFloat kButtonBuffer = kVerticalSpacing / 2; |
| 206 CGSize startTextBoundingSize = | 216 CGSize startTextBoundingSize = |
| 207 [startText cr_boundingSizeWithSize:maxSize | 217 [startText cr_boundingSizeWithSize:maxSize |
| 208 font:[startButton_ titleLabel].font]; | 218 font:[startButton_ titleLabel].font]; |
| 209 frame.size.height = startTextBoundingSize.height + kButtonBuffer; | 219 frame.size.height = startTextBoundingSize.height + kButtonBuffer; |
| 210 [startButton_ setFrame:frame]; | 220 [startButton_ setFrame:frame]; |
| 211 [startButton_ addTarget:self | 221 [startButton_ addTarget:self |
| 212 action:@selector(startBrowserFromSafeMode) | 222 action:@selector(startBrowserFromSafeMode) |
| 213 forControlEvents:UIControlEventTouchUpInside]; | 223 forControlEvents:UIControlEventTouchUpInside]; |
| 214 [self centerView:startButton_ afterView:description]; | 224 [self centerView:startButton_ afterView:description]; |
| 215 [innerView_ addSubview:startButton_]; | 225 [innerView_ addSubview:startButton_]; |
| 216 | 226 |
| 217 UIView* lastView = startButton_; | 227 UIView* lastView = startButton_; |
| 218 if ([SafeModeViewController hasReportToUpload]) { | 228 if ([SafeModeViewController hasReportToUpload]) { |
| 219 breakpad_helper::StartUploadingReportsInRecoveryMode(); | 229 breakpad_helper::StartUploadingReportsInRecoveryMode(); |
| 220 | 230 |
| 221 // If there are no jailbreak modifications, then present the "Sending crash | 231 // If there are no jailbreak modifications, then present the "Sending crash |
| 222 // report..." UI. | 232 // report..." UI. |
| 223 if (![SafeModeViewController detectedThirdPartyMods]) { | 233 if (![SafeModeViewController detectedThirdPartyMods]) { |
| 224 [startButton_ setEnabled:NO]; | 234 [startButton_ setEnabled:NO]; |
| 225 | 235 |
| 226 uploadDescription_.reset([[UILabel alloc] init]); | 236 uploadDescription_ = [[UILabel alloc] init]; |
| 227 [uploadDescription_ | 237 [uploadDescription_ |
| 228 setText:NSLocalizedString(@"IDS_IOS_SAFE_MODE_SENDING_CRASH_REPORT", | 238 setText:NSLocalizedString(@"IDS_IOS_SAFE_MODE_SENDING_CRASH_REPORT", |
| 229 @"")]; | 239 @"")]; |
| 230 [uploadDescription_ setBackgroundColor:[UIColor clearColor]]; | 240 [uploadDescription_ setBackgroundColor:[UIColor clearColor]]; |
| 231 [uploadDescription_ setFont:[UIFont systemFontOfSize:13]]; | 241 [uploadDescription_ setFont:[UIFont systemFontOfSize:13]]; |
| 232 [uploadDescription_ setTextColor:[UIColor colorWithWhite:0.31 alpha:1.0]]; | 242 [uploadDescription_ setTextColor:[UIColor colorWithWhite:0.31 alpha:1.0]]; |
| 233 [uploadDescription_ sizeToFit]; | 243 [uploadDescription_ sizeToFit]; |
| 234 [self centerView:uploadDescription_ afterView:startButton_]; | 244 [self centerView:uploadDescription_ afterView:startButton_]; |
| 235 [innerView_ addSubview:uploadDescription_]; | 245 [innerView_ addSubview:uploadDescription_]; |
| 236 | 246 |
| 237 uploadProgress_.reset([[UIProgressView alloc] | 247 uploadProgress_ = [[UIProgressView alloc] |
| 238 initWithProgressViewStyle:UIProgressViewStyleDefault]); | 248 initWithProgressViewStyle:UIProgressViewStyleDefault]; |
| 239 [self centerView:uploadProgress_ afterView:nil]; | 249 [self centerView:uploadProgress_ afterView:nil]; |
| 240 frame = [uploadProgress_ frame]; | 250 frame = [uploadProgress_ frame]; |
| 241 frame.origin.y = | 251 frame.origin.y = |
| 242 CGRectGetMaxY([uploadDescription_ frame]) + kUploadProgressSpacing; | 252 CGRectGetMaxY([uploadDescription_ frame]) + kUploadProgressSpacing; |
| 243 [uploadProgress_ setFrame:frame]; | 253 [uploadProgress_ setFrame:frame]; |
| 244 [innerView_ addSubview:uploadProgress_]; | 254 [innerView_ addSubview:uploadProgress_]; |
| 245 | 255 |
| 246 lastView = uploadProgress_; | 256 lastView = uploadProgress_; |
| 247 [self startUploadProgress]; | 257 [self startUploadProgress]; |
| 248 } | 258 } |
| 249 } | 259 } |
| 250 | 260 |
| 251 CGSize scrollSize = | 261 CGSize scrollSize = |
| 252 CGSizeMake(mainBounds.size.width, | 262 CGSizeMake(mainBounds.size.width, |
| 253 CGRectGetMaxY([lastView frame]) + kVerticalSpacing); | 263 CGRectGetMaxY([lastView frame]) + kVerticalSpacing); |
| 254 frame = [innerView_ frame]; | 264 frame = [innerView_ frame]; |
| 255 frame.size.height = scrollSize.height; | 265 frame.size.height = scrollSize.height; |
| 256 [innerView_ setFrame:frame]; | 266 [innerView_ setFrame:frame]; |
| 257 scrollSize.height += frame.origin.y; | 267 scrollSize.height += frame.origin.y; |
| 258 [scrollView setContentSize:scrollSize]; | 268 [scrollView setContentSize:scrollSize]; |
| 259 } | 269 } |
| 260 | 270 |
| 261 - (NSUInteger)supportedInterfaceOrientations { | 271 - (NSUInteger)supportedInterfaceOrientations { |
| 262 return UIInterfaceOrientationMaskPortrait; | 272 return UIInterfaceOrientationMaskPortrait; |
| 263 } | 273 } |
| 264 | 274 |
| 265 #pragma mark - Private | 275 #pragma mark - Private |
| 266 | 276 |
| 267 - (void)startUploadProgress { | 277 - (void)startUploadProgress { |
| 268 uploadStartTime_.reset([[NSDate date] retain]); | 278 uploadStartTime_ = [NSDate date]; |
| 269 uploadTimer_.reset( | 279 uploadTimer_ = |
| 270 [[NSTimer scheduledTimerWithTimeInterval:kUploadPumpInterval | 280 [NSTimer scheduledTimerWithTimeInterval:kUploadPumpInterval |
| 271 target:self | 281 target:self |
| 272 selector:@selector(pumpUploadProgress) | 282 selector:@selector(pumpUploadProgress) |
| 273 userInfo:nil | 283 userInfo:nil |
| 274 repeats:YES] retain]); | 284 repeats:YES]; |
| 275 } | 285 } |
| 276 | 286 |
| 277 - (void)pumpUploadProgress { | 287 - (void)pumpUploadProgress { |
| 278 NSTimeInterval elapsed = | 288 NSTimeInterval elapsed = |
| 279 [[NSDate date] timeIntervalSinceDate:uploadStartTime_]; | 289 [[NSDate date] timeIntervalSinceDate:uploadStartTime_]; |
| 280 // Theoretically we could stop early when the value returned by | 290 // Theoretically we could stop early when the value returned by |
| 281 // ios_internal::breakpad::GetCrashReportCount() changes, but this is | 291 // ios_internal::breakpad::GetCrashReportCount() changes, but this is |
| 282 // simpler. If we decide to look for a change in crash report count, then we | 292 // simpler. If we decide to look for a change in crash report count, then we |
| 283 // also probably want to replace the UIProgressView with a | 293 // also probably want to replace the UIProgressView with a |
| 284 // UIActivityIndicatorView. | 294 // UIActivityIndicatorView. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 296 [uploadProgress_ setHidden:YES]; | 306 [uploadProgress_ setHidden:YES]; |
| 297 } | 307 } |
| 298 } | 308 } |
| 299 | 309 |
| 300 - (void)startBrowserFromSafeMode { | 310 - (void)startBrowserFromSafeMode { |
| 301 breakpad_helper::RestoreDefaultConfiguration(); | 311 breakpad_helper::RestoreDefaultConfiguration(); |
| 302 [delegate_ startBrowserFromSafeMode]; | 312 [delegate_ startBrowserFromSafeMode]; |
| 303 } | 313 } |
| 304 | 314 |
| 305 @end | 315 @end |
| OLD | NEW |