shell_view_controller.m 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524
  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. #import "ios/web_view/shell/shell_view_controller.h"
  5. #import <MobileCoreServices/MobileCoreServices.h>
  6. #import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
  7. #import "ios/web_view/shell/shell_auth_service.h"
  8. #import "ios/web_view/shell/shell_autofill_delegate.h"
  9. #import "ios/web_view/shell/shell_translation_delegate.h"
  10. #import "ios/web_view/shell/shell_trusted_vault_provider.h"
  11. #if !defined(__has_feature) || !__has_feature(objc_arc)
  12. #error "This file requires ARC support."
  13. #endif
  14. // Externed accessibility identifier.
  15. NSString* const kWebViewShellBackButtonAccessibilityLabel = @"Back";
  16. NSString* const kWebViewShellForwardButtonAccessibilityLabel = @"Forward";
  17. NSString* const kWebViewShellAddressFieldAccessibilityLabel = @"Address field";
  18. NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier =
  19. @"WebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier";
  20. @interface ShellViewController () <CWVAutofillDataManagerObserver,
  21. CWVDownloadTaskDelegate,
  22. CWVLeakCheckServiceObserver,
  23. CWVNavigationDelegate,
  24. CWVUIDelegate,
  25. CWVScriptCommandHandler,
  26. CWVSyncControllerDelegate,
  27. UIScrollViewDelegate,
  28. UITextFieldDelegate>
  29. // Header containing navigation buttons and |field|.
  30. @property(nonatomic, strong) UIView* headerBackgroundView;
  31. // Header containing navigation buttons and |field|.
  32. @property(nonatomic, strong) UIView* headerContentView;
  33. // Button to navigate backwards.
  34. @property(nonatomic, strong) UIButton* backButton;
  35. // Button to navigate forwards.
  36. @property(nonatomic, strong) UIButton* forwardButton;
  37. // Button that either refresh the page or stops the page load.
  38. @property(nonatomic, strong) UIButton* reloadOrStopButton;
  39. // Button that shows the menu
  40. @property(nonatomic, strong) UIButton* menuButton;
  41. // Text field used for navigating to URLs.
  42. @property(nonatomic, strong) UITextField* field;
  43. // Container for |webView|.
  44. @property(nonatomic, strong) UIView* contentView;
  45. // Handles the autofill of the content displayed in |webView|.
  46. @property(nonatomic, strong) ShellAutofillDelegate* autofillDelegate;
  47. // Handles the translation of the content displayed in |webView|.
  48. @property(nonatomic, strong) ShellTranslationDelegate* translationDelegate;
  49. // The on-going download task if any.
  50. @property(nonatomic, strong, nullable) CWVDownloadTask* downloadTask;
  51. // The path to a local file which the download task is writing to.
  52. @property(nonatomic, strong, nullable) NSString* downloadFilePath;
  53. // A controller to show a "Share" menu for the downloaded file.
  54. @property(nonatomic, strong, nullable)
  55. UIDocumentInteractionController* documentInteractionController;
  56. // Service that provides authentication to ChromeWebView.
  57. @property(nonatomic, strong) ShellAuthService* authService;
  58. // Provides trusted vault functions to ChromeWebView.
  59. @property(nonatomic, strong) ShellTrustedVaultProvider* trustedVaultProvider;
  60. // The newly opened popup windows e.g., by JavaScript function "window.open()",
  61. // HTML "<a target='_blank'>".
  62. @property(nonatomic, strong) NSMutableArray<CWVWebView*>* popupWebViews;
  63. // A list of active leak checks. These map to a list of passwords since
  64. // you can have multiple passwords that map to the same canonical leak check.
  65. @property(nonatomic, strong)
  66. NSMutableDictionary<CWVLeakCheckCredential*, NSMutableArray<CWVPassword*>*>*
  67. pendingLeakChecks;
  68. - (void)back;
  69. - (void)forward;
  70. - (void)reloadOrStop;
  71. // Disconnects and release the |webView|.
  72. - (void)removeWebView;
  73. // Resets translate settings back to default.
  74. - (void)resetTranslateSettings;
  75. @end
  76. @implementation ShellViewController
  77. @synthesize autofillDelegate = _autofillDelegate;
  78. @synthesize backButton = _backButton;
  79. @synthesize contentView = _contentView;
  80. @synthesize field = _field;
  81. @synthesize forwardButton = _forwardButton;
  82. @synthesize reloadOrStopButton = _reloadOrStopButton;
  83. @synthesize menuButton = _menuButton;
  84. @synthesize headerBackgroundView = _headerBackgroundView;
  85. @synthesize headerContentView = _headerContentView;
  86. @synthesize webView = _webView;
  87. @synthesize translationDelegate = _translationDelegate;
  88. @synthesize downloadTask = _downloadTask;
  89. @synthesize downloadFilePath = _downloadFilePath;
  90. @synthesize documentInteractionController = _documentInteractionController;
  91. @synthesize authService = _authService;
  92. @synthesize trustedVaultProvider = _trustedVaultProvider;
  93. @synthesize popupWebViews = _popupWebViews;
  94. @synthesize pendingLeakChecks = _pendingLeakChecks;
  95. - (void)viewDidLoad {
  96. [super viewDidLoad];
  97. self.popupWebViews = [[NSMutableArray alloc] init];
  98. // View creation.
  99. self.headerBackgroundView = [[UIView alloc] init];
  100. self.headerContentView = [[UIView alloc] init];
  101. self.contentView = [[UIView alloc] init];
  102. self.backButton = [[UIButton alloc] init];
  103. self.forwardButton = [[UIButton alloc] init];
  104. self.reloadOrStopButton = [[UIButton alloc] init];
  105. self.menuButton = [[UIButton alloc] init];
  106. self.field = [[UITextField alloc] init];
  107. // View hierarchy.
  108. [self.view addSubview:_headerBackgroundView];
  109. [self.view addSubview:_contentView];
  110. [_headerBackgroundView addSubview:_headerContentView];
  111. [_headerContentView addSubview:_backButton];
  112. [_headerContentView addSubview:_forwardButton];
  113. [_headerContentView addSubview:_reloadOrStopButton];
  114. [_headerContentView addSubview:_menuButton];
  115. [_headerContentView addSubview:_field];
  116. // Additional view setup.
  117. _headerBackgroundView.backgroundColor = [UIColor colorWithRed:66.0 / 255.0
  118. green:133.0 / 255.0
  119. blue:244.0 / 255.0
  120. alpha:1.0];
  121. [_backButton setImage:[UIImage imageNamed:@"ic_back"]
  122. forState:UIControlStateNormal];
  123. _backButton.tintColor = [UIColor whiteColor];
  124. [_backButton addTarget:self
  125. action:@selector(back)
  126. forControlEvents:UIControlEventTouchUpInside];
  127. [_backButton addTarget:self
  128. action:@selector(logBackStack)
  129. forControlEvents:UIControlEventTouchDragOutside];
  130. [_backButton setAccessibilityLabel:kWebViewShellBackButtonAccessibilityLabel];
  131. [_forwardButton setImage:[UIImage imageNamed:@"ic_forward"]
  132. forState:UIControlStateNormal];
  133. _forwardButton.tintColor = [UIColor whiteColor];
  134. [_forwardButton addTarget:self
  135. action:@selector(forward)
  136. forControlEvents:UIControlEventTouchUpInside];
  137. [_forwardButton addTarget:self
  138. action:@selector(logForwardStack)
  139. forControlEvents:UIControlEventTouchDragOutside];
  140. [_forwardButton
  141. setAccessibilityLabel:kWebViewShellForwardButtonAccessibilityLabel];
  142. _reloadOrStopButton.tintColor = [UIColor whiteColor];
  143. [_reloadOrStopButton addTarget:self
  144. action:@selector(reloadOrStop)
  145. forControlEvents:UIControlEventTouchUpInside];
  146. _menuButton.tintColor = [UIColor whiteColor];
  147. [_menuButton setImage:[UIImage imageNamed:@"ic_menu"]
  148. forState:UIControlStateNormal];
  149. [_menuButton addTarget:self
  150. action:@selector(showMainMenu)
  151. forControlEvents:UIControlEventTouchUpInside];
  152. _field.placeholder = @"Search or type URL";
  153. _field.backgroundColor = [UIColor whiteColor];
  154. _field.tintColor = _headerBackgroundView.backgroundColor;
  155. [_field setContentHuggingPriority:UILayoutPriorityDefaultLow - 1
  156. forAxis:UILayoutConstraintAxisHorizontal];
  157. _field.delegate = self;
  158. _field.layer.cornerRadius = 2.0;
  159. _field.keyboardType = UIKeyboardTypeWebSearch;
  160. _field.autocapitalizationType = UITextAutocapitalizationTypeNone;
  161. _field.clearButtonMode = UITextFieldViewModeWhileEditing;
  162. _field.autocorrectionType = UITextAutocorrectionTypeNo;
  163. UIView* spacerView = [[UIView alloc] init];
  164. spacerView.frame = CGRectMake(0, 0, 8, 8);
  165. _field.leftViewMode = UITextFieldViewModeAlways;
  166. _field.leftView = spacerView;
  167. // Constraints.
  168. _headerBackgroundView.translatesAutoresizingMaskIntoConstraints = NO;
  169. [NSLayoutConstraint activateConstraints:@[
  170. [_headerBackgroundView.topAnchor
  171. constraintEqualToAnchor:self.view.topAnchor],
  172. [_headerBackgroundView.leadingAnchor
  173. constraintEqualToAnchor:self.view.leadingAnchor],
  174. [_headerBackgroundView.trailingAnchor
  175. constraintEqualToAnchor:self.view.trailingAnchor],
  176. [_headerBackgroundView.bottomAnchor
  177. constraintEqualToAnchor:_headerContentView.bottomAnchor],
  178. ]];
  179. _headerContentView.translatesAutoresizingMaskIntoConstraints = NO;
  180. [NSLayoutConstraint activateConstraints:@[
  181. [_headerContentView.topAnchor
  182. constraintEqualToAnchor:_headerBackgroundView.safeAreaLayoutGuide
  183. .topAnchor],
  184. [_headerContentView.leadingAnchor
  185. constraintEqualToAnchor:_headerBackgroundView.safeAreaLayoutGuide
  186. .leadingAnchor],
  187. [_headerContentView.trailingAnchor
  188. constraintEqualToAnchor:_headerBackgroundView.safeAreaLayoutGuide
  189. .trailingAnchor],
  190. [_headerContentView.heightAnchor constraintEqualToConstant:56.0],
  191. ]];
  192. _contentView.translatesAutoresizingMaskIntoConstraints = NO;
  193. [NSLayoutConstraint activateConstraints:@[
  194. [_contentView.topAnchor
  195. constraintEqualToAnchor:_headerBackgroundView.bottomAnchor],
  196. [_contentView.leadingAnchor
  197. constraintEqualToAnchor:self.view.leadingAnchor],
  198. [_contentView.trailingAnchor
  199. constraintEqualToAnchor:self.view.trailingAnchor],
  200. [_contentView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
  201. ]];
  202. _backButton.translatesAutoresizingMaskIntoConstraints = NO;
  203. [NSLayoutConstraint activateConstraints:@[
  204. [_backButton.leadingAnchor
  205. constraintEqualToAnchor:_headerContentView.safeAreaLayoutGuide
  206. .leadingAnchor
  207. constant:16.0],
  208. [_backButton.centerYAnchor
  209. constraintEqualToAnchor:_headerContentView.centerYAnchor],
  210. ]];
  211. _forwardButton.translatesAutoresizingMaskIntoConstraints = NO;
  212. [NSLayoutConstraint activateConstraints:@[
  213. [_forwardButton.leadingAnchor
  214. constraintEqualToAnchor:_backButton.trailingAnchor
  215. constant:16.0],
  216. [_forwardButton.centerYAnchor
  217. constraintEqualToAnchor:_headerContentView.centerYAnchor],
  218. ]];
  219. _reloadOrStopButton.translatesAutoresizingMaskIntoConstraints = NO;
  220. [NSLayoutConstraint activateConstraints:@[
  221. [_reloadOrStopButton.leadingAnchor
  222. constraintEqualToAnchor:_forwardButton.trailingAnchor
  223. constant:16.0],
  224. [_reloadOrStopButton.centerYAnchor
  225. constraintEqualToAnchor:_headerContentView.centerYAnchor],
  226. ]];
  227. _menuButton.translatesAutoresizingMaskIntoConstraints = NO;
  228. [NSLayoutConstraint activateConstraints:@[
  229. [_menuButton.leadingAnchor
  230. constraintEqualToAnchor:_reloadOrStopButton.trailingAnchor
  231. constant:16.0],
  232. [_menuButton.centerYAnchor
  233. constraintEqualToAnchor:_headerContentView.centerYAnchor],
  234. ]];
  235. _field.translatesAutoresizingMaskIntoConstraints = NO;
  236. [NSLayoutConstraint activateConstraints:@[
  237. [_field.leadingAnchor constraintEqualToAnchor:_menuButton.trailingAnchor
  238. constant:16.0],
  239. [_field.centerYAnchor
  240. constraintEqualToAnchor:_headerContentView.centerYAnchor],
  241. [_field.trailingAnchor
  242. constraintEqualToAnchor:_headerContentView.safeAreaLayoutGuide
  243. .trailingAnchor
  244. constant:-16.0],
  245. [_field.heightAnchor constraintEqualToConstant:32.0],
  246. ]];
  247. [CWVWebView setUserAgentProduct:@"Dummy/1.0"];
  248. CWVWebView.chromeContextMenuEnabled = YES;
  249. _authService = [[ShellAuthService alloc] init];
  250. CWVSyncController.dataSource = _authService;
  251. _trustedVaultProvider =
  252. [[ShellTrustedVaultProvider alloc] initWithAuthService:_authService];
  253. CWVSyncController.trustedVaultProvider = _trustedVaultProvider;
  254. _pendingLeakChecks = [NSMutableDictionary dictionary];
  255. CWVWebViewConfiguration* configuration =
  256. [CWVWebViewConfiguration defaultConfiguration];
  257. [configuration.autofillDataManager addObserver:self];
  258. configuration.syncController.delegate = self;
  259. [configuration.leakCheckService addObserver:self];
  260. self.webView = [self createWebViewWithConfiguration:configuration];
  261. }
  262. - (void)applicationFinishedRestoringState {
  263. [super applicationFinishedRestoringState];
  264. // The scroll view is reset on state restoration. So the delegate must be
  265. // reassigned.
  266. self.webView.scrollView.delegate = self;
  267. }
  268. - (UIStatusBarStyle)preferredStatusBarStyle {
  269. return UIStatusBarStyleLightContent;
  270. }
  271. - (void)observeValueForKeyPath:(NSString*)keyPath
  272. ofObject:(id)object
  273. change:(NSDictionary<NSKeyValueChangeKey, id>*)change
  274. context:(void*)context {
  275. if ([keyPath isEqualToString:@"canGoBack"]) {
  276. _backButton.enabled = [_webView canGoBack];
  277. } else if ([keyPath isEqualToString:@"canGoForward"]) {
  278. _forwardButton.enabled = [_webView canGoForward];
  279. } else if ([keyPath isEqualToString:@"loading"]) {
  280. NSString* imageName = _webView.loading ? @"ic_stop" : @"ic_reload";
  281. [_reloadOrStopButton setImage:[UIImage imageNamed:imageName]
  282. forState:UIControlStateNormal];
  283. }
  284. }
  285. - (void)back {
  286. if ([_webView canGoBack]) {
  287. [_webView goBack];
  288. }
  289. }
  290. - (void)logBackStack {
  291. if (!_webView.canGoBack) {
  292. return;
  293. }
  294. CWVBackForwardList* list = _webView.backForwardList;
  295. CWVBackForwardListItemArray* backList = list.backList;
  296. for (size_t i = 0; i < backList.count; i++) {
  297. CWVBackForwardListItem* item = backList[i];
  298. NSLog(@"BackStack Item #%ld: <URL='%@', title='%@'>", i, item.URL,
  299. item.title);
  300. }
  301. }
  302. - (void)forward {
  303. if ([_webView canGoForward]) {
  304. [_webView goForward];
  305. }
  306. }
  307. - (void)logForwardStack {
  308. if (!_webView.canGoForward) {
  309. return;
  310. }
  311. CWVBackForwardList* list = _webView.backForwardList;
  312. CWVBackForwardListItemArray* forwardList = list.forwardList;
  313. for (size_t i = 0; i < forwardList.count; i++) {
  314. CWVBackForwardListItem* item = forwardList[i];
  315. NSLog(@"ForwardStack Item #%ld: <URL='%@', title='%@'>", i, item.URL,
  316. item.title);
  317. }
  318. }
  319. - (void)reloadOrStop {
  320. if (_webView.loading) {
  321. [_webView stopLoading];
  322. } else {
  323. [_webView reload];
  324. }
  325. }
  326. - (void)showAddressData {
  327. CWVAutofillDataManager* dataManager =
  328. _webView.configuration.autofillDataManager;
  329. [dataManager fetchProfilesWithCompletionHandler:^(
  330. NSArray<CWVAutofillProfile*>* _Nonnull profiles) {
  331. NSMutableArray<NSString*>* descriptions = [profiles
  332. valueForKey:NSStringFromSelector(@selector(debugDescription))];
  333. NSString* message = [descriptions componentsJoinedByString:@"\n\n"];
  334. UIAlertController* alertController = [self actionSheetWithTitle:@"Addresses"
  335. message:message];
  336. for (CWVAutofillProfile* profile in profiles) {
  337. NSString* title = [NSString
  338. stringWithFormat:@"Delete %@", @([profiles indexOfObject:profile])];
  339. UIAlertAction* action =
  340. [UIAlertAction actionWithTitle:title
  341. style:UIAlertActionStyleDefault
  342. handler:^(UIAlertAction* theAction) {
  343. [dataManager deleteProfile:profile];
  344. }];
  345. [alertController addAction:action];
  346. }
  347. [alertController
  348. addAction:[UIAlertAction actionWithTitle:@"Done"
  349. style:UIAlertActionStyleCancel
  350. handler:nil]];
  351. [self presentViewController:alertController animated:YES completion:nil];
  352. }];
  353. }
  354. - (void)showCreditCardData {
  355. CWVAutofillDataManager* dataManager =
  356. _webView.configuration.autofillDataManager;
  357. [dataManager fetchCreditCardsWithCompletionHandler:^(
  358. NSArray<CWVCreditCard*>* _Nonnull creditCards) {
  359. NSMutableArray<NSString*>* descriptions = [creditCards
  360. valueForKey:NSStringFromSelector(@selector(debugDescription))];
  361. NSString* message = [descriptions componentsJoinedByString:@"\n\n"];
  362. UIAlertController* alertController =
  363. [self actionSheetWithTitle:@"Credit cards" message:message];
  364. __weak ShellViewController* weakSelf = self;
  365. [alertController
  366. addAction:[UIAlertAction
  367. actionWithTitle:@"Manage Google pay cards"
  368. style:UIAlertActionStyleDefault
  369. handler:^(UIAlertAction* action) {
  370. __weak ShellViewController* strongSelf =
  371. weakSelf;
  372. NSString* URL;
  373. if ([CWVFlags sharedInstance]
  374. .usesSyncAndWalletSandbox) {
  375. URL = @"https://pay.sandbox.google.com/"
  376. @"payments/home#paymentMethods";
  377. } else {
  378. URL = @"https://pay.google.com/payments/"
  379. @"home#paymentMethods";
  380. }
  381. NSURLRequest* request = [NSURLRequest
  382. requestWithURL:[NSURL URLWithString:URL]];
  383. [strongSelf.webView loadRequest:request];
  384. }]];
  385. [alertController
  386. addAction:[UIAlertAction actionWithTitle:@"Done"
  387. style:UIAlertActionStyleCancel
  388. handler:nil]];
  389. [self presentViewController:alertController animated:YES completion:nil];
  390. }];
  391. }
  392. - (void)showPasswordData {
  393. __weak ShellViewController* weakSelf = self;
  394. CWVAutofillDataManager* dataManager =
  395. _webView.configuration.autofillDataManager;
  396. [dataManager fetchPasswordsWithCompletionHandler:^(
  397. NSArray<CWVPassword*>* _Nonnull passwords) {
  398. NSMutableArray<NSString*>* descriptions = [NSMutableArray array];
  399. for (CWVPassword* password in passwords) {
  400. NSString* description = [NSString
  401. stringWithFormat:@"%@:\n%@", @([passwords indexOfObject:password]),
  402. password.debugDescription];
  403. [descriptions addObject:description];
  404. }
  405. NSString* message = [descriptions componentsJoinedByString:@"\n\n"];
  406. UIAlertController* alertController = [self actionSheetWithTitle:@"Passwords"
  407. message:message];
  408. [alertController
  409. addAction:[UIAlertAction actionWithTitle:@"Add new"
  410. style:UIAlertActionStyleDefault
  411. handler:^(UIAlertAction* action) {
  412. [weakSelf showAddNewPasswordDialog];
  413. }]];
  414. for (CWVPassword* password in passwords) {
  415. NSString* title = [NSString
  416. stringWithFormat:@"Select %@", @([passwords indexOfObject:password])];
  417. UIAlertAction* action =
  418. [UIAlertAction actionWithTitle:title
  419. style:UIAlertActionStyleDefault
  420. handler:^(UIAlertAction* theAction) {
  421. [weakSelf showMenuForPassword:password];
  422. }];
  423. [alertController addAction:action];
  424. }
  425. [alertController
  426. addAction:[UIAlertAction actionWithTitle:@"Done"
  427. style:UIAlertActionStyleCancel
  428. handler:nil]];
  429. [self presentViewController:alertController animated:YES completion:nil];
  430. }];
  431. }
  432. - (void)showAddNewPasswordDialog {
  433. CWVAutofillDataManager* dataManager =
  434. _webView.configuration.autofillDataManager;
  435. UIAlertController* alertController =
  436. [UIAlertController alertControllerWithTitle:@"Add password"
  437. message:nil
  438. preferredStyle:UIAlertControllerStyleAlert];
  439. [alertController
  440. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  441. textField.placeholder = @"Username";
  442. }];
  443. [alertController
  444. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  445. textField.placeholder = @"Password";
  446. }];
  447. [alertController
  448. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  449. textField.placeholder = @"Site";
  450. }];
  451. [alertController
  452. addAction:[UIAlertAction actionWithTitle:@"Cancel"
  453. style:UIAlertActionStyleCancel
  454. handler:nil]];
  455. __weak UIAlertController* weakAlertController = alertController;
  456. [alertController
  457. addAction:[UIAlertAction
  458. actionWithTitle:@"Done"
  459. style:UIAlertActionStyleDefault
  460. handler:^(UIAlertAction* action) {
  461. NSString* username =
  462. weakAlertController.textFields[0].text;
  463. NSString* password =
  464. weakAlertController.textFields[1].text;
  465. NSString* site =
  466. weakAlertController.textFields[2].text;
  467. [dataManager addNewPasswordForUsername:username
  468. password:password
  469. site:site];
  470. }]];
  471. [self presentViewController:alertController animated:YES completion:nil];
  472. }
  473. - (void)showMenuForPassword:(CWVPassword*)password {
  474. UIAlertController* alertController =
  475. [self actionSheetWithTitle:password.title
  476. message:password.debugDescription];
  477. CWVAutofillDataManager* dataManager =
  478. _webView.configuration.autofillDataManager;
  479. __weak ShellViewController* weakSelf = self;
  480. UIAlertAction* update =
  481. [UIAlertAction actionWithTitle:@"Update"
  482. style:UIAlertActionStyleDefault
  483. handler:^(UIAlertAction* theAction) {
  484. [weakSelf showUpdateDialogForPassword:password];
  485. }];
  486. [alertController addAction:update];
  487. UIAlertAction* delete =
  488. [UIAlertAction actionWithTitle:@"Delete"
  489. style:UIAlertActionStyleDefault
  490. handler:^(UIAlertAction* theAction) {
  491. [dataManager deletePassword:password];
  492. }];
  493. [alertController addAction:delete];
  494. [alertController
  495. addAction:[UIAlertAction actionWithTitle:@"Done"
  496. style:UIAlertActionStyleCancel
  497. handler:nil]];
  498. [self presentViewController:alertController animated:YES completion:nil];
  499. }
  500. - (void)showUpdateDialogForPassword:(CWVPassword*)password {
  501. CWVAutofillDataManager* dataManager =
  502. _webView.configuration.autofillDataManager;
  503. UIAlertController* alertController =
  504. [UIAlertController alertControllerWithTitle:password.title
  505. message:password.debugDescription
  506. preferredStyle:UIAlertControllerStyleAlert];
  507. [alertController
  508. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  509. textField.placeholder = @"New username";
  510. }];
  511. [alertController
  512. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  513. textField.placeholder = @"New password";
  514. }];
  515. [alertController
  516. addAction:[UIAlertAction actionWithTitle:@"Cancel"
  517. style:UIAlertActionStyleCancel
  518. handler:nil]];
  519. __weak UIAlertController* weakAlertController = alertController;
  520. [alertController
  521. addAction:[UIAlertAction actionWithTitle:@"Done"
  522. style:UIAlertActionStyleDefault
  523. handler:^(UIAlertAction* action) {
  524. NSString* newUsername =
  525. weakAlertController.textFields
  526. .firstObject.text;
  527. NSString* newPassword =
  528. weakAlertController.textFields
  529. .lastObject.text;
  530. [dataManager
  531. updatePassword:password
  532. newUsername:newUsername
  533. newPassword:newPassword];
  534. }]];
  535. [self presentViewController:alertController animated:YES completion:nil];
  536. }
  537. - (void)showSyncMenu {
  538. CWVSyncController* syncController = _webView.configuration.syncController;
  539. NSString* message = [NSString
  540. stringWithFormat:@"Passphrase required: %@\nTrusted vault keys required: "
  541. @"%@\nTrusted vault recoverability degraded: %@",
  542. @(syncController.passphraseNeeded),
  543. @(syncController.trustedVaultKeysRequired),
  544. @(syncController.trustedVaultRecoverabilityDegraded)];
  545. UIAlertController* alertController = [self actionSheetWithTitle:@"Sync menu"
  546. message:message];
  547. CWVIdentity* currentIdentity = syncController.currentIdentity;
  548. __weak ShellViewController* weakSelf = self;
  549. if (currentIdentity) {
  550. NSString* title = [NSString
  551. stringWithFormat:@"Stop syncing for %@", currentIdentity.email];
  552. [alertController
  553. addAction:[UIAlertAction
  554. actionWithTitle:title
  555. style:UIAlertActionStyleDefault
  556. handler:^(UIAlertAction* action) {
  557. [syncController stopSyncAndClearIdentity];
  558. }]];
  559. if (syncController.passphraseNeeded) {
  560. [alertController
  561. addAction:[UIAlertAction
  562. actionWithTitle:@"Unlock using passphrase"
  563. style:UIAlertActionStyleDefault
  564. handler:^(UIAlertAction* action) {
  565. [weakSelf showPassphraseUnlockAlert];
  566. }]];
  567. } else if (syncController.trustedVaultKeysRequired) {
  568. [alertController
  569. addAction:[UIAlertAction
  570. actionWithTitle:@"Fetch trusted vault keys"
  571. style:UIAlertActionStyleDefault
  572. handler:^(UIAlertAction* action) {
  573. [weakSelf.trustedVaultProvider
  574. showFetchKeysFlowForIdentity:
  575. currentIdentity
  576. fromViewController:weakSelf];
  577. }]];
  578. } else if (syncController.trustedVaultRecoverabilityDegraded) {
  579. [alertController
  580. addAction:
  581. [UIAlertAction
  582. actionWithTitle:@"Fix degraded recoverability"
  583. style:UIAlertActionStyleDefault
  584. handler:^(UIAlertAction* action) {
  585. [weakSelf.trustedVaultProvider
  586. showFixDegradedRecoverabilityFlowForIdentity:
  587. currentIdentity
  588. fromViewController:
  589. weakSelf];
  590. }]];
  591. }
  592. } else {
  593. for (CWVIdentity* identity in [_authService identities]) {
  594. NSString* title =
  595. [NSString stringWithFormat:@"Start sync with %@", identity.email];
  596. [alertController
  597. addAction:[UIAlertAction
  598. actionWithTitle:title
  599. style:UIAlertActionStyleDefault
  600. handler:^(UIAlertAction* action) {
  601. [syncController
  602. startSyncWithIdentity:identity];
  603. }]];
  604. }
  605. NSString* sandboxTitle = [CWVFlags sharedInstance].usesSyncAndWalletSandbox
  606. ? @"Use production sync/wallet"
  607. : @"Use sandbox sync/wallet";
  608. [alertController
  609. addAction:[UIAlertAction actionWithTitle:sandboxTitle
  610. style:UIAlertActionStyleDefault
  611. handler:^(UIAlertAction* action) {
  612. [CWVFlags sharedInstance]
  613. .usesSyncAndWalletSandbox ^= YES;
  614. }]];
  615. }
  616. [alertController
  617. addAction:[UIAlertAction actionWithTitle:@"Show autofill data"
  618. style:UIAlertActionStyleDefault
  619. handler:^(UIAlertAction* action) {
  620. [weakSelf showAddressData];
  621. }]];
  622. [alertController
  623. addAction:[UIAlertAction actionWithTitle:@"Show credit card data"
  624. style:UIAlertActionStyleDefault
  625. handler:^(UIAlertAction* action) {
  626. [weakSelf showCreditCardData];
  627. }]];
  628. [alertController
  629. addAction:[UIAlertAction actionWithTitle:@"Show password data"
  630. style:UIAlertActionStyleDefault
  631. handler:^(UIAlertAction* action) {
  632. [weakSelf showPasswordData];
  633. }]];
  634. [alertController
  635. addAction:[UIAlertAction actionWithTitle:@"Check leaked passwords"
  636. style:UIAlertActionStyleDefault
  637. handler:^(UIAlertAction* action) {
  638. [weakSelf checkLeakedPasswords];
  639. }]];
  640. [alertController
  641. addAction:[UIAlertAction actionWithTitle:@"Cancel"
  642. style:UIAlertActionStyleCancel
  643. handler:nil]];
  644. [self presentViewController:alertController animated:YES completion:nil];
  645. }
  646. - (void)showPassphraseUnlockAlert {
  647. UIAlertController* alertController =
  648. [UIAlertController alertControllerWithTitle:@"Unlock sync"
  649. message:@"Enter passphrase"
  650. preferredStyle:UIAlertControllerStyleAlert];
  651. __weak UIAlertController* weakAlertController = alertController;
  652. CWVSyncController* syncController = _webView.configuration.syncController;
  653. UIAlertAction* submit = [UIAlertAction
  654. actionWithTitle:@"Unlock"
  655. style:UIAlertActionStyleDefault
  656. handler:^(UIAlertAction* action) {
  657. UITextField* textField =
  658. weakAlertController.textFields.firstObject;
  659. NSString* passphrase = textField.text;
  660. BOOL result = [syncController unlockWithPassphrase:passphrase];
  661. NSLog(@"Sync passphrase unlock result: %d", result);
  662. }];
  663. [alertController addAction:submit];
  664. UIAlertAction* cancel =
  665. [UIAlertAction actionWithTitle:@"Cancel"
  666. style:UIAlertActionStyleCancel
  667. handler:nil];
  668. [alertController addAction:cancel];
  669. [alertController
  670. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  671. textField.placeholder = @"passphrase";
  672. textField.keyboardType = UIKeyboardTypeDefault;
  673. }];
  674. [self presentViewController:alertController animated:YES completion:nil];
  675. }
  676. - (void)showMainMenu {
  677. UIAlertController* alertController = [self actionSheetWithTitle:@"Main menu"
  678. message:nil];
  679. [alertController
  680. addAction:[UIAlertAction actionWithTitle:@"Cancel"
  681. style:UIAlertActionStyleCancel
  682. handler:nil]];
  683. __weak ShellViewController* weakSelf = self;
  684. // Toggles the incognito mode.
  685. NSString* incognitoActionTitle = _webView.configuration.persistent
  686. ? @"Enter incognito"
  687. : @"Exit incognito";
  688. [alertController
  689. addAction:[UIAlertAction actionWithTitle:incognitoActionTitle
  690. style:UIAlertActionStyleDefault
  691. handler:^(UIAlertAction* action) {
  692. [weakSelf toggleIncognito];
  693. }]];
  694. // Removes the web view from the view hierarchy, releases it, and recreates
  695. // the web view with the same configuration. This is for testing deallocation
  696. // and sharing configuration.
  697. [alertController
  698. addAction:[UIAlertAction
  699. actionWithTitle:@"Recreate web view"
  700. style:UIAlertActionStyleDefault
  701. handler:^(UIAlertAction* action) {
  702. CWVWebViewConfiguration* configuration =
  703. weakSelf.webView.configuration;
  704. [weakSelf removeWebView];
  705. weakSelf.webView = [weakSelf
  706. createWebViewWithConfiguration:configuration];
  707. }]];
  708. // Developers can choose to use system or Chrome context menu in the shell
  709. // app. This will also recreate the web view.
  710. BOOL chromeContextMenuEnabled = CWVWebView.chromeContextMenuEnabled;
  711. NSString* contextMenuSwitchActionTitle = [NSString
  712. stringWithFormat:@"%@ Chrome context menu",
  713. chromeContextMenuEnabled ? @"Disable" : @"Enable"];
  714. [alertController
  715. addAction:[UIAlertAction
  716. actionWithTitle:contextMenuSwitchActionTitle
  717. style:UIAlertActionStyleDefault
  718. handler:^(UIAlertAction* action) {
  719. CWVWebView.chromeContextMenuEnabled =
  720. !chromeContextMenuEnabled;
  721. NSLog(@"Chrome context menu is %@ now.",
  722. !chromeContextMenuEnabled ? @"OFF" : @"ON");
  723. CWVWebViewConfiguration* configuration =
  724. weakSelf.webView.configuration;
  725. [weakSelf removeWebView];
  726. weakSelf.field.text = @"";
  727. weakSelf.webView = [weakSelf
  728. createWebViewWithConfiguration:configuration];
  729. }]];
  730. // Resets all translation settings to default values.
  731. [alertController
  732. addAction:[UIAlertAction actionWithTitle:@"Reset translate settings"
  733. style:UIAlertActionStyleDefault
  734. handler:^(UIAlertAction* action) {
  735. [weakSelf resetTranslateSettings];
  736. }]];
  737. [alertController
  738. addAction:[UIAlertAction actionWithTitle:@"Request translation offer"
  739. style:UIAlertActionStyleDefault
  740. handler:^(UIAlertAction* action) {
  741. [weakSelf requestTranslationOffer];
  742. }]];
  743. // Shows sync menu.
  744. [alertController
  745. addAction:[UIAlertAction actionWithTitle:@"Sync menu"
  746. style:UIAlertActionStyleDefault
  747. handler:^(UIAlertAction* action) {
  748. [weakSelf showSyncMenu];
  749. }]];
  750. [alertController
  751. addAction:[UIAlertAction actionWithTitle:@"Show Certificate Details"
  752. style:UIAlertActionStyleDefault
  753. handler:^(UIAlertAction* action) {
  754. [weakSelf showCertificateDetails];
  755. }]];
  756. [alertController
  757. addAction:[UIAlertAction actionWithTitle:@"Evaluate JavaScript"
  758. style:UIAlertActionStyleDefault
  759. handler:^(UIAlertAction* action) {
  760. [weakSelf showEvaluateJavaScriptUI];
  761. }]];
  762. if (self.downloadTask) {
  763. [alertController
  764. addAction:[UIAlertAction actionWithTitle:@"Cancel download"
  765. style:UIAlertActionStyleDefault
  766. handler:^(UIAlertAction* action) {
  767. [weakSelf.downloadTask cancel];
  768. }]];
  769. }
  770. [self presentViewController:alertController animated:YES completion:nil];
  771. }
  772. - (void)showCertificateDetails {
  773. CWVX509Certificate* certificate = [[_webView visibleSSLStatus] certificate];
  774. NSString* message;
  775. if (certificate) {
  776. message = [NSString stringWithFormat:@"Issuer: %@\nExpires: %@",
  777. certificate.issuerDisplayName,
  778. certificate.validExpiry];
  779. } else {
  780. message = @"No Certificate";
  781. }
  782. UIAlertController* alertController =
  783. [self actionSheetWithTitle:@"Certificate Details" message:message];
  784. [alertController
  785. addAction:[UIAlertAction actionWithTitle:@"Done"
  786. style:UIAlertActionStyleCancel
  787. handler:nil]];
  788. [self presentViewController:alertController animated:YES completion:nil];
  789. }
  790. - (void)checkLeakedPasswords {
  791. CWVAutofillDataManager* dataManager =
  792. _webView.configuration.autofillDataManager;
  793. // Request a check for any password in autofill data manager that is not
  794. // currently being requested.
  795. [dataManager fetchPasswordsWithCompletionHandler:^(
  796. NSArray<CWVPassword*>* _Nonnull passwords) {
  797. NSMutableArray<CWVLeakCheckCredential*>* credentialsToCheck =
  798. [NSMutableArray array];
  799. for (CWVPassword* password in passwords) {
  800. CWVLeakCheckCredential* credential = [CWVLeakCheckCredential
  801. canonicalLeakCheckCredentialWithPassword:password];
  802. NSMutableArray<CWVPassword*>* passwordsForCredential =
  803. self.pendingLeakChecks[credential];
  804. if (!passwordsForCredential) {
  805. passwordsForCredential = [NSMutableArray array];
  806. self.pendingLeakChecks[credential] = passwordsForCredential;
  807. [credentialsToCheck addObject:credential];
  808. }
  809. [passwordsForCredential addObject:password];
  810. }
  811. NSLog(@"Checking leaks for %@ credentials.", @(credentialsToCheck.count));
  812. [self.webView.configuration.leakCheckService
  813. checkCredentials:credentialsToCheck];
  814. }];
  815. }
  816. - (void)showEvaluateJavaScriptUI {
  817. UIAlertController* alertController =
  818. [UIAlertController alertControllerWithTitle:@"Evaluate JavaScript"
  819. message:nil
  820. preferredStyle:UIAlertControllerStyleAlert];
  821. alertController.popoverPresentationController.sourceView = _menuButton;
  822. alertController.popoverPresentationController.sourceRect =
  823. CGRectMake(CGRectGetWidth(_menuButton.bounds) / 2,
  824. CGRectGetHeight(_menuButton.bounds), 1, 1);
  825. [alertController
  826. addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  827. textField.placeholder = @"alert('Hello')";
  828. }];
  829. __weak UIAlertController* weakAlertController = alertController;
  830. __weak ShellViewController* weakSelf = self;
  831. [alertController
  832. addAction:[UIAlertAction
  833. actionWithTitle:@"Evaluate"
  834. style:UIAlertActionStyleDefault
  835. handler:^(UIAlertAction* action) {
  836. NSString* javascript =
  837. weakAlertController.textFields[0].text;
  838. [weakSelf evaluateJavaScript:javascript];
  839. }]];
  840. [alertController
  841. addAction:[UIAlertAction actionWithTitle:@"Cancel"
  842. style:UIAlertActionStyleCancel
  843. handler:nil]];
  844. [self presentViewController:alertController animated:YES completion:nil];
  845. }
  846. - (void)evaluateJavaScript:(NSString*)javascript {
  847. [self.webView
  848. evaluateJavaScript:javascript
  849. completion:^(id result, BOOL success) {
  850. NSString* formatString =
  851. success ? @"JavaScript evaluation finished with result: %@"
  852. : @"JavaScript evaluation FAILED with result: %@";
  853. NSLog(formatString, result);
  854. }];
  855. }
  856. - (void)resetTranslateSettings {
  857. CWVWebViewConfiguration* configuration =
  858. [CWVWebViewConfiguration defaultConfiguration];
  859. [configuration.preferences resetTranslationSettings];
  860. }
  861. - (void)requestTranslationOffer {
  862. BOOL offered = [_webView.translationController requestTranslationOffer];
  863. NSLog(@"Manual translation was offered: %d", offered);
  864. }
  865. - (void)toggleIncognito {
  866. BOOL wasPersistent = _webView.configuration.persistent;
  867. [self removeWebView];
  868. CWVWebViewConfiguration* newConfiguration =
  869. wasPersistent ? [CWVWebViewConfiguration nonPersistentConfiguration]
  870. : [CWVWebViewConfiguration defaultConfiguration];
  871. self.webView = [self createWebViewWithConfiguration:newConfiguration];
  872. }
  873. - (CWVWebView*)createWebViewWithConfiguration:
  874. (CWVWebViewConfiguration*)configuration {
  875. // Set a non empty CGRect to avoid DCHECKs that occur when a load happens
  876. // after state restoration, and before the view hierarchy is laid out for the
  877. // first time.
  878. // https://source.chromium.org/chromium/chromium/src/+/main:ios/web/web_state/ui/crw_web_request_controller.mm;l=518;drc=df887034106ef438611326745a7cd276eedd4953
  879. CGRect frame = CGRectMake(0, 0, 1, 1);
  880. CWVWebView* webView = [[CWVWebView alloc] initWithFrame:frame
  881. configuration:configuration];
  882. [_contentView addSubview:webView];
  883. // Gives a restoration identifier so that state restoration works.
  884. webView.restorationIdentifier = @"webView";
  885. // Configure delegates.
  886. webView.navigationDelegate = self;
  887. webView.UIDelegate = self;
  888. _translationDelegate = [[ShellTranslationDelegate alloc] init];
  889. webView.translationController.delegate = _translationDelegate;
  890. _autofillDelegate = [[ShellAutofillDelegate alloc] init];
  891. webView.autofillController.delegate = _autofillDelegate;
  892. webView.scrollView.delegate = self;
  893. // Constraints.
  894. webView.translatesAutoresizingMaskIntoConstraints = NO;
  895. [NSLayoutConstraint activateConstraints:@[
  896. [webView.topAnchor
  897. constraintEqualToAnchor:_contentView.safeAreaLayoutGuide.topAnchor],
  898. [webView.leadingAnchor
  899. constraintEqualToAnchor:_contentView.safeAreaLayoutGuide.leadingAnchor],
  900. [webView.trailingAnchor
  901. constraintEqualToAnchor:_contentView.safeAreaLayoutGuide
  902. .trailingAnchor],
  903. [webView.bottomAnchor
  904. constraintEqualToAnchor:_contentView.safeAreaLayoutGuide.bottomAnchor],
  905. ]];
  906. [webView addObserver:self
  907. forKeyPath:@"canGoBack"
  908. options:NSKeyValueObservingOptionNew |
  909. NSKeyValueObservingOptionInitial
  910. context:nil];
  911. [webView addObserver:self
  912. forKeyPath:@"canGoForward"
  913. options:NSKeyValueObservingOptionNew |
  914. NSKeyValueObservingOptionInitial
  915. context:nil];
  916. [webView addObserver:self
  917. forKeyPath:@"loading"
  918. options:NSKeyValueObservingOptionNew |
  919. NSKeyValueObservingOptionInitial
  920. context:nil];
  921. [webView addScriptCommandHandler:self commandPrefix:@"test"];
  922. [webView
  923. addMessageHandler:^(NSDictionary* payload) {
  924. NSLog(@"message handler payload received =\n%@", payload);
  925. }
  926. forCommand:@"messageHandlerCommand"];
  927. return webView;
  928. }
  929. - (void)removeWebView {
  930. [_webView removeFromSuperview];
  931. [_webView removeObserver:self forKeyPath:@"canGoBack"];
  932. [_webView removeObserver:self forKeyPath:@"canGoForward"];
  933. [_webView removeObserver:self forKeyPath:@"loading"];
  934. [_webView removeScriptCommandHandlerForCommandPrefix:@"test"];
  935. [_webView removeMessageHandlerForCommand:@"messageHandlerCommand"];
  936. _webView = nil;
  937. }
  938. - (void)dealloc {
  939. [_webView removeObserver:self forKeyPath:@"canGoBack"];
  940. [_webView removeObserver:self forKeyPath:@"canGoForward"];
  941. [_webView removeObserver:self forKeyPath:@"loading"];
  942. [_webView removeScriptCommandHandlerForCommandPrefix:@"test"];
  943. [_webView removeMessageHandlerForCommand:@"messageHandlerCommand"];
  944. }
  945. - (BOOL)textFieldShouldReturn:(UITextField*)field {
  946. NSURL* URL = [NSURL URLWithString:field.text];
  947. if (URL.scheme.length == 0) {
  948. NSString* enteredText = field.text;
  949. enteredText =
  950. [enteredText stringByAddingPercentEncodingWithAllowedCharacters:
  951. [NSCharacterSet URLQueryAllowedCharacterSet]];
  952. enteredText = [NSString
  953. stringWithFormat:@"https://www.google.com/search?q=%@", enteredText];
  954. URL = [NSURL URLWithString:enteredText];
  955. }
  956. NSURLRequest* request = [NSURLRequest requestWithURL:URL];
  957. [_webView loadRequest:request];
  958. [field resignFirstResponder];
  959. [self updateToolbar];
  960. return YES;
  961. }
  962. - (void)updateToolbar {
  963. // Do not update the URL if the text field is currently being edited.
  964. if ([_field isFirstResponder]) {
  965. return;
  966. }
  967. [_field setText:[[_webView visibleURL] absoluteString]];
  968. }
  969. - (UIAlertController*)actionSheetWithTitle:(nullable NSString*)title
  970. message:(nullable NSString*)message {
  971. UIAlertController* alertController = [UIAlertController
  972. alertControllerWithTitle:title
  973. message:message
  974. preferredStyle:UIAlertControllerStyleActionSheet];
  975. alertController.popoverPresentationController.sourceView = _menuButton;
  976. alertController.popoverPresentationController.sourceRect =
  977. CGRectMake(CGRectGetWidth(_menuButton.bounds) / 2,
  978. CGRectGetHeight(_menuButton.bounds), 1, 1);
  979. return alertController;
  980. }
  981. - (void)closePopupWebView {
  982. if (self.popupWebViews.count) {
  983. [self.popupWebViews.lastObject removeFromSuperview];
  984. [self.popupWebViews removeLastObject];
  985. }
  986. }
  987. #pragma mark CWVUIDelegate methods
  988. - (CWVWebView*)webView:(CWVWebView*)webView
  989. createWebViewWithConfiguration:(CWVWebViewConfiguration*)configuration
  990. forNavigationAction:(CWVNavigationAction*)action {
  991. NSLog(@"Create new CWVWebView for %@. User initiated? %@", action.request.URL,
  992. action.userInitiated ? @"Yes" : @"No");
  993. CWVWebView* newWebView = [self createWebViewWithConfiguration:configuration];
  994. [self.popupWebViews addObject:newWebView];
  995. UIButton* closeWindowButton = [[UIButton alloc] init];
  996. [closeWindowButton setImage:[UIImage imageNamed:@"ic_stop"]
  997. forState:UIControlStateNormal];
  998. closeWindowButton.tintColor = [UIColor blackColor];
  999. closeWindowButton.backgroundColor = [UIColor whiteColor];
  1000. [closeWindowButton addTarget:self
  1001. action:@selector(closePopupWebView)
  1002. forControlEvents:UIControlEventTouchUpInside];
  1003. [newWebView addSubview:closeWindowButton];
  1004. closeWindowButton.translatesAutoresizingMaskIntoConstraints = NO;
  1005. [NSLayoutConstraint activateConstraints:@[
  1006. [closeWindowButton.topAnchor constraintEqualToAnchor:newWebView.topAnchor
  1007. constant:16.0],
  1008. [closeWindowButton.centerXAnchor
  1009. constraintEqualToAnchor:newWebView.centerXAnchor],
  1010. ]];
  1011. return newWebView;
  1012. }
  1013. - (void)webViewDidClose:(CWVWebView*)webView {
  1014. NSLog(@"webViewDidClose");
  1015. }
  1016. - (void)webView:(CWVWebView*)webView
  1017. contextMenuConfigurationForElement:(CWVHTMLElement*)element
  1018. completionHandler:(void (^)(UIContextMenuConfiguration*))
  1019. completionHandler {
  1020. void (^copyHandler)(UIAction*) = ^(UIAction* action) {
  1021. NSDictionary* item = @{
  1022. (NSString*)(UTTypeURL) : element.hyperlink.absoluteString,
  1023. (NSString*)(UTTypeUTF8PlainText) : [element.hyperlink.absoluteString
  1024. dataUsingEncoding:NSUTF8StringEncoding],
  1025. };
  1026. [[UIPasteboard generalPasteboard] setItems:@[ item ]];
  1027. };
  1028. UIContextMenuConfiguration* configuration = [UIContextMenuConfiguration
  1029. configurationWithIdentifier:nil
  1030. previewProvider:^{
  1031. UIViewController* controller = [[UIViewController alloc] init];
  1032. CGRect frame = CGRectMake(10, 200, 200, 21);
  1033. UILabel* label = [[UILabel alloc] initWithFrame:frame];
  1034. label.text = @"iOS13 Preview Page";
  1035. [controller.view addSubview:label];
  1036. return controller;
  1037. }
  1038. actionProvider:^(id _) {
  1039. NSArray* actions = @[
  1040. [UIAction actionWithTitle:@"Copy Link"
  1041. image:nil
  1042. identifier:nil
  1043. handler:copyHandler],
  1044. [UIAction actionWithTitle:@"Cancel"
  1045. image:nil
  1046. identifier:nil
  1047. handler:^(id ignore){
  1048. }]
  1049. ];
  1050. NSString* menuTitle =
  1051. [NSString stringWithFormat:@"iOS13 Context Menu: %@",
  1052. element.hyperlink.absoluteString];
  1053. return [UIMenu menuWithTitle:menuTitle children:actions];
  1054. }];
  1055. completionHandler(configuration);
  1056. }
  1057. - (void)webView:(CWVWebView*)webView
  1058. contextMenuWillCommitWithAnimator:
  1059. (id<UIContextMenuInteractionCommitAnimating>)animator {
  1060. NSLog(@"webView:contextMenuWillCommitWithAnimator:");
  1061. }
  1062. - (void)webView:(CWVWebView*)webView
  1063. runJavaScriptAlertPanelWithMessage:(NSString*)message
  1064. pageURL:(NSURL*)URL
  1065. completionHandler:(void (^)(void))handler {
  1066. UIAlertController* alert =
  1067. [UIAlertController alertControllerWithTitle:nil
  1068. message:message
  1069. preferredStyle:UIAlertControllerStyleAlert];
  1070. [alert addAction:[UIAlertAction actionWithTitle:@"Ok"
  1071. style:UIAlertActionStyleDefault
  1072. handler:^(UIAlertAction* action) {
  1073. handler();
  1074. }]];
  1075. [self presentViewController:alert animated:YES completion:nil];
  1076. }
  1077. - (void)webView:(CWVWebView*)webView
  1078. runJavaScriptConfirmPanelWithMessage:(NSString*)message
  1079. pageURL:(NSURL*)URL
  1080. completionHandler:(void (^)(BOOL))handler {
  1081. UIAlertController* alert =
  1082. [UIAlertController alertControllerWithTitle:nil
  1083. message:message
  1084. preferredStyle:UIAlertControllerStyleAlert];
  1085. [alert addAction:[UIAlertAction actionWithTitle:@"Ok"
  1086. style:UIAlertActionStyleDefault
  1087. handler:^(UIAlertAction* action) {
  1088. handler(YES);
  1089. }]];
  1090. [alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
  1091. style:UIAlertActionStyleCancel
  1092. handler:^(UIAlertAction* action) {
  1093. handler(NO);
  1094. }]];
  1095. [self presentViewController:alert animated:YES completion:nil];
  1096. }
  1097. - (void)webView:(CWVWebView*)webView
  1098. runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
  1099. defaultText:(NSString*)defaultText
  1100. pageURL:(NSURL*)URL
  1101. completionHandler:(void (^)(NSString*))handler {
  1102. UIAlertController* alert =
  1103. [UIAlertController alertControllerWithTitle:nil
  1104. message:prompt
  1105. preferredStyle:UIAlertControllerStyleAlert];
  1106. [alert addTextFieldWithConfigurationHandler:^(UITextField* textField) {
  1107. textField.text = defaultText;
  1108. textField.accessibilityIdentifier =
  1109. kWebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier;
  1110. }];
  1111. __weak UIAlertController* weakAlert = alert;
  1112. [alert addAction:[UIAlertAction
  1113. actionWithTitle:@"Ok"
  1114. style:UIAlertActionStyleDefault
  1115. handler:^(UIAlertAction* action) {
  1116. NSString* textInput =
  1117. weakAlert.textFields.firstObject.text;
  1118. handler(textInput);
  1119. }]];
  1120. [alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
  1121. style:UIAlertActionStyleCancel
  1122. handler:^(UIAlertAction* action) {
  1123. handler(nil);
  1124. }]];
  1125. [self presentViewController:alert animated:YES completion:nil];
  1126. }
  1127. - (void)webView:(CWVWebView*)webView
  1128. didLoadFavicons:(NSArray<CWVFavicon*>*)favIcons {
  1129. NSLog(@"%@", NSStringFromSelector(_cmd));
  1130. }
  1131. #pragma mark CWVNavigationDelegate methods
  1132. - (BOOL)webView:(CWVWebView*)webView
  1133. shouldStartLoadWithRequest:(NSURLRequest*)request
  1134. navigationType:(CWVNavigationType)navigationType {
  1135. NSLog(@"%@", NSStringFromSelector(_cmd));
  1136. return YES;
  1137. }
  1138. - (BOOL)webView:(CWVWebView*)webView
  1139. shouldContinueLoadWithResponse:(NSURLResponse*)response
  1140. forMainFrame:(BOOL)forMainFrame {
  1141. NSLog(@"%@", NSStringFromSelector(_cmd));
  1142. return YES;
  1143. }
  1144. - (void)webViewDidStartNavigation:(CWVWebView*)webView {
  1145. NSLog(@"%@", NSStringFromSelector(_cmd));
  1146. [self updateToolbar];
  1147. }
  1148. - (void)webViewDidCommitNavigation:(CWVWebView*)webView {
  1149. NSLog(@"%@", NSStringFromSelector(_cmd));
  1150. [self updateToolbar];
  1151. }
  1152. - (void)webViewDidFinishNavigation:(CWVWebView*)webView {
  1153. NSLog(@"%@", NSStringFromSelector(_cmd));
  1154. // TODO(crbug.com/679895): Add some visual indication that the page load has
  1155. // finished.
  1156. [self updateToolbar];
  1157. }
  1158. - (void)webView:(CWVWebView*)webView
  1159. didFailNavigationWithError:(NSError*)error {
  1160. NSLog(@"%@", NSStringFromSelector(_cmd));
  1161. [self updateToolbar];
  1162. }
  1163. - (void)webView:(CWVWebView*)webView
  1164. handleSSLErrorWithHandler:(CWVSSLErrorHandler*)handler {
  1165. NSLog(@"%@", NSStringFromSelector(_cmd));
  1166. [handler displayErrorPageWithHTML:handler.error.localizedDescription];
  1167. if (!handler.overridable) {
  1168. return;
  1169. }
  1170. UIAlertController* alertController =
  1171. [self actionSheetWithTitle:@"SSL error encountered"
  1172. message:@"Would you like to continue anyways?"];
  1173. [alertController
  1174. addAction:[UIAlertAction actionWithTitle:@"Yes"
  1175. style:UIAlertActionStyleDefault
  1176. handler:^(UIAlertAction* action) {
  1177. [handler overrideErrorAndReloadPage];
  1178. }]];
  1179. [alertController
  1180. addAction:[UIAlertAction actionWithTitle:@"Cancel"
  1181. style:UIAlertActionStyleCancel
  1182. handler:nil]];
  1183. [self presentViewController:alertController animated:YES completion:nil];
  1184. }
  1185. - (void)webView:(CWVWebView*)webView
  1186. handleLookalikeURLWithHandler:(CWVLookalikeURLHandler*)handler {
  1187. NSLog(@"%@", NSStringFromSelector(_cmd));
  1188. NSString* html =
  1189. [NSString stringWithFormat:@"%@ requested, did you mean %@?",
  1190. handler.requestURL, handler.safeURL];
  1191. [handler displayInterstitialPageWithHTML:html];
  1192. UIAlertController* alertController =
  1193. [self actionSheetWithTitle:@"Lookalike URL encountered"
  1194. message:@"Choose how to proceed."];
  1195. [alertController
  1196. addAction:
  1197. [UIAlertAction
  1198. actionWithTitle:@"Proceed to request URL"
  1199. style:UIAlertActionStyleDefault
  1200. handler:^(UIAlertAction* action) {
  1201. CWVLookalikeURLHandlerDecision decision =
  1202. CWVLookalikeURLHandlerDecisionProceedToRequestURL;
  1203. [handler commitDecision:decision];
  1204. }]];
  1205. [alertController
  1206. addAction:
  1207. [UIAlertAction
  1208. actionWithTitle:@"Proceed to safe URL"
  1209. style:UIAlertActionStyleDefault
  1210. handler:^(UIAlertAction* action) {
  1211. [handler
  1212. commitDecision:
  1213. CWVLookalikeURLHandlerDecisionProceedToSafeURL];
  1214. }]];
  1215. [alertController
  1216. addAction:
  1217. [UIAlertAction
  1218. actionWithTitle:@"Go back or close"
  1219. style:UIAlertActionStyleDefault
  1220. handler:^(UIAlertAction* action) {
  1221. [handler
  1222. commitDecision:
  1223. CWVLookalikeURLHandlerDecisionGoBackOrClose];
  1224. }]];
  1225. [alertController
  1226. addAction:[UIAlertAction actionWithTitle:@"Cancel"
  1227. style:UIAlertActionStyleCancel
  1228. handler:nil]];
  1229. [self presentViewController:alertController animated:YES completion:nil];
  1230. }
  1231. - (void)webView:(CWVWebView*)webView
  1232. handleUnsafeURLWithHandler:(CWVUnsafeURLHandler*)handler {
  1233. NSLog(@"%@", NSStringFromSelector(_cmd));
  1234. NSString* html =
  1235. [NSString stringWithFormat:@"%@ requested %@ which might be unsafe.",
  1236. handler.mainFrameURL, handler.requestURL];
  1237. [handler displayInterstitialPageWithHTML:html];
  1238. UIAlertController* alertController =
  1239. [self actionSheetWithTitle:@"Unsafe URL encountered"
  1240. message:@"Choose how to proceed."];
  1241. [alertController
  1242. addAction:[UIAlertAction actionWithTitle:@"Proceed"
  1243. style:UIAlertActionStyleDefault
  1244. handler:^(UIAlertAction* action) {
  1245. [handler proceed];
  1246. }]];
  1247. [alertController
  1248. addAction:[UIAlertAction actionWithTitle:@"Go back or close"
  1249. style:UIAlertActionStyleDefault
  1250. handler:^(UIAlertAction* action) {
  1251. [handler goBack];
  1252. }]];
  1253. [alertController
  1254. addAction:[UIAlertAction actionWithTitle:@"Cancel"
  1255. style:UIAlertActionStyleCancel
  1256. handler:nil]];
  1257. [self presentViewController:alertController animated:YES completion:nil];
  1258. }
  1259. - (void)webViewWebContentProcessDidTerminate:(CWVWebView*)webView {
  1260. NSLog(@"%@", NSStringFromSelector(_cmd));
  1261. }
  1262. - (void)webView:(CWVWebView*)webView
  1263. didRequestDownloadWithTask:(CWVDownloadTask*)task {
  1264. NSLog(@"%@", NSStringFromSelector(_cmd));
  1265. self.downloadTask = task;
  1266. NSString* documentDirectoryPath = NSSearchPathForDirectoriesInDomains(
  1267. NSDocumentDirectory, NSUserDomainMask, YES)[0];
  1268. self.downloadFilePath = [documentDirectoryPath
  1269. stringByAppendingPathComponent:task.suggestedFileName];
  1270. task.delegate = self;
  1271. [task startDownloadToLocalFileAtPath:self.downloadFilePath];
  1272. }
  1273. #pragma mark CWVScriptCommandHandler
  1274. - (BOOL)webView:(CWVWebView*)webView
  1275. handleScriptCommand:(nonnull CWVScriptCommand*)command
  1276. fromMainFrame:(BOOL)fromMainFrame {
  1277. NSLog(@"%@ command.content=%@", NSStringFromSelector(_cmd), command.content);
  1278. return YES;
  1279. }
  1280. #pragma mark CWVAutofillDataManagerObserver
  1281. - (void)autofillDataManagerDataDidChange:
  1282. (CWVAutofillDataManager*)autofillDataManager {
  1283. NSLog(@"%@", NSStringFromSelector(_cmd));
  1284. }
  1285. - (void)autofillDataManager:(CWVAutofillDataManager*)autofillDataManager
  1286. didChangePasswordsByAdding:(NSArray<CWVPassword*>*)added
  1287. updating:(NSArray<CWVPassword*>*)updated
  1288. removing:(NSArray<CWVPassword*>*)removed {
  1289. NSLog(@"%@: added %@, updated %@, and removed %@ passwords",
  1290. NSStringFromSelector(_cmd), @(added.count), @(updated.count),
  1291. @(removed.count));
  1292. }
  1293. #pragma mark CWVDownloadTaskDelegate
  1294. - (void)downloadTask:(CWVDownloadTask*)downloadTask
  1295. didFinishWithError:(nullable NSError*)error {
  1296. NSLog(@"%@", NSStringFromSelector(_cmd));
  1297. if (!error) {
  1298. NSURL* url = [NSURL fileURLWithPath:self.downloadFilePath];
  1299. self.documentInteractionController =
  1300. [UIDocumentInteractionController interactionControllerWithURL:url];
  1301. [self.documentInteractionController presentOptionsMenuFromRect:CGRectZero
  1302. inView:self.view
  1303. animated:YES];
  1304. }
  1305. self.downloadTask = nil;
  1306. self.downloadFilePath = nil;
  1307. }
  1308. - (void)downloadTaskProgressDidChange:(CWVDownloadTask*)downloadTask {
  1309. NSLog(@"%@", NSStringFromSelector(_cmd));
  1310. }
  1311. #pragma mark CWVSyncControllerDelegate
  1312. - (void)syncControllerDidStartSync:(CWVSyncController*)syncController {
  1313. NSLog(@"%@", NSStringFromSelector(_cmd));
  1314. }
  1315. - (void)syncController:(CWVSyncController*)syncController
  1316. didFailWithError:(NSError*)error {
  1317. NSLog(@"%@:%@", NSStringFromSelector(_cmd), error);
  1318. }
  1319. - (void)syncControllerDidStopSync:(CWVSyncController*)syncController {
  1320. NSLog(@"%@", NSStringFromSelector(_cmd));
  1321. }
  1322. - (void)syncControllerDidUpdateState:(CWVSyncController*)syncController {
  1323. NSLog(@"%@", NSStringFromSelector(_cmd));
  1324. }
  1325. #pragma mark CWVLeakCheckServiceObserver
  1326. - (void)leakCheckServiceDidChangeState:(CWVLeakCheckService*)leakCheckService {
  1327. NSLog(@"%@:%d", NSStringFromSelector(_cmd), (int)leakCheckService.state);
  1328. if (leakCheckService.state != CWVLeakCheckServiceStateRunning) {
  1329. [self.pendingLeakChecks removeAllObjects];
  1330. }
  1331. }
  1332. - (void)leakCheckService:(CWVLeakCheckService*)leakCheckService
  1333. didCheckCredential:(CWVLeakCheckCredential*)credential
  1334. isLeaked:(BOOL)isLeaked {
  1335. NSMutableArray<CWVPassword*>* passwordsForCredential =
  1336. [self.pendingLeakChecks objectForKey:credential];
  1337. if (!passwordsForCredential) {
  1338. NSLog(@"No passwords for CWVLeakCheckCredential!");
  1339. return;
  1340. }
  1341. [self.pendingLeakChecks removeObjectForKey:credential];
  1342. NSMutableArray<NSString*>* passwordDescriptions = [passwordsForCredential
  1343. valueForKey:NSStringFromSelector(@selector(debugDescription))];
  1344. NSString* passwordsDescription =
  1345. [passwordDescriptions componentsJoinedByString:@"\n\n"];
  1346. NSString* message = [NSString
  1347. stringWithFormat:@"Leak check returned %@ for %@ passwords:\n%@",
  1348. isLeaked ? @"LEAKED" : @"OK",
  1349. @(passwordsForCredential.count), passwordsDescription];
  1350. NSLog(@"%@", message);
  1351. NSLog(@"%@ Leak checks remaining...", @(self.pendingLeakChecks.count));
  1352. }
  1353. #pragma mark UIScrollViewDelegate
  1354. - (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView {
  1355. NSLog(@"%@", NSStringFromSelector(_cmd));
  1356. }
  1357. @end