Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(286)

Side by Side Diff: ios/chrome/browser/ui/browser_view_controller.mm

Issue 2654433007: [ios] Moves find-in-page code out of Tab and into FindTabHelper. (Closed)
Patch Set: Fix tests. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 "ios/chrome/browser/ui/browser_view_controller.h" 5 #import "ios/chrome/browser/ui/browser_view_controller.h"
6 6
7 #import <AssetsLibrary/AssetsLibrary.h> 7 #import <AssetsLibrary/AssetsLibrary.h>
8 #import <MobileCoreServices/MobileCoreServices.h> 8 #import <MobileCoreServices/MobileCoreServices.h>
9 #import <PassKit/PassKit.h> 9 #import <PassKit/PassKit.h>
10 #import <Photos/Photos.h> 10 #import <Photos/Photos.h>
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h" 50 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
51 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" 51 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
52 #include "ios/chrome/browser/chrome_url_constants.h" 52 #include "ios/chrome/browser/chrome_url_constants.h"
53 #include "ios/chrome/browser/chrome_url_util.h" 53 #include "ios/chrome/browser/chrome_url_util.h"
54 #import "ios/chrome/browser/content_suggestions/content_suggestions_coordinator. h" 54 #import "ios/chrome/browser/content_suggestions/content_suggestions_coordinator. h"
55 #include "ios/chrome/browser/experimental_flags.h" 55 #include "ios/chrome/browser/experimental_flags.h"
56 #import "ios/chrome/browser/favicon/favicon_loader.h" 56 #import "ios/chrome/browser/favicon/favicon_loader.h"
57 #include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h" 57 #include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h"
58 #import "ios/chrome/browser/find_in_page/find_in_page_controller.h" 58 #import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
59 #import "ios/chrome/browser/find_in_page/find_in_page_model.h" 59 #import "ios/chrome/browser/find_in_page/find_in_page_model.h"
60 #import "ios/chrome/browser/find_in_page/find_tab_helper.h"
60 #include "ios/chrome/browser/first_run/first_run.h" 61 #include "ios/chrome/browser/first_run/first_run.h"
61 #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h" 62 #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
62 #include "ios/chrome/browser/infobars/infobar_container_ios.h" 63 #include "ios/chrome/browser/infobars/infobar_container_ios.h"
63 #include "ios/chrome/browser/infobars/infobar_container_view.h" 64 #include "ios/chrome/browser/infobars/infobar_container_view.h"
64 #import "ios/chrome/browser/metrics/new_tab_page_uma.h" 65 #import "ios/chrome/browser/metrics/new_tab_page_uma.h"
65 #include "ios/chrome/browser/metrics/tab_usage_recorder.h" 66 #include "ios/chrome/browser/metrics/tab_usage_recorder.h"
66 #import "ios/chrome/browser/native_app_launcher/native_app_navigation_controller .h" 67 #import "ios/chrome/browser/native_app_launcher/native_app_navigation_controller .h"
67 #import "ios/chrome/browser/open_url_util.h" 68 #import "ios/chrome/browser/open_url_util.h"
68 #import "ios/chrome/browser/passwords/password_controller.h" 69 #import "ios/chrome/browser/passwords/password_controller.h"
69 #import "ios/chrome/browser/payments/payment_request_manager.h" 70 #import "ios/chrome/browser/payments/payment_request_manager.h"
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
281 return true; 282 return true;
282 std::string url_host = url.host(); 283 std::string url_host = url.host();
283 return url_host != kChromeUIHistoryHost && 284 return url_host != kChromeUIHistoryHost &&
284 url_host != kChromeUIHistoryFrameHost; 285 url_host != kChromeUIHistoryFrameHost;
285 } 286 }
286 287
287 // Temporary key to use when storing native controllers vended to tabs before 288 // Temporary key to use when storing native controllers vended to tabs before
288 // they are added to the tab model. 289 // they are added to the tab model.
289 NSString* const kNativeControllerTemporaryKey = @"NativeControllerTemporaryKey"; 290 NSString* const kNativeControllerTemporaryKey = @"NativeControllerTemporaryKey";
290 291
292 // Helper function to return the FindInPageController for the given |tab|. If
293 // |tab| is nullptr or has no FindTabHelper, returns nil.
294 FindInPageController* GetFindInPageController(Tab* tab) {
295 if (!tab) {
296 return nil;
297 }
298 FindTabHelper* helper = FindTabHelper::FromWebState(tab.webState);
299 if (!helper) {
300 return nil;
301 }
302 return helper->GetController();
303 }
304
291 } // anonymous namespace 305 } // anonymous namespace
292 306
293 @interface BrowserViewController ()<AppRatingPromptDelegate, 307 @interface BrowserViewController ()<AppRatingPromptDelegate,
294 ContextualSearchControllerDelegate, 308 ContextualSearchControllerDelegate,
295 ContextualSearchPanelMotionObserver, 309 ContextualSearchPanelMotionObserver,
296 CRWNativeContentProvider, 310 CRWNativeContentProvider,
297 CRWWebStateDelegate, 311 CRWWebStateDelegate,
298 DialogPresenterDelegate, 312 DialogPresenterDelegate,
299 FullScreenControllerDelegate, 313 FullScreenControllerDelegate,
300 KeyCommandsPlumbing, 314 KeyCommandsPlumbing,
(...skipping 756 matching lines...) Expand 10 before | Expand all | Expand 10 after
1057 Tab* tab = [_model currentTab]; 1071 Tab* tab = [_model currentTab];
1058 // TODO(shreyasv): Make it so the URL returned by the tab is always valid and 1072 // TODO(shreyasv): Make it so the URL returned by the tab is always valid and
1059 // remove check on net::NSURLWithGURL(tab.url) ( http://crbug.com/400999 ). 1073 // remove check on net::NSURLWithGURL(tab.url) ( http://crbug.com/400999 ).
1060 return tab && !tab.url.SchemeIs(kChromeUIScheme) && 1074 return tab && !tab.url.SchemeIs(kChromeUIScheme) &&
1061 net::NSURLWithGURL(tab.url); 1075 net::NSURLWithGURL(tab.url);
1062 } 1076 }
1063 1077
1064 - (BOOL)canShowFindBar { 1078 - (BOOL)canShowFindBar {
1065 // Make sure web controller can handle find in page. 1079 // Make sure web controller can handle find in page.
1066 Tab* tab = [_model currentTab]; 1080 Tab* tab = [_model currentTab];
1067 if (![tab.findInPageController canFindInPage]) 1081 FindInPageController* controller = GetFindInPageController(tab);
1082 if (![controller canFindInPage])
1068 return NO; 1083 return NO;
1069 1084
1070 // Don't show twice. 1085 // Don't show twice.
1071 if (tab.findInPageController.findInPageModel.enabled) 1086 if (controller.findInPageModel.enabled)
1072 return NO; 1087 return NO;
1073 1088
1074 return YES; 1089 return YES;
1075 } 1090 }
1076 1091
1077 - (void)setVisible:(BOOL)visible { 1092 - (void)setVisible:(BOOL)visible {
1078 if (_visible == visible) 1093 if (_visible == visible)
1079 return; 1094 return;
1080 _visible = visible; 1095 _visible = visible;
1081 } 1096 }
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after
1421 selector:@selector(tabWasAdded:) 1436 selector:@selector(tabWasAdded:)
1422 name:kTabModelNewTabWillOpenNotification 1437 name:kTabModelNewTabWillOpenNotification
1423 object:_model]; 1438 object:_model];
1424 } 1439 }
1425 1440
1426 - (void)pageLoadStarting:(NSNotification*)notify { 1441 - (void)pageLoadStarting:(NSNotification*)notify {
1427 Tab* tab = notify.userInfo[kTabModelTabKey]; 1442 Tab* tab = notify.userInfo[kTabModelTabKey];
1428 DCHECK(tab && ([_model indexOfTab:tab] != NSNotFound)); 1443 DCHECK(tab && ([_model indexOfTab:tab] != NSNotFound));
1429 // Hide find bar when navigating to a new page. 1444 // Hide find bar when navigating to a new page.
1430 [self hideFindBarWithAnimation:NO]; 1445 [self hideFindBarWithAnimation:NO];
1431 tab.findInPageController.findInPageModel.enabled = NO; 1446
1447 FindInPageController* controller = GetFindInPageController(tab);
1448 controller.findInPageModel.enabled = NO;
1449
1432 if (tab == [_model currentTab]) { 1450 if (tab == [_model currentTab]) {
1433 // TODO(pinkerton): Fill in here about hiding the forward button on 1451 // TODO(pinkerton): Fill in here about hiding the forward button on
1434 // navigation. 1452 // navigation.
1435 } 1453 }
1436 } 1454 }
1437 1455
1438 - (void)pageLoadStarted:(NSNotification*)notify { 1456 - (void)pageLoadStarted:(NSNotification*)notify {
1439 Tab* tab = notify.userInfo[kTabModelTabKey]; 1457 Tab* tab = notify.userInfo[kTabModelTabKey];
1440 DCHECK(tab); 1458 DCHECK(tab);
1441 if (tab == [_model currentTab]) { 1459 if (tab == [_model currentTab]) {
(...skipping 433 matching lines...) Expand 10 before | Expand all | Expand 10 after
1875 1893
1876 if (tab.isPrerenderTab && !_toolbarModelIOS->IsLoading()) 1894 if (tab.isPrerenderTab && !_toolbarModelIOS->IsLoading())
1877 [_toolbarController showPrerenderingAnimation]; 1895 [_toolbarController showPrerenderingAnimation];
1878 1896
1879 // Also update the loading state for the tools menu (that is really an 1897 // Also update the loading state for the tools menu (that is really an
1880 // extension of the toolbar on the iPhone). 1898 // extension of the toolbar on the iPhone).
1881 if (!IsIPadIdiom()) 1899 if (!IsIPadIdiom())
1882 [[_toolbarController toolsPopupController] 1900 [[_toolbarController toolsPopupController]
1883 setIsTabLoading:_toolbarModelIOS->IsLoading()]; 1901 setIsTabLoading:_toolbarModelIOS->IsLoading()];
1884 1902
1885 if (tab.findInPageController.findInPageModel.enabled) 1903 FindInPageController* controller = GetFindInPageController(tab);
1904 if (controller.findInPageModel.enabled) {
1886 [self showFindBarWithAnimation:NO 1905 [self showFindBarWithAnimation:NO
1887 selectText:YES 1906 selectText:YES
1888 shouldFocus:[_findBarController isFocused]]; 1907 shouldFocus:[_findBarController isFocused]];
1908 }
1889 1909
1890 // Hide the toolbar if displaying phone NTP. 1910 // Hide the toolbar if displaying phone NTP.
1891 if (!IsIPadIdiom()) { 1911 if (!IsIPadIdiom()) {
1892 CRWSessionEntry* entry = 1912 CRWSessionEntry* entry =
1893 [[tab navigationManager]->GetSessionController() currentEntry]; 1913 [[tab navigationManager]->GetSessionController() currentEntry];
1894 BOOL hideToolbar = NO; 1914 BOOL hideToolbar = NO;
1895 if (entry) { 1915 if (entry) {
1896 GURL url = [entry navigationItem]->GetURL(); 1916 GURL url = [entry navigationItem]->GetURL();
1897 BOOL isNTP = url.GetOrigin() == GURL(kChromeUINewTabURL); 1917 BOOL isNTP = url.GetOrigin() == GURL(kChromeUINewTabURL);
1898 hideToolbar = isNTP && !_isOffTheRecord && 1918 hideToolbar = isNTP && !_isOffTheRecord &&
(...skipping 1904 matching lines...) Expand 10 before | Expand all | Expand 10 after
3803 currentlyBookmarked:_toolbarModelIOS->IsCurrentTabBookmarkedByUser() 3823 currentlyBookmarked:_toolbarModelIOS->IsCurrentTabBookmarkedByUser()
3804 inView:[_toolbarController bookmarkButtonView] 3824 inView:[_toolbarController bookmarkButtonView]
3805 originRect:[_toolbarController bookmarkButtonAnchorRect]]; 3825 originRect:[_toolbarController bookmarkButtonAnchorRect]];
3806 break; 3826 break;
3807 case IDC_CLOSE_TAB: 3827 case IDC_CLOSE_TAB:
3808 [self closeCurrentTab]; 3828 [self closeCurrentTab];
3809 break; 3829 break;
3810 case IDC_FIND: 3830 case IDC_FIND:
3811 [self initFindBarForTab]; 3831 [self initFindBarForTab];
3812 break; 3832 break;
3813 case IDC_FIND_NEXT: 3833 case IDC_FIND_NEXT: {
3834 FindInPageController* findInPageController =
Eugene But (OOO till 7-30) 2017/02/10 00:03:36 |FindInPageController* findInPageController = GetF
rohitrao (ping after 24h) 2017/02/10 13:52:18 Missed these two, thanks.
3835 FindTabHelper::FromWebState([_model currentTab].webState)
3836 ->GetController();
3814 // TODO(crbug.com/603524): Reshow find bar if necessary. 3837 // TODO(crbug.com/603524): Reshow find bar if necessary.
3815 [[_model currentTab].findInPageController 3838 [findInPageController findNextStringInPageWithCompletionHandler:^{
3816 findNextStringInPageWithCompletionHandler:^{ 3839 FindInPageModel* model = findInPageController.findInPageModel;
3817 FindInPageModel* model = 3840 [_findBarController updateResultsCount:model];
3818 [_model currentTab].findInPageController.findInPageModel; 3841 }];
3819 [_findBarController updateResultsCount:model];
3820 }];
3821 break; 3842 break;
3822 case IDC_FIND_PREVIOUS: 3843 }
3844 case IDC_FIND_PREVIOUS: {
3845 FindInPageController* findInPageController =
Eugene But (OOO till 7-30) 2017/02/10 00:03:36 ditto
3846 FindTabHelper::FromWebState([_model currentTab].webState)
3847 ->GetController();
3823 // TODO(crbug.com/603524): Reshow find bar if necessary. 3848 // TODO(crbug.com/603524): Reshow find bar if necessary.
3824 [[_model currentTab].findInPageController 3849 [findInPageController findPreviousStringInPageWithCompletionHandler:^{
3825 findPreviousStringInPageWithCompletionHandler:^{ 3850 FindInPageModel* model = findInPageController.findInPageModel;
3826 FindInPageModel* model = 3851 [_findBarController updateResultsCount:model];
3827 [_model currentTab].findInPageController.findInPageModel; 3852 }];
3828 [_findBarController updateResultsCount:model];
3829 }];
3830 break; 3853 break;
3854 }
3831 case IDC_FIND_CLOSE: 3855 case IDC_FIND_CLOSE:
3832 [self closeFindInPage]; 3856 [self closeFindInPage];
3833 break; 3857 break;
3834 case IDC_FIND_UPDATE: 3858 case IDC_FIND_UPDATE:
3835 [self searchFindInPage]; 3859 [self searchFindInPage];
3836 break; 3860 break;
3837 case IDC_FORWARD: 3861 case IDC_FORWARD:
3838 [[_model currentTab] goForward]; 3862 [[_model currentTab] goForward];
3839 break; 3863 break;
3840 case IDC_FULLSCREEN: 3864 case IDC_FULLSCREEN:
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
4023 4047
4024 - (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion { 4048 - (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion {
4025 [[_dependencyFactory shareControllerInstance] cancelShareAnimated:NO]; 4049 [[_dependencyFactory shareControllerInstance] cancelShareAnimated:NO];
4026 [_bookmarkInteractionController dismissBookmarkModalControllerAnimated:NO]; 4050 [_bookmarkInteractionController dismissBookmarkModalControllerAnimated:NO];
4027 [_bookmarkInteractionController dismissSnackbar]; 4051 [_bookmarkInteractionController dismissSnackbar];
4028 [_toolbarController cancelOmniboxEdit]; 4052 [_toolbarController cancelOmniboxEdit];
4029 [_dialogPresenter cancelAllDialogs]; 4053 [_dialogPresenter cancelAllDialogs];
4030 [self hidePageInfoPopupForView:nil]; 4054 [self hidePageInfoPopupForView:nil];
4031 if (_voiceSearchController) 4055 if (_voiceSearchController)
4032 _voiceSearchController->DismissMicPermissionsHelp(); 4056 _voiceSearchController->DismissMicPermissionsHelp();
4033 [[_model currentTab] dismissModals]; 4057
4034 [[_model currentTab].findInPageController 4058 Tab* currentTab = [_model currentTab];
4035 disableFindInPageWithCompletionHandler:^{ 4059 [currentTab dismissModals];
4036 [self updateFindBar:NO shouldFocus:NO]; 4060
4037 }]; 4061 FindInPageController* findInPageController =
4062 GetFindInPageController(currentTab);
4063 [findInPageController disableFindInPageWithCompletionHandler:^{
4064 [self updateFindBar:NO shouldFocus:NO];
4065 }];
4066
4038 [_contextualSearchController movePanelOffscreen]; 4067 [_contextualSearchController movePanelOffscreen];
4039
4040 [_paymentRequestManager cancelRequest]; 4068 [_paymentRequestManager cancelRequest];
4041
4042 [_printController dismissAnimated:YES]; 4069 [_printController dismissAnimated:YES];
4043 _printController.reset(); 4070 _printController.reset();
4044 [_toolbarController dismissToolsMenuPopup]; 4071 [_toolbarController dismissToolsMenuPopup];
4045 [_contextMenuCoordinator stop]; 4072 [_contextMenuCoordinator stop];
4046 [self dismissRateThisAppDialog]; 4073 [self dismissRateThisAppDialog];
4047 4074
4048 [_contentSuggestionsCoordinator stop]; 4075 [_contentSuggestionsCoordinator stop];
4049 4076
4050 if (self.presentedViewController) { 4077 if (self.presentedViewController) {
4051 // Dismisses any other modal controllers that may be present, e.g. Recent 4078 // Dismisses any other modal controllers that may be present, e.g. Recent
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
4128 // Create find bar controller and pass it to the web controller. 4155 // Create find bar controller and pass it to the web controller.
4129 - (void)initFindBarForTab { 4156 - (void)initFindBarForTab {
4130 if (!self.canShowFindBar) 4157 if (!self.canShowFindBar)
4131 return; 4158 return;
4132 4159
4133 if (!_findBarController) 4160 if (!_findBarController)
4134 _findBarController.reset( 4161 _findBarController.reset(
4135 [[FindBarControllerIOS alloc] initWithIncognito:_isOffTheRecord]); 4162 [[FindBarControllerIOS alloc] initWithIncognito:_isOffTheRecord]);
4136 4163
4137 Tab* tab = [_model currentTab]; 4164 Tab* tab = [_model currentTab];
4138 DCHECK(!tab.findInPageController.findInPageModel.enabled); 4165 FindInPageController* controller = GetFindInPageController(tab);
4139 tab.findInPageController.findInPageModel.enabled = YES; 4166 DCHECK(!controller.findInPageModel.enabled);
4167 controller.findInPageModel.enabled = YES;
4140 [self showFindBarWithAnimation:YES selectText:YES shouldFocus:YES]; 4168 [self showFindBarWithAnimation:YES selectText:YES shouldFocus:YES];
4141 } 4169 }
4142 4170
4143 - (void)searchFindInPage { 4171 - (void)searchFindInPage {
4144 FindInPageController* findInPageController = 4172 FindInPageController* findInPageController =
4145 [[_model currentTab] findInPageController]; 4173 GetFindInPageController([_model currentTab]);
4146 base::WeakNSObject<BrowserViewController> weakSelf(self); 4174 base::WeakNSObject<BrowserViewController> weakSelf(self);
4147 [findInPageController 4175 [findInPageController findStringInPage:[_findBarController searchTerm]
4148 findStringInPage:[_findBarController searchTerm] 4176 completionHandler:^{
4149 completionHandler:^{ 4177 FindInPageModel* model =
4150 FindInPageModel* model = 4178 findInPageController.findInPageModel;
4151 [_model currentTab].findInPageController.findInPageModel; 4179 [_findBarController updateResultsCount:model];
4152 [_findBarController updateResultsCount:model]; 4180 }];
4153 }];
4154 if (!_isOffTheRecord) 4181 if (!_isOffTheRecord)
4155 [findInPageController saveSearchTerm]; 4182 [findInPageController saveSearchTerm];
4156 } 4183 }
4157 4184
4158 - (void)closeFindInPage { 4185 - (void)closeFindInPage {
4159 base::WeakNSObject<BrowserViewController> weakSelf(self); 4186 base::WeakNSObject<BrowserViewController> weakSelf(self);
4160 [[_model currentTab].findInPageController 4187 FindInPageController* findInPageController =
4161 disableFindInPageWithCompletionHandler:^{ 4188 GetFindInPageController([_model currentTab]);
4162 [weakSelf updateFindBar:NO shouldFocus:NO]; 4189 [findInPageController disableFindInPageWithCompletionHandler:^{
4163 }]; 4190 [weakSelf updateFindBar:NO shouldFocus:NO];
4191 }];
4164 } 4192 }
4165 4193
4166 - (void)updateFindBar:(BOOL)initialUpdate shouldFocus:(BOOL)shouldFocus { 4194 - (void)updateFindBar:(BOOL)initialUpdate shouldFocus:(BOOL)shouldFocus {
4167 FindInPageModel* model = 4195 FindInPageController* findInPageController =
4168 [_model currentTab].findInPageController.findInPageModel; 4196 GetFindInPageController([_model currentTab]);
4197 FindInPageModel* model = findInPageController.findInPageModel;
4169 if (model.enabled) { 4198 if (model.enabled) {
4170 if (initialUpdate && !_isOffTheRecord) { 4199 if (initialUpdate && !_isOffTheRecord) {
4171 [[_model currentTab].findInPageController restoreSearchTerm]; 4200 [findInPageController restoreSearchTerm];
4172 } 4201 }
4173 4202
4174 [self setFramesForHeaders:[self headerViews] 4203 [self setFramesForHeaders:[self headerViews]
4175 atOffset:[self currentHeaderOffset]]; 4204 atOffset:[self currentHeaderOffset]];
4176 [_findBarController updateView:model 4205 [_findBarController updateView:model
4177 initialUpdate:initialUpdate 4206 initialUpdate:initialUpdate
4178 focusTextfield:shouldFocus]; 4207 focusTextfield:shouldFocus];
4179 } else { 4208 } else {
4180 [self hideFindBarWithAnimation:YES]; 4209 [self hideFindBarWithAnimation:YES];
4181 } 4210 }
(...skipping 780 matching lines...) Expand 10 before | Expand all | Expand 10 after
4962 4991
4963 - (UIView*)voiceSearchButton { 4992 - (UIView*)voiceSearchButton {
4964 return _voiceSearchButton; 4993 return _voiceSearchButton;
4965 } 4994 }
4966 4995
4967 - (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner { 4996 - (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner {
4968 return [self currentLogoAnimationControllerOwner]; 4997 return [self currentLogoAnimationControllerOwner];
4969 } 4998 }
4970 4999
4971 @end 5000 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698