Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "ios/chrome/browser/memory/memory_debugger.h" | |
| 6 | |
| 7 #include "base/ios/ios_util.h" | |
| 8 #import "base/mac/scoped_nsobject.h" | |
| 9 #import "base/memory/scoped_ptr.h" | |
| 10 #import "ios/chrome/browser/memory/memory_metrics.h" | |
| 11 #include "ios/chrome/browser/ui/ui_util.h" | |
| 12 #import "ios/chrome/browser/ui/uikit_ui_util.h" | |
| 13 | |
| 14 namespace { | |
| 15 // The number of bytes in a megabyte. | |
| 16 const CGFloat kNumBytesInMB = 1024 * 1024; | |
| 17 // The horizontal and vertical padding between subviews. | |
| 18 const CGFloat kPadding = 10; | |
| 19 } // namespace | |
| 20 | |
| 21 @implementation MemoryDebugger { | |
| 22 // A timer to trigger refreshes. | |
| 23 base::scoped_nsobject<NSTimer> _refreshTimer; | |
| 24 | |
| 25 // A timer to trigger continuous memory warnings. | |
| 26 base::scoped_nsobject<NSTimer> _memoryWarningTimer; | |
| 27 | |
| 28 // The font to use. | |
| 29 base::scoped_nsobject<UIFont> _font; | |
| 30 | |
| 31 // Labels for memory metrics. | |
| 32 base::scoped_nsobject<UILabel> _physicalFreeMemoryLabel; | |
| 33 base::scoped_nsobject<UILabel> _realMemoryUsedLabel; | |
| 34 base::scoped_nsobject<UILabel> _xcodeGaugeLabel; | |
| 35 base::scoped_nsobject<UILabel> _dirtyVirtualMemoryLabel; | |
| 36 | |
| 37 // Inputs for memory commands. | |
| 38 base::scoped_nsobject<UITextField> _bloatField; | |
| 39 base::scoped_nsobject<UITextField> _refreshField; | |
| 40 base::scoped_nsobject<UITextField> _continuousMemoryWarningField; | |
| 41 | |
| 42 // A place to store the artifical memory bloat. | |
| 43 scoped_ptr<uint8> _bloat; | |
| 44 | |
| 45 // Distance the view was pushed up to accomodate the keyboard. | |
| 46 CGFloat _keyboardOffset; | |
| 47 | |
| 48 // The current orientation of the device. | |
| 49 BOOL _currentOrientation; | |
| 50 } | |
| 51 | |
| 52 - (instancetype)init { | |
| 53 self = [super initWithFrame:CGRectZero]; | |
| 54 if (self) { | |
| 55 _font.reset([[UIFont systemFontOfSize:14] retain]); | |
| 56 self.backgroundColor = [UIColor colorWithWhite:0.8f alpha:0.9f]; | |
| 57 self.opaque = NO; | |
| 58 | |
| 59 [self addSubviews]; | |
| 60 [self adjustForOrientation:nil]; | |
| 61 [self sizeToFit]; | |
| 62 [self registerForNotifications]; | |
| 63 } | |
| 64 return self; | |
| 65 } | |
| 66 | |
| 67 // NSTimers create a retain cycle so they must be invalidated before this | |
| 68 // instance can be deallocated. | |
| 69 - (void)invalidateTimers { | |
| 70 [_refreshTimer invalidate]; | |
| 71 [_memoryWarningTimer invalidate]; | |
| 72 } | |
| 73 | |
| 74 - (void)dealloc { | |
| 75 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 76 [super dealloc]; | |
| 77 } | |
| 78 | |
| 79 #pragma mark UIView methods | |
| 80 | |
| 81 - (CGSize)sizeThatFits:(CGSize)size { | |
| 82 CGFloat width = 0; | |
| 83 CGFloat height = 0; | |
| 84 for (UIView* subview in self.subviews) { | |
| 85 width = MAX(width, CGRectGetMaxX(subview.frame)); | |
| 86 height = MAX(height, CGRectGetMaxY(subview.frame)); | |
| 87 } | |
| 88 return CGSizeMake(width + kPadding, height + kPadding); | |
| 89 } | |
| 90 | |
| 91 #pragma mark initialization helpers | |
| 92 | |
| 93 - (void)addSubviews { | |
| 94 // |index| is used to calculate the veritical position of each element in | |
|
sdefresne
2015/04/03 08:21:49
nit: s/veritical/vertical/
| |
| 95 // the debugger view. | |
| 96 NSUInteger index = 0; | |
| 97 | |
| 98 // Display some metrics. | |
| 99 _physicalFreeMemoryLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]); | |
| 100 [self addMetricWithName:@"Physical Free" | |
| 101 atIndex:index++ | |
| 102 usingLabel:_physicalFreeMemoryLabel]; | |
| 103 _realMemoryUsedLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]); | |
| 104 [self addMetricWithName:@"Real Memory Used" | |
| 105 atIndex:index++ | |
| 106 usingLabel:_realMemoryUsedLabel]; | |
| 107 _xcodeGaugeLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]); | |
| 108 [self addMetricWithName:@"Xcode Gauge" | |
| 109 atIndex:index++ | |
| 110 usingLabel:_xcodeGaugeLabel]; | |
| 111 _dirtyVirtualMemoryLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]); | |
| 112 [self addMetricWithName:@"Dirty VM" | |
| 113 atIndex:index++ | |
| 114 usingLabel:_dirtyVirtualMemoryLabel]; | |
| 115 | |
| 116 // Since _performMemoryWarning is a private API it can't be compiled into | |
| 117 // stable builds. | |
|
sdefresne
2015/04/03 08:21:48
nit: s/stable/official/
| |
| 118 // TODO(lliabraa): Figure out how to support memory warnings (or something | |
| 119 // like them) in official builds. | |
| 120 #if CHROMIUM_BUILD | |
| 121 [self addButtonWithTitle:@"Trigger Memory Warning" | |
| 122 target:[UIApplication sharedApplication] | |
| 123 action:@selector(_performMemoryWarning) | |
| 124 withOrigin:[self originForSubviewAtIndex:index++]]; | |
| 125 #endif // CHROMIUM_BUILD | |
| 126 | |
| 127 // Display a text input to set the amount of artificial memory bloat and a | |
| 128 // button to reset the bloat to zero. | |
| 129 _bloatField.reset([[UITextField alloc] initWithFrame:CGRectZero]); | |
| 130 [self addLabelWithText:@"Set bloat (MB)" | |
| 131 input:_bloatField | |
| 132 inputTarget:self | |
| 133 inputAction:@selector(updateBloat) | |
| 134 buttonWithTitle:@"Clear" | |
| 135 buttonTarget:self | |
| 136 buttonAction:@selector(clearBloat) | |
| 137 atIndex:index++]; | |
| 138 [_bloatField setText:@"0"]; | |
| 139 [self updateBloat]; | |
| 140 | |
| 141 // Since _performMemoryWarning is a private API it can't be compiled into | |
| 142 // stable builds. | |
|
sdefresne
2015/04/03 08:21:48
nit: s/stable/official/
| |
| 143 // TODO(lliabraa): Figure out how to support memory warnings (or something | |
| 144 // like them) in official builds. | |
| 145 #if CHROMIUM_BUILD | |
| 146 // Display a text input to control the rate of continuous memory warnings. | |
| 147 _continuousMemoryWarningField.reset( | |
| 148 [[UITextField alloc] initWithFrame:CGRectZero]); | |
| 149 [self addLabelWithText:@"Set memory warning interval (secs)" | |
| 150 input:_continuousMemoryWarningField | |
| 151 inputTarget:self | |
| 152 inputAction:@selector(updateMemoryWarningInterval) | |
| 153 atIndex:index++]; | |
| 154 [_continuousMemoryWarningField setText:@"0.0"]; | |
| 155 #endif // CHROMIUM_BUILD | |
| 156 | |
| 157 // Display a text input to control the refresh rate of the memory debugger. | |
| 158 _refreshField.reset([[UITextField alloc] initWithFrame:CGRectZero]); | |
| 159 [self addLabelWithText:@"Set refresh interval (secs)" | |
| 160 input:_refreshField | |
| 161 inputTarget:self | |
| 162 inputAction:@selector(updateRefreshInterval) | |
| 163 atIndex:index++]; | |
| 164 [_refreshField setText:@"0.5"]; | |
| 165 [self updateRefreshInterval]; | |
| 166 } | |
| 167 | |
| 168 - (void)registerForNotifications { | |
| 169 // On iOS 7, the screen coordinate system is not dependent on orientation so | |
| 170 // the debugger has to handle its own rotation. | |
| 171 if (!base::ios::IsRunningOnIOS8OrLater()) { | |
| 172 // Register to receive orientation notifications. | |
| 173 [[NSNotificationCenter defaultCenter] | |
| 174 addObserver:self | |
| 175 selector:@selector(adjustForOrientation:) | |
| 176 name:UIDeviceOrientationDidChangeNotification | |
| 177 object:nil]; | |
| 178 } | |
| 179 | |
| 180 // Register to receive memory warning. | |
| 181 [[NSNotificationCenter defaultCenter] | |
| 182 addObserver:self | |
| 183 selector:@selector(lowMemoryWarningReceived:) | |
| 184 name:UIApplicationDidReceiveMemoryWarningNotification | |
| 185 object:nil]; | |
| 186 | |
| 187 // Register to receive keyboard will show notification. | |
| 188 [[NSNotificationCenter defaultCenter] | |
| 189 addObserver:self | |
| 190 selector:@selector(keyboardWillShow:) | |
| 191 name:UIKeyboardWillShowNotification | |
| 192 object:nil]; | |
| 193 | |
| 194 // Register to receive keyboard will hide notification. | |
| 195 [[NSNotificationCenter defaultCenter] | |
| 196 addObserver:self | |
| 197 selector:@selector(keyboardWillHide:) | |
| 198 name:UIKeyboardWillHideNotification | |
| 199 object:nil]; | |
| 200 } | |
| 201 | |
| 202 // Adds subviews for the specified metric, the value of which will be displayed | |
| 203 // in |label|. | |
| 204 - (void)addMetricWithName:(NSString*)name | |
| 205 atIndex:(NSUInteger)index | |
| 206 usingLabel:(UILabel*)label { | |
| 207 // The width of the view for the metric's name. | |
| 208 const CGFloat kNameWidth = 150; | |
| 209 // The width of the view for each metric. | |
| 210 const CGFloat kMetricWidth = 100; | |
| 211 CGPoint nameOrigin = [self originForSubviewAtIndex:index]; | |
| 212 CGRect nameFrame = | |
| 213 CGRectMake(nameOrigin.x, nameOrigin.y, kNameWidth, [_font lineHeight]); | |
| 214 base::scoped_nsobject<UILabel> nameLabel( | |
| 215 [[UILabel alloc] initWithFrame:nameFrame]); | |
| 216 [nameLabel setText:[NSString stringWithFormat:@"%@: ", name]]; | |
| 217 [nameLabel setFont:_font]; | |
| 218 [self addSubview:nameLabel]; | |
| 219 label.frame = CGRectMake(CGRectGetMaxX(nameFrame), nameFrame.origin.y, | |
| 220 kMetricWidth, [_font lineHeight]); | |
| 221 [label setFont:_font]; | |
| 222 [label setTextAlignment:NSTextAlignmentRight]; | |
| 223 [self addSubview:label]; | |
| 224 } | |
| 225 | |
| 226 // Adds a subview for a button with the given title and target/action. | |
| 227 - (void)addButtonWithTitle:(NSString*)title | |
| 228 target:(id)target | |
| 229 action:(SEL)action | |
| 230 withOrigin:(CGPoint)origin { | |
| 231 base::scoped_nsobject<UIButton> button( | |
| 232 [[UIButton buttonWithType:UIButtonTypeSystem] retain]); | |
| 233 [button setTitle:title forState:UIControlStateNormal]; | |
| 234 [button titleLabel].font = _font; | |
| 235 [[button titleLabel] setTextAlignment:NSTextAlignmentCenter]; | |
| 236 [button sizeToFit]; | |
| 237 [button setFrame:CGRectMake(origin.x, origin.y, [button frame].size.width, | |
| 238 [_font lineHeight])]; | |
| 239 [button addTarget:target | |
| 240 action:action | |
| 241 forControlEvents:UIControlEventTouchUpInside]; | |
| 242 [self addSubview:button]; | |
| 243 } | |
| 244 | |
| 245 // Adds subviews for a UI component with label and input text field. | |
| 246 // | |
| 247 // ------------------------- | |
| 248 // | labelText | <input> | | |
| 249 // ------------------------- | |
| 250 // | |
| 251 // The inputTarget/inputAction will be invoked when the user finishes editing | |
| 252 // in |input|. | |
| 253 - (void)addLabelWithText:(NSString*)labelText | |
| 254 input:(UITextField*)input | |
| 255 inputTarget:(id)inputTarget | |
| 256 inputAction:(SEL)inputAction | |
| 257 atIndex:(NSUInteger)index { | |
| 258 [self addLabelWithText:labelText | |
| 259 input:input | |
| 260 inputTarget:inputTarget | |
| 261 inputAction:inputAction | |
| 262 buttonWithTitle:nil | |
| 263 buttonTarget:nil | |
| 264 buttonAction:nil | |
| 265 atIndex:index]; | |
| 266 } | |
| 267 | |
| 268 // Adds subviews for a UI component with label, input text field and button. | |
| 269 // | |
| 270 // ------------------------------------- | |
| 271 // | labelText | <input> | <button> | | |
| 272 // ------------------------------------- | |
| 273 // | |
| 274 // The inputTarget/inputAction will be invoked when the user finishes editing | |
| 275 // in |input|. | |
| 276 - (void)addLabelWithText:(NSString*)labelText | |
| 277 input:(UITextField*)input | |
| 278 inputTarget:(id)inputTarget | |
| 279 inputAction:(SEL)inputAction | |
| 280 buttonWithTitle:(NSString*)buttonTitle | |
| 281 buttonTarget:(id)buttonTarget | |
| 282 buttonAction:(SEL)buttonAction | |
| 283 atIndex:(NSUInteger)index { | |
| 284 base::scoped_nsobject<UILabel> label( | |
| 285 [[UILabel alloc] initWithFrame:CGRectZero]); | |
| 286 if (labelText) { | |
| 287 [label setText:[NSString stringWithFormat:@"%@: ", labelText]]; | |
| 288 } | |
| 289 [label setFont:_font]; | |
| 290 [label sizeToFit]; | |
| 291 CGPoint labelOrigin = [self originForSubviewAtIndex:index]; | |
| 292 [label setFrame:CGRectOffset([label frame], labelOrigin.x, labelOrigin.y)]; | |
| 293 [self addSubview:label]; | |
| 294 if (input) { | |
| 295 // The width of the views for each input text field. | |
| 296 const CGFloat kInputWidth = 50; | |
| 297 input.frame = | |
| 298 CGRectMake(CGRectGetMaxX([label frame]) + kPadding, | |
| 299 [label frame].origin.y, kInputWidth, [_font lineHeight]); | |
| 300 input.font = _font; | |
| 301 input.backgroundColor = [UIColor whiteColor]; | |
| 302 input.delegate = self; | |
| 303 input.keyboardType = UIKeyboardTypeNumbersAndPunctuation; | |
| 304 input.adjustsFontSizeToFitWidth = YES; | |
| 305 input.textAlignment = NSTextAlignmentRight; | |
| 306 [input addTarget:inputTarget | |
| 307 action:inputAction | |
| 308 forControlEvents:UIControlEventEditingDidEnd]; | |
| 309 | |
| 310 [self addSubview:input]; | |
| 311 } | |
| 312 | |
| 313 if (buttonTitle) { | |
| 314 const CGFloat kButtonXOffset = | |
| 315 input ? CGRectGetMaxX(input.frame) : CGRectGetMaxX([label frame]); | |
| 316 CGPoint origin = | |
| 317 CGPointMake(kButtonXOffset + kPadding, [label frame].origin.y); | |
| 318 [self addButtonWithTitle:buttonTitle | |
| 319 target:buttonTarget | |
| 320 action:buttonAction | |
| 321 withOrigin:origin]; | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 // Returns the CGPoint of the origin of the subview at |index|. | |
| 326 - (CGPoint)originForSubviewAtIndex:(NSUInteger)index { | |
| 327 return CGPointMake(kPadding, | |
| 328 (index + 1) * kPadding + index * [_font lineHeight]); | |
| 329 } | |
| 330 | |
| 331 #pragma mark Refresh callback | |
| 332 | |
| 333 // Updates content and ensures the view is visible. | |
| 334 - (void)refresh:(NSTimer*)timer { | |
| 335 [self.superview bringSubviewToFront:self]; | |
| 336 [self updateMemoryInfo]; | |
| 337 } | |
| 338 | |
| 339 #pragma mark Memory inspection | |
| 340 | |
| 341 // Updates the memory metrics shown. | |
| 342 - (void)updateMemoryInfo { | |
| 343 CGFloat value = memory_util::GetFreePhysicalBytes() / kNumBytesInMB; | |
| 344 [_physicalFreeMemoryLabel | |
| 345 setText:[NSString stringWithFormat:@"%.2f MB", value]]; | |
| 346 value = memory_util::GetRealMemoryUsedInBytes() / kNumBytesInMB; | |
| 347 [_realMemoryUsedLabel setText:[NSString stringWithFormat:@"%.2f MB", value]]; | |
| 348 value = memory_util::GetInternalVMBytes() / kNumBytesInMB; | |
| 349 [_xcodeGaugeLabel setText:[NSString stringWithFormat:@"%.2f MB", value]]; | |
| 350 value = memory_util::GetDirtyVMBytes() / kNumBytesInMB; | |
| 351 [_dirtyVirtualMemoryLabel | |
| 352 setText:[NSString stringWithFormat:@"%.2f MB", value]]; | |
| 353 } | |
| 354 | |
| 355 #pragma mark Memory Warning notification callback | |
| 356 | |
| 357 // Flashes the debugger to indicate memory warning. | |
| 358 - (void)lowMemoryWarningReceived:(NSNotification*)notification { | |
| 359 UIColor* originalColor = self.backgroundColor; | |
| 360 self.backgroundColor = | |
| 361 [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.9]; | |
| 362 [UIView animateWithDuration:1.0 | |
| 363 delay:0.0 | |
| 364 options:UIViewAnimationOptionAllowUserInteraction | |
| 365 animations:^{ | |
| 366 self.backgroundColor = originalColor; | |
| 367 } | |
| 368 completion:nil]; | |
| 369 } | |
| 370 | |
| 371 #pragma mark Rotation notification callback | |
| 372 | |
| 373 - (void)didMoveToSuperview { | |
| 374 UIView* superview = [self superview]; | |
| 375 if (superview) | |
| 376 [self setCenter:[superview center]]; | |
| 377 } | |
| 378 | |
| 379 - (void)adjustForOrientation:(NSNotification*)notification { | |
| 380 if (base::ios::IsRunningOnIOS8OrLater()) { | |
| 381 return; | |
| 382 } | |
| 383 UIInterfaceOrientation orientation = | |
| 384 [[UIApplication sharedApplication] statusBarOrientation]; | |
| 385 if (orientation == _currentOrientation) { | |
| 386 return; | |
| 387 } | |
| 388 _currentOrientation = orientation; | |
| 389 CGFloat angle; | |
| 390 switch (orientation) { | |
| 391 case UIInterfaceOrientationPortrait: | |
| 392 angle = 0; | |
| 393 break; | |
| 394 case UIInterfaceOrientationPortraitUpsideDown: | |
| 395 angle = M_PI; | |
| 396 break; | |
| 397 case UIInterfaceOrientationLandscapeLeft: | |
| 398 angle = -M_PI_2; | |
| 399 break; | |
| 400 case UIInterfaceOrientationLandscapeRight: | |
| 401 angle = M_PI_2; | |
| 402 break; | |
| 403 case UIInterfaceOrientationUnknown: | |
| 404 default: | |
| 405 angle = 0; | |
| 406 } | |
| 407 | |
| 408 // Since the debugger view is in screen coordinates and handles its own | |
| 409 // rotation via the |transform| property, the view's position after rotation | |
| 410 // can be unexpected and partially off-screen. Centering the view before | |
| 411 // rotating it ensures that the view remains within the bounds of the screen. | |
| 412 // TODO(lliabraa): Correctly handle rotation (crbug/368263). | |
|
sdefresne
2015/04/03 08:21:48
nit: Remove this comment as the bug has been marke
| |
| 413 if (self.superview) { | |
| 414 self.center = self.superview.center; | |
| 415 } | |
| 416 self.transform = CGAffineTransformMakeRotation(angle); | |
| 417 } | |
| 418 | |
| 419 #pragma mark Keyboard notification callbacks | |
| 420 | |
| 421 // Ensures the debugger is visible by shifting it up as the keyboard animates | |
| 422 // in. | |
| 423 - (void)keyboardWillShow:(NSNotification*)notification { | |
| 424 NSDictionary* userInfo = [notification userInfo]; | |
| 425 NSValue* keyboardFrameValue = | |
| 426 [userInfo valueForKey:UIKeyboardFrameEndUserInfoKey]; | |
| 427 CGFloat keyboardHeight = CurrentKeyboardHeight(keyboardFrameValue); | |
| 428 | |
| 429 // Get the coord of the bottom of the debugger's frame. This is orientation | |
| 430 // dependent on iOS 7 because the debugger is in screen coords. | |
| 431 CGFloat bottomOfFrame = CGRectGetMaxY(self.frame); | |
| 432 if (!base::ios::IsRunningOnIOS8OrLater() && IsLandscape()) | |
| 433 bottomOfFrame = CGRectGetMaxX(self.frame); | |
| 434 | |
| 435 // Shift the debugger up by the "height" of the keyboard, but since the | |
| 436 // keyboard rect is in screen coords, use the orientation to find the height. | |
| 437 CGFloat distanceFromBottom = CurrentScreenHeight() - bottomOfFrame; | |
| 438 _keyboardOffset = -1 * fmax(0.0f, keyboardHeight - distanceFromBottom); | |
| 439 [self animateForKeyboardNotification:notification | |
| 440 withOffset:CGPointMake(0, _keyboardOffset)]; | |
| 441 } | |
| 442 | |
| 443 // Shifts the debugger back down when the keyboard is hidden. | |
| 444 - (void)keyboardWillHide:(NSNotification*)notification { | |
| 445 [self animateForKeyboardNotification:notification | |
| 446 withOffset:CGPointMake(0, -_keyboardOffset)]; | |
| 447 } | |
| 448 | |
| 449 - (void)animateForKeyboardNotification:(NSNotification*)notification | |
| 450 withOffset:(CGPoint)offset { | |
| 451 // Account for orientation. | |
| 452 offset = CGPointApplyAffineTransform(offset, self.transform); | |
| 453 // Normally this would use an animation block, but there is no API to | |
| 454 // convert the UIKeyboardAnimationCurveUserInfoKey's value from a | |
| 455 // UIViewAnimationCurve to a UIViewAnimationOption. Awesome! | |
| 456 NSDictionary* userInfo = [notification userInfo]; | |
| 457 [UIView beginAnimations:nil context:NULL]; | |
| 458 [UIView setAnimationDuration: | |
| 459 [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; | |
| 460 NSInteger animationCurveKeyValue = | |
| 461 [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; | |
| 462 UIViewAnimationCurve animationCurve = | |
| 463 (UIViewAnimationCurve)animationCurveKeyValue; | |
| 464 [UIView setAnimationCurve:animationCurve]; | |
| 465 [UIView setAnimationBeginsFromCurrentState:YES]; | |
| 466 self.frame = CGRectOffset(self.frame, offset.x, offset.y); | |
| 467 [UIView commitAnimations]; | |
| 468 } | |
| 469 | |
| 470 #pragma mark Artificial memory bloat methods | |
| 471 | |
| 472 - (void)updateBloat { | |
| 473 double bloatSizeMB; | |
| 474 NSScanner* scanner = [NSScanner scannerWithString:[_bloatField text]]; | |
| 475 if (![scanner scanDouble:&bloatSizeMB] || bloatSizeMB < 0.0) { | |
| 476 bloatSizeMB = 0; | |
| 477 NSString* errorMessage = | |
| 478 [NSString stringWithFormat:@"Invalid value \"%@\" for bloat size.\n" | |
| 479 @"Must be a positive number.\n" | |
| 480 @"Resetting to %.1f MB", | |
| 481 [_bloatField text], bloatSizeMB]; | |
| 482 [self alert:errorMessage]; | |
| 483 [_bloatField setText:[NSString stringWithFormat:@"%.1f", bloatSizeMB]]; | |
| 484 } | |
| 485 const CGFloat kBloatSizeBytes = ceil(bloatSizeMB * kNumBytesInMB); | |
| 486 const uint64 kNumberOfBytes = static_cast<uint64>(kBloatSizeBytes); | |
| 487 _bloat.reset(kNumberOfBytes ? (uint8*)malloc(kNumberOfBytes) : NULL); | |
|
sdefresne
2015/04/03 08:21:49
style: C-style cast are forbidden
Moreover _bloat
| |
| 488 if (_bloat) { | |
| 489 memset(_bloat.get(), -1, kNumberOfBytes); // Occupy memory. | |
| 490 } else { | |
| 491 if (kNumberOfBytes) { | |
| 492 [self alert:@"Could not allocate memory."]; | |
| 493 } | |
| 494 } | |
| 495 } | |
| 496 | |
| 497 - (void)clearBloat { | |
| 498 [_bloatField setText:@"0"]; | |
| 499 [_bloatField resignFirstResponder]; | |
| 500 [self updateBloat]; | |
| 501 } | |
| 502 | |
| 503 #pragma mark Refresh interval methods | |
| 504 | |
| 505 - (void)updateRefreshInterval { | |
| 506 double refreshTimerValue; | |
| 507 NSScanner* scanner = [NSScanner scannerWithString:[_refreshField text]]; | |
| 508 if (![scanner scanDouble:&refreshTimerValue] || refreshTimerValue < 0.0) { | |
| 509 refreshTimerValue = 0.5; | |
| 510 NSString* errorMessage = [NSString | |
| 511 stringWithFormat:@"Invalid value \"%@\" for refresh interval.\n" | |
| 512 @"Must be a positive number.\n" @"Resetting to %.1f", | |
| 513 [_refreshField text], refreshTimerValue]; | |
| 514 [self alert:errorMessage]; | |
| 515 [_refreshField | |
| 516 setText:[NSString stringWithFormat:@"%.1f", refreshTimerValue]]; | |
| 517 return; | |
| 518 } | |
| 519 [_refreshTimer invalidate]; | |
| 520 _refreshTimer.reset( | |
| 521 [[NSTimer scheduledTimerWithTimeInterval:refreshTimerValue | |
| 522 target:self | |
| 523 selector:@selector(refresh:) | |
| 524 userInfo:nil | |
| 525 repeats:YES] retain]); | |
| 526 } | |
| 527 | |
| 528 #pragma mark Memory warning interval methods | |
| 529 | |
| 530 // Since _performMemoryWarning is a private API it can't be compiled into | |
| 531 // stable builds. | |
|
sdefresne
2015/04/03 08:21:48
nit: s/stable/official/
| |
| 532 // TODO(lliabraa): Figure out how to support memory warnings (or something | |
| 533 // like them) in official builds. | |
| 534 #if CHROMIUM_BUILD | |
| 535 - (void)updateMemoryWarningInterval { | |
| 536 [_memoryWarningTimer invalidate]; | |
| 537 double timerValue; | |
| 538 NSString* text = [_continuousMemoryWarningField text]; | |
| 539 NSScanner* scanner = [NSScanner scannerWithString:text]; | |
| 540 BOOL valueFound = [scanner scanDouble:&timerValue]; | |
| 541 // If the text field is empty or contains 0, return early to turn off | |
| 542 // continuous memory warnings. | |
| 543 if (![text length] || timerValue == 0.0) { | |
| 544 return; | |
| 545 } | |
| 546 // If no value could be parsed or a non-positive value was found, throw up an | |
| 547 // error message and return early to turn off continuous memory warnings. | |
| 548 if (!valueFound || timerValue <= 0.0) { | |
| 549 NSString* errorMessage = [NSString | |
| 550 stringWithFormat:@"Invalid value \"%@\" for memory warning interval.\n" | |
| 551 @"Must be a positive number.\n" | |
| 552 @"Turning off continuous memory warnings", | |
| 553 text]; | |
| 554 [self alert:errorMessage]; | |
| 555 [_continuousMemoryWarningField setText:@""]; | |
| 556 return; | |
| 557 } | |
| 558 // If a valid value was found have the timer start triggering continuous | |
| 559 // memory warnings. | |
| 560 _memoryWarningTimer.reset( | |
| 561 [[NSTimer scheduledTimerWithTimeInterval:timerValue | |
| 562 target:[UIApplication sharedApplication] | |
| 563 selector:@selector(_performMemoryWarning) | |
| 564 userInfo:nil | |
| 565 repeats:YES] retain]); | |
| 566 } | |
| 567 #endif // CHROMIUM_BUILD | |
| 568 | |
| 569 #pragma mark UITextViewDelegate methods | |
| 570 | |
| 571 // Dismisses the keyboard if the user hits return. | |
| 572 - (BOOL)textFieldShouldReturn:(UITextField*)textField { | |
| 573 [textField resignFirstResponder]; | |
| 574 return YES; | |
| 575 } | |
| 576 | |
| 577 #pragma mark UIResponder methods | |
| 578 | |
| 579 // Allows the debugger to be dragged around the screen. | |
| 580 - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { | |
| 581 UITouch* touch = [touches anyObject]; | |
| 582 CGPoint start = [touch previousLocationInView:self]; | |
| 583 CGPoint end = [touch locationInView:self]; | |
| 584 CGPoint offset = CGPointMake(end.x - start.x, end.y - start.y); | |
| 585 offset = CGPointApplyAffineTransform(offset, self.transform); | |
| 586 self.frame = CGRectOffset(self.frame, offset.x, offset.y); | |
| 587 } | |
| 588 | |
| 589 #pragma mark Error handling | |
| 590 | |
| 591 // Shows an alert with the given |errorMessage|. | |
| 592 - (void)alert:(NSString*)errorMessage { | |
| 593 UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Error" | |
| 594 message:errorMessage | |
| 595 delegate:self | |
| 596 cancelButtonTitle:@"OK" | |
| 597 otherButtonTitles:nil, nil]; | |
| 598 [alert show]; | |
| 599 } | |
| 600 | |
| 601 @end | |
| OLD | NEW |