Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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 "ui/base/ios/cru_context_menu_controller.h" | 5 #import "ui/base/ios/cru_context_menu_controller.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/ios/ios_util.h" | 9 #include "base/ios/ios_util.h" |
| 10 #include "base/ios/weak_nsobject.h" | |
| 10 #include "base/logging.h" | 11 #include "base/logging.h" |
| 11 #import "base/mac/scoped_nsobject.h" | 12 #import "base/mac/scoped_nsobject.h" |
| 12 #include "base/strings/sys_string_conversions.h" | |
| 13 #include "ui/base/device_form_factor.h" | 13 #include "ui/base/device_form_factor.h" |
| 14 #import "ui/base/ios/cru_context_menu_holder.h" | 14 #import "ui/base/ios/cru_context_menu_holder.h" |
| 15 #include "ui/base/l10n/l10n_util.h" | 15 #include "ui/base/l10n/l10n_util.h" |
| 16 #include "ui/gfx/font_list.h" | |
| 17 #import "ui/gfx/ios/NSString+CrStringDrawing.h" | 16 #import "ui/gfx/ios/NSString+CrStringDrawing.h" |
| 18 #include "ui/gfx/text_elider.h" | |
| 19 #include "ui/strings/grit/ui_strings.h" | 17 #include "ui/strings/grit/ui_strings.h" |
| 20 | 18 |
| 19 namespace { | |
| 20 | |
| 21 // Returns the screen's height in pixels. | |
|
lliabraa
2015/04/23 20:06:40
s/pixels/points/
Eugene But (OOO till 7-30)
2015/04/23 20:59:52
Done.
| |
| 22 CGFloat GetScreenHeight() { | |
| 23 DCHECK(!base::ios::IsRunningOnIOS8OrLater()); | |
| 24 switch ([[UIApplication sharedApplication] statusBarOrientation]) { | |
| 25 case UIInterfaceOrientationLandscapeLeft: | |
| 26 case UIInterfaceOrientationLandscapeRight: | |
| 27 return CGRectGetWidth([[UIScreen mainScreen] applicationFrame]); | |
| 28 case UIInterfaceOrientationPortraitUpsideDown: | |
| 29 case UIInterfaceOrientationPortrait: | |
| 30 case UIInterfaceOrientationUnknown: | |
| 31 return CGRectGetHeight([[UIScreen mainScreen] applicationFrame]); | |
| 32 } | |
| 33 } | |
| 34 | |
| 35 } // namespace | |
| 36 | |
| 37 // Abstracts system implementation of popovers and action sheets. | |
| 38 @protocol CRUContextMenuControllerImpl<NSObject> | |
| 39 | |
| 40 // Whether the context menu is visible. | |
| 41 @property(nonatomic, readonly, getter=isVisible) BOOL visible; | |
| 42 | |
| 43 // Displays a context menu. | |
| 44 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder | |
| 45 atPoint:(CGPoint)localPoint | |
| 46 inView:(UIView*)view; | |
| 47 @end | |
| 48 | |
| 49 // Backs up CRUContextMenuController on iOS 7 by using UIActionSheet. | |
| 50 @interface CRUActionSheetController | |
| 51 : NSObject<CRUContextMenuControllerImpl, UIActionSheetDelegate> { | |
| 52 // The action sheet used to display the UI. | |
| 53 base::scoped_nsobject<UIActionSheet> _sheet; | |
| 54 // Holds all the titles and actions for the menu. | |
| 55 base::scoped_nsobject<CRUContextMenuHolder> _menuHolder; | |
| 56 } | |
| 57 @end | |
| 58 | |
| 59 // Backs up CRUContextMenuController on iOS 8 and higher by using | |
| 60 // UIAlertController. | |
| 61 @interface CRUAlertController : NSObject<CRUContextMenuControllerImpl> | |
| 62 @end | |
| 63 | |
| 64 // Displays a context menu. Implements Bridge pattern. | |
| 21 @implementation CRUContextMenuController { | 65 @implementation CRUContextMenuController { |
| 22 // Holds all the titles and actions for the menu. | 66 // Implementation specific for iOS version. |
| 23 base::scoped_nsobject<CRUContextMenuHolder> menuHolder_; | 67 base::scoped_nsprotocol<id<CRUContextMenuControllerImpl>> _impl; |
| 24 // The action sheet controller used to display the UI. | 68 } |
| 25 base::scoped_nsobject<UIActionSheet> sheet_; | 69 |
| 26 // Whether the context menu is visible. | 70 - (BOOL)isVisible { |
| 27 BOOL visible_; | 71 return [_impl isVisible]; |
| 28 } | 72 } |
| 29 | 73 |
| 30 | 74 - (instancetype)init { |
| 31 // Clean up and reset for the next time. | 75 self = [super init]; |
| 32 - (void)cleanup { | 76 if (self) { |
| 33 // iOS 8 fires multiple callbacks when a button is clicked; one round for the | 77 if (base::ios::IsRunningOnIOS8OrLater()) { |
| 34 // button that's clicked and a second round with |buttonIndex| equivalent to | 78 _impl.reset([[CRUAlertController alloc] init]); |
| 35 // tapping outside the context menu. Here the sheet's delegate is reset so | 79 } else { |
| 36 // that only the first round of callbacks is processed. | 80 _impl.reset([[CRUActionSheetController alloc] init]); |
| 37 // Note that iOS 8 needs the |sheet_| to stay alive so it's not reset until | 81 } |
| 38 // this CRUContextMenuController is dealloc'd. | 82 } |
| 39 [sheet_ setDelegate:nil]; | 83 return self; |
| 40 menuHolder_.reset(); | 84 } |
| 41 visible_ = NO; | 85 |
| 42 } | 86 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder |
| 87 atPoint:(CGPoint)point | |
| 88 inView:(UIView*)view { | |
| 89 DCHECK(menuHolder.itemCount); | |
| 90 // Check that the view is still visible on screen, otherwise just return and | |
| 91 // don't show the context menu. | |
| 92 DCHECK([view window] || [view isKindOfClass:[UIWindow class]]); | |
| 93 if (![view window] && ![view isKindOfClass:[UIWindow class]]) | |
| 94 return; | |
| 95 [_impl showWithHolder:menuHolder atPoint:point inView:view]; | |
| 96 } | |
| 97 | |
| 98 @end | |
| 99 | |
| 100 #pragma mark - | |
| 101 #pragma mark iOS 7 | |
| 102 | |
| 103 @implementation CRUActionSheetController | |
| 104 @synthesize visible = _visible; | |
| 43 | 105 |
| 44 - (void)dealloc { | 106 - (void)dealloc { |
| 45 if (visible_) { | 107 if (_visible) { |
| 46 // Context menu must be dismissed explicitly if it is still visible at this | 108 // Context menu must be dismissed explicitly if it is still visible. |
| 47 // stage. | 109 NSUInteger cancelButtonIndex = [_menuHolder itemCount]; |
| 48 NSUInteger cancelButtonIndex = [menuHolder_ itemCount]; | 110 [_sheet dismissWithClickedButtonIndex:cancelButtonIndex animated:NO]; |
| 49 [sheet_ dismissWithClickedButtonIndex:cancelButtonIndex animated:NO]; | 111 } |
| 50 } | |
| 51 sheet_.reset(); | |
| 52 [super dealloc]; | 112 [super dealloc]; |
| 53 } | 113 } |
| 54 | 114 |
| 55 - (BOOL)isVisible { | 115 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder |
| 56 return visible_; | 116 atPoint:(CGPoint)point |
| 57 } | 117 inView:(UIView*)view { |
| 118 // If the content of UIActionSheet does not fit the screen then scroll bars | |
| 119 // are added to the menu items area. If that's the case - elide the title to | |
| 120 // avoid having scrollbars for menu items. | |
| 121 CGSize spaceAvailableForTitle = | |
| 122 [self sizeForTitleThatFitsMenuWithHolder:menuHolder | |
| 123 atPoint:point | |
| 124 inView:view]; | |
| 125 NSString* menuTitle = menuHolder.menuTitle; | |
| 126 if (menuTitle) { | |
| 127 // Show at least one line of text, even if that means the action sheet's | |
| 128 // items will need to scroll. | |
| 129 const CGFloat kMinimumVerticalSpace = 21; | |
| 130 spaceAvailableForTitle.height = | |
| 131 std::max(kMinimumVerticalSpace, spaceAvailableForTitle.height); | |
| 132 menuTitle = [menuTitle cr_stringByElidingToFitSize:spaceAvailableForTitle]; | |
| 133 } | |
| 134 | |
| 135 // Present UIActionSheet. | |
| 136 _sheet.reset( | |
| 137 [self newActionSheetWithHolder:menuHolder title:menuTitle delegate:self]); | |
| 138 [_sheet setCancelButtonIndex:menuHolder.itemCount]; | |
| 139 [_sheet showFromRect:CGRectMake(point.x, point.y, 1.0, 1.0) | |
| 140 inView:view | |
| 141 animated:YES]; | |
| 142 | |
| 143 _menuHolder.reset([menuHolder retain]); | |
| 144 _visible = YES; | |
| 145 } | |
| 146 | |
| 147 #pragma mark Implementation | |
| 148 | |
| 149 // Returns an approximation of the free space available for the title of an | |
| 150 // actionSheet filled with |menu| shown in |view| at |point|. | |
| 151 - (CGSize)sizeForTitleThatFitsMenuWithHolder:(CRUContextMenuHolder*)menuHolder | |
| 152 atPoint:(CGPoint)point | |
| 153 inView:(UIView*)view { | |
| 154 // Create a dummy UIActionSheet. | |
| 155 base::scoped_nsobject<UIActionSheet> dummySheet( | |
| 156 [self newActionSheetWithHolder:menuHolder title:nil delegate:nil]); | |
| 157 // Temporarily add the dummy UIActionSheet to |view|. | |
| 158 [dummySheet showFromRect:CGRectMake(point.x, point.y, 1.0, 1.0) | |
| 159 inView:view | |
| 160 animated:NO]; | |
| 161 // On iPad the actionsheet is positioned under or over |point| (as opposed | |
| 162 // to next to it) when the user clicks within approximately 200 points of | |
| 163 // respectively the top or bottom edge. This reduces the amount of vertical | |
| 164 // space available for the title, hence the large padding on ipad. | |
| 165 const CGFloat kPaddingiPad = 200; | |
| 166 const CGFloat kPaddingiPhone = 20; | |
| 167 BOOL isIPad = ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET; | |
| 168 const CGFloat padding = isIPad ? kPaddingiPad : kPaddingiPhone; | |
| 169 // A title uses the full width of the actionsheet and all the vertical | |
| 170 // space on the screen. | |
| 171 CGSize result = CGSizeMake( | |
| 172 CGRectGetWidth([dummySheet frame]), | |
| 173 GetScreenHeight() - CGRectGetHeight([dummySheet frame]) - padding); | |
| 174 [dummySheet dismissWithClickedButtonIndex:0 animated:NO]; | |
| 175 return result; | |
| 176 } | |
| 177 | |
| 178 // Returns an UIActionSheet. Callers responsible for releasing returned object. | |
| 179 - (UIActionSheet*)newActionSheetWithHolder:(CRUContextMenuHolder*)menuHolder | |
| 180 title:(NSString*)title | |
| 181 delegate:(id<UIActionSheetDelegate>)delegate { | |
| 182 UIActionSheet* sheet = [[UIActionSheet alloc] initWithTitle:title | |
| 183 delegate:delegate | |
| 184 cancelButtonTitle:nil | |
| 185 destructiveButtonTitle:nil | |
| 186 otherButtonTitles:nil]; | |
| 187 | |
| 188 for (NSString* itemTitle in menuHolder.itemTitles) { | |
| 189 [sheet addButtonWithTitle:itemTitle]; | |
| 190 } | |
| 191 [sheet addButtonWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL)]; | |
| 192 return sheet; | |
| 193 } | |
| 194 | |
| 195 #pragma mark UIActionSheetDelegate | |
| 58 | 196 |
| 59 // Called when the action sheet is dismissed in the modal context menu sheet. | 197 // Called when the action sheet is dismissed in the modal context menu sheet. |
| 60 // There is no way to dismiss the sheet without going through this method. Note | 198 // There is no way to dismiss the sheet without going through this method. Note |
| 61 // that on iPad this method is called with the index of an nonexistent cancel | 199 // that on iPad this method is called with the index of an nonexistent cancel |
| 62 // button when the user taps outside the sheet. | 200 // button when the user taps outside the sheet. |
| 63 - (void)actionSheet:(UIActionSheet*)actionSheet | 201 - (void)actionSheet:(UIActionSheet*)actionSheet |
| 64 didDismissWithButtonIndex:(NSInteger)buttonIndex { | 202 didDismissWithButtonIndex:(NSInteger)buttonIndex { |
| 65 // On iOS 8, if the user taps an item in the context menu, then taps outside | 203 NSUInteger unsignedButtonIndex = buttonIndex; |
| 66 // the context menu, the |buttonIndex| passed into this method may be | |
| 67 // different from the |buttonIndex| passed into | |
| 68 // |actionsheet:willDismissWithButtonIndex:|. See crbug.com/411894. | |
| 69 NSUInteger buttonIndexU = buttonIndex; | |
| 70 // Assumes "cancel" button is last in order. | 204 // Assumes "cancel" button is last in order. |
| 71 if (buttonIndexU < [menuHolder_ itemCount]) | 205 if (unsignedButtonIndex < [_menuHolder itemCount]) |
| 72 [menuHolder_ performActionAtIndex:buttonIndexU]; | 206 [_menuHolder performActionAtIndex:unsignedButtonIndex]; |
| 73 [self cleanup]; | 207 _menuHolder.reset(); |
| 208 _visible = NO; | |
| 74 } | 209 } |
| 75 | 210 |
| 76 // Called when the user chooses a button in the modal context menu sheet. Note | 211 // Called when the user chooses a button in the modal context menu sheet. Note |
| 77 // that on iPad this method is called with the index of an nonexistent cancel | 212 // that on iPad this method is called with the index of an nonexistent cancel |
| 78 // button when the user taps outside the sheet. | 213 // button when the user taps outside the sheet. |
| 79 - (void)actionSheet:(UIActionSheet*)actionSheet | 214 - (void)actionSheet:(UIActionSheet*)actionSheet |
| 80 clickedButtonAtIndex:(NSInteger)buttonIndex { | 215 clickedButtonAtIndex:(NSInteger)buttonIndex { |
| 81 // Some use cases (e.g. opening a new tab on handset) should not wait for the | 216 // Some use cases (e.g. opening a new tab on handset) should not wait for the |
| 82 // action sheet to animate away before executing the action. | 217 // action sheet to animate away before executing the action. |
| 83 if ([menuHolder_ shouldDismissImmediatelyOnClickedAtIndex:buttonIndex]) { | 218 if ([_menuHolder shouldDismissImmediatelyOnClickedAtIndex:buttonIndex]) { |
| 84 [sheet_ dismissWithClickedButtonIndex:buttonIndex animated:NO]; | 219 [_sheet dismissWithClickedButtonIndex:buttonIndex animated:NO]; |
| 85 } | 220 } |
| 86 } | 221 } |
| 222 | |
| 223 @end | |
| 87 | 224 |
| 88 #pragma mark - | 225 #pragma mark - |
| 89 #pragma mark WebContextMenuDelegate methods | 226 #pragma mark iOS8 and higher |
| 90 | 227 |
| 91 // Displays a menu using a sheet with the given title. | 228 @implementation CRUAlertController |
| 92 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder | 229 @synthesize visible = _visible; |
| 93 atPoint:(CGPoint)localPoint | 230 |
| 231 - (CGSize)sizeForTitleThatFitsMenuWithHolder:(CRUContextMenuHolder*)menuHolder | |
| 232 atPoint:(CGPoint)point | |
| 233 inView:(UIView*)view { | |
| 234 // On presenting and dismissing a dummy UIAlertController flushes a screen. | |
| 235 // As a workaround return an estimation of the space available depending | |
| 236 // on the device's type. | |
| 237 const CGFloat kAvailableWidth = 320; | |
| 238 const CGFloat kAvailableHeightTablet = 200; | |
| 239 const CGFloat kAvailableHeightPhone = 100; | |
| 240 if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) { | |
| 241 return CGSizeMake(kAvailableWidth, kAvailableHeightTablet); | |
| 242 } | |
| 243 return CGSizeMake(kAvailableWidth, kAvailableHeightPhone); | |
| 244 } | |
| 245 | |
| 246 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder | |
| 247 atPoint:(CGPoint)point | |
| 94 inView:(UIView*)view { | 248 inView:(UIView*)view { |
| 95 DCHECK([menuHolder itemCount]); | 249 UIAlertController* alert = [UIAlertController |
| 96 menuHolder_.reset([menuHolder retain]); | 250 alertControllerWithTitle:menuHolder.menuTitle |
| 97 // Check that the view is still visible on screen, otherwise just return and | 251 message:nil |
| 98 // don't show the context menu. | 252 preferredStyle:UIAlertControllerStyleActionSheet]; |
| 99 DCHECK([view window] || [view isKindOfClass:[UIWindow class]]); | 253 alert.popoverPresentationController.sourceView = view; |
| 100 if (![view window] && ![view isKindOfClass:[UIWindow class]]) | 254 alert.popoverPresentationController.sourceRect = |
| 101 return; | 255 CGRectMake(point.x, point.y, 1.0, 1.0); |
| 102 CGSize spaceAvailableForTitle = | 256 |
| 103 [CRUContextMenuController | 257 // Add the actions. |
| 104 availableSpaceForTitleInActionSheetWithMenu:menuHolder_ | 258 base::WeakNSObject<CRUAlertController> weakSelf(self); |
| 105 atPoint:localPoint | 259 [menuHolder.itemTitles enumerateObjectsUsingBlock:^(NSString* itemTitle, |
| 106 inView:view]; | 260 NSUInteger itemIndex, |
| 107 NSString* title = menuHolder.menuTitle; | 261 BOOL*) { |
| 108 if (title) { | 262 void (^actionHandler)(UIAlertAction*) = ^(UIAlertAction* action) { |
| 109 // Show at least one line of text, even if that means the UIActionSheet's | 263 [menuHolder performActionAtIndex:itemIndex]; |
| 110 // items will need to scroll. | 264 weakSelf.get()->_visible = NO; |
| 111 const CGFloat kMinimumVerticalSpace = 21; | 265 }; |
| 112 spaceAvailableForTitle.height = | 266 [alert addAction:[UIAlertAction actionWithTitle:itemTitle |
| 113 std::max(kMinimumVerticalSpace, spaceAvailableForTitle.height); | 267 style:UIAlertActionStyleDefault |
| 114 title = [title cr_stringByElidingToFitSize:spaceAvailableForTitle]; | 268 handler:actionHandler]]; |
| 115 } | 269 }]; |
| 116 // Create the sheet. | 270 |
| 117 sheet_.reset( | |
| 118 [[UIActionSheet alloc] initWithTitle:title | |
| 119 delegate:self | |
| 120 cancelButtonTitle:nil | |
| 121 destructiveButtonTitle:nil | |
| 122 otherButtonTitles:nil]); | |
| 123 // Add the labels, in order, to the sheet. | |
| 124 for (NSString* label in [menuHolder_ itemTitles]) { | |
| 125 [sheet_ addButtonWithTitle:label]; | |
| 126 } | |
| 127 // Cancel button goes last, to match other browsers. | 271 // Cancel button goes last, to match other browsers. |
| 128 [sheet_ addButtonWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL)]; | 272 UIAlertAction* cancel_action = |
| 129 [sheet_ setCancelButtonIndex:[menuHolder_ itemCount]]; | 273 [UIAlertAction actionWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL) |
| 130 [sheet_ showFromRect:CGRectMake(localPoint.x, localPoint.y, 1.0, 1.0) | 274 style:UIAlertActionStyleCancel |
| 131 inView:view | 275 handler:nil]; |
| 132 animated:YES]; | 276 [alert addAction:cancel_action]; |
| 133 | 277 |
| 134 visible_ = YES; | 278 // Present sheet/popover using controller that is added to view hierarchy. |
| 135 } | 279 UIViewController* topController = view.window.rootViewController; |
| 136 | 280 while (topController.presentedViewController) |
| 137 // Returns an approximation of the free space available for the title of an | 281 topController = topController.presentedViewController; |
| 138 // actionSheet filled with |menu| shown in |view| at |point|. | 282 [topController presentViewController:alert |
| 139 + (CGSize) | 283 animated:YES |
| 140 availableSpaceForTitleInActionSheetWithMenu:(CRUContextMenuHolder*)menu | 284 completion:^{ |
| 141 atPoint:(CGPoint)point | 285 weakSelf.get()->_visible = YES; |
| 142 inView:(UIView*)view { | 286 }]; |
| 143 BOOL isIpad = ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET; | 287 } |
| 144 if (base::ios::IsRunningOnIOS8OrLater()) { | 288 |
| 145 // On iOS8 presenting and dismissing a dummy UIActionSheet does not work | 289 @end |
| 146 // (http://crbug.com/392245 and rdar://17677745). | |
| 147 // As a workaround we return an estimation of the space available depending | |
| 148 // on the device's type. | |
| 149 const CGFloat kAvailableWidth = 320; | |
| 150 const CGFloat kAvailableHeightTablet = 200; | |
| 151 const CGFloat kAvailableHeightPhone = 100; | |
| 152 if (isIpad) { | |
| 153 return CGSizeMake(kAvailableWidth, kAvailableHeightTablet); | |
| 154 } | |
| 155 return CGSizeMake(kAvailableWidth, kAvailableHeightPhone); | |
| 156 } else { | |
| 157 // Creates a dummy UIActionSheet. | |
| 158 base::scoped_nsobject<UIActionSheet> dummyActionSheet( | |
| 159 [[UIActionSheet alloc] initWithTitle:nil | |
| 160 delegate:nil | |
| 161 cancelButtonTitle:nil | |
| 162 destructiveButtonTitle:nil | |
| 163 otherButtonTitles:nil]); | |
| 164 for (NSString* label in [menu itemTitles]) { | |
| 165 [dummyActionSheet addButtonWithTitle:label]; | |
| 166 } | |
| 167 [dummyActionSheet addButtonWithTitle: | |
| 168 l10n_util::GetNSString(IDS_APP_CANCEL)]; | |
| 169 // Temporarily adds the dummy UIActionSheet to |view|. | |
| 170 [dummyActionSheet showFromRect:CGRectMake(point.x, point.y, 1.0, 1.0) | |
| 171 inView:view | |
| 172 animated:NO]; | |
| 173 // On iPad the actionsheet is positioned under or over |point| (as opposed | |
| 174 // to next to it) when the user clicks within approximately 200 points of | |
| 175 // respectively the top or bottom edge. This reduces the amount of vertical | |
| 176 // space available for the title, hence the large padding on ipad. | |
| 177 const int kPaddingiPad = 200; | |
| 178 const int kPaddingiPhone = 20; | |
| 179 CGFloat padding = isIpad ? kPaddingiPad : kPaddingiPhone; | |
| 180 // A title uses the full width of the actionsheet and all the vertical | |
| 181 // space on the screen. | |
| 182 CGSize availableSpaceForTitle = | |
| 183 CGSizeMake([dummyActionSheet frame].size.width, | |
| 184 [CRUContextMenuController screenHeight] - | |
| 185 [dummyActionSheet frame].size.height - | |
| 186 padding); | |
| 187 [dummyActionSheet dismissWithClickedButtonIndex:0 animated:NO]; | |
| 188 return availableSpaceForTitle; | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 // Returns the screen's height in pixels. | |
| 193 + (int)screenHeight { | |
| 194 DCHECK(!base::ios::IsRunningOnIOS8OrLater()); | |
| 195 switch ([[UIApplication sharedApplication] statusBarOrientation]) { | |
| 196 case UIInterfaceOrientationLandscapeLeft: | |
| 197 case UIInterfaceOrientationLandscapeRight: | |
| 198 return [[UIScreen mainScreen] applicationFrame].size.width; | |
| 199 case UIInterfaceOrientationPortraitUpsideDown: | |
| 200 case UIInterfaceOrientationPortrait: | |
| 201 case UIInterfaceOrientationUnknown: | |
| 202 return [[UIScreen mainScreen] applicationFrame].size.height; | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 @end | |
| 207 | |
| 208 @implementation CRUContextMenuController (UsedForTesting) | |
| 209 - (UIActionSheet*)sheet { | |
| 210 return sheet_.get(); | |
| 211 } | |
| 212 @end | |
| OLD | NEW |