OLD | NEW |
(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 |
OLD | NEW |