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

Side by Side Diff: ios/chrome/browser/tabs/tab.mm

Issue 2585233003: Upstream Chrome on iOS source code [2/11]. (Closed)
Patch Set: Created 4 years 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
« no previous file with comments | « ios/chrome/browser/tabs/tab.h ('k') | ios/chrome/browser/tabs/tab_delegate.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/tabs/tab.h"
6
7 #import <CoreLocation/CoreLocation.h>
8 #import <UIKit/UIKit.h>
9
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/ios/block_types.h"
15 #import "base/ios/weak_nsobject.h"
16 #include "base/json/string_escape.h"
17 #include "base/logging.h"
18 #include "base/mac/bind_objc_block.h"
19 #include "base/mac/foundation_util.h"
20 #include "base/mac/objc_property_releaser.h"
21 #include "base/mac/scoped_nsobject.h"
22 #include "base/metrics/histogram.h"
23 #include "base/metrics/user_metrics.h"
24 #include "base/metrics/user_metrics_action.h"
25 #include "base/scoped_observer.h"
26 #include "base/strings/string_split.h"
27 #include "base/strings/sys_string_conversions.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/time/time.h"
30 #include "components/content_settings/core/browser/host_content_settings_map.h"
31 #include "components/favicon/core/favicon_driver_observer.h"
32 #include "components/favicon/ios/web_favicon_driver.h"
33 #include "components/google/core/browser/google_util.h"
34 #include "components/history/core/browser/history_context.h"
35 #include "components/history/core/browser/history_service.h"
36 #include "components/history/core/browser/top_sites.h"
37 #include "components/history/ios/browser/web_state_top_sites_observer.h"
38 #include "components/infobars/core/infobar_manager.h"
39 #include "components/keyed_service/core/service_access_type.h"
40 #include "components/metrics_services_manager/metrics_services_manager.h"
41 #include "components/navigation_metrics/navigation_metrics.h"
42 #include "components/navigation_metrics/origins_seen_service.h"
43 #include "components/prefs/pref_service.h"
44 #include "components/search_engines/template_url_service.h"
45 #include "components/sessions/core/session_types.h"
46 #include "components/sessions/ios/ios_serialized_navigation_builder.h"
47 #include "components/signin/core/browser/account_reconcilor.h"
48 #include "components/signin/core/browser/signin_metrics.h"
49 #import "components/signin/ios/browser/account_consistency_service.h"
50 #include "components/strings/grit/components_strings.h"
51 #include "components/url_formatter/url_formatter.h"
52 #include "ios/chrome/browser/application_context.h"
53 #import "ios/chrome/browser/autofill/autofill_controller.h"
54 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
55 #import "ios/chrome/browser/autofill/form_suggestion_controller.h"
56 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
57 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
58 #include "ios/chrome/browser/chrome_url_constants.h"
59 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory. h"
60 #import "ios/chrome/browser/crash_loop_detection_util.h"
61 #include "ios/chrome/browser/experimental_flags.h"
62 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
63 #import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
64 #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
65 #include "ios/chrome/browser/history/history_service_factory.h"
66 #include "ios/chrome/browser/history/top_sites_factory.h"
67 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
68 #include "ios/chrome/browser/metrics/ios_chrome_origins_seen_service_factory.h"
69 #import "ios/chrome/browser/metrics/tab_usage_recorder.h"
70 #import "ios/chrome/browser/native_app_launcher/native_app_navigation_controller .h"
71 #import "ios/chrome/browser/net/metrics_network_client_manager.h"
72 #import "ios/chrome/browser/passwords/credential_manager.h"
73 #import "ios/chrome/browser/passwords/js_credential_manager.h"
74 #import "ios/chrome/browser/passwords/password_controller.h"
75 #import "ios/chrome/browser/passwords/passwords_ui_delegate_impl.h"
76 #include "ios/chrome/browser/pref_names.h"
77 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
78 #include "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h"
79 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
80 #include "ios/chrome/browser/signin/account_reconcilor_factory.h"
81 #include "ios/chrome/browser/signin/authentication_service.h"
82 #include "ios/chrome/browser/signin/authentication_service_factory.h"
83 #include "ios/chrome/browser/signin/signin_capability.h"
84 #import "ios/chrome/browser/snapshots/snapshot_manager.h"
85 #import "ios/chrome/browser/snapshots/snapshot_overlay_provider.h"
86 #import "ios/chrome/browser/snapshots/web_controller_snapshot_helper.h"
87 #include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
88 #import "ios/chrome/browser/storekit_launcher.h"
89 #include "ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h"
90 #import "ios/chrome/browser/tabs/tab_delegate.h"
91 #import "ios/chrome/browser/tabs/tab_dialog_delegate.h"
92 #import "ios/chrome/browser/tabs/tab_model.h"
93 #import "ios/chrome/browser/tabs/tab_private.h"
94 #import "ios/chrome/browser/tabs/tab_snapshotting_delegate.h"
95 #include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
96 #import "ios/chrome/browser/u2f/u2f_controller.h"
97 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
98 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
99 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
100 #import "ios/chrome/browser/ui/commands/open_url_command.h"
101 #import "ios/chrome/browser/ui/commands/show_signin_command.h"
102 #import "ios/chrome/browser/ui/downloads/download_manager_controller.h"
103 #import "ios/chrome/browser/ui/fullscreen_controller.h"
104 #import "ios/chrome/browser/ui/open_in_controller.h"
105 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller. h"
106 #import "ios/chrome/browser/ui/prerender_delegate.h"
107 #import "ios/chrome/browser/ui/reader_mode/reader_mode_checker.h"
108 #import "ios/chrome/browser/ui/reader_mode/reader_mode_controller.h"
109 #import "ios/chrome/browser/ui/sad_tab/sad_tab_view.h"
110 #include "ios/chrome/browser/ui/ui_util.h"
111 #import "ios/chrome/browser/web/auto_reload_bridge.h"
112 #import "ios/chrome/browser/web/blocked_popup_handler.h"
113 #import "ios/chrome/browser/web/external_app_launcher.h"
114 #include "ios/chrome/browser/web/network_activity_indicator_tab_helper.h"
115 #import "ios/chrome/browser/web/passkit_dialog_provider.h"
116 #include "ios/chrome/browser/web/print_observer.h"
117 #import "ios/chrome/browser/web/resubmit_data_controller.h"
118 #import "ios/chrome/browser/xcallback_parameters.h"
119 #include "ios/chrome/grit/ios_strings.h"
120 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
121 #import "ios/public/provider/chrome/browser/native_app_launcher/native_app_metad ata.h"
122 #import "ios/public/provider/chrome/browser/native_app_launcher/native_app_white list_manager.h"
123 #import "ios/web/navigation/crw_session_controller.h"
124 #import "ios/web/navigation/crw_session_entry.h"
125 #import "ios/web/navigation/navigation_item_impl.h"
126 #import "ios/web/navigation/navigation_manager_impl.h"
127 #include "ios/web/net/request_tracker_impl.h"
128 #include "ios/web/public/favicon_status.h"
129 #include "ios/web/public/favicon_url.h"
130 #include "ios/web/public/interstitials/web_interstitial.h"
131 #import "ios/web/public/navigation_manager.h"
132 #include "ios/web/public/referrer.h"
133 #include "ios/web/public/ssl_status.h"
134 #include "ios/web/public/url_scheme_util.h"
135 #include "ios/web/public/url_util.h"
136 #include "ios/web/public/web_client.h"
137 #import "ios/web/public/web_state/crw_web_user_interface_delegate.h"
138 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
139 #import "ios/web/public/web_state/ui/crw_generic_content_view.h"
140 #include "ios/web/public/web_state/web_state.h"
141 #include "ios/web/public/web_thread.h"
142 #import "ios/web/web_state/ui/crw_web_controller.h"
143 #import "ios/web/web_state/web_state_impl.h"
144 #include "net/base/escape.h"
145 #include "net/base/filename_util.h"
146 #import "net/base/mac/url_conversions.h"
147 #include "net/base/net_errors.h"
148 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
149 #include "net/cert/x509_certificate.h"
150 #include "net/http/http_response_headers.h"
151 #include "net/url_request/url_fetcher.h"
152 #include "ui/base/l10n/l10n_util.h"
153 #include "ui/base/page_transition_types.h"
154 #include "url/origin.h"
155
156 using base::UserMetricsAction;
157 using web::NavigationManagerImpl;
158 using net::RequestTracker;
159
160 NSString* const kTabUrlStartedLoadingNotificationForCrashReporting =
161 @"kTabUrlStartedLoadingNotificationForCrashReporting";
162 NSString* const kTabUrlMayStartLoadingNotificationForCrashReporting =
163 @"kTabUrlMayStartLoadingNotificationForCrashReporting";
164 NSString* const kTabIsShowingExportableNotificationForCrashReporting =
165 @"kTabIsShowingExportableNotificationForCrashReporting";
166 NSString* const kTabClosingCurrentDocumentNotificationForCrashReporting =
167 @"kTabClosingCurrentDocumentNotificationForCrashReporting";
168
169 NSString* const kTabUrlKey = @"url";
170
171 namespace {
172 class TabHistoryContext;
173 class FaviconDriverObserverBridge;
174 class TabInfoBarObserver;
175
176 // Name of histogram for recording the state of the tab when the renderer is
177 // terminated.
178 const char kRendererTerminationStateHistogram[] =
179 "Tab.StateAtRendererTermination";
180
181 // Referrer used for clicks on article suggestions on the NTP.
182 const char kChromeContentSuggestionsReferrer[] =
183 "https://www.googleapis.com/auth/chrome-content-suggestions";
184
185 // Enum corresponding to UMA's TabForegroundState, for
186 // Tab.StateAtRendererTermination. Must be kept in sync with the UMA enum.
187 enum class RendererTerminationTabState {
188 // These two values are for when the app is in the foreground.
189 FOREGROUND_TAB_FOREGROUND_APP = 0,
190 BACKGROUND_TAB_FOREGROUND_APP,
191 // These are for when the app is in the background.
192 FOREGROUND_TAB_BACKGROUND_APP,
193 BACKGROUND_TAB_BACKGROUND_APP,
194 TERMINATION_TAB_STATE_COUNT
195 };
196 } // namespace
197
198 @interface Tab ()<BlockedPopupHandlerDelegate,
199 CRWWebUserInterfaceDelegate,
200 FindInPageControllerDelegate,
201 ReaderModeControllerDelegate> {
202 TabModel* parentTabModel_; // weak
203 ios::ChromeBrowserState* browserState_; // weak
204
205 base::scoped_nsobject<OpenInController> openInController_;
206 base::WeakNSProtocol<id<PassKitDialogProvider>> passKitDialogProvider_;
207 // TODO(crbug.com/546213): Move this out of Tab, probably to native app
208 // launcher since that's the only thing that uses it.
209 base::WeakNSProtocol<id<StoreKitLauncher>> storeKitLauncher_;
210
211 // Whether or not this tab is currently being displayed.
212 BOOL visible_;
213
214 // Used between -webWillStartLoadingURL: and -webDidStartLoadingURL:.
215 BOOL isUserNavigationEvent_;
216
217 // Holds entries that need to be added to the history DB. Prerender tabs do
218 // not write navigation data to the history DB. Instead, they cache history
219 // data in this vector and add it to the DB when the prerender status is
220 // removed (when the Tab is swapped in as a real Tab).
221 std::vector<history::HistoryAddPageArgs> addPageVector_;
222
223 // YES if this Tab is being prerendered.
224 BOOL isPrerenderTab_;
225
226 // YES if this Tab was initiated from a voice search.
227 BOOL isVoiceSearchResultsTab_;
228
229 // YES if the Tab needs to be reloaded after the app becomes active.
230 BOOL requireReloadAfterBecomingActive_;
231
232 base::mac::ObjCPropertyReleaser propertyReleaser_Tab_;
233
234 id<TabDelegate> delegate_; // weak
235 base::WeakNSProtocol<id<TabDialogDelegate>> dialogDelegate_;
236 base::WeakNSProtocol<id<SnapshotOverlayProvider>> snapshotOverlayProvider_;
237
238 // Delegate used for snapshotting geometry.
239 id<TabSnapshottingDelegate> tabSnapshottingDelegate_; // weak
240
241 // The Full Screen Controller responsible for hiding/showing the toolbar.
242 base::scoped_nsobject<FullScreenController> fullScreenController_;
243
244 base::WeakNSProtocol<id<FullScreenControllerDelegate>>
245 fullScreenControllerDelegate_;
246
247 // The Overscroll controller responsible for displaying the
248 // overscrollActionsView above the toolbar.
249 base::scoped_nsobject<OverscrollActionsController>
250 overscrollActionsController_;
251 base::WeakNSProtocol<id<OverscrollActionsControllerDelegate>>
252 overscrollActionsControllerDelegate_;
253
254 // Lightweight object dealing with various different UI behaviours when
255 // opening a URL in an external application.
256 base::scoped_nsobject<ExternalAppLauncher> externalAppLauncher_;
257
258 // Handles suggestions for form entry.
259 base::scoped_nsobject<FormSuggestionController> suggestionController_;
260
261 // Manages the input accessory view during form input.
262 base::scoped_nsobject<FormInputAccessoryViewController>
263 inputAccessoryViewController_;
264
265 // TODO(crbug.com/661665): move the WebContentsObservers into their own
266 // container.
267 // Handles saving and autofill of passwords.
268 base::scoped_nsobject<PasswordController> passwordController_;
269
270 // Handles autofill.
271 base::scoped_nsobject<AutofillController> autofillController_;
272
273 // The popup blocker to show blocked popup to the user.
274 std::unique_ptr<BlockedPopupHandler> popupHandler_;
275
276 // Handles find on page.
277 base::scoped_nsobject<FindInPageController> findInPageController_;
278
279 // Handles GAL infobar on web pages.
280 base::scoped_nsobject<NativeAppNavigationController>
281 nativeAppNavigationController_;
282
283 // Handles caching and retrieving of snapshots.
284 base::scoped_nsobject<SnapshotManager> snapshotManager_;
285
286 // Handles retrieving, generating and updating snapshots of CRWWebController's
287 // web page.
288 base::scoped_nsobject<WebControllerSnapshotHelper>
289 webControllerSnapshotHelper_;
290
291 // The controller that displays an action sheet to confirm form data
292 // resubmission.
293 base::scoped_nsobject<ResubmitDataController> resubmitDataController_;
294
295 // Number of attempts to show the resubmit data action sheet.
296 NSUInteger showResubmitDataActionSheetAttempt_;
297
298 // Handles support for window.print JavaScript calls.
299 std::unique_ptr<PrintObserver> printObserver_;
300
301 // AutoReloadBridge for this tab.
302 base::scoped_nsobject<AutoReloadBridge> autoReloadBridge_;
303
304 // WebStateImpl for this tab.
305 std::unique_ptr<web::WebStateImpl> webStateImpl_;
306
307 // Context used by history to scope the lifetime of navigation entry
308 // references to Tab.
309 std::unique_ptr<TabHistoryContext> tabHistoryContext_;
310
311 // The controller for everything related to reader mode.
312 base::scoped_nsobject<ReaderModeController> readerModeController_;
313
314 // C++ bridge that receives notifications from the FaviconDriver.
315 std::unique_ptr<FaviconDriverObserverBridge> faviconDriverObserverBridge_;
316
317 // U2F call controller object.
318 base::scoped_nsobject<U2FController> U2FController_;
319
320 // C++ observer used to trigger snapshots after the removal of InfoBars.
321 std::unique_ptr<TabInfoBarObserver> tabInfoBarObserver_;
322
323 // C++ observer to implement the credential management JavaScript API.
324 std::unique_ptr<CredentialManager> credentialManager_;
325
326 // Client factory created for metrics tracking. The Tab will signal page
327 // load starts and finishes to this.
328 base::scoped_nsobject<MetricsNetworkClientManager> metricsClientManager_;
329 }
330
331 // Returns the current sessionEntry for the sesionController associated with
332 // this tab. Don't use this to get the underlying NavigationItem; instead
333 // go through the NavigationManager.
334 // This is nil if there's no NavigationManager.
335 @property(nonatomic, readonly) CRWSessionEntry* currentSessionEntry;
336
337 // Returns the tab's reader mode controller. May contain nil if the feature is
338 // disabled.
339 @property(nonatomic, readonly) ReaderModeController* readerModeController;
340
341 // Returns a list of FormSuggestionProviders to be queried for suggestions
342 // in order of priority.
343 - (NSArray*)suggestionProviders;
344
345 // Returns a list of FormInputAccessoryViewProviders to be queried for an input
346 // accessory view in order of priority.
347 - (NSArray*)accessoryViewProviders;
348
349 // Sets the favicon on the current NavigationItem.
350 - (void)setFavicon:(const gfx::Image*)image;
351
352 // Updates the title field of the current session entry. Also updates the
353 // history database.
354 - (void)updateTitle:(NSString*)title;
355
356 // Saves the current title to the history database.
357 - (void)saveTitleToHistoryDB;
358
359 // Returns a lazily instantiated popup handler.
360 - (BlockedPopupHandler*)popupHandler;
361
362 // Adds the current session entry to this history database.
363 - (void)addCurrentEntryToHistoryDB;
364
365 // Adds any cached entries from |addPageVector_| to the history DB.
366 - (void)commitCachedEntriesToHistoryDB;
367
368 // Returns the OpenInController for this tab.
369 - (OpenInController*)openInController;
370
371 // Calls the model and ask to close this tab.
372 - (void)closeThisTab;
373
374 // Shows the ResubmitDataActionSheet to the user to allow the user to make a
375 // choice.
376 - (void)showResubmitDataActionSheet;
377
378 // Clears the ResubmitDataActionSheet from the UI.
379 - (void)clearResubmitDataActionSheet;
380
381 // Initialize the Native App Launcher controller.
382 - (void)initNativeAppNavigationController;
383
384 // YES if toEntry is behind fromEntry in the current history stack.
385 - (BOOL)navigationIsBackwards:(const CRWSessionEntry*)fromEntry
386 toEntry:(const CRWSessionEntry*)toEntry;
387
388 // Opens a link in an external app. Returns YES iff |url| is launched in an
389 // external app.
390 - (BOOL)openExternalURL:(const GURL&)url linkClicked:(BOOL)linkClicked;
391
392 // Handles exportable files if possible.
393 - (void)handleExportableFile:(net::HttpResponseHeaders*)headers;
394
395 // Called after the session history is replaced, useful for updating members
396 // with new sessionID.
397 - (void)didReplaceSessionHistory;
398
399 // Called when the UIApplication's state becomes active.
400 - (void)applicationDidBecomeActive;
401 @end
402
403 namespace {
404 // TabHistoryContext is used by history to scope the lifetime of navigation
405 // entry references to Tab.
406 class TabHistoryContext : public history::Context {
407 public:
408 TabHistoryContext() {}
409 ~TabHistoryContext() {}
410
411 private:
412 DISALLOW_COPY_AND_ASSIGN(TabHistoryContext);
413 };
414
415 class FaviconDriverObserverBridge : public favicon::FaviconDriverObserver {
416 public:
417 FaviconDriverObserverBridge(Tab* owner,
418 favicon::FaviconDriver* favicon_driver);
419 ~FaviconDriverObserverBridge() override;
420
421 // favicon::FaviconDriverObserver implementation.
422 void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
423 NotificationIconType notification_icon_type,
424 const GURL& icon_url,
425 bool icon_url_changed,
426 const gfx::Image& image) override;
427
428 private:
429 Tab* owner_; // Owns this instance.
430 ScopedObserver<favicon::FaviconDriver, favicon::FaviconDriverObserver>
431 scoped_observer_;
432 DISALLOW_COPY_AND_ASSIGN(FaviconDriverObserverBridge);
433 };
434
435 FaviconDriverObserverBridge::FaviconDriverObserverBridge(
436 Tab* owner,
437 favicon::FaviconDriver* favicon_driver)
438 : owner_(owner), scoped_observer_(this) {
439 scoped_observer_.Add(favicon_driver);
440 }
441
442 FaviconDriverObserverBridge::~FaviconDriverObserverBridge() {}
443
444 void FaviconDriverObserverBridge::OnFaviconUpdated(
445 favicon::FaviconDriver* favicon_driver,
446 NotificationIconType notification_icon_type,
447 const GURL& icon_url,
448 bool icon_url_changed,
449 const gfx::Image& image) {
450 [owner_ setFavicon:&image];
451 }
452
453 // Observer class that listens for infobar signals.
454 class TabInfoBarObserver : public infobars::InfoBarManager::Observer {
455 public:
456 explicit TabInfoBarObserver(Tab* owner);
457 ~TabInfoBarObserver() override;
458 void SetShouldObserveInfoBarManager(bool should_observe);
459 void OnInfoBarAdded(infobars::InfoBar* infobar) override;
460 void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override;
461 void OnInfoBarReplaced(infobars::InfoBar* old_infobar,
462 infobars::InfoBar* new_infobar) override;
463
464 private:
465 Tab* owner_; // Owns this instance;
466 ScopedObserver<infobars::InfoBarManager, TabInfoBarObserver> scoped_observer_;
467 DISALLOW_COPY_AND_ASSIGN(TabInfoBarObserver);
468 };
469
470 TabInfoBarObserver::TabInfoBarObserver(Tab* owner)
471 : owner_(owner), scoped_observer_(this) {}
472
473 TabInfoBarObserver::~TabInfoBarObserver() {}
474
475 void TabInfoBarObserver::SetShouldObserveInfoBarManager(bool should_observe) {
476 infobars::InfoBarManager* infobar_manager = [owner_ infoBarManager];
477 if (!infobar_manager)
478 return;
479
480 if (should_observe) {
481 if (!scoped_observer_.IsObserving(infobar_manager))
482 scoped_observer_.Add(infobar_manager);
483 } else {
484 scoped_observer_.Remove(infobar_manager);
485 }
486 }
487
488 void TabInfoBarObserver::OnInfoBarAdded(infobars::InfoBar* infobar) {
489 // Update snapshots after the infobar has been added.
490 [owner_ updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
491 }
492
493 void TabInfoBarObserver::OnInfoBarRemoved(infobars::InfoBar* infobar,
494 bool animate) {
495 // Update snapshots after the infobar has been removed.
496 [owner_ updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
497 }
498
499 void TabInfoBarObserver::OnInfoBarReplaced(infobars::InfoBar* old_infobar,
500 infobars::InfoBar* new_infobar) {
501 // Update snapshots after the infobar has been replaced.
502 [owner_ updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
503 }
504
505 // Registers |factory| with |tracker| on the IO thread.
506 void AddNetworkClientFactoryOnIOThread(
507 web::RequestTrackerImpl* tracker,
508 CRNForwardingNetworkClientFactory* factory) {
509 base::scoped_nsobject<CRNForwardingNetworkClientFactory> scoped_factory(
510 [factory retain]);
511 tracker->PostIOTask(base::Bind(&net::RequestTracker::AddNetworkClientFactory,
512 tracker, scoped_factory));
513 }
514
515 } // anonymous namespace
516
517 @implementation Tab
518
519 @synthesize browserState = browserState_;
520 @synthesize useGreyImageCache = useGreyImageCache_;
521 @synthesize isPrerenderTab = isPrerenderTab_;
522 @synthesize isLinkLoadingPrerenderTab = isLinkLoadingPrerenderTab_;
523 @synthesize isVoiceSearchResultsTab = isVoiceSearchResultsTab_;
524 @synthesize delegate = delegate_;
525 @synthesize tabSnapshottingDelegate = tabSnapshottingDelegate_;
526
527 - (instancetype)initWithWindowName:(NSString*)windowName
528 opener:(Tab*)opener
529 openedByDOM:(BOOL)openedByDOM
530 model:(TabModel*)parentModel
531 browserState:(ios::ChromeBrowserState*)browserState {
532 NSInteger openerIndex = -1;
533 if ([opener navigationManager]) {
534 NavigationManagerImpl* openerNavManager = [opener navigationManager];
535 openerIndex = openerNavManager->GetLastCommittedItemIndex();
536 }
537 std::unique_ptr<web::WebStateImpl> webState(
538 new web::WebStateImpl(browserState));
539 webState->GetNavigationManagerImpl().InitializeSession(
540 windowName, [opener currentSessionID], openedByDOM, openerIndex);
541
542 return [self initWithWebState:std::move(webState) model:parentModel];
543 }
544
545 - (instancetype)initWithWebState:(std::unique_ptr<web::WebState>)webState
546 model:(TabModel*)parentModel {
547 DCHECK(webState);
548 self = [super init];
549 if (self) {
550 propertyReleaser_Tab_.Init(self, [Tab class]);
551 tabHistoryContext_.reset(new TabHistoryContext());
552 parentTabModel_ = parentModel;
553 browserState_ =
554 ios::ChromeBrowserState::FromBrowserState(webState->GetBrowserState());
555
556 webStateImpl_.reset(static_cast<web::WebStateImpl*>(webState.release()));
557 [self.webController setDelegate:self];
558 [self.webController setUIDelegate:self];
559
560 NSString* sessionID = [self currentSessionID];
561 DCHECK(sessionID);
562 snapshotManager_.reset([[SnapshotManager alloc] init]);
563
564 webControllerSnapshotHelper_.reset([[WebControllerSnapshotHelper alloc]
565 initWithSnapshotManager:snapshotManager_
566 tab:self]);
567
568 findInPageController_.reset([[FindInPageController alloc]
569 initWithWebState:self.webState
570 delegate:self]);
571
572 [self initNativeAppNavigationController];
573 // IOSChromeSessionTabHelper comes first because it sets up the tab ID, and
574 // other helpers may rely on that.
575 IOSChromeSessionTabHelper::CreateForWebState(self.webState);
576 NetworkActivityIndicatorTabHelper::CreateForWebState(self.webState,
577 self.tabId);
578 IOSChromeSyncedTabDelegate::CreateForWebState(self.webState);
579 InfoBarManagerImpl::CreateForWebState(self.webState);
580 IOSSecurityStateTabHelper::CreateForWebState(self.webState);
581
582 tabInfoBarObserver_.reset(new TabInfoBarObserver(self));
583 tabInfoBarObserver_->SetShouldObserveInfoBarManager(true);
584
585 if (AccountConsistencyService* account_consistency_service =
586 ios::AccountConsistencyServiceFactory::GetForBrowserState(
587 browserState_)) {
588 account_consistency_service->SetWebStateHandler(self.webState, self);
589 }
590 ChromeIOSTranslateClient::CreateForWebState(self.webState);
591 if (experimental_flags::IsAutoReloadEnabled()) {
592 autoReloadBridge_.reset([[AutoReloadBridge alloc] initWithTab:self]);
593 }
594 printObserver_.reset(new PrintObserver(self.webState));
595
596 base::scoped_nsprotocol<id<PasswordsUiDelegate>> passwordsUiDelegate(
597 [[PasswordsUiDelegateImpl alloc] init]);
598 passwordController_.reset([[PasswordController alloc]
599 initWithWebState:self.webState
600 passwordsUiDelegate:passwordsUiDelegate]);
601 password_manager::PasswordGenerationManager* passwordGenerationManager =
602 [passwordController_ passwordGenerationManager];
603 autofillController_.reset([[AutofillController alloc]
604 initWithBrowserState:browserState_
605 passwordGenerationManager:passwordGenerationManager
606 webState:self.webState]);
607 suggestionController_.reset([[FormSuggestionController alloc]
608 initWithWebState:self.webState
609 providers:[self suggestionProviders]]);
610 inputAccessoryViewController_.reset(
611 [[FormInputAccessoryViewController alloc]
612 initWithWebState:self.webState
613 providers:[self accessoryViewProviders]]);
614 if (experimental_flags::IsCredentialManagementEnabled()) {
615 credentialManager_.reset(new CredentialManager(
616 self.webState, [passwordController_ passwordManagerClient],
617 [passwordController_ passwordManagerDriver],
618 base::mac::ObjCCastStrict<JSCredentialManager>(
619 [self.webState->GetJSInjectionReceiver()
620 instanceOfClass:[JSCredentialManager class]])));
621 }
622
623 ios::ChromeBrowserState* original_browser_state =
624 ios::ChromeBrowserState::FromBrowserState(
625 self.webState->GetBrowserState())
626 ->GetOriginalChromeBrowserState();
627 favicon::WebFaviconDriver::CreateForWebState(
628 self.webState,
629 ios::FaviconServiceFactory::GetForBrowserState(
630 original_browser_state, ServiceAccessType::IMPLICIT_ACCESS),
631 ios::HistoryServiceFactory::GetForBrowserState(
632 original_browser_state, ServiceAccessType::IMPLICIT_ACCESS),
633 ios::BookmarkModelFactory::GetForBrowserState(original_browser_state));
634 history::WebStateTopSitesObserver::CreateForWebState(
635 self.webState,
636 ios::TopSitesFactory::GetForBrowserState(original_browser_state).get());
637 [self setShouldObserveFaviconChanges:YES];
638 web::RequestTrackerImpl* requestTracker =
639 webStateImpl_->GetRequestTracker();
640
641 metricsClientManager_.reset([[MetricsNetworkClientManager alloc] init]);
642 AddNetworkClientFactoryOnIOThread(requestTracker, metricsClientManager_);
643
644 if (parentModel && parentModel.syncedWindowDelegate) {
645 IOSChromeSessionTabHelper::FromWebState(self.webState)
646 ->SetWindowID(parentModel.sessionID);
647 }
648
649 // Create the ReaderModeController immediately so it can register for
650 // WebState changes.
651 if (experimental_flags::IsReaderModeEnabled()) {
652 readerModeController_.reset([[ReaderModeController alloc]
653 initWithWebState:self.webState
654 delegate:self]);
655 }
656
657 // Allow the embedder to attach tab helpers.
658 ios::GetChromeBrowserProvider()->AttachTabHelpers(self.webState, self);
659
660 [[NSNotificationCenter defaultCenter]
661 addObserver:self
662 selector:@selector(applicationDidBecomeActive)
663 name:UIApplicationDidBecomeActiveNotification
664 object:nil];
665 }
666 return self;
667 }
668
669 - (instancetype)init {
670 NOTREACHED();
671 return nil;
672 }
673
674 - (NSArray*)accessoryViewProviders {
675 NSMutableArray* providers = [NSMutableArray array];
676 id<FormInputAccessoryViewProvider> provider =
677 [passwordController_ accessoryViewProvider];
678 if (provider)
679 [providers addObject:provider];
680 [providers addObject:[suggestionController_ accessoryViewProvider]];
681 return providers;
682 }
683
684 - (NSArray*)suggestionProviders {
685 NSMutableArray* providers = [NSMutableArray array];
686 [providers addObject:[passwordController_ suggestionProvider]];
687 [providers addObject:[autofillController_ suggestionProvider]];
688 return providers;
689 }
690
691 + (Tab*)newPreloadingTabWithBrowserState:(ios::ChromeBrowserState*)browserState
692 url:(const GURL&)URL
693 referrer:(const web::Referrer&)referrer
694 transition:(ui::PageTransition)transition
695 provider:(id<CRWNativeContentProvider>)provider
696 opener:(Tab*)opener
697 desktopUserAgent:(BOOL)desktopUserAgent
698 configuration:(void (^)(Tab*))configuration {
699 Tab* tab = [[[Tab alloc] initWithWindowName:nil
700 opener:opener
701 openedByDOM:NO
702 model:nil
703 browserState:browserState] autorelease];
704 if (desktopUserAgent)
705 [tab enableDesktopUserAgent];
706 [[tab webController] setNativeProvider:provider];
707 [[tab webController] setWebUsageEnabled:YES];
708
709 if (configuration)
710 configuration(tab);
711
712 web::NavigationManager::WebLoadParams params(URL);
713 params.transition_type = transition;
714 params.referrer = referrer;
715 [[tab webController] loadWithParams:params];
716
717 return tab;
718 }
719
720 - (void)dealloc {
721 DCHECK([NSThread isMainThread]);
722 // Note that -[CRWWebController close] has already been called, so nothing
723 // significant should be done with it in this method.
724 DCHECK_NE(self.webController.delegate, self);
725 [super dealloc];
726 }
727
728 - (void)setParentTabModel:(TabModel*)model {
729 DCHECK(!model || !parentTabModel_);
730 parentTabModel_ = model;
731
732 if (parentTabModel_.syncedWindowDelegate) {
733 IOSChromeSessionTabHelper::FromWebState(self.webState)
734 ->SetWindowID(model.sessionID);
735 }
736 }
737
738 - (NSString*)description {
739 return [NSString stringWithFormat:@"%p ... %@ - %s", self, self.title,
740 self.url.spec().c_str()];
741 }
742
743 - (CRWWebController*)webController {
744 return webStateImpl_ ? webStateImpl_->GetWebController() : nil;
745 }
746
747 - (id<TabDialogDelegate>)dialogDelegate {
748 return dialogDelegate_;
749 }
750
751 - (void)setDialogDelegate:(id<TabDialogDelegate>)dialogDelegate {
752 dialogDelegate_.reset(dialogDelegate);
753 }
754
755 - (void)setIsVoiceSearchResultsTab:(BOOL)isVoiceSearchResultsTab {
756 // There is intentionally no equality check in this setter, as we want the
757 // notificaiton to be sent regardless of whether the value has changed.
758 isVoiceSearchResultsTab_ = isVoiceSearchResultsTab;
759 [parentTabModel_ notifyTabChanged:self];
760 }
761
762 - (PasswordController*)passwordController {
763 return passwordController_.get();
764 }
765
766 - (void)retrieveSnapshot:(void (^)(UIImage*))callback {
767 [webControllerSnapshotHelper_
768 retrieveSnapshotForWebController:self.webController
769 sessionID:[self currentSessionID]
770 withOverlays:[self snapshotOverlays]
771 callback:callback];
772 }
773
774 - (const GURL&)url {
775 // See note in header; this method should be removed.
776 web::NavigationItem* item = [[self currentSessionEntry] navigationItem];
777 return item ? item->GetVirtualURL() : GURL::EmptyGURL();
778 }
779
780 - (NSString*)title {
781 base::string16 title = self.webStateImpl->GetTitle();
782 if (title.empty())
783 title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE);
784 return base::SysUTF16ToNSString(title);
785 }
786
787 - (NSString*)originalTitle {
788 // Do not use self.webState->GetTitle() as it returns the display title,
789 // not the original page title.
790 DCHECK([self navigationManager]);
791 web::NavigationItem* item = [self navigationManager]->GetLastCommittedItem();
792 if (!item)
793 return nil;
794 base::string16 pageTitle = item->GetTitle();
795 return pageTitle.empty() ? nil : base::SysUTF16ToNSString(pageTitle);
796 }
797
798 - (NSString*)urlDisplayString {
799 base::string16 urlText = url_formatter::FormatUrl(
800 self.url, url_formatter::kFormatUrlOmitNothing, net::UnescapeRule::SPACES,
801 nullptr, nullptr, nullptr);
802 return base::SysUTF16ToNSString(urlText);
803 }
804
805 - (NSString*)windowName {
806 DCHECK([self navigationManager]);
807 return [self navigationManager]->GetSessionController().windowName;
808 }
809
810 - (NSString*)tabId {
811 DCHECK([self navigationManager]);
812 return [[self navigationManager]->GetSessionController() tabId];
813 }
814
815 - (web::WebState*)webState {
816 return webStateImpl_.get();
817 }
818
819 - (web::WebStateImpl*)webStateImpl {
820 return webStateImpl_.get();
821 }
822
823 - (void)fetchFavicon {
824 const GURL& url = self.url;
825 if (!url.is_valid())
826 return;
827
828 favicon::FaviconDriver* faviconDriver =
829 favicon::WebFaviconDriver::FromWebState(self.webState);
830 if (faviconDriver) {
831 faviconDriver->FetchFavicon(url);
832 }
833 }
834
835 - (void)setFavicon:(const gfx::Image*)image {
836 web::NavigationItem* item = [self navigationManager]->GetVisibleItem();
837 if (!item)
838 return;
839 if (image) {
840 item->GetFavicon().image = *image;
841 item->GetFavicon().valid = true;
842 }
843 [parentTabModel_ notifyTabChanged:self];
844 }
845
846 - (UIImage*)favicon {
847 DCHECK([self navigationManager]);
848 web::NavigationItem* item = [self navigationManager]->GetVisibleItem();
849 if (!item)
850 return nil;
851 const gfx::Image& image = item->GetFavicon().image;
852 if (image.IsEmpty())
853 return nil;
854 return image.ToUIImage();
855 }
856
857 - (UIView*)view {
858 // Record reload of previously-evicted tab.
859 if (![self.webController isViewAlive] && [parentTabModel_ tabUsageRecorder]) {
860 [parentTabModel_ tabUsageRecorder]->RecordPageLoadStart(self);
861 }
862 return self.webState ? self.webState->GetView() : nil;
863 }
864
865 - (UIView*)viewForPrinting {
866 return self.webController.viewForPrinting;
867 }
868
869 - (NavigationManagerImpl*)navigationManager {
870 if (!self.webStateImpl)
871 return nil;
872 return &(self.webStateImpl->GetNavigationManagerImpl());
873 }
874
875 - (id<StoreKitLauncher>)storeKitLauncher {
876 return storeKitLauncher_.get();
877 }
878
879 - (void)setStoreKitLauncher:(id<StoreKitLauncher>)storeKitLauncher {
880 storeKitLauncher_.reset(storeKitLauncher);
881 }
882
883 // Swap out the existing session history with a new list of navigations. Forces
884 // the tab to reload to update the UI accordingly. This is ok because none of
885 // the session history is stored in the tab; it's always fetched through the
886 // navigation manager.
887 - (void)replaceHistoryWithNavigations:
888 (const std::vector<sessions::SerializedNavigationEntry>&)navigations
889 currentIndex:(NSInteger)currentIndex {
890 ScopedVector<web::NavigationItem> items =
891 sessions::IOSSerializedNavigationBuilder::ToNavigationItems(navigations);
892 [self navigationManager]->ReplaceSessionHistory(std::move(items),
893 currentIndex);
894 [self didReplaceSessionHistory];
895
896 [self.webController loadCurrentURL];
897 }
898
899 - (void)didReplaceSessionHistory {
900 // Replace fullScreenController_ with a new sessionID when the navigation
901 // manager changes.
902 // TODO(crbug.com/661666): Consider just updating sessionID and not replacing
903 // |fullScreenController_|.
904 if (fullScreenController_) {
905 [fullScreenController_ invalidate];
906 [self.webController removeObserver:fullScreenController_];
907 fullScreenController_.reset([[FullScreenController alloc]
908 initWithDelegate:fullScreenControllerDelegate_
909 navigationManager:&(self.webStateImpl->GetNavigationManagerImpl())
910 sessionID:[self currentSessionID]]);
911 [self.webController addObserver:fullScreenController_];
912 // If the content of the page was loaded without knowledge of the
913 // toolbar position it will be misplaced under the toolbar instead of
914 // right below. This happens e.g. in the case of preloading. This is to make
915 // sure the content is moved to the right place.
916 [fullScreenController_ moveContentBelowHeader];
917 }
918 }
919
920 - (void)setIsLinkLoadingPrerenderTab:(BOOL)isLinkLoadingPrerenderTab {
921 isLinkLoadingPrerenderTab_ = isLinkLoadingPrerenderTab;
922 [self setIsPrerenderTab:isLinkLoadingPrerenderTab];
923 }
924
925 - (void)setIsPrerenderTab:(BOOL)isPrerender {
926 if (isPrerenderTab_ == isPrerender)
927 return;
928
929 isPrerenderTab_ = isPrerender;
930
931 self.webController.shouldSuppressDialogs =
932 (isPrerender && !isLinkLoadingPrerenderTab_);
933
934 if (isPrerenderTab_)
935 return;
936
937 [fullScreenController_ moveContentBelowHeader];
938 [self commitCachedEntriesToHistoryDB];
939 [self saveTitleToHistoryDB];
940
941 // If the page has finished loading, take a snapshot. If the page is still
942 // loading, do nothing, as CRWWebController will automatically take a
943 // snapshot once the load completes.
944 BOOL loadingFinished = self.webController.loadPhase == web::PAGE_LOADED;
945 if (loadingFinished)
946 [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
947
948 [[OmniboxGeolocationController sharedInstance]
949 finishPageLoadForTab:self
950 loadSuccess:loadingFinished];
951 [self countMainFrameLoad];
952 }
953
954 - (id<FullScreenControllerDelegate>)fullScreenControllerDelegate {
955 return fullScreenControllerDelegate_.get();
956 }
957
958 - (void)setFullScreenControllerDelegate:
959 (id<FullScreenControllerDelegate>)fullScreenControllerDelegate {
960 if (fullScreenControllerDelegate == fullScreenControllerDelegate_) {
961 return;
962 }
963 // Lazily create a FullScreenController.
964 // The check for fullScreenControllerDelegate is necessary to avoid recreating
965 // a FullScreenController during teardown.
966 if (!fullScreenController_ && fullScreenControllerDelegate) {
967 NavigationManagerImpl* navigationManager =
968 &(self.webStateImpl->GetNavigationManagerImpl());
969 fullScreenController_.reset([[FullScreenController alloc]
970 initWithDelegate:fullScreenControllerDelegate
971 navigationManager:navigationManager
972 sessionID:[self currentSessionID]]);
973 if (fullScreenController_) {
974 [self.webController addObserver:fullScreenController_];
975 }
976 // If the content of the page was loaded without knowledge of the
977 // toolbar position it will be misplaced under the toolbar instead of
978 // right below. This happens e.g. in the case of preloading. This is to make
979 // sure the content is moved to the right place.
980 [fullScreenController_ moveContentBelowHeader];
981 }
982 fullScreenControllerDelegate_.reset(fullScreenControllerDelegate);
983 }
984
985 - (OverscrollActionsController*)overscrollActionsController {
986 return overscrollActionsController_.get();
987 }
988
989 - (id<OverscrollActionsControllerDelegate>)overscrollActionsControllerDelegate {
990 return overscrollActionsControllerDelegate_.get();
991 }
992
993 - (void)setOverscrollActionsControllerDelegate:
994 (id<OverscrollActionsControllerDelegate>)
995 overscrollActionsControllerDelegate {
996 if (overscrollActionsControllerDelegate_ ==
997 overscrollActionsControllerDelegate)
998 return;
999
1000 // Lazily create a OverscrollActionsController.
1001 // The check for overscrollActionsControllerDelegate is necessary to avoid
1002 // recreating a OverscrollActionsController during teardown.
1003 if (!overscrollActionsController_) {
1004 overscrollActionsController_.reset(
1005 [[OverscrollActionsController alloc] init]);
1006 [self.webController addObserver:overscrollActionsController_];
1007 }
1008 ios_internal::OverscrollStyle style =
1009 ios_internal::OverscrollStyle::REGULAR_PAGE_NON_INCOGNITO;
1010 if (browserState_->IsOffTheRecord()) {
1011 style = ios_internal::OverscrollStyle::REGULAR_PAGE_INCOGNITO;
1012 }
1013 [overscrollActionsController_ setStyle:style];
1014 [overscrollActionsController_
1015 setDelegate:overscrollActionsControllerDelegate];
1016 overscrollActionsControllerDelegate_.reset(
1017 overscrollActionsControllerDelegate);
1018 }
1019
1020 - (void)updateTitle:(NSString*)title {
1021 web::NavigationItem* item = [self navigationManager]->GetVisibleItem();
1022 if (!item)
1023 return;
1024 item->SetTitle(base::SysNSStringToUTF16(title));
1025 // TODO(crbug.com/546218): See if this can be removed; it's not clear that
1026 // other platforms send this (tab sync triggers need to be compared against
1027 // upstream).
1028 if (self.webStateImpl)
1029 self.webStateImpl->GetNavigationManagerImpl().OnNavigationItemChanged();
1030
1031 [self saveTitleToHistoryDB];
1032 }
1033
1034 - (void)saveTitleToHistoryDB {
1035 // If incognito, don't update history.
1036 if (browserState_->IsOffTheRecord())
1037 return;
1038 // Don't update the history if current entry has no title.
1039 NSString* title = [self title];
1040 if (![title length] ||
1041 [title isEqualToString:l10n_util::GetNSString(IDS_DEFAULT_TAB_TITLE)])
1042 return;
1043
1044 history::HistoryService* historyService =
1045 ios::HistoryServiceFactory::GetForBrowserState(
1046 browserState_, ServiceAccessType::IMPLICIT_ACCESS);
1047 DCHECK(historyService);
1048 historyService->SetPageTitle(self.url, base::SysNSStringToUTF16(title));
1049 }
1050
1051 - (void)addCurrentEntryToHistoryDB {
1052 DCHECK(self.currentSessionEntry);
1053 // If incognito, don't update history.
1054 if (browserState_->IsOffTheRecord())
1055 return;
1056
1057 CRWSessionEntry* sessionEntry = self.currentSessionEntry;
1058 web::NavigationItem* item = [self navigationManager]->GetVisibleItem();
1059
1060 // Do not update the history db for back/forward navigations.
1061 // TODO(crbug.com/661667): We do not currently tag the entry with a
1062 // FORWARD_BACK transition. Fix.
1063 if (item->GetTransitionType() & ui::PAGE_TRANSITION_FORWARD_BACK)
1064 return;
1065
1066 history::HistoryService* historyService =
1067 ios::HistoryServiceFactory::GetForBrowserState(
1068 browserState_, ServiceAccessType::IMPLICIT_ACCESS);
1069 DCHECK(historyService);
1070
1071 const GURL url(item->GetURL());
1072 const web::Referrer& referrer = item->GetReferrer();
1073
1074 // Do not update the history db for data: urls. This diverges from upstream,
1075 // but prevents us from dumping huge view-source urls into the history
1076 // database. Since view-source is only activated in Debug builds, this check
1077 // can be Debug-only as well.
1078 #ifndef NDEBUG
1079 if (url.scheme() == url::kDataScheme)
1080 return;
1081 #endif
1082
1083 history::RedirectList redirects;
1084 if (item->GetURL() != sessionEntry.originalUrl) {
1085 // Simulate a valid redirect chain in case of URL that have been modified
1086 // in |CRWWebController finishHistoryNavigationFromEntry:|.
1087 const std::string& urlSpec = item->GetURL().spec();
1088 size_t urlSpecLength = urlSpec.size();
1089 if (item->GetTransitionType() & ui::PAGE_TRANSITION_CLIENT_REDIRECT ||
1090 (urlSpecLength && (urlSpec.at(urlSpecLength - 1) == '#') &&
1091 !urlSpec.compare(0, urlSpecLength - 1,
1092 sessionEntry.originalUrl.spec()))) {
1093 redirects.push_back(referrer.url);
1094 }
1095 // TODO(crbug.com/661670): the redirect chain is not constructed the same
1096 // way as upstream so this part needs to be revised.
1097 redirects.push_back(sessionEntry.originalUrl);
1098 redirects.push_back(url);
1099 }
1100
1101 DCHECK(item->GetTimestamp().ToInternalValue() > 0);
1102 if ([self isPrerenderTab]) {
1103 // Clicks on content suggestions on the NTP should not contribute to the
1104 // Most Visited tiles in the NTP.
1105 const bool consider_for_ntp_most_visited =
1106 referrer.url != GURL(kChromeContentSuggestionsReferrer);
1107
1108 history::HistoryAddPageArgs args(
1109 url, item->GetTimestamp(), tabHistoryContext_.get(),
1110 item->GetUniqueID(), referrer.url, redirects, item->GetTransitionType(),
1111 history::SOURCE_BROWSED, false, consider_for_ntp_most_visited);
1112 addPageVector_.push_back(args);
1113 } else {
1114 historyService->AddPage(url, item->GetTimestamp(), tabHistoryContext_.get(),
1115 item->GetUniqueID(), referrer.url, redirects,
1116 item->GetTransitionType(), history::SOURCE_BROWSED,
1117 false);
1118 [self saveTitleToHistoryDB];
1119 }
1120 }
1121
1122 - (void)commitCachedEntriesToHistoryDB {
1123 // If OTR, don't update history.
1124 if (browserState_->IsOffTheRecord()) {
1125 DCHECK_EQ(0U, addPageVector_.size());
1126 return;
1127 }
1128
1129 history::HistoryService* historyService =
1130 ios::HistoryServiceFactory::GetForBrowserState(
1131 browserState_, ServiceAccessType::IMPLICIT_ACCESS);
1132 DCHECK(historyService);
1133
1134 for (size_t i = 0; i < addPageVector_.size(); ++i)
1135 historyService->AddPage(addPageVector_[i]);
1136 addPageVector_.clear();
1137 }
1138
1139 - (void)webWillInitiateLoadWithParams:
1140 (web::NavigationManager::WebLoadParams&)params {
1141 GURL navUrl = params.url;
1142
1143 // After a crash the NTP is loaded by default.
1144 if (navUrl.host() != kChromeUINewTabHost) {
1145 static BOOL hasLoadedPage = NO;
1146 if (!hasLoadedPage) {
1147 // As soon as an URL is loaded, a crash shouldn't be counted as a startup
1148 // crash. Since loading an url requires user action and is a significant
1149 // source of crashes that could lead to false positives in crash loop
1150 // detection.
1151 crash_util::ResetFailedStartupAttemptCount();
1152 hasLoadedPage = YES;
1153 }
1154 }
1155 }
1156
1157 - (void)webDidUpdateSessionForLoadWithParams:
1158 (const web::NavigationManager::WebLoadParams&)params
1159 wasInitialNavigation:(BOOL)initialNavigation {
1160 GURL navUrl = params.url;
1161 ui::PageTransition transition = params.transition_type;
1162
1163 // Record any explicit, non-redirect navigation as a clobber (as long as it's
1164 // in a real tab).
1165 if (!initialNavigation && !isPrerenderTab_ &&
1166 !PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD) &&
1167 (transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) == 0) {
1168 base::RecordAction(UserMetricsAction("MobileTabClobbered"));
1169 }
1170 if ([parentTabModel_ tabUsageRecorder])
1171 [parentTabModel_ tabUsageRecorder]->RecordPageLoadStart(self);
1172
1173 // Reset |isVoiceSearchResultsTab| since a new page is being navigated to.
1174 self.isVoiceSearchResultsTab = NO;
1175
1176 [[OmniboxGeolocationController sharedInstance]
1177 addLocationToNavigationItem:self.currentSessionEntry.navigationItem
1178 browserState:browserState_];
1179 }
1180
1181 - (void)loadSessionTab:(const sessions::SessionTab*)sessionTab {
1182 DCHECK(sessionTab);
1183 [self replaceHistoryWithNavigations:sessionTab->navigations
1184 currentIndex:sessionTab->current_navigation_index];
1185 }
1186
1187 - (void)openJavascript:(NSString*)javaScript {
1188 DCHECK(javaScript);
1189 javaScript = [javaScript stringByRemovingPercentEncoding];
1190 if (webStateImpl_)
1191 webStateImpl_->ExecuteJavaScript(base::SysNSStringToUTF16(javaScript));
1192 }
1193
1194 - (void)reload {
1195 // TODO(crbug.com/661671): Convert callers to go through CRWWebController
1196 // directly and remove this passthrough method.
1197 [self.webController reload];
1198 }
1199
1200 - (void)webWillReload {
1201 if ([parentTabModel_ tabUsageRecorder]) {
1202 [parentTabModel_ tabUsageRecorder]->RecordReload(self);
1203 }
1204 }
1205
1206 // Stop the page loading.
1207 // Equivalent to the user pressing 'stop', or a window.stop() command.
1208 - (void)stopLoading {
1209 [self.webController stopLoading];
1210 }
1211
1212 // Halt the tab, which amounts to halting its webController.
1213 - (void)terminateNetworkActivity {
1214 [self.webController terminateNetworkActivity];
1215 }
1216
1217 // This can't be done in dealloc in case someone holds an extra strong
1218 // reference to the Tab, which would cause the close sequence to fire at a
1219 // random time.
1220 - (void)close {
1221 self.fullScreenControllerDelegate = nil;
1222 self.overscrollActionsControllerDelegate = nil;
1223 self.passKitDialogProvider = nil;
1224 self.snapshotOverlayProvider = nil;
1225 self.storeKitLauncher = nil;
1226
1227 [[NSNotificationCenter defaultCenter] removeObserver:self];
1228
1229 [findInPageController_ detachFromWebState];
1230 findInPageController_.reset();
1231
1232 [passwordController_ detach];
1233 passwordController_.reset();
1234 tabInfoBarObserver_.reset();
1235
1236 faviconDriverObserverBridge_.reset();
1237 [openInController_ detachFromWebController];
1238 openInController_.reset();
1239 [autofillController_ detachFromWebState];
1240 [suggestionController_ detachFromWebState];
1241 if (fullScreenController_)
1242 [self.webController removeObserver:fullScreenController_];
1243 [fullScreenController_ invalidate];
1244 fullScreenController_.reset();
1245 if (overscrollActionsController_)
1246 [self.webController removeObserver:overscrollActionsController_];
1247 [overscrollActionsController_ invalidate];
1248 overscrollActionsController_.reset();
1249 [readerModeController_ detachFromWebState];
1250 readerModeController_.reset();
1251
1252 [self clearResubmitDataActionSheet];
1253
1254 // Invalidate any snapshot stored for this session.
1255 NSString* sessionID = [self currentSessionID];
1256 DCHECK(sessionID);
1257 [snapshotManager_ removeImageWithSessionID:sessionID];
1258 // Reset association with the webController.
1259 [self.webController setDelegate:nil];
1260 [self.webController setUIDelegate:nil];
1261
1262 webStateImpl_->ClearTransientContentView();
1263 // Terminate the network activity before notifying the parent model, because
1264 // the parent model may initiate the request context destruction.
1265 [self terminateNetworkActivity];
1266
1267 // Cancel any queued dialogs.
1268 [self.dialogDelegate cancelDialogForTab:self];
1269
1270 // These steps must be done last, and must be done in this order; nothing
1271 // involving the tab should be done after didCloseTab:, and the
1272 // CRWWebController backing the tab should outlive anything done during tab
1273 // closure (since -[CRWWebController close] is what begins tearing down the
1274 // web/ layer, and tab closure may trigger operations that need to query the
1275 // web/ layer). The scoped strong ref is because didCloseTab: is often the
1276 // trigger for deallocating the tab, but that can in turn cause
1277 // CRWWebController to be deallocated before its close is called. The facade
1278 // delegate should be torn down after |-didCloseTab:| so components triggered
1279 // by tab closure can use the content facade, and it should be deleted before
1280 // the web controller since the web controller owns the facade's backing
1281 // objects.
1282 // TODO(crbug.com/546222): Fix the need for this; TabModel should be
1283 // responsible for making the lifetime of Tab sane, rather than allowing Tab
1284 // to drive its own destruction.
1285 base::scoped_nsobject<Tab> kungFuDeathGrip([self retain]);
1286 [parentTabModel_ didCloseTab:self]; // Inform parent of tab closure.
1287 webStateImpl_.reset();
1288 }
1289
1290 - (void)dismissModals {
1291 [openInController_ disable];
1292 [self.webController dismissModals];
1293 }
1294
1295 - (void)updateDesktopUserAgentForEntry:(CRWSessionEntry*)entry
1296 fromEntry:(CRWSessionEntry*)fromEntry {
1297 web::NavigationItemImpl* item = entry.navigationItemImpl;
1298 web::NavigationItemImpl* fromItem = fromEntry.navigationItemImpl;
1299 if (!item || !fromItem)
1300 return;
1301 bool useDesktopUserAgent = item->IsOverridingUserAgent();
1302 if (useDesktopUserAgent != fromItem->IsOverridingUserAgent()) {
1303 [self.webController requirePageReconstruction];
1304 }
1305 }
1306
1307 - (CRWSessionEntry*)currentSessionEntry {
1308 if (![self navigationManager])
1309 return nil;
1310 return [[self navigationManager]->GetSessionController() currentEntry];
1311 }
1312
1313 - (void)setShouldObserveInfoBarManager:(BOOL)shouldObserveInfoBarManager {
1314 tabInfoBarObserver_->SetShouldObserveInfoBarManager(
1315 shouldObserveInfoBarManager);
1316 }
1317
1318 - (void)setShouldObserveFaviconChanges:(BOOL)shouldObserveFaviconChanges {
1319 if (shouldObserveFaviconChanges) {
1320 favicon::FaviconDriver* faviconDriver =
1321 favicon::WebFaviconDriver::FromWebState(self.webState);
1322 // Some MockWebContents used in tests do not support the FaviconDriver.
1323 if (faviconDriver) {
1324 faviconDriverObserverBridge_.reset(
1325 new FaviconDriverObserverBridge(self, faviconDriver));
1326 }
1327 } else {
1328 faviconDriverObserverBridge_.reset();
1329 }
1330 }
1331
1332 - (void)goBack {
1333 if (self.navigationManager) {
1334 DCHECK(self.navigationManager->CanGoBack());
1335 base::RecordAction(UserMetricsAction("Back"));
1336 self.navigationManager->GoBack();
1337 }
1338 }
1339
1340 - (void)goForward {
1341 if (self.navigationManager) {
1342 DCHECK(self.navigationManager->CanGoForward());
1343 base::RecordAction(UserMetricsAction("Forward"));
1344 self.navigationManager->GoForward();
1345 }
1346 }
1347
1348 - (BOOL)canGoBack {
1349 return self.navigationManager && self.navigationManager->CanGoBack();
1350 }
1351
1352 - (BOOL)canGoForward {
1353 return self.navigationManager && self.navigationManager->CanGoForward();
1354 }
1355
1356 - (void)goToEntry:(CRWSessionEntry*)entry {
1357 DCHECK(entry);
1358
1359 if (self.navigationManager) {
1360 CRWSessionController* sessionController =
1361 self.navigationManager->GetSessionController();
1362 DCHECK([sessionController.entries containsObject:entry]);
1363 NSUInteger index = [sessionController.entries indexOfObject:entry];
1364 self.navigationManager->GoToIndex(index);
1365 }
1366 }
1367
1368 - (void)openURLWithParams:(const web::WebState::OpenURLParams&)params {
1369 switch (params.disposition) {
1370 case WindowOpenDisposition::NEW_FOREGROUND_TAB:
1371 case WindowOpenDisposition::NEW_BACKGROUND_TAB:
1372 [parentTabModel_
1373 insertOrUpdateTabWithURL:params.url
1374 referrer:params.referrer
1375 transition:params.transition
1376 windowName:nil
1377 opener:self
1378 openedByDOM:NO
1379 atIndex:TabModelConstants::kTabPositionAutomatically
1380 inBackground:(params.disposition ==
1381 WindowOpenDisposition::NEW_BACKGROUND_TAB)];
1382 break;
1383 case WindowOpenDisposition::CURRENT_TAB: {
1384 web::NavigationManager::WebLoadParams loadParams(params.url);
1385 loadParams.referrer = params.referrer;
1386 loadParams.transition_type = params.transition;
1387 loadParams.is_renderer_initiated = params.is_renderer_initiated;
1388 self.navigationManager->LoadURLWithParams(loadParams);
1389 } break;
1390 default:
1391 NOTIMPLEMENTED();
1392 break;
1393 };
1394 }
1395
1396 - (BOOL)openExternalURL:(const GURL&)url linkClicked:(BOOL)linkClicked {
1397 if (!externalAppLauncher_.get())
1398 externalAppLauncher_.reset([[ExternalAppLauncher alloc] init]);
1399
1400 // This method may release CRWWebController which may cause a crash
1401 // (crbug.com/393949).
1402 [[self.webController retain] autorelease];
1403
1404 // Make a local url copy for possible modification.
1405 GURL finalURL = url;
1406
1407 // Check if it's a direct FIDO U2F x-callback call. If so, do not open it, to
1408 // prevent pages from spoofing requests with different origins.
1409 if (finalURL.SchemeIs("u2f-x-callback")) {
1410 return NO;
1411 }
1412
1413 // Check if it's a FIDO U2F call.
1414 if (finalURL.SchemeIs("u2f")) {
1415 // Create U2FController object lazily.
1416 if (!U2FController_) {
1417 U2FController_.reset([[U2FController alloc] init]);
1418 }
1419
1420 DCHECK([self navigationManager]);
1421 GURL origin =
1422 [self navigationManager]->GetLastCommittedItem()->GetURL().GetOrigin();
1423
1424 // Compose u2f-x-callback URL and update urlToOpen.
1425 finalURL = [U2FController_ XCallbackFromRequestURL:finalURL
1426 originURL:origin
1427 tabURL:self.url
1428 tabID:self.tabId];
1429
1430 if (!finalURL.is_valid()) {
1431 return NO;
1432 }
1433 }
1434
1435 if ([externalAppLauncher_ openURL:finalURL linkClicked:linkClicked]) {
1436 // Clears pending navigation history after successfully launching the
1437 // external app.
1438 DCHECK([self navigationManager]);
1439 [[self navigationManager]->GetSessionController()
1440 discardNonCommittedEntries];
1441 // Ensure the UI reflects the current entry, not the just-discarded pending
1442 // entry.
1443 [parentTabModel_ notifyTabChanged:self];
1444 return YES;
1445 }
1446 return NO;
1447 }
1448
1449 - (void)webWillFinishHistoryNavigationFromEntry:(CRWSessionEntry*)fromEntry {
1450 DCHECK(fromEntry);
1451 [self updateDesktopUserAgentForEntry:self.currentSessionEntry
1452 fromEntry:fromEntry];
1453 [parentTabModel_ notifyTabChanged:self];
1454 }
1455
1456 - (void)webDidUpdateHistoryStateWithPageURL:(const GURL&)pageUrl {
1457 favicon::FaviconDriver* faviconDriver =
1458 favicon::WebFaviconDriver::FromWebState(self.webState);
1459 if (faviconDriver) {
1460 // Fetch the favicon for the new URL.
1461 faviconDriver->FetchFavicon(pageUrl);
1462 }
1463 [parentTabModel_ notifyTabChanged:self];
1464 }
1465
1466 // Records the state (scroll position, form values, whatever can be
1467 // harvested) from the current page into the current session entry.
1468 - (void)recordStateInHistory {
1469 // Link-loading prerender tab may not have correct zoom value during the load.
1470 if (!self.isLinkLoadingPrerenderTab)
1471 [self.webController recordStateInHistory];
1472 }
1473
1474 // Records metric for the interface's orientation.
1475 - (void)recordInterfaceOrientation {
1476 switch ([[UIApplication sharedApplication] statusBarOrientation]) {
1477 case UIInterfaceOrientationPortrait:
1478 case UIInterfaceOrientationPortraitUpsideDown:
1479 UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", YES);
1480 break;
1481 case UIInterfaceOrientationLandscapeLeft:
1482 case UIInterfaceOrientationLandscapeRight:
1483 UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", NO);
1484 break;
1485 case UIInterfaceOrientationUnknown:
1486 // TODO(crbug.com/228832): Convert from a boolean histogram to an
1487 // enumerated histogram and log this case as well.
1488 break;
1489 }
1490 }
1491
1492 - (OpenInController*)openInController {
1493 if (!openInController_) {
1494 openInController_.reset([[OpenInController alloc]
1495 initWithRequestContext:browserState_->GetRequestContext()
1496 webController:self.webController]);
1497 }
1498 return openInController_.get();
1499 }
1500
1501 - (void)closeThisTab {
1502 if (!parentTabModel_)
1503 return;
1504
1505 NSUInteger index = [parentTabModel_ indexOfTab:self];
1506 if (index != NSNotFound)
1507 [parentTabModel_ closeTabAtIndex:index];
1508 }
1509
1510 - (id<CRWNativeContent>)controllerForUnhandledContentAtURL:(const GURL&)url {
1511 DownloadManagerController* downloadController =
1512 [[[DownloadManagerController alloc]
1513 initWithURL:url
1514 requestContextGetter:browserState_->GetRequestContext()
1515 storeKitLauncher:self.storeKitLauncher] autorelease];
1516 [downloadController start];
1517 return downloadController;
1518 }
1519
1520 - (void)handleExportableFile:(net::HttpResponseHeaders*)headers {
1521 // Only "application/pdf" is supported for now.
1522 if (self.webState->GetContentsMimeType() != "application/pdf")
1523 return;
1524
1525 [[NSNotificationCenter defaultCenter]
1526 postNotificationName:kTabIsShowingExportableNotificationForCrashReporting
1527 object:self];
1528 // Try to generate a filename by first looking at |content_disposition_|, then
1529 // at the last component of |self.url| and if both of these fail use the
1530 // default filename "document".
1531 std::string contentDisposition;
1532 if (headers)
1533 headers->GetNormalizedHeader("content-disposition", &contentDisposition);
1534 std::string defaultFilename =
1535 l10n_util::GetStringUTF8(IDS_IOS_OPEN_IN_FILE_DEFAULT_TITLE);
1536 base::string16 filename =
1537 net::GetSuggestedFilename(self.url, contentDisposition,
1538 "", // referrer-charset
1539 "", // suggested-name
1540 "application/pdf", // mime-type
1541 defaultFilename);
1542 [[self openInController]
1543 enableWithDocumentURL:self.url
1544 suggestedFilename:base::SysUTF16ToNSString(filename)];
1545 }
1546
1547 - (void)countMainFrameLoad {
1548 if ([self isPrerenderTab] || [self url].SchemeIs(kChromeUIScheme)) {
1549 return;
1550 }
1551 base::RecordAction(UserMetricsAction("MobilePageLoaded"));
1552 }
1553
1554 - (void)applicationDidBecomeActive {
1555 if (requireReloadAfterBecomingActive_) {
1556 if (visible_) {
1557 [self.webController reload];
1558 } else {
1559 [self.webController requirePageReload];
1560 }
1561 requireReloadAfterBecomingActive_ = NO;
1562 }
1563 }
1564
1565 #pragma mark -
1566 #pragma mark FindInPageControllerDelegate
1567
1568 - (void)willAdjustScrollPosition {
1569 // Skip the next attempt to correct the scroll offset for the toolbar height.
1570 // Used when programatically scrolling down the y offset.
1571 [fullScreenController_ shouldSkipNextScrollOffsetForHeader];
1572 }
1573
1574 #pragma mark -
1575 #pragma mark FullScreen
1576
1577 - (void)updateFullscreenWithToolbarVisible:(BOOL)visible {
1578 [fullScreenController_ moveHeaderToRestingPosition:visible];
1579 }
1580
1581 #pragma mark -
1582 #pragma mark Reader mode
1583
1584 - (UIView*)superviewForReaderModePanel {
1585 return self.view;
1586 }
1587
1588 - (ReaderModeController*)readerModeController {
1589 return readerModeController_.get();
1590 }
1591
1592 - (BOOL)canSwitchToReaderMode {
1593 // Only if the page is loaded and the page passes suitability checks.
1594 ReaderModeController* controller = self.readerModeController;
1595 return controller && controller.checker->CanSwitchToReaderMode();
1596 }
1597
1598 - (void)switchToReaderMode {
1599 DCHECK(self.view);
1600 [self.readerModeController switchToReaderMode];
1601 }
1602
1603 - (void)loadReaderModeHTML:(NSString*)html forURL:(const GURL&)url {
1604 // Before changing the HTML on the current page, this checks that the URL has
1605 // not changed since reader mode was requested. This could happen for example
1606 // if the page does a late redirect itself or if the user tapped on a link and
1607 // triggered reader mode before the page load is detected by webState.
1608 if (url == self.url)
1609 [self.webController loadHTMLForCurrentURL:html];
1610
1611 [self.readerModeController exitReaderMode];
1612 }
1613
1614 #pragma mark -
1615
1616 - (void)openAppStore:(NSString*)appId {
1617 [storeKitLauncher_ openAppStore:appId];
1618 }
1619
1620 - (std::vector<GURL>)currentRedirectedUrls {
1621 DCHECK([self navigationManager]);
1622 return
1623 [[self navigationManager]->GetSessionController() currentRedirectedUrls];
1624 }
1625
1626 - (BOOL)useDesktopUserAgent {
1627 web::NavigationItem* currentItem = self.currentSessionEntry.navigationItem;
1628 return currentItem && currentItem->IsOverridingUserAgent();
1629 }
1630
1631 - (void)enableDesktopUserAgent {
1632 DCHECK_EQ(self.useDesktopUserAgent, NO);
1633 DCHECK([self navigationManager]);
1634 [[self navigationManager]->GetSessionController()
1635 useDesktopUserAgentForNextPendingEntry];
1636 }
1637
1638 - (void)reloadForDesktopUserAgent {
1639 // |loadWithParams| will recreate the removed UIWebView.
1640 [self.webController requirePageReconstruction];
1641
1642 // TODO(crbug.com/228171): A hack in session_controller -addPendingEntry
1643 // discusses making tab responsible for distinguishing history stack
1644 // navigation from new navigations. Because we want a new navigation here, we
1645 // use |PAGE_TRANSITION_FORM_SUBMIT|. When session_controller changes, so
1646 // should this.
1647 ui::PageTransition transition =
1648 ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORM_SUBMIT);
1649 DCHECK([self navigationManager]);
1650 CRWSessionController* sessionController =
1651 [self navigationManager]->GetSessionController();
1652 CRWSessionEntry* lastUserEntry = [sessionController lastUserEntry];
1653 if (!lastUserEntry)
1654 return;
1655
1656 // |originalUrl| will be empty if a page was open by DOM.
1657 GURL reloadURL(lastUserEntry.originalUrl);
1658 if (reloadURL.is_empty()) {
1659 DCHECK(sessionController.openedByDOM);
1660 reloadURL = [lastUserEntry navigationItem]->GetVirtualURL();
1661 }
1662
1663 web::NavigationManager::WebLoadParams params(reloadURL);
1664 params.referrer = lastUserEntry.navigationItem->GetReferrer();
1665 params.transition_type = transition;
1666 if (self.navigationManager)
1667 self.navigationManager->LoadURLWithParams(params);
1668 }
1669
1670 - (id<SnapshotOverlayProvider>)snapshotOverlayProvider {
1671 return snapshotOverlayProvider_.get();
1672 }
1673
1674 - (void)setSnapshotOverlayProvider:
1675 (id<SnapshotOverlayProvider>)snapshotOverlayProvider {
1676 snapshotOverlayProvider_.reset(snapshotOverlayProvider);
1677 }
1678
1679 - (BlockedPopupHandler*)popupHandler {
1680 if (!popupHandler_.get()) {
1681 popupHandler_.reset(new BlockedPopupHandler(self.browserState));
1682 popupHandler_->SetDelegate(self);
1683 }
1684 return popupHandler_.get();
1685 }
1686
1687 - (void)evaluateU2FResultFromURL:(const GURL&)URL {
1688 DCHECK(U2FController_);
1689 [U2FController_ evaluateU2FResultFromU2FURL:URL webState:self.webState];
1690 }
1691
1692 - (NSString*)currentSessionID {
1693 return [self tabId];
1694 }
1695
1696 #pragma mark - WebDelegate protocol methods.
1697
1698 - (CRWWebController*)webPageOrderedOpen:(const GURL&)URL
1699 referrer:(const web::Referrer&)referrer
1700 windowName:(NSString*)windowName
1701 inBackground:(BOOL)inBackground {
1702 // Prerendered Tabs cannot open child windows.
1703 // TODO(crbug.com/661673): Should we kill prerendering in this case?
1704 if (!parentTabModel_)
1705 return nil;
1706 if (!inBackground)
1707 [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
1708 // Open a new tab or update an existing one. Tabs opened from a web page are
1709 Tab* tab = [parentTabModel_
1710 insertOrUpdateTabWithURL:URL
1711 referrer:referrer
1712 transition:ui::PAGE_TRANSITION_LINK
1713 windowName:windowName
1714 opener:self
1715 openedByDOM:YES
1716 atIndex:TabModelConstants::kTabPositionAutomatically
1717 inBackground:inBackground];
1718 return tab.webController;
1719 }
1720
1721 // This can be combined with the other versions once Tab loading is separated
1722 // from creation.
1723 - (CRWWebController*)webPageOrderedOpen {
1724 [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
1725
1726 Tab* tab = [parentTabModel_
1727 insertBlankTabWithTransition:ui::PAGE_TRANSITION_LINK
1728 opener:self
1729 openedByDOM:YES
1730 atIndex:TabModelConstants::kTabPositionAutomatically
1731 inBackground:NO];
1732 return tab.webController;
1733 }
1734
1735 - (void)webController:(CRWWebController*)webController
1736 onFormResubmissionForRequest:(NSURLRequest*)request
1737 continueBlock:(ProceduralBlock)continueBlock
1738 cancelBlock:(ProceduralBlock)cancelBlock {
1739 // Clear the resubmit data action sheet before loading a new request.
1740 [self clearResubmitDataActionSheet];
1741 resubmitDataController_.reset([[ResubmitDataController alloc]
1742 initWithContinueBlock:continueBlock
1743 cancelBlock:cancelBlock]);
1744 [self showResubmitDataActionSheet];
1745 }
1746
1747 - (void)showResubmitDataActionSheet {
1748 // Return early if the CRWWebController has been closed or web
1749 // usage is disabled on it.
1750 if (![self.webController webUsageEnabled])
1751 return;
1752 // Check to see if an action sheet can be shown.
1753 if (self.webState && [self.webState->GetView() window]) {
1754 // Display the action sheet with the arrow pointing at the top center of the
1755 // web contents.
1756 CGFloat xOrigin = CGRectGetMidX(self.webState->GetView().frame);
1757 CGFloat yOrigin = CGRectGetMinY(self.webState->GetView().frame) +
1758 [[self fullScreenControllerDelegate] headerHeight];
1759 [resubmitDataController_
1760 presentActionSheetFromRect:CGRectMake(xOrigin, yOrigin, 1, 1)
1761 inView:self.webState->GetView()];
1762 showResubmitDataActionSheetAttempt_ = 0;
1763 return;
1764 }
1765
1766 // The resubmit data action cannot be presented as the |contentView_| was not
1767 // yet added to the window. Retry after |kDelayBetweenAttemptsNanoSecs|.
1768 // TODO(crbug.com/227868): The strategy to poll until the resubmit data action
1769 // sheet can be presented is a temporary workaround. This needs to be
1770 // refactored to match the Chromium implementation:
1771 // * web_controller should notify/ the BVC once an action sheet should be
1772 // shown.
1773 // * BVC should present the action sheet and then trigger the reload
1774 const NSUInteger kMaximumNumberAttempts = 10;
1775 // 400 milliseconds
1776 const int64_t kDelayBetweenAttemptsNanoSecs = 0.4 * NSEC_PER_SEC;
1777 if (showResubmitDataActionSheetAttempt_ >= kMaximumNumberAttempts) {
1778 NOTREACHED();
1779 [self clearResubmitDataActionSheet];
1780 return;
1781 }
1782 base::WeakNSObject<Tab> weakTab(self);
1783 dispatch_after(
1784 dispatch_time(DISPATCH_TIME_NOW, kDelayBetweenAttemptsNanoSecs),
1785 dispatch_get_main_queue(), ^{
1786 [weakTab showResubmitDataActionSheet];
1787 });
1788 showResubmitDataActionSheetAttempt_++;
1789 }
1790
1791 - (void)clearResubmitDataActionSheet {
1792 [resubmitDataController_ dismissActionSheet];
1793 resubmitDataController_.reset();
1794 showResubmitDataActionSheetAttempt_ = 0;
1795 }
1796
1797 // The web page wants to close its own window.
1798 - (void)webPageOrderedClose {
1799 // Only allow a web page to close itself if it was opened by DOM, or if there
1800 // are no navigation items.
1801 DCHECK([[self navigationManager]->GetSessionController() isOpenedByDOM] ||
1802 ![self navigationManager]->GetItemCount());
1803 [self closeThisTab];
1804 }
1805
1806 // This method is invoked whenever the system believes the URL is about to
1807 // change, or immediately after any unexpected change of the URL. The apparent
1808 // destination URL is included in the |url| parameter.
1809 // Warning: because of the present design it is possible for malicious websites
1810 // to invoke superflous instances of this delegate with artibrary URLs.
1811 // Ensure there is nothing here that could be a risk to the user beyond mild
1812 // confusion in that event (e.g. progress bar starting unexpectedly).
1813 - (void)webWillAddPendingURL:(const GURL&)url
1814 transition:(ui::PageTransition)transition {
1815 DCHECK(self.webController.loadPhase == web::LOAD_REQUESTED);
1816 DCHECK([self navigationManager]);
1817 [self clearResubmitDataActionSheet];
1818
1819 // Move the toolbar to visible during page load.
1820 [fullScreenController_ disableFullScreen];
1821
1822 isUserNavigationEvent_ =
1823 (transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) == 0;
1824 // Check for link-follow clobbers. These are changes where there is no
1825 // pending entry (since that means the change wasn't caused by this class),
1826 // and where the URL changes (to avoid counting page resurrection).
1827 // TODO(crbug.com/546401): Consider moving this into NavigationManager, or
1828 // into a NavigationManager observer callback, so it doesn't need to be
1829 // checked in several places.
1830 if (isUserNavigationEvent_ && !isPrerenderTab_ &&
1831 ![self navigationManager]->GetPendingItem() && url != self.url) {
1832 base::RecordAction(UserMetricsAction("MobileTabClobbered"));
1833 if ([parentTabModel_ tabUsageRecorder])
1834 [parentTabModel_ tabUsageRecorder]->RecordPageLoadStart(self);
1835 }
1836 if (![self navigationManager]->GetPendingItem()) {
1837 // Reset |isVoiceSearchResultsTab| since a new page is being navigated to.
1838 self.isVoiceSearchResultsTab = NO;
1839 }
1840 }
1841
1842 - (void)webDidAddPendingURL {
1843 [parentTabModel_ notifyTabChanged:self];
1844 [openInController_ disable];
1845 [[NSNotificationCenter defaultCenter]
1846 postNotificationName:
1847 kTabClosingCurrentDocumentNotificationForCrashReporting
1848 object:self];
1849 [metricsClientManager_ pageLoadStarted:self.url];
1850 }
1851
1852 - (void)webCancelStartLoadingRequest {
1853 DCHECK(self.webController.loadPhase == web::PAGE_LOADED);
1854 [parentTabModel_ notifyTabChanged:self];
1855 }
1856
1857 // Called when the page URL has changed.
1858 - (void)webDidStartLoadingURL:(const GURL&)currentUrl
1859 shouldUpdateHistory:(BOOL)updateHistory {
1860 DCHECK(self.webController.loadPhase == web::PAGE_LOADING);
1861 DCHECK([self navigationManager]);
1862 // |webWillStartLoading:| is not called for native page loads.
1863 // TODO(crbug.com/381201): Move this call there once that bug is fixed so that
1864 // |disableFullScreen| is called only from one place.
1865 [fullScreenController_ disableFullScreen];
1866 [findInPageController_ disableFindInPageWithCompletionHandler:nil];
1867 [autoReloadBridge_ loadStartedForURL:currentUrl];
1868
1869 if (isUserNavigationEvent_) {
1870 [[NSNotificationCenter defaultCenter]
1871 postNotificationName:kTabModelUserNavigatedNotification
1872 object:self];
1873 }
1874 if (parentTabModel_) {
1875 [[NSNotificationCenter defaultCenter]
1876 postNotificationName:kTabModelTabWillStartLoadingNotification
1877 object:parentTabModel_
1878 userInfo:[NSDictionary
1879 dictionaryWithObject:self
1880 forKey:kTabModelTabKey]];
1881 }
1882 favicon::FaviconDriver* faviconDriver =
1883 favicon::WebFaviconDriver::FromWebState(self.webState);
1884 if (faviconDriver) {
1885 faviconDriver->FetchFavicon(currentUrl);
1886 }
1887 [parentTabModel_ notifyTabChanged:self];
1888 if (parentTabModel_) {
1889 [[NSNotificationCenter defaultCenter]
1890 postNotificationName:kTabModelTabDidStartLoadingNotification
1891 object:parentTabModel_
1892 userInfo:[NSDictionary
1893 dictionaryWithObject:self
1894 forKey:kTabModelTabKey]];
1895 }
1896
1897 [parentTabModel_ navigationCommittedInTab:self];
1898 if (updateHistory) {
1899 [self addCurrentEntryToHistoryDB];
1900 [self countMainFrameLoad];
1901 }
1902
1903 // Sending a notification about the url change for crash reporting.
1904 // TODO(crbug.com/661675): Consider using the navigation entry committed
1905 // notification now that it's in the right place.
1906 NSString* urlString = base::SysUTF8ToNSString(currentUrl.spec());
1907 if ([urlString length]) {
1908 [[NSNotificationCenter defaultCenter]
1909 postNotificationName:kTabUrlStartedLoadingNotificationForCrashReporting
1910 object:self
1911 userInfo:[NSDictionary dictionaryWithObject:urlString
1912 forKey:kTabUrlKey]];
1913 }
1914 }
1915
1916 // Called when the page finishes loading, with the URL and a boolean indicating
1917 // if the page was successfully loaded.
1918 - (void)webDidFinishWithURL:(const GURL&)url loadSuccess:(BOOL)loadSuccess {
1919 DCHECK(self.webController.loadPhase == web::PAGE_LOADED);
1920
1921 // Cancel prerendering if response is "application/octet-stream". It can be a
1922 // video file which should not be played from preload tab (crbug.com/436813).
1923 if (isPrerenderTab_ &&
1924 self.webState->GetContentsMimeType() == "application/octet-stream") {
1925 [delegate_ discardPrerender];
1926 }
1927
1928 bool wasPost = false;
1929 if (self.currentSessionEntry)
1930 wasPost = self.currentSessionEntry.navigationItem->HasPostData();
1931 if (loadSuccess)
1932 [autoReloadBridge_ loadFinishedForURL:url wasPost:wasPost];
1933 else
1934 [autoReloadBridge_ loadFailedForURL:url wasPost:wasPost];
1935 [webControllerSnapshotHelper_ setSnapshotCoalescingEnabled:YES];
1936 if (!loadSuccess) {
1937 [fullScreenController_ disableFullScreen];
1938 }
1939 [self recordInterfaceOrientation];
1940 navigation_metrics::OriginsSeenService* originsSeenService =
1941 IOSChromeOriginsSeenServiceFactory::GetForBrowserState(self.browserState);
1942 bool already_seen = originsSeenService->Insert(url::Origin::Origin(url));
1943 navigation_metrics::RecordMainFrameNavigation(
1944 url, true, self.browserState->IsOffTheRecord(), already_seen);
1945
1946 if (loadSuccess) {
1947 scoped_refptr<net::HttpResponseHeaders> headers =
1948 self.webStateImpl->GetHttpResponseHeaders();
1949 [self handleExportableFile:headers.get()];
1950 }
1951
1952 [metricsClientManager_ pageLoadCompleted];
1953 [parentTabModel_ notifyTabChanged:self];
1954
1955 if (parentTabModel_) {
1956 if ([parentTabModel_ tabUsageRecorder])
1957 [parentTabModel_ tabUsageRecorder]->RecordPageLoadDone(self, loadSuccess);
1958 [[NSNotificationCenter defaultCenter]
1959 postNotificationName:kTabModelTabDidFinishLoadingNotification
1960 object:parentTabModel_
1961 userInfo:[NSDictionary
1962 dictionaryWithObjectsAndKeys:
1963 self, kTabModelTabKey,
1964 [NSNumber numberWithBool:loadSuccess],
1965 kTabModelPageLoadSuccess, nil]];
1966 }
1967 [[OmniboxGeolocationController sharedInstance]
1968 finishPageLoadForTab:self
1969 loadSuccess:loadSuccess];
1970
1971 // Always take snapshots on iPad if the tab switcher is enabled.
1972 // If the tab switcher is not enabled, don't take snapshot of chrome scheme
1973 // pages.
1974 BOOL takeSnapshotOnIpad =
1975 IsIPadIdiom() && (experimental_flags::IsTabSwitcherEnabled() ||
1976 !web::GetWebClient()->IsAppSpecificURL(url));
1977 // Always take snapshot on iPhone.
1978 BOOL takeSnapshot = !IsIPadIdiom() || takeSnapshotOnIpad;
1979 if (loadSuccess && takeSnapshot) {
1980 [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
1981 }
1982 [webControllerSnapshotHelper_ setSnapshotCoalescingEnabled:NO];
1983 }
1984
1985 - (void)webLoadCancelled:(const GURL&)url {
1986 // When a load is cancelled, this is the maximum that a page will ever load.
1987 [fullScreenController_ enableFullScreen];
1988 [parentTabModel_ notifyTabChanged:self];
1989 }
1990
1991 - (BOOL)webController:(CRWWebController*)webController
1992 shouldOpenExternalURL:(const GURL&)URL {
1993 if (isPrerenderTab_ && !isLinkLoadingPrerenderTab_) {
1994 [delegate_ discardPrerender];
1995 return NO;
1996 }
1997 return YES;
1998 }
1999
2000 - (void)webController:(CRWWebController*)webController
2001 titleDidChange:(NSString*)title {
2002 NSString* oldTitle = [self title];
2003 BOOL isTitleChanged = (!oldTitle && title) || (oldTitle && !title) ||
2004 (![oldTitle isEqualToString:title]);
2005 if (isTitleChanged) {
2006 [self updateTitle:title];
2007 [parentTabModel_ notifyTabChanged:self];
2008 }
2009 }
2010
2011 - (BOOL)urlTriggersNativeAppLaunch:(const GURL&)url
2012 sourceURL:(const GURL&)sourceURL
2013 linkClicked:(BOOL)linkClicked {
2014 // Don't open any native app directly when prerendering or from Incognito.
2015 if (isPrerenderTab_ || self.browserState->IsOffTheRecord())
2016 return NO;
2017
2018 base::scoped_nsprotocol<id<NativeAppMetadata>> metadata(
2019 [ios::GetChromeBrowserProvider()->GetNativeAppWhitelistManager()
2020 newNativeAppForURL:url]);
2021 if (![metadata shouldAutoOpenLinks])
2022 return NO;
2023
2024 AuthenticationService* authenticationService =
2025 AuthenticationServiceFactory::GetForBrowserState(self.browserState);
2026 ChromeIdentity* identity = authenticationService->GetAuthenticatedIdentity();
2027
2028 // Attempts to open external app without x-callback.
2029 if ([self openExternalURL:[metadata launchURLWithURL:url identity:identity]
2030 linkClicked:linkClicked]) {
2031 return YES;
2032 }
2033
2034 // Auto-open didn't work. Reset the auto-open flag.
2035 [metadata unsetShouldAutoOpenLinks];
2036 return NO;
2037 }
2038
2039 - (double)lastVisitedTimestamp {
2040 DCHECK([self navigationManager]);
2041 return
2042 [[self navigationManager]->GetSessionController() lastVisitedTimestamp];
2043 }
2044
2045 - (void)updateLastVisitedTimestamp {
2046 // Stores this information in self.history and it will be written into disc
2047 // with other information when needed.
2048 DCHECK([self navigationManager]);
2049 [[self navigationManager]->GetSessionController()
2050 setLastVisitedTimestamp:[[NSDate date] timeIntervalSince1970]];
2051 }
2052
2053 - (infobars::InfoBarManager*)infoBarManager {
2054 DCHECK(self.webState);
2055 return InfoBarManagerImpl::FromWebState(self.webState);
2056 }
2057
2058 - (NSArray*)snapshotOverlays {
2059 return [snapshotOverlayProvider_ snapshotOverlaysForTab:self];
2060 }
2061
2062 - (void)webViewRemoved {
2063 [openInController_ disable];
2064 }
2065
2066 - (BOOL)webController:(CRWWebController*)webController
2067 shouldOpenURL:(const GURL&)url
2068 mainDocumentURL:(const GURL&)mainDocumentURL
2069 linkClicked:(BOOL)linkClicked {
2070 // chrome:// URLs are only allowed if the mainDocumentURL is also a chrome://
2071 // URL.
2072 if (url.SchemeIs(kChromeUIScheme) &&
2073 !mainDocumentURL.SchemeIs(kChromeUIScheme)) {
2074 return NO;
2075 }
2076
2077 // Always allow frame loads.
2078 BOOL isFrameLoad = (url != mainDocumentURL);
2079 if (isFrameLoad) {
2080 return YES;
2081 }
2082
2083 // TODO(crbug.com/546402): If this turns out to be useful, find a less hacky
2084 // hook point to send this from.
2085 NSString* urlString = base::SysUTF8ToNSString(url.spec());
2086 if ([urlString length]) {
2087 [[NSNotificationCenter defaultCenter]
2088 postNotificationName:kTabUrlMayStartLoadingNotificationForCrashReporting
2089 object:self
2090 userInfo:[NSDictionary dictionaryWithObject:urlString
2091 forKey:kTabUrlKey]];
2092 }
2093
2094 return YES;
2095 }
2096
2097 - (void)webController:(CRWWebController*)webController
2098 retrievePlaceholderOverlayImage:(void (^)(UIImage*))block {
2099 NSString* sessionID = [self currentSessionID];
2100 // The snapshot is always grey, even if |useGreyImageCache_| is NO, as this
2101 // overlay represents an out-of-date website and is shown only until the
2102 // has begun loading. However, if |useGreyImageCache_| is YES, the grey image
2103 // is already cached in memory for swiping, and a cache miss is acceptable.
2104 // In other cases, such as during startup, either disk access or a greyspace
2105 // conversion is required, as there will be no grey snapshots in memory.
2106 if (useGreyImageCache_) {
2107 [snapshotManager_ greyImageForSessionID:sessionID callback:block];
2108 } else {
2109 [webControllerSnapshotHelper_
2110 retrieveGreySnapshotForWebController:webController
2111 sessionID:sessionID
2112 withOverlays:[self snapshotOverlays]
2113 callback:block];
2114 }
2115 }
2116
2117 - (UIImage*)updateSnapshotWithOverlay:(BOOL)shouldAddOverlay
2118 visibleFrameOnly:(BOOL)visibleFrameOnly {
2119 NSArray* overlays = shouldAddOverlay ? [self snapshotOverlays] : nil;
2120 UIImage* snapshot = [webControllerSnapshotHelper_
2121 updateSnapshotForWebController:self.webController
2122 sessionID:self.tabId
2123 withOverlays:overlays
2124 visibleFrameOnly:visibleFrameOnly];
2125 [parentTabModel_ notifyTabSnapshotChanged:self withImage:snapshot];
2126 return snapshot;
2127 }
2128
2129 - (UIImage*)generateSnapshotWithOverlay:(BOOL)shouldAddOverlay
2130 visibleFrameOnly:(BOOL)visibleFrameOnly {
2131 NSArray* overlays = shouldAddOverlay ? [self snapshotOverlays] : nil;
2132 return [webControllerSnapshotHelper_
2133 generateSnapshotForWebController:self.webController
2134 withOverlays:overlays
2135 visibleFrameOnly:visibleFrameOnly];
2136 }
2137
2138 - (void)setSnapshotCoalescingEnabled:(BOOL)snapshotCoalescingEnabled {
2139 [webControllerSnapshotHelper_
2140 setSnapshotCoalescingEnabled:snapshotCoalescingEnabled];
2141 }
2142
2143 - (CGRect)snapshotContentArea {
2144 CGRect snapshotContentArea = CGRectZero;
2145 if (self.tabSnapshottingDelegate) {
2146 snapshotContentArea =
2147 [self.tabSnapshottingDelegate snapshotContentAreaForTab:self];
2148 } else {
2149 UIEdgeInsets visiblePageInsets = UIEdgeInsetsMake(
2150 [self headerHeightForWebController:self.webController], 0.0, 0.0, 0.0);
2151 snapshotContentArea = UIEdgeInsetsInsetRect(self.webController.view.bounds,
2152 visiblePageInsets);
2153 }
2154 return snapshotContentArea;
2155 }
2156
2157 - (void)willUpdateSnapshot {
2158 if ([[self.webController nativeController]
2159 respondsToSelector:@selector(willUpdateSnapshot)]) {
2160 [[self.webController nativeController] willUpdateSnapshot];
2161 }
2162 [overscrollActionsController_ clear];
2163 }
2164
2165 - (void)setWebUsageEnabled:(BOOL)webUsageEnabled {
2166 [self.webController setWebUsageEnabled:webUsageEnabled];
2167 }
2168
2169 - (void)webControllerDidSuppressDialog:(id)webController {
2170 DCHECK(isPrerenderTab_);
2171 [delegate_ discardPrerender];
2172 }
2173
2174 - (BOOL)webController:(CRWWebController*)webController
2175 shouldBlockPopupWithURL:(const GURL&)popupURL
2176 sourceURL:(const GURL&)sourceURL {
2177 ContentSetting setting =
2178 ios::HostContentSettingsMapFactory::GetForBrowserState(browserState_)
2179 ->GetContentSetting(sourceURL, sourceURL,
2180 CONTENT_SETTINGS_TYPE_POPUPS, std::string());
2181
2182 return setting != CONTENT_SETTING_ALLOW;
2183 }
2184
2185 - (void)webController:(CRWWebController*)webController
2186 didBlockPopup:(const web::BlockedPopupInfo&)blockedPopupInfo {
2187 [self popupHandler]->HandlePopup(blockedPopupInfo);
2188 }
2189
2190 - (CGFloat)headerHeightForWebController:(CRWWebController*)webController {
2191 return [fullScreenControllerDelegate_ headerHeight];
2192 }
2193
2194 - (void)webControllerDidUpdateSSLStatusForCurrentNavigationItem:
2195 (CRWWebController*)webController {
2196 // Disable fullscreen if SSL cert is invalid.
2197 web::NavigationItem* item = [self navigationManager]->GetTransientItem();
2198 web::SecurityStyle securityStyle =
2199 item ? item->GetSSL().security_style : web::SECURITY_STYLE_UNKNOWN;
2200 if (securityStyle == web::SECURITY_STYLE_AUTHENTICATION_BROKEN) {
2201 [fullScreenController_ disableFullScreen];
2202 }
2203
2204 [parentTabModel_ notifyTabChanged:self];
2205 [self updateFullscreenWithToolbarVisible:YES];
2206 }
2207
2208 - (void)webControllerWebProcessDidCrash:(CRWWebController*)webController {
2209 if (browserState_ && !browserState_->IsOffTheRecord()) {
2210 // Report the crash.
2211 GetApplicationContext()
2212 ->GetMetricsServicesManager()
2213 ->OnRendererProcessCrash();
2214
2215 // Log the tab state for the termination.
2216 RendererTerminationTabState tab_state =
2217 visible_ ? RendererTerminationTabState::FOREGROUND_TAB_FOREGROUND_APP
2218 : RendererTerminationTabState::BACKGROUND_TAB_FOREGROUND_APP;
2219 if ([UIApplication sharedApplication].applicationState ==
2220 UIApplicationStateBackground) {
2221 tab_state =
2222 visible_ ? RendererTerminationTabState::FOREGROUND_TAB_BACKGROUND_APP
2223 : RendererTerminationTabState::BACKGROUND_TAB_BACKGROUND_APP;
2224 }
2225 UMA_HISTOGRAM_ENUMERATION(
2226 kRendererTerminationStateHistogram, static_cast<int>(tab_state),
2227 static_cast<int>(
2228 RendererTerminationTabState::TERMINATION_TAB_STATE_COUNT));
2229 if ([parentTabModel_ tabUsageRecorder])
2230 [parentTabModel_ tabUsageRecorder]->RendererTerminated(self, visible_);
2231 }
2232
2233 BOOL applicationIsBackgrounded =
2234 [UIApplication sharedApplication].applicationState ==
2235 UIApplicationStateBackground;
2236 if (visible_) {
2237 if (!applicationIsBackgrounded) {
2238 base::WeakNSObject<Tab> weakSelf(self);
2239 base::scoped_nsobject<SadTabView> sadTabView(
2240 [[SadTabView alloc] initWithReloadHandler:^{
2241 base::scoped_nsobject<Tab> strongSelf([weakSelf retain]);
2242 [strongSelf reload];
2243 }]);
2244 base::scoped_nsobject<CRWContentView> contentView(
2245 [[CRWGenericContentView alloc] initWithView:sadTabView]);
2246 self.webState->ShowTransientContentView(contentView);
2247 [fullScreenController_ disableFullScreen];
2248 }
2249 } else {
2250 [self.webController requirePageReload];
2251 }
2252 // Returning to the app (after the renderer crashed in the background) and
2253 // having the page reload is much less confusing for the user.
2254 // Note: Given that the tab is visible, calling |requirePageReload| will not
2255 // work when the app becomes active because there is nothing to trigger
2256 // a view redisplay in that scenario.
2257 requireReloadAfterBecomingActive_ = visible_ && applicationIsBackgrounded;
2258 }
2259
2260 - (void)webController:(CRWWebController*)webController
2261 didLoadPassKitObject:(NSData*)data {
2262 [self.passKitDialogProvider presentPassKitDialog:data];
2263 }
2264
2265 #pragma mark - WebUserInterfaceDelegate methods.
2266
2267 - (void)webController:(CRWWebController*)webController
2268 runAuthDialogForProtectionSpace:(NSURLProtectionSpace*)protectionSpace
2269 proposedCredential:(NSURLCredential*)credential
2270 completionHandler:
2271 (void (^)(NSString* user, NSString* password))handler {
2272 if (self.dialogDelegate) {
2273 [self.dialogDelegate tab:self
2274 runAuthDialogForProtectionSpace:protectionSpace
2275 proposedCredential:credential
2276 completionHandler:handler];
2277 } else if (handler) {
2278 handler(nil, nil);
2279 }
2280 }
2281
2282 - (void)cancelDialogsForWebController:(CRWWebController*)webController {
2283 [self.dialogDelegate cancelDialogForTab:self];
2284 }
2285
2286 #pragma mark - PrerenderDelegate
2287
2288 - (void)discardPrerender {
2289 DCHECK(isPrerenderTab_);
2290 [delegate_ discardPrerender];
2291 }
2292
2293 - (BOOL)isPrerenderTab {
2294 return isPrerenderTab_;
2295 }
2296
2297 #pragma mark - ManageAccountsDelegate
2298
2299 - (void)onManageAccounts {
2300 if (isPrerenderTab_) {
2301 [delegate_ discardPrerender];
2302 return;
2303 }
2304 if (self != [parentTabModel_ currentTab])
2305 return;
2306
2307 signin_metrics::LogAccountReconcilorStateOnGaiaResponse(
2308 ios::AccountReconcilorFactory::GetForBrowserState(browserState_)
2309 ->GetState());
2310 base::scoped_nsobject<GenericChromeCommand> command(
2311 [[GenericChromeCommand alloc] initWithTag:IDC_SHOW_ACCOUNTS_SETTINGS]);
2312 [self.view chromeExecuteCommand:command];
2313 }
2314
2315 - (void)onAddAccount {
2316 if (isPrerenderTab_) {
2317 [delegate_ discardPrerender];
2318 return;
2319 }
2320 if (self != [parentTabModel_ currentTab])
2321 return;
2322
2323 signin_metrics::LogAccountReconcilorStateOnGaiaResponse(
2324 ios::AccountReconcilorFactory::GetForBrowserState(browserState_)
2325 ->GetState());
2326 base::scoped_nsobject<GenericChromeCommand> command(
2327 [[GenericChromeCommand alloc] initWithTag:IDC_SHOW_ADD_ACCOUNT]);
2328 [self.view chromeExecuteCommand:command];
2329 }
2330
2331 - (void)onGoIncognito:(const GURL&)url {
2332 if (isPrerenderTab_) {
2333 [delegate_ discardPrerender];
2334 return;
2335 }
2336 if (self != [parentTabModel_ currentTab])
2337 return;
2338
2339 // The user taps on go incognito from the mobile U-turn webpage (the web page
2340 // that displays all users accounts available in the content area). As the
2341 // user chooses to go to incognito, the mobile U-turn page is no longer
2342 // neeeded. The current solution is to go back in history. This has the
2343 // advantage of keeping the current browsing session and give a good user
2344 // experience when the user comes back from incognito.
2345 [self goBack];
2346
2347 if (url.is_valid()) {
2348 base::scoped_nsobject<OpenUrlCommand> command([[OpenUrlCommand alloc]
2349 initWithURL:url
2350 referrer:web::Referrer() // Strip referrer when switching modes.
2351 windowName:nil
2352 inIncognito:YES
2353 inBackground:NO
2354 appendTo:kLastTab]);
2355 [self.view chromeExecuteCommand:command];
2356 } else {
2357 base::scoped_nsobject<GenericChromeCommand> chromeCommand(
2358 [[GenericChromeCommand alloc] initWithTag:IDC_NEW_INCOGNITO_TAB]);
2359 [self.view chromeExecuteCommand:chromeCommand];
2360 }
2361 }
2362
2363 - (FindInPageController*)findInPageController {
2364 return findInPageController_;
2365 }
2366
2367 - (NativeAppNavigationController*)nativeAppNavigationController {
2368 return nativeAppNavigationController_;
2369 }
2370
2371 - (void)initNativeAppNavigationController {
2372 if (browserState_->IsOffTheRecord())
2373 return;
2374 DCHECK(!nativeAppNavigationController_);
2375 nativeAppNavigationController_.reset([[NativeAppNavigationController alloc]
2376 initWithRequestContextGetter:browserState_->GetRequestContext()
2377 tab:self]);
2378 [self.webController addObserver:nativeAppNavigationController_];
2379 DCHECK(nativeAppNavigationController_);
2380 }
2381
2382 - (id<PassKitDialogProvider>)passKitDialogProvider {
2383 return passKitDialogProvider_.get();
2384 }
2385
2386 - (void)setPassKitDialogProvider:(id<PassKitDialogProvider>)provider {
2387 passKitDialogProvider_.reset(provider);
2388 }
2389
2390 - (void)wasShown {
2391 visible_ = YES;
2392 [self updateFullscreenWithToolbarVisible:YES];
2393 [self.webController wasShown];
2394 [inputAccessoryViewController_ wasShown];
2395 }
2396
2397 - (void)wasHidden {
2398 visible_ = NO;
2399 [self updateFullscreenWithToolbarVisible:YES];
2400 [self.webController wasHidden];
2401 [inputAccessoryViewController_ wasHidden];
2402 }
2403
2404 - (BOOL)navigationIsBackwards:(const CRWSessionEntry*)fromEntry
2405 toEntry:(const CRWSessionEntry*)toEntry {
2406 DCHECK([self navigationManager]);
2407 NSArray* entries = [self navigationManager]->GetSessionController().entries;
2408 NSInteger fromIndex = [entries indexOfObject:fromEntry];
2409 NSInteger toIndex = [entries indexOfObject:toEntry];
2410 return (fromIndex != NSNotFound && toIndex != NSNotFound &&
2411 fromIndex > toIndex);
2412 }
2413
2414 @end
2415
2416 #pragma mark - TestingSupport
2417
2418 @implementation Tab (TestingSupport)
2419
2420 - (void)replaceWebStateImpl:(std::unique_ptr<web::WebStateImpl>)webState {
2421 // Stop observing the old InfoBarManager and FaviconDriver since they will
2422 // be deleted with the old web controller.
2423 [self setShouldObserveInfoBarManager:NO];
2424 [self setShouldObserveFaviconChanges:NO];
2425 [self.webController setDelegate:nil];
2426 // Set the new web state.
2427 webStateImpl_.reset(webState.release());
2428 [self.webController setDelegate:self];
2429 // SessionTabHelper comes first because it sets up the tab ID, and other
2430 // helpers may rely on that.
2431 IOSChromeSessionTabHelper::CreateForWebState(webStateImpl_.get());
2432 IOSChromeSyncedTabDelegate::CreateForWebState(webStateImpl_.get());
2433 // Start observing the new web controller's InfoBarManager and FaviconDriver.
2434 [self setShouldObserveInfoBarManager:YES];
2435 [self setShouldObserveFaviconChanges:YES];
2436 findInPageController_.reset();
2437 }
2438
2439 - (void)replaceExternalAppLauncher:(id)externalAppLauncher {
2440 externalAppLauncher_.reset([externalAppLauncher retain]);
2441 }
2442
2443 - (TabModel*)parentTabModel {
2444 return parentTabModel_;
2445 }
2446
2447 - (FormInputAccessoryViewController*)inputAccessoryViewController {
2448 return inputAccessoryViewController_.get();
2449 }
2450
2451 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/tabs/tab.h ('k') | ios/chrome/browser/tabs/tab_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698