| Index: ios/chrome/browser/tabs/tab.mm
|
| diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6c7d0c2b988b7ccc4dff1a19bd58dc0d69fa091d
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/tabs/tab.mm
|
| @@ -0,0 +1,2451 @@
|
| +// Copyright 2012 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ios/chrome/browser/tabs/tab.h"
|
| +
|
| +#import <CoreLocation/CoreLocation.h>
|
| +#import <UIKit/UIKit.h>
|
| +
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/ios/block_types.h"
|
| +#import "base/ios/weak_nsobject.h"
|
| +#include "base/json/string_escape.h"
|
| +#include "base/logging.h"
|
| +#include "base/mac/bind_objc_block.h"
|
| +#include "base/mac/foundation_util.h"
|
| +#include "base/mac/objc_property_releaser.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/metrics/histogram.h"
|
| +#include "base/metrics/user_metrics.h"
|
| +#include "base/metrics/user_metrics_action.h"
|
| +#include "base/scoped_observer.h"
|
| +#include "base/strings/string_split.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "base/time/time.h"
|
| +#include "components/content_settings/core/browser/host_content_settings_map.h"
|
| +#include "components/favicon/core/favicon_driver_observer.h"
|
| +#include "components/favicon/ios/web_favicon_driver.h"
|
| +#include "components/google/core/browser/google_util.h"
|
| +#include "components/history/core/browser/history_context.h"
|
| +#include "components/history/core/browser/history_service.h"
|
| +#include "components/history/core/browser/top_sites.h"
|
| +#include "components/history/ios/browser/web_state_top_sites_observer.h"
|
| +#include "components/infobars/core/infobar_manager.h"
|
| +#include "components/keyed_service/core/service_access_type.h"
|
| +#include "components/metrics_services_manager/metrics_services_manager.h"
|
| +#include "components/navigation_metrics/navigation_metrics.h"
|
| +#include "components/navigation_metrics/origins_seen_service.h"
|
| +#include "components/prefs/pref_service.h"
|
| +#include "components/search_engines/template_url_service.h"
|
| +#include "components/sessions/core/session_types.h"
|
| +#include "components/sessions/ios/ios_serialized_navigation_builder.h"
|
| +#include "components/signin/core/browser/account_reconcilor.h"
|
| +#include "components/signin/core/browser/signin_metrics.h"
|
| +#import "components/signin/ios/browser/account_consistency_service.h"
|
| +#include "components/strings/grit/components_strings.h"
|
| +#include "components/url_formatter/url_formatter.h"
|
| +#include "ios/chrome/browser/application_context.h"
|
| +#import "ios/chrome/browser/autofill/autofill_controller.h"
|
| +#import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
|
| +#import "ios/chrome/browser/autofill/form_suggestion_controller.h"
|
| +#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
|
| +#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
|
| +#include "ios/chrome/browser/chrome_url_constants.h"
|
| +#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
|
| +#import "ios/chrome/browser/crash_loop_detection_util.h"
|
| +#include "ios/chrome/browser/experimental_flags.h"
|
| +#include "ios/chrome/browser/favicon/favicon_service_factory.h"
|
| +#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
|
| +#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
|
| +#include "ios/chrome/browser/history/history_service_factory.h"
|
| +#include "ios/chrome/browser/history/top_sites_factory.h"
|
| +#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
|
| +#include "ios/chrome/browser/metrics/ios_chrome_origins_seen_service_factory.h"
|
| +#import "ios/chrome/browser/metrics/tab_usage_recorder.h"
|
| +#import "ios/chrome/browser/native_app_launcher/native_app_navigation_controller.h"
|
| +#import "ios/chrome/browser/net/metrics_network_client_manager.h"
|
| +#import "ios/chrome/browser/passwords/credential_manager.h"
|
| +#import "ios/chrome/browser/passwords/js_credential_manager.h"
|
| +#import "ios/chrome/browser/passwords/password_controller.h"
|
| +#import "ios/chrome/browser/passwords/passwords_ui_delegate_impl.h"
|
| +#include "ios/chrome/browser/pref_names.h"
|
| +#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
|
| +#include "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h"
|
| +#include "ios/chrome/browser/signin/account_consistency_service_factory.h"
|
| +#include "ios/chrome/browser/signin/account_reconcilor_factory.h"
|
| +#include "ios/chrome/browser/signin/authentication_service.h"
|
| +#include "ios/chrome/browser/signin/authentication_service_factory.h"
|
| +#include "ios/chrome/browser/signin/signin_capability.h"
|
| +#import "ios/chrome/browser/snapshots/snapshot_manager.h"
|
| +#import "ios/chrome/browser/snapshots/snapshot_overlay_provider.h"
|
| +#import "ios/chrome/browser/snapshots/web_controller_snapshot_helper.h"
|
| +#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
|
| +#import "ios/chrome/browser/storekit_launcher.h"
|
| +#include "ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h"
|
| +#import "ios/chrome/browser/tabs/tab_delegate.h"
|
| +#import "ios/chrome/browser/tabs/tab_dialog_delegate.h"
|
| +#import "ios/chrome/browser/tabs/tab_model.h"
|
| +#import "ios/chrome/browser/tabs/tab_private.h"
|
| +#import "ios/chrome/browser/tabs/tab_snapshotting_delegate.h"
|
| +#include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
|
| +#import "ios/chrome/browser/u2f/u2f_controller.h"
|
| +#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
|
| +#import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
|
| +#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
|
| +#import "ios/chrome/browser/ui/commands/open_url_command.h"
|
| +#import "ios/chrome/browser/ui/commands/show_signin_command.h"
|
| +#import "ios/chrome/browser/ui/downloads/download_manager_controller.h"
|
| +#import "ios/chrome/browser/ui/fullscreen_controller.h"
|
| +#import "ios/chrome/browser/ui/open_in_controller.h"
|
| +#import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h"
|
| +#import "ios/chrome/browser/ui/prerender_delegate.h"
|
| +#import "ios/chrome/browser/ui/reader_mode/reader_mode_checker.h"
|
| +#import "ios/chrome/browser/ui/reader_mode/reader_mode_controller.h"
|
| +#import "ios/chrome/browser/ui/sad_tab/sad_tab_view.h"
|
| +#include "ios/chrome/browser/ui/ui_util.h"
|
| +#import "ios/chrome/browser/web/auto_reload_bridge.h"
|
| +#import "ios/chrome/browser/web/blocked_popup_handler.h"
|
| +#import "ios/chrome/browser/web/external_app_launcher.h"
|
| +#include "ios/chrome/browser/web/network_activity_indicator_tab_helper.h"
|
| +#import "ios/chrome/browser/web/passkit_dialog_provider.h"
|
| +#include "ios/chrome/browser/web/print_observer.h"
|
| +#import "ios/chrome/browser/web/resubmit_data_controller.h"
|
| +#import "ios/chrome/browser/xcallback_parameters.h"
|
| +#include "ios/chrome/grit/ios_strings.h"
|
| +#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
|
| +#import "ios/public/provider/chrome/browser/native_app_launcher/native_app_metadata.h"
|
| +#import "ios/public/provider/chrome/browser/native_app_launcher/native_app_whitelist_manager.h"
|
| +#import "ios/web/navigation/crw_session_controller.h"
|
| +#import "ios/web/navigation/crw_session_entry.h"
|
| +#import "ios/web/navigation/navigation_item_impl.h"
|
| +#import "ios/web/navigation/navigation_manager_impl.h"
|
| +#include "ios/web/net/request_tracker_impl.h"
|
| +#include "ios/web/public/favicon_status.h"
|
| +#include "ios/web/public/favicon_url.h"
|
| +#include "ios/web/public/interstitials/web_interstitial.h"
|
| +#import "ios/web/public/navigation_manager.h"
|
| +#include "ios/web/public/referrer.h"
|
| +#include "ios/web/public/ssl_status.h"
|
| +#include "ios/web/public/url_scheme_util.h"
|
| +#include "ios/web/public/url_util.h"
|
| +#include "ios/web/public/web_client.h"
|
| +#import "ios/web/public/web_state/crw_web_user_interface_delegate.h"
|
| +#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
|
| +#import "ios/web/public/web_state/ui/crw_generic_content_view.h"
|
| +#include "ios/web/public/web_state/web_state.h"
|
| +#include "ios/web/public/web_thread.h"
|
| +#import "ios/web/web_state/ui/crw_web_controller.h"
|
| +#import "ios/web/web_state/web_state_impl.h"
|
| +#include "net/base/escape.h"
|
| +#include "net/base/filename_util.h"
|
| +#import "net/base/mac/url_conversions.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
| +#include "net/cert/x509_certificate.h"
|
| +#include "net/http/http_response_headers.h"
|
| +#include "net/url_request/url_fetcher.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/page_transition_types.h"
|
| +#include "url/origin.h"
|
| +
|
| +using base::UserMetricsAction;
|
| +using web::NavigationManagerImpl;
|
| +using net::RequestTracker;
|
| +
|
| +NSString* const kTabUrlStartedLoadingNotificationForCrashReporting =
|
| + @"kTabUrlStartedLoadingNotificationForCrashReporting";
|
| +NSString* const kTabUrlMayStartLoadingNotificationForCrashReporting =
|
| + @"kTabUrlMayStartLoadingNotificationForCrashReporting";
|
| +NSString* const kTabIsShowingExportableNotificationForCrashReporting =
|
| + @"kTabIsShowingExportableNotificationForCrashReporting";
|
| +NSString* const kTabClosingCurrentDocumentNotificationForCrashReporting =
|
| + @"kTabClosingCurrentDocumentNotificationForCrashReporting";
|
| +
|
| +NSString* const kTabUrlKey = @"url";
|
| +
|
| +namespace {
|
| +class TabHistoryContext;
|
| +class FaviconDriverObserverBridge;
|
| +class TabInfoBarObserver;
|
| +
|
| +// Name of histogram for recording the state of the tab when the renderer is
|
| +// terminated.
|
| +const char kRendererTerminationStateHistogram[] =
|
| + "Tab.StateAtRendererTermination";
|
| +
|
| +// Referrer used for clicks on article suggestions on the NTP.
|
| +const char kChromeContentSuggestionsReferrer[] =
|
| + "https://www.googleapis.com/auth/chrome-content-suggestions";
|
| +
|
| +// Enum corresponding to UMA's TabForegroundState, for
|
| +// Tab.StateAtRendererTermination. Must be kept in sync with the UMA enum.
|
| +enum class RendererTerminationTabState {
|
| + // These two values are for when the app is in the foreground.
|
| + FOREGROUND_TAB_FOREGROUND_APP = 0,
|
| + BACKGROUND_TAB_FOREGROUND_APP,
|
| + // These are for when the app is in the background.
|
| + FOREGROUND_TAB_BACKGROUND_APP,
|
| + BACKGROUND_TAB_BACKGROUND_APP,
|
| + TERMINATION_TAB_STATE_COUNT
|
| +};
|
| +} // namespace
|
| +
|
| +@interface Tab ()<BlockedPopupHandlerDelegate,
|
| + CRWWebUserInterfaceDelegate,
|
| + FindInPageControllerDelegate,
|
| + ReaderModeControllerDelegate> {
|
| + TabModel* parentTabModel_; // weak
|
| + ios::ChromeBrowserState* browserState_; // weak
|
| +
|
| + base::scoped_nsobject<OpenInController> openInController_;
|
| + base::WeakNSProtocol<id<PassKitDialogProvider>> passKitDialogProvider_;
|
| + // TODO(crbug.com/546213): Move this out of Tab, probably to native app
|
| + // launcher since that's the only thing that uses it.
|
| + base::WeakNSProtocol<id<StoreKitLauncher>> storeKitLauncher_;
|
| +
|
| + // Whether or not this tab is currently being displayed.
|
| + BOOL visible_;
|
| +
|
| + // Used between -webWillStartLoadingURL: and -webDidStartLoadingURL:.
|
| + BOOL isUserNavigationEvent_;
|
| +
|
| + // Holds entries that need to be added to the history DB. Prerender tabs do
|
| + // not write navigation data to the history DB. Instead, they cache history
|
| + // data in this vector and add it to the DB when the prerender status is
|
| + // removed (when the Tab is swapped in as a real Tab).
|
| + std::vector<history::HistoryAddPageArgs> addPageVector_;
|
| +
|
| + // YES if this Tab is being prerendered.
|
| + BOOL isPrerenderTab_;
|
| +
|
| + // YES if this Tab was initiated from a voice search.
|
| + BOOL isVoiceSearchResultsTab_;
|
| +
|
| + // YES if the Tab needs to be reloaded after the app becomes active.
|
| + BOOL requireReloadAfterBecomingActive_;
|
| +
|
| + base::mac::ObjCPropertyReleaser propertyReleaser_Tab_;
|
| +
|
| + id<TabDelegate> delegate_; // weak
|
| + base::WeakNSProtocol<id<TabDialogDelegate>> dialogDelegate_;
|
| + base::WeakNSProtocol<id<SnapshotOverlayProvider>> snapshotOverlayProvider_;
|
| +
|
| + // Delegate used for snapshotting geometry.
|
| + id<TabSnapshottingDelegate> tabSnapshottingDelegate_; // weak
|
| +
|
| + // The Full Screen Controller responsible for hiding/showing the toolbar.
|
| + base::scoped_nsobject<FullScreenController> fullScreenController_;
|
| +
|
| + base::WeakNSProtocol<id<FullScreenControllerDelegate>>
|
| + fullScreenControllerDelegate_;
|
| +
|
| + // The Overscroll controller responsible for displaying the
|
| + // overscrollActionsView above the toolbar.
|
| + base::scoped_nsobject<OverscrollActionsController>
|
| + overscrollActionsController_;
|
| + base::WeakNSProtocol<id<OverscrollActionsControllerDelegate>>
|
| + overscrollActionsControllerDelegate_;
|
| +
|
| + // Lightweight object dealing with various different UI behaviours when
|
| + // opening a URL in an external application.
|
| + base::scoped_nsobject<ExternalAppLauncher> externalAppLauncher_;
|
| +
|
| + // Handles suggestions for form entry.
|
| + base::scoped_nsobject<FormSuggestionController> suggestionController_;
|
| +
|
| + // Manages the input accessory view during form input.
|
| + base::scoped_nsobject<FormInputAccessoryViewController>
|
| + inputAccessoryViewController_;
|
| +
|
| + // TODO(crbug.com/661665): move the WebContentsObservers into their own
|
| + // container.
|
| + // Handles saving and autofill of passwords.
|
| + base::scoped_nsobject<PasswordController> passwordController_;
|
| +
|
| + // Handles autofill.
|
| + base::scoped_nsobject<AutofillController> autofillController_;
|
| +
|
| + // The popup blocker to show blocked popup to the user.
|
| + std::unique_ptr<BlockedPopupHandler> popupHandler_;
|
| +
|
| + // Handles find on page.
|
| + base::scoped_nsobject<FindInPageController> findInPageController_;
|
| +
|
| + // Handles GAL infobar on web pages.
|
| + base::scoped_nsobject<NativeAppNavigationController>
|
| + nativeAppNavigationController_;
|
| +
|
| + // Handles caching and retrieving of snapshots.
|
| + base::scoped_nsobject<SnapshotManager> snapshotManager_;
|
| +
|
| + // Handles retrieving, generating and updating snapshots of CRWWebController's
|
| + // web page.
|
| + base::scoped_nsobject<WebControllerSnapshotHelper>
|
| + webControllerSnapshotHelper_;
|
| +
|
| + // The controller that displays an action sheet to confirm form data
|
| + // resubmission.
|
| + base::scoped_nsobject<ResubmitDataController> resubmitDataController_;
|
| +
|
| + // Number of attempts to show the resubmit data action sheet.
|
| + NSUInteger showResubmitDataActionSheetAttempt_;
|
| +
|
| + // Handles support for window.print JavaScript calls.
|
| + std::unique_ptr<PrintObserver> printObserver_;
|
| +
|
| + // AutoReloadBridge for this tab.
|
| + base::scoped_nsobject<AutoReloadBridge> autoReloadBridge_;
|
| +
|
| + // WebStateImpl for this tab.
|
| + std::unique_ptr<web::WebStateImpl> webStateImpl_;
|
| +
|
| + // Context used by history to scope the lifetime of navigation entry
|
| + // references to Tab.
|
| + std::unique_ptr<TabHistoryContext> tabHistoryContext_;
|
| +
|
| + // The controller for everything related to reader mode.
|
| + base::scoped_nsobject<ReaderModeController> readerModeController_;
|
| +
|
| + // C++ bridge that receives notifications from the FaviconDriver.
|
| + std::unique_ptr<FaviconDriverObserverBridge> faviconDriverObserverBridge_;
|
| +
|
| + // U2F call controller object.
|
| + base::scoped_nsobject<U2FController> U2FController_;
|
| +
|
| + // C++ observer used to trigger snapshots after the removal of InfoBars.
|
| + std::unique_ptr<TabInfoBarObserver> tabInfoBarObserver_;
|
| +
|
| + // C++ observer to implement the credential management JavaScript API.
|
| + std::unique_ptr<CredentialManager> credentialManager_;
|
| +
|
| + // Client factory created for metrics tracking. The Tab will signal page
|
| + // load starts and finishes to this.
|
| + base::scoped_nsobject<MetricsNetworkClientManager> metricsClientManager_;
|
| +}
|
| +
|
| +// Returns the current sessionEntry for the sesionController associated with
|
| +// this tab. Don't use this to get the underlying NavigationItem; instead
|
| +// go through the NavigationManager.
|
| +// This is nil if there's no NavigationManager.
|
| +@property(nonatomic, readonly) CRWSessionEntry* currentSessionEntry;
|
| +
|
| +// Returns the tab's reader mode controller. May contain nil if the feature is
|
| +// disabled.
|
| +@property(nonatomic, readonly) ReaderModeController* readerModeController;
|
| +
|
| +// Returns a list of FormSuggestionProviders to be queried for suggestions
|
| +// in order of priority.
|
| +- (NSArray*)suggestionProviders;
|
| +
|
| +// Returns a list of FormInputAccessoryViewProviders to be queried for an input
|
| +// accessory view in order of priority.
|
| +- (NSArray*)accessoryViewProviders;
|
| +
|
| +// Sets the favicon on the current NavigationItem.
|
| +- (void)setFavicon:(const gfx::Image*)image;
|
| +
|
| +// Updates the title field of the current session entry. Also updates the
|
| +// history database.
|
| +- (void)updateTitle:(NSString*)title;
|
| +
|
| +// Saves the current title to the history database.
|
| +- (void)saveTitleToHistoryDB;
|
| +
|
| +// Returns a lazily instantiated popup handler.
|
| +- (BlockedPopupHandler*)popupHandler;
|
| +
|
| +// Adds the current session entry to this history database.
|
| +- (void)addCurrentEntryToHistoryDB;
|
| +
|
| +// Adds any cached entries from |addPageVector_| to the history DB.
|
| +- (void)commitCachedEntriesToHistoryDB;
|
| +
|
| +// Returns the OpenInController for this tab.
|
| +- (OpenInController*)openInController;
|
| +
|
| +// Calls the model and ask to close this tab.
|
| +- (void)closeThisTab;
|
| +
|
| +// Shows the ResubmitDataActionSheet to the user to allow the user to make a
|
| +// choice.
|
| +- (void)showResubmitDataActionSheet;
|
| +
|
| +// Clears the ResubmitDataActionSheet from the UI.
|
| +- (void)clearResubmitDataActionSheet;
|
| +
|
| +// Initialize the Native App Launcher controller.
|
| +- (void)initNativeAppNavigationController;
|
| +
|
| +// YES if toEntry is behind fromEntry in the current history stack.
|
| +- (BOOL)navigationIsBackwards:(const CRWSessionEntry*)fromEntry
|
| + toEntry:(const CRWSessionEntry*)toEntry;
|
| +
|
| +// Opens a link in an external app. Returns YES iff |url| is launched in an
|
| +// external app.
|
| +- (BOOL)openExternalURL:(const GURL&)url linkClicked:(BOOL)linkClicked;
|
| +
|
| +// Handles exportable files if possible.
|
| +- (void)handleExportableFile:(net::HttpResponseHeaders*)headers;
|
| +
|
| +// Called after the session history is replaced, useful for updating members
|
| +// with new sessionID.
|
| +- (void)didReplaceSessionHistory;
|
| +
|
| +// Called when the UIApplication's state becomes active.
|
| +- (void)applicationDidBecomeActive;
|
| +@end
|
| +
|
| +namespace {
|
| +// TabHistoryContext is used by history to scope the lifetime of navigation
|
| +// entry references to Tab.
|
| +class TabHistoryContext : public history::Context {
|
| + public:
|
| + TabHistoryContext() {}
|
| + ~TabHistoryContext() {}
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(TabHistoryContext);
|
| +};
|
| +
|
| +class FaviconDriverObserverBridge : public favicon::FaviconDriverObserver {
|
| + public:
|
| + FaviconDriverObserverBridge(Tab* owner,
|
| + favicon::FaviconDriver* favicon_driver);
|
| + ~FaviconDriverObserverBridge() override;
|
| +
|
| + // favicon::FaviconDriverObserver implementation.
|
| + void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
|
| + NotificationIconType notification_icon_type,
|
| + const GURL& icon_url,
|
| + bool icon_url_changed,
|
| + const gfx::Image& image) override;
|
| +
|
| + private:
|
| + Tab* owner_; // Owns this instance.
|
| + ScopedObserver<favicon::FaviconDriver, favicon::FaviconDriverObserver>
|
| + scoped_observer_;
|
| + DISALLOW_COPY_AND_ASSIGN(FaviconDriverObserverBridge);
|
| +};
|
| +
|
| +FaviconDriverObserverBridge::FaviconDriverObserverBridge(
|
| + Tab* owner,
|
| + favicon::FaviconDriver* favicon_driver)
|
| + : owner_(owner), scoped_observer_(this) {
|
| + scoped_observer_.Add(favicon_driver);
|
| +}
|
| +
|
| +FaviconDriverObserverBridge::~FaviconDriverObserverBridge() {}
|
| +
|
| +void FaviconDriverObserverBridge::OnFaviconUpdated(
|
| + favicon::FaviconDriver* favicon_driver,
|
| + NotificationIconType notification_icon_type,
|
| + const GURL& icon_url,
|
| + bool icon_url_changed,
|
| + const gfx::Image& image) {
|
| + [owner_ setFavicon:&image];
|
| +}
|
| +
|
| +// Observer class that listens for infobar signals.
|
| +class TabInfoBarObserver : public infobars::InfoBarManager::Observer {
|
| + public:
|
| + explicit TabInfoBarObserver(Tab* owner);
|
| + ~TabInfoBarObserver() override;
|
| + void SetShouldObserveInfoBarManager(bool should_observe);
|
| + void OnInfoBarAdded(infobars::InfoBar* infobar) override;
|
| + void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override;
|
| + void OnInfoBarReplaced(infobars::InfoBar* old_infobar,
|
| + infobars::InfoBar* new_infobar) override;
|
| +
|
| + private:
|
| + Tab* owner_; // Owns this instance;
|
| + ScopedObserver<infobars::InfoBarManager, TabInfoBarObserver> scoped_observer_;
|
| + DISALLOW_COPY_AND_ASSIGN(TabInfoBarObserver);
|
| +};
|
| +
|
| +TabInfoBarObserver::TabInfoBarObserver(Tab* owner)
|
| + : owner_(owner), scoped_observer_(this) {}
|
| +
|
| +TabInfoBarObserver::~TabInfoBarObserver() {}
|
| +
|
| +void TabInfoBarObserver::SetShouldObserveInfoBarManager(bool should_observe) {
|
| + infobars::InfoBarManager* infobar_manager = [owner_ infoBarManager];
|
| + if (!infobar_manager)
|
| + return;
|
| +
|
| + if (should_observe) {
|
| + if (!scoped_observer_.IsObserving(infobar_manager))
|
| + scoped_observer_.Add(infobar_manager);
|
| + } else {
|
| + scoped_observer_.Remove(infobar_manager);
|
| + }
|
| +}
|
| +
|
| +void TabInfoBarObserver::OnInfoBarAdded(infobars::InfoBar* infobar) {
|
| + // Update snapshots after the infobar has been added.
|
| + [owner_ updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
|
| +}
|
| +
|
| +void TabInfoBarObserver::OnInfoBarRemoved(infobars::InfoBar* infobar,
|
| + bool animate) {
|
| + // Update snapshots after the infobar has been removed.
|
| + [owner_ updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
|
| +}
|
| +
|
| +void TabInfoBarObserver::OnInfoBarReplaced(infobars::InfoBar* old_infobar,
|
| + infobars::InfoBar* new_infobar) {
|
| + // Update snapshots after the infobar has been replaced.
|
| + [owner_ updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
|
| +}
|
| +
|
| +// Registers |factory| with |tracker| on the IO thread.
|
| +void AddNetworkClientFactoryOnIOThread(
|
| + web::RequestTrackerImpl* tracker,
|
| + CRNForwardingNetworkClientFactory* factory) {
|
| + base::scoped_nsobject<CRNForwardingNetworkClientFactory> scoped_factory(
|
| + [factory retain]);
|
| + tracker->PostIOTask(base::Bind(&net::RequestTracker::AddNetworkClientFactory,
|
| + tracker, scoped_factory));
|
| +}
|
| +
|
| +} // anonymous namespace
|
| +
|
| +@implementation Tab
|
| +
|
| +@synthesize browserState = browserState_;
|
| +@synthesize useGreyImageCache = useGreyImageCache_;
|
| +@synthesize isPrerenderTab = isPrerenderTab_;
|
| +@synthesize isLinkLoadingPrerenderTab = isLinkLoadingPrerenderTab_;
|
| +@synthesize isVoiceSearchResultsTab = isVoiceSearchResultsTab_;
|
| +@synthesize delegate = delegate_;
|
| +@synthesize tabSnapshottingDelegate = tabSnapshottingDelegate_;
|
| +
|
| +- (instancetype)initWithWindowName:(NSString*)windowName
|
| + opener:(Tab*)opener
|
| + openedByDOM:(BOOL)openedByDOM
|
| + model:(TabModel*)parentModel
|
| + browserState:(ios::ChromeBrowserState*)browserState {
|
| + NSInteger openerIndex = -1;
|
| + if ([opener navigationManager]) {
|
| + NavigationManagerImpl* openerNavManager = [opener navigationManager];
|
| + openerIndex = openerNavManager->GetLastCommittedItemIndex();
|
| + }
|
| + std::unique_ptr<web::WebStateImpl> webState(
|
| + new web::WebStateImpl(browserState));
|
| + webState->GetNavigationManagerImpl().InitializeSession(
|
| + windowName, [opener currentSessionID], openedByDOM, openerIndex);
|
| +
|
| + return [self initWithWebState:std::move(webState) model:parentModel];
|
| +}
|
| +
|
| +- (instancetype)initWithWebState:(std::unique_ptr<web::WebState>)webState
|
| + model:(TabModel*)parentModel {
|
| + DCHECK(webState);
|
| + self = [super init];
|
| + if (self) {
|
| + propertyReleaser_Tab_.Init(self, [Tab class]);
|
| + tabHistoryContext_.reset(new TabHistoryContext());
|
| + parentTabModel_ = parentModel;
|
| + browserState_ =
|
| + ios::ChromeBrowserState::FromBrowserState(webState->GetBrowserState());
|
| +
|
| + webStateImpl_.reset(static_cast<web::WebStateImpl*>(webState.release()));
|
| + [self.webController setDelegate:self];
|
| + [self.webController setUIDelegate:self];
|
| +
|
| + NSString* sessionID = [self currentSessionID];
|
| + DCHECK(sessionID);
|
| + snapshotManager_.reset([[SnapshotManager alloc] init]);
|
| +
|
| + webControllerSnapshotHelper_.reset([[WebControllerSnapshotHelper alloc]
|
| + initWithSnapshotManager:snapshotManager_
|
| + tab:self]);
|
| +
|
| + findInPageController_.reset([[FindInPageController alloc]
|
| + initWithWebState:self.webState
|
| + delegate:self]);
|
| +
|
| + [self initNativeAppNavigationController];
|
| + // IOSChromeSessionTabHelper comes first because it sets up the tab ID, and
|
| + // other helpers may rely on that.
|
| + IOSChromeSessionTabHelper::CreateForWebState(self.webState);
|
| + NetworkActivityIndicatorTabHelper::CreateForWebState(self.webState,
|
| + self.tabId);
|
| + IOSChromeSyncedTabDelegate::CreateForWebState(self.webState);
|
| + InfoBarManagerImpl::CreateForWebState(self.webState);
|
| + IOSSecurityStateTabHelper::CreateForWebState(self.webState);
|
| +
|
| + tabInfoBarObserver_.reset(new TabInfoBarObserver(self));
|
| + tabInfoBarObserver_->SetShouldObserveInfoBarManager(true);
|
| +
|
| + if (AccountConsistencyService* account_consistency_service =
|
| + ios::AccountConsistencyServiceFactory::GetForBrowserState(
|
| + browserState_)) {
|
| + account_consistency_service->SetWebStateHandler(self.webState, self);
|
| + }
|
| + ChromeIOSTranslateClient::CreateForWebState(self.webState);
|
| + if (experimental_flags::IsAutoReloadEnabled()) {
|
| + autoReloadBridge_.reset([[AutoReloadBridge alloc] initWithTab:self]);
|
| + }
|
| + printObserver_.reset(new PrintObserver(self.webState));
|
| +
|
| + base::scoped_nsprotocol<id<PasswordsUiDelegate>> passwordsUiDelegate(
|
| + [[PasswordsUiDelegateImpl alloc] init]);
|
| + passwordController_.reset([[PasswordController alloc]
|
| + initWithWebState:self.webState
|
| + passwordsUiDelegate:passwordsUiDelegate]);
|
| + password_manager::PasswordGenerationManager* passwordGenerationManager =
|
| + [passwordController_ passwordGenerationManager];
|
| + autofillController_.reset([[AutofillController alloc]
|
| + initWithBrowserState:browserState_
|
| + passwordGenerationManager:passwordGenerationManager
|
| + webState:self.webState]);
|
| + suggestionController_.reset([[FormSuggestionController alloc]
|
| + initWithWebState:self.webState
|
| + providers:[self suggestionProviders]]);
|
| + inputAccessoryViewController_.reset(
|
| + [[FormInputAccessoryViewController alloc]
|
| + initWithWebState:self.webState
|
| + providers:[self accessoryViewProviders]]);
|
| + if (experimental_flags::IsCredentialManagementEnabled()) {
|
| + credentialManager_.reset(new CredentialManager(
|
| + self.webState, [passwordController_ passwordManagerClient],
|
| + [passwordController_ passwordManagerDriver],
|
| + base::mac::ObjCCastStrict<JSCredentialManager>(
|
| + [self.webState->GetJSInjectionReceiver()
|
| + instanceOfClass:[JSCredentialManager class]])));
|
| + }
|
| +
|
| + ios::ChromeBrowserState* original_browser_state =
|
| + ios::ChromeBrowserState::FromBrowserState(
|
| + self.webState->GetBrowserState())
|
| + ->GetOriginalChromeBrowserState();
|
| + favicon::WebFaviconDriver::CreateForWebState(
|
| + self.webState,
|
| + ios::FaviconServiceFactory::GetForBrowserState(
|
| + original_browser_state, ServiceAccessType::IMPLICIT_ACCESS),
|
| + ios::HistoryServiceFactory::GetForBrowserState(
|
| + original_browser_state, ServiceAccessType::IMPLICIT_ACCESS),
|
| + ios::BookmarkModelFactory::GetForBrowserState(original_browser_state));
|
| + history::WebStateTopSitesObserver::CreateForWebState(
|
| + self.webState,
|
| + ios::TopSitesFactory::GetForBrowserState(original_browser_state).get());
|
| + [self setShouldObserveFaviconChanges:YES];
|
| + web::RequestTrackerImpl* requestTracker =
|
| + webStateImpl_->GetRequestTracker();
|
| +
|
| + metricsClientManager_.reset([[MetricsNetworkClientManager alloc] init]);
|
| + AddNetworkClientFactoryOnIOThread(requestTracker, metricsClientManager_);
|
| +
|
| + if (parentModel && parentModel.syncedWindowDelegate) {
|
| + IOSChromeSessionTabHelper::FromWebState(self.webState)
|
| + ->SetWindowID(parentModel.sessionID);
|
| + }
|
| +
|
| + // Create the ReaderModeController immediately so it can register for
|
| + // WebState changes.
|
| + if (experimental_flags::IsReaderModeEnabled()) {
|
| + readerModeController_.reset([[ReaderModeController alloc]
|
| + initWithWebState:self.webState
|
| + delegate:self]);
|
| + }
|
| +
|
| + // Allow the embedder to attach tab helpers.
|
| + ios::GetChromeBrowserProvider()->AttachTabHelpers(self.webState, self);
|
| +
|
| + [[NSNotificationCenter defaultCenter]
|
| + addObserver:self
|
| + selector:@selector(applicationDidBecomeActive)
|
| + name:UIApplicationDidBecomeActiveNotification
|
| + object:nil];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (instancetype)init {
|
| + NOTREACHED();
|
| + return nil;
|
| +}
|
| +
|
| +- (NSArray*)accessoryViewProviders {
|
| + NSMutableArray* providers = [NSMutableArray array];
|
| + id<FormInputAccessoryViewProvider> provider =
|
| + [passwordController_ accessoryViewProvider];
|
| + if (provider)
|
| + [providers addObject:provider];
|
| + [providers addObject:[suggestionController_ accessoryViewProvider]];
|
| + return providers;
|
| +}
|
| +
|
| +- (NSArray*)suggestionProviders {
|
| + NSMutableArray* providers = [NSMutableArray array];
|
| + [providers addObject:[passwordController_ suggestionProvider]];
|
| + [providers addObject:[autofillController_ suggestionProvider]];
|
| + return providers;
|
| +}
|
| +
|
| ++ (Tab*)newPreloadingTabWithBrowserState:(ios::ChromeBrowserState*)browserState
|
| + url:(const GURL&)URL
|
| + referrer:(const web::Referrer&)referrer
|
| + transition:(ui::PageTransition)transition
|
| + provider:(id<CRWNativeContentProvider>)provider
|
| + opener:(Tab*)opener
|
| + desktopUserAgent:(BOOL)desktopUserAgent
|
| + configuration:(void (^)(Tab*))configuration {
|
| + Tab* tab = [[[Tab alloc] initWithWindowName:nil
|
| + opener:opener
|
| + openedByDOM:NO
|
| + model:nil
|
| + browserState:browserState] autorelease];
|
| + if (desktopUserAgent)
|
| + [tab enableDesktopUserAgent];
|
| + [[tab webController] setNativeProvider:provider];
|
| + [[tab webController] setWebUsageEnabled:YES];
|
| +
|
| + if (configuration)
|
| + configuration(tab);
|
| +
|
| + web::NavigationManager::WebLoadParams params(URL);
|
| + params.transition_type = transition;
|
| + params.referrer = referrer;
|
| + [[tab webController] loadWithParams:params];
|
| +
|
| + return tab;
|
| +}
|
| +
|
| +- (void)dealloc {
|
| + DCHECK([NSThread isMainThread]);
|
| + // Note that -[CRWWebController close] has already been called, so nothing
|
| + // significant should be done with it in this method.
|
| + DCHECK_NE(self.webController.delegate, self);
|
| + [super dealloc];
|
| +}
|
| +
|
| +- (void)setParentTabModel:(TabModel*)model {
|
| + DCHECK(!model || !parentTabModel_);
|
| + parentTabModel_ = model;
|
| +
|
| + if (parentTabModel_.syncedWindowDelegate) {
|
| + IOSChromeSessionTabHelper::FromWebState(self.webState)
|
| + ->SetWindowID(model.sessionID);
|
| + }
|
| +}
|
| +
|
| +- (NSString*)description {
|
| + return [NSString stringWithFormat:@"%p ... %@ - %s", self, self.title,
|
| + self.url.spec().c_str()];
|
| +}
|
| +
|
| +- (CRWWebController*)webController {
|
| + return webStateImpl_ ? webStateImpl_->GetWebController() : nil;
|
| +}
|
| +
|
| +- (id<TabDialogDelegate>)dialogDelegate {
|
| + return dialogDelegate_;
|
| +}
|
| +
|
| +- (void)setDialogDelegate:(id<TabDialogDelegate>)dialogDelegate {
|
| + dialogDelegate_.reset(dialogDelegate);
|
| +}
|
| +
|
| +- (void)setIsVoiceSearchResultsTab:(BOOL)isVoiceSearchResultsTab {
|
| + // There is intentionally no equality check in this setter, as we want the
|
| + // notificaiton to be sent regardless of whether the value has changed.
|
| + isVoiceSearchResultsTab_ = isVoiceSearchResultsTab;
|
| + [parentTabModel_ notifyTabChanged:self];
|
| +}
|
| +
|
| +- (PasswordController*)passwordController {
|
| + return passwordController_.get();
|
| +}
|
| +
|
| +- (void)retrieveSnapshot:(void (^)(UIImage*))callback {
|
| + [webControllerSnapshotHelper_
|
| + retrieveSnapshotForWebController:self.webController
|
| + sessionID:[self currentSessionID]
|
| + withOverlays:[self snapshotOverlays]
|
| + callback:callback];
|
| +}
|
| +
|
| +- (const GURL&)url {
|
| + // See note in header; this method should be removed.
|
| + web::NavigationItem* item = [[self currentSessionEntry] navigationItem];
|
| + return item ? item->GetVirtualURL() : GURL::EmptyGURL();
|
| +}
|
| +
|
| +- (NSString*)title {
|
| + base::string16 title = self.webStateImpl->GetTitle();
|
| + if (title.empty())
|
| + title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE);
|
| + return base::SysUTF16ToNSString(title);
|
| +}
|
| +
|
| +- (NSString*)originalTitle {
|
| + // Do not use self.webState->GetTitle() as it returns the display title,
|
| + // not the original page title.
|
| + DCHECK([self navigationManager]);
|
| + web::NavigationItem* item = [self navigationManager]->GetLastCommittedItem();
|
| + if (!item)
|
| + return nil;
|
| + base::string16 pageTitle = item->GetTitle();
|
| + return pageTitle.empty() ? nil : base::SysUTF16ToNSString(pageTitle);
|
| +}
|
| +
|
| +- (NSString*)urlDisplayString {
|
| + base::string16 urlText = url_formatter::FormatUrl(
|
| + self.url, url_formatter::kFormatUrlOmitNothing, net::UnescapeRule::SPACES,
|
| + nullptr, nullptr, nullptr);
|
| + return base::SysUTF16ToNSString(urlText);
|
| +}
|
| +
|
| +- (NSString*)windowName {
|
| + DCHECK([self navigationManager]);
|
| + return [self navigationManager]->GetSessionController().windowName;
|
| +}
|
| +
|
| +- (NSString*)tabId {
|
| + DCHECK([self navigationManager]);
|
| + return [[self navigationManager]->GetSessionController() tabId];
|
| +}
|
| +
|
| +- (web::WebState*)webState {
|
| + return webStateImpl_.get();
|
| +}
|
| +
|
| +- (web::WebStateImpl*)webStateImpl {
|
| + return webStateImpl_.get();
|
| +}
|
| +
|
| +- (void)fetchFavicon {
|
| + const GURL& url = self.url;
|
| + if (!url.is_valid())
|
| + return;
|
| +
|
| + favicon::FaviconDriver* faviconDriver =
|
| + favicon::WebFaviconDriver::FromWebState(self.webState);
|
| + if (faviconDriver) {
|
| + faviconDriver->FetchFavicon(url);
|
| + }
|
| +}
|
| +
|
| +- (void)setFavicon:(const gfx::Image*)image {
|
| + web::NavigationItem* item = [self navigationManager]->GetVisibleItem();
|
| + if (!item)
|
| + return;
|
| + if (image) {
|
| + item->GetFavicon().image = *image;
|
| + item->GetFavicon().valid = true;
|
| + }
|
| + [parentTabModel_ notifyTabChanged:self];
|
| +}
|
| +
|
| +- (UIImage*)favicon {
|
| + DCHECK([self navigationManager]);
|
| + web::NavigationItem* item = [self navigationManager]->GetVisibleItem();
|
| + if (!item)
|
| + return nil;
|
| + const gfx::Image& image = item->GetFavicon().image;
|
| + if (image.IsEmpty())
|
| + return nil;
|
| + return image.ToUIImage();
|
| +}
|
| +
|
| +- (UIView*)view {
|
| + // Record reload of previously-evicted tab.
|
| + if (![self.webController isViewAlive] && [parentTabModel_ tabUsageRecorder]) {
|
| + [parentTabModel_ tabUsageRecorder]->RecordPageLoadStart(self);
|
| + }
|
| + return self.webState ? self.webState->GetView() : nil;
|
| +}
|
| +
|
| +- (UIView*)viewForPrinting {
|
| + return self.webController.viewForPrinting;
|
| +}
|
| +
|
| +- (NavigationManagerImpl*)navigationManager {
|
| + if (!self.webStateImpl)
|
| + return nil;
|
| + return &(self.webStateImpl->GetNavigationManagerImpl());
|
| +}
|
| +
|
| +- (id<StoreKitLauncher>)storeKitLauncher {
|
| + return storeKitLauncher_.get();
|
| +}
|
| +
|
| +- (void)setStoreKitLauncher:(id<StoreKitLauncher>)storeKitLauncher {
|
| + storeKitLauncher_.reset(storeKitLauncher);
|
| +}
|
| +
|
| +// Swap out the existing session history with a new list of navigations. Forces
|
| +// the tab to reload to update the UI accordingly. This is ok because none of
|
| +// the session history is stored in the tab; it's always fetched through the
|
| +// navigation manager.
|
| +- (void)replaceHistoryWithNavigations:
|
| + (const std::vector<sessions::SerializedNavigationEntry>&)navigations
|
| + currentIndex:(NSInteger)currentIndex {
|
| + ScopedVector<web::NavigationItem> items =
|
| + sessions::IOSSerializedNavigationBuilder::ToNavigationItems(navigations);
|
| + [self navigationManager]->ReplaceSessionHistory(std::move(items),
|
| + currentIndex);
|
| + [self didReplaceSessionHistory];
|
| +
|
| + [self.webController loadCurrentURL];
|
| +}
|
| +
|
| +- (void)didReplaceSessionHistory {
|
| + // Replace fullScreenController_ with a new sessionID when the navigation
|
| + // manager changes.
|
| + // TODO(crbug.com/661666): Consider just updating sessionID and not replacing
|
| + // |fullScreenController_|.
|
| + if (fullScreenController_) {
|
| + [fullScreenController_ invalidate];
|
| + [self.webController removeObserver:fullScreenController_];
|
| + fullScreenController_.reset([[FullScreenController alloc]
|
| + initWithDelegate:fullScreenControllerDelegate_
|
| + navigationManager:&(self.webStateImpl->GetNavigationManagerImpl())
|
| + sessionID:[self currentSessionID]]);
|
| + [self.webController addObserver:fullScreenController_];
|
| + // If the content of the page was loaded without knowledge of the
|
| + // toolbar position it will be misplaced under the toolbar instead of
|
| + // right below. This happens e.g. in the case of preloading. This is to make
|
| + // sure the content is moved to the right place.
|
| + [fullScreenController_ moveContentBelowHeader];
|
| + }
|
| +}
|
| +
|
| +- (void)setIsLinkLoadingPrerenderTab:(BOOL)isLinkLoadingPrerenderTab {
|
| + isLinkLoadingPrerenderTab_ = isLinkLoadingPrerenderTab;
|
| + [self setIsPrerenderTab:isLinkLoadingPrerenderTab];
|
| +}
|
| +
|
| +- (void)setIsPrerenderTab:(BOOL)isPrerender {
|
| + if (isPrerenderTab_ == isPrerender)
|
| + return;
|
| +
|
| + isPrerenderTab_ = isPrerender;
|
| +
|
| + self.webController.shouldSuppressDialogs =
|
| + (isPrerender && !isLinkLoadingPrerenderTab_);
|
| +
|
| + if (isPrerenderTab_)
|
| + return;
|
| +
|
| + [fullScreenController_ moveContentBelowHeader];
|
| + [self commitCachedEntriesToHistoryDB];
|
| + [self saveTitleToHistoryDB];
|
| +
|
| + // If the page has finished loading, take a snapshot. If the page is still
|
| + // loading, do nothing, as CRWWebController will automatically take a
|
| + // snapshot once the load completes.
|
| + BOOL loadingFinished = self.webController.loadPhase == web::PAGE_LOADED;
|
| + if (loadingFinished)
|
| + [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
|
| +
|
| + [[OmniboxGeolocationController sharedInstance]
|
| + finishPageLoadForTab:self
|
| + loadSuccess:loadingFinished];
|
| + [self countMainFrameLoad];
|
| +}
|
| +
|
| +- (id<FullScreenControllerDelegate>)fullScreenControllerDelegate {
|
| + return fullScreenControllerDelegate_.get();
|
| +}
|
| +
|
| +- (void)setFullScreenControllerDelegate:
|
| + (id<FullScreenControllerDelegate>)fullScreenControllerDelegate {
|
| + if (fullScreenControllerDelegate == fullScreenControllerDelegate_) {
|
| + return;
|
| + }
|
| + // Lazily create a FullScreenController.
|
| + // The check for fullScreenControllerDelegate is necessary to avoid recreating
|
| + // a FullScreenController during teardown.
|
| + if (!fullScreenController_ && fullScreenControllerDelegate) {
|
| + NavigationManagerImpl* navigationManager =
|
| + &(self.webStateImpl->GetNavigationManagerImpl());
|
| + fullScreenController_.reset([[FullScreenController alloc]
|
| + initWithDelegate:fullScreenControllerDelegate
|
| + navigationManager:navigationManager
|
| + sessionID:[self currentSessionID]]);
|
| + if (fullScreenController_) {
|
| + [self.webController addObserver:fullScreenController_];
|
| + }
|
| + // If the content of the page was loaded without knowledge of the
|
| + // toolbar position it will be misplaced under the toolbar instead of
|
| + // right below. This happens e.g. in the case of preloading. This is to make
|
| + // sure the content is moved to the right place.
|
| + [fullScreenController_ moveContentBelowHeader];
|
| + }
|
| + fullScreenControllerDelegate_.reset(fullScreenControllerDelegate);
|
| +}
|
| +
|
| +- (OverscrollActionsController*)overscrollActionsController {
|
| + return overscrollActionsController_.get();
|
| +}
|
| +
|
| +- (id<OverscrollActionsControllerDelegate>)overscrollActionsControllerDelegate {
|
| + return overscrollActionsControllerDelegate_.get();
|
| +}
|
| +
|
| +- (void)setOverscrollActionsControllerDelegate:
|
| + (id<OverscrollActionsControllerDelegate>)
|
| + overscrollActionsControllerDelegate {
|
| + if (overscrollActionsControllerDelegate_ ==
|
| + overscrollActionsControllerDelegate)
|
| + return;
|
| +
|
| + // Lazily create a OverscrollActionsController.
|
| + // The check for overscrollActionsControllerDelegate is necessary to avoid
|
| + // recreating a OverscrollActionsController during teardown.
|
| + if (!overscrollActionsController_) {
|
| + overscrollActionsController_.reset(
|
| + [[OverscrollActionsController alloc] init]);
|
| + [self.webController addObserver:overscrollActionsController_];
|
| + }
|
| + ios_internal::OverscrollStyle style =
|
| + ios_internal::OverscrollStyle::REGULAR_PAGE_NON_INCOGNITO;
|
| + if (browserState_->IsOffTheRecord()) {
|
| + style = ios_internal::OverscrollStyle::REGULAR_PAGE_INCOGNITO;
|
| + }
|
| + [overscrollActionsController_ setStyle:style];
|
| + [overscrollActionsController_
|
| + setDelegate:overscrollActionsControllerDelegate];
|
| + overscrollActionsControllerDelegate_.reset(
|
| + overscrollActionsControllerDelegate);
|
| +}
|
| +
|
| +- (void)updateTitle:(NSString*)title {
|
| + web::NavigationItem* item = [self navigationManager]->GetVisibleItem();
|
| + if (!item)
|
| + return;
|
| + item->SetTitle(base::SysNSStringToUTF16(title));
|
| + // TODO(crbug.com/546218): See if this can be removed; it's not clear that
|
| + // other platforms send this (tab sync triggers need to be compared against
|
| + // upstream).
|
| + if (self.webStateImpl)
|
| + self.webStateImpl->GetNavigationManagerImpl().OnNavigationItemChanged();
|
| +
|
| + [self saveTitleToHistoryDB];
|
| +}
|
| +
|
| +- (void)saveTitleToHistoryDB {
|
| + // If incognito, don't update history.
|
| + if (browserState_->IsOffTheRecord())
|
| + return;
|
| + // Don't update the history if current entry has no title.
|
| + NSString* title = [self title];
|
| + if (![title length] ||
|
| + [title isEqualToString:l10n_util::GetNSString(IDS_DEFAULT_TAB_TITLE)])
|
| + return;
|
| +
|
| + history::HistoryService* historyService =
|
| + ios::HistoryServiceFactory::GetForBrowserState(
|
| + browserState_, ServiceAccessType::IMPLICIT_ACCESS);
|
| + DCHECK(historyService);
|
| + historyService->SetPageTitle(self.url, base::SysNSStringToUTF16(title));
|
| +}
|
| +
|
| +- (void)addCurrentEntryToHistoryDB {
|
| + DCHECK(self.currentSessionEntry);
|
| + // If incognito, don't update history.
|
| + if (browserState_->IsOffTheRecord())
|
| + return;
|
| +
|
| + CRWSessionEntry* sessionEntry = self.currentSessionEntry;
|
| + web::NavigationItem* item = [self navigationManager]->GetVisibleItem();
|
| +
|
| + // Do not update the history db for back/forward navigations.
|
| + // TODO(crbug.com/661667): We do not currently tag the entry with a
|
| + // FORWARD_BACK transition. Fix.
|
| + if (item->GetTransitionType() & ui::PAGE_TRANSITION_FORWARD_BACK)
|
| + return;
|
| +
|
| + history::HistoryService* historyService =
|
| + ios::HistoryServiceFactory::GetForBrowserState(
|
| + browserState_, ServiceAccessType::IMPLICIT_ACCESS);
|
| + DCHECK(historyService);
|
| +
|
| + const GURL url(item->GetURL());
|
| + const web::Referrer& referrer = item->GetReferrer();
|
| +
|
| +// Do not update the history db for data: urls. This diverges from upstream,
|
| +// but prevents us from dumping huge view-source urls into the history
|
| +// database. Since view-source is only activated in Debug builds, this check
|
| +// can be Debug-only as well.
|
| +#ifndef NDEBUG
|
| + if (url.scheme() == url::kDataScheme)
|
| + return;
|
| +#endif
|
| +
|
| + history::RedirectList redirects;
|
| + if (item->GetURL() != sessionEntry.originalUrl) {
|
| + // Simulate a valid redirect chain in case of URL that have been modified
|
| + // in |CRWWebController finishHistoryNavigationFromEntry:|.
|
| + const std::string& urlSpec = item->GetURL().spec();
|
| + size_t urlSpecLength = urlSpec.size();
|
| + if (item->GetTransitionType() & ui::PAGE_TRANSITION_CLIENT_REDIRECT ||
|
| + (urlSpecLength && (urlSpec.at(urlSpecLength - 1) == '#') &&
|
| + !urlSpec.compare(0, urlSpecLength - 1,
|
| + sessionEntry.originalUrl.spec()))) {
|
| + redirects.push_back(referrer.url);
|
| + }
|
| + // TODO(crbug.com/661670): the redirect chain is not constructed the same
|
| + // way as upstream so this part needs to be revised.
|
| + redirects.push_back(sessionEntry.originalUrl);
|
| + redirects.push_back(url);
|
| + }
|
| +
|
| + DCHECK(item->GetTimestamp().ToInternalValue() > 0);
|
| + if ([self isPrerenderTab]) {
|
| + // Clicks on content suggestions on the NTP should not contribute to the
|
| + // Most Visited tiles in the NTP.
|
| + const bool consider_for_ntp_most_visited =
|
| + referrer.url != GURL(kChromeContentSuggestionsReferrer);
|
| +
|
| + history::HistoryAddPageArgs args(
|
| + url, item->GetTimestamp(), tabHistoryContext_.get(),
|
| + item->GetUniqueID(), referrer.url, redirects, item->GetTransitionType(),
|
| + history::SOURCE_BROWSED, false, consider_for_ntp_most_visited);
|
| + addPageVector_.push_back(args);
|
| + } else {
|
| + historyService->AddPage(url, item->GetTimestamp(), tabHistoryContext_.get(),
|
| + item->GetUniqueID(), referrer.url, redirects,
|
| + item->GetTransitionType(), history::SOURCE_BROWSED,
|
| + false);
|
| + [self saveTitleToHistoryDB];
|
| + }
|
| +}
|
| +
|
| +- (void)commitCachedEntriesToHistoryDB {
|
| + // If OTR, don't update history.
|
| + if (browserState_->IsOffTheRecord()) {
|
| + DCHECK_EQ(0U, addPageVector_.size());
|
| + return;
|
| + }
|
| +
|
| + history::HistoryService* historyService =
|
| + ios::HistoryServiceFactory::GetForBrowserState(
|
| + browserState_, ServiceAccessType::IMPLICIT_ACCESS);
|
| + DCHECK(historyService);
|
| +
|
| + for (size_t i = 0; i < addPageVector_.size(); ++i)
|
| + historyService->AddPage(addPageVector_[i]);
|
| + addPageVector_.clear();
|
| +}
|
| +
|
| +- (void)webWillInitiateLoadWithParams:
|
| + (web::NavigationManager::WebLoadParams&)params {
|
| + GURL navUrl = params.url;
|
| +
|
| + // After a crash the NTP is loaded by default.
|
| + if (navUrl.host() != kChromeUINewTabHost) {
|
| + static BOOL hasLoadedPage = NO;
|
| + if (!hasLoadedPage) {
|
| + // As soon as an URL is loaded, a crash shouldn't be counted as a startup
|
| + // crash. Since loading an url requires user action and is a significant
|
| + // source of crashes that could lead to false positives in crash loop
|
| + // detection.
|
| + crash_util::ResetFailedStartupAttemptCount();
|
| + hasLoadedPage = YES;
|
| + }
|
| + }
|
| +}
|
| +
|
| +- (void)webDidUpdateSessionForLoadWithParams:
|
| + (const web::NavigationManager::WebLoadParams&)params
|
| + wasInitialNavigation:(BOOL)initialNavigation {
|
| + GURL navUrl = params.url;
|
| + ui::PageTransition transition = params.transition_type;
|
| +
|
| + // Record any explicit, non-redirect navigation as a clobber (as long as it's
|
| + // in a real tab).
|
| + if (!initialNavigation && !isPrerenderTab_ &&
|
| + !PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD) &&
|
| + (transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) == 0) {
|
| + base::RecordAction(UserMetricsAction("MobileTabClobbered"));
|
| + }
|
| + if ([parentTabModel_ tabUsageRecorder])
|
| + [parentTabModel_ tabUsageRecorder]->RecordPageLoadStart(self);
|
| +
|
| + // Reset |isVoiceSearchResultsTab| since a new page is being navigated to.
|
| + self.isVoiceSearchResultsTab = NO;
|
| +
|
| + [[OmniboxGeolocationController sharedInstance]
|
| + addLocationToNavigationItem:self.currentSessionEntry.navigationItem
|
| + browserState:browserState_];
|
| +}
|
| +
|
| +- (void)loadSessionTab:(const sessions::SessionTab*)sessionTab {
|
| + DCHECK(sessionTab);
|
| + [self replaceHistoryWithNavigations:sessionTab->navigations
|
| + currentIndex:sessionTab->current_navigation_index];
|
| +}
|
| +
|
| +- (void)openJavascript:(NSString*)javaScript {
|
| + DCHECK(javaScript);
|
| + javaScript = [javaScript stringByRemovingPercentEncoding];
|
| + if (webStateImpl_)
|
| + webStateImpl_->ExecuteJavaScript(base::SysNSStringToUTF16(javaScript));
|
| +}
|
| +
|
| +- (void)reload {
|
| + // TODO(crbug.com/661671): Convert callers to go through CRWWebController
|
| + // directly and remove this passthrough method.
|
| + [self.webController reload];
|
| +}
|
| +
|
| +- (void)webWillReload {
|
| + if ([parentTabModel_ tabUsageRecorder]) {
|
| + [parentTabModel_ tabUsageRecorder]->RecordReload(self);
|
| + }
|
| +}
|
| +
|
| +// Stop the page loading.
|
| +// Equivalent to the user pressing 'stop', or a window.stop() command.
|
| +- (void)stopLoading {
|
| + [self.webController stopLoading];
|
| +}
|
| +
|
| +// Halt the tab, which amounts to halting its webController.
|
| +- (void)terminateNetworkActivity {
|
| + [self.webController terminateNetworkActivity];
|
| +}
|
| +
|
| +// This can't be done in dealloc in case someone holds an extra strong
|
| +// reference to the Tab, which would cause the close sequence to fire at a
|
| +// random time.
|
| +- (void)close {
|
| + self.fullScreenControllerDelegate = nil;
|
| + self.overscrollActionsControllerDelegate = nil;
|
| + self.passKitDialogProvider = nil;
|
| + self.snapshotOverlayProvider = nil;
|
| + self.storeKitLauncher = nil;
|
| +
|
| + [[NSNotificationCenter defaultCenter] removeObserver:self];
|
| +
|
| + [findInPageController_ detachFromWebState];
|
| + findInPageController_.reset();
|
| +
|
| + [passwordController_ detach];
|
| + passwordController_.reset();
|
| + tabInfoBarObserver_.reset();
|
| +
|
| + faviconDriverObserverBridge_.reset();
|
| + [openInController_ detachFromWebController];
|
| + openInController_.reset();
|
| + [autofillController_ detachFromWebState];
|
| + [suggestionController_ detachFromWebState];
|
| + if (fullScreenController_)
|
| + [self.webController removeObserver:fullScreenController_];
|
| + [fullScreenController_ invalidate];
|
| + fullScreenController_.reset();
|
| + if (overscrollActionsController_)
|
| + [self.webController removeObserver:overscrollActionsController_];
|
| + [overscrollActionsController_ invalidate];
|
| + overscrollActionsController_.reset();
|
| + [readerModeController_ detachFromWebState];
|
| + readerModeController_.reset();
|
| +
|
| + [self clearResubmitDataActionSheet];
|
| +
|
| + // Invalidate any snapshot stored for this session.
|
| + NSString* sessionID = [self currentSessionID];
|
| + DCHECK(sessionID);
|
| + [snapshotManager_ removeImageWithSessionID:sessionID];
|
| + // Reset association with the webController.
|
| + [self.webController setDelegate:nil];
|
| + [self.webController setUIDelegate:nil];
|
| +
|
| + webStateImpl_->ClearTransientContentView();
|
| + // Terminate the network activity before notifying the parent model, because
|
| + // the parent model may initiate the request context destruction.
|
| + [self terminateNetworkActivity];
|
| +
|
| + // Cancel any queued dialogs.
|
| + [self.dialogDelegate cancelDialogForTab:self];
|
| +
|
| + // These steps must be done last, and must be done in this order; nothing
|
| + // involving the tab should be done after didCloseTab:, and the
|
| + // CRWWebController backing the tab should outlive anything done during tab
|
| + // closure (since -[CRWWebController close] is what begins tearing down the
|
| + // web/ layer, and tab closure may trigger operations that need to query the
|
| + // web/ layer). The scoped strong ref is because didCloseTab: is often the
|
| + // trigger for deallocating the tab, but that can in turn cause
|
| + // CRWWebController to be deallocated before its close is called. The facade
|
| + // delegate should be torn down after |-didCloseTab:| so components triggered
|
| + // by tab closure can use the content facade, and it should be deleted before
|
| + // the web controller since the web controller owns the facade's backing
|
| + // objects.
|
| + // TODO(crbug.com/546222): Fix the need for this; TabModel should be
|
| + // responsible for making the lifetime of Tab sane, rather than allowing Tab
|
| + // to drive its own destruction.
|
| + base::scoped_nsobject<Tab> kungFuDeathGrip([self retain]);
|
| + [parentTabModel_ didCloseTab:self]; // Inform parent of tab closure.
|
| + webStateImpl_.reset();
|
| +}
|
| +
|
| +- (void)dismissModals {
|
| + [openInController_ disable];
|
| + [self.webController dismissModals];
|
| +}
|
| +
|
| +- (void)updateDesktopUserAgentForEntry:(CRWSessionEntry*)entry
|
| + fromEntry:(CRWSessionEntry*)fromEntry {
|
| + web::NavigationItemImpl* item = entry.navigationItemImpl;
|
| + web::NavigationItemImpl* fromItem = fromEntry.navigationItemImpl;
|
| + if (!item || !fromItem)
|
| + return;
|
| + bool useDesktopUserAgent = item->IsOverridingUserAgent();
|
| + if (useDesktopUserAgent != fromItem->IsOverridingUserAgent()) {
|
| + [self.webController requirePageReconstruction];
|
| + }
|
| +}
|
| +
|
| +- (CRWSessionEntry*)currentSessionEntry {
|
| + if (![self navigationManager])
|
| + return nil;
|
| + return [[self navigationManager]->GetSessionController() currentEntry];
|
| +}
|
| +
|
| +- (void)setShouldObserveInfoBarManager:(BOOL)shouldObserveInfoBarManager {
|
| + tabInfoBarObserver_->SetShouldObserveInfoBarManager(
|
| + shouldObserveInfoBarManager);
|
| +}
|
| +
|
| +- (void)setShouldObserveFaviconChanges:(BOOL)shouldObserveFaviconChanges {
|
| + if (shouldObserveFaviconChanges) {
|
| + favicon::FaviconDriver* faviconDriver =
|
| + favicon::WebFaviconDriver::FromWebState(self.webState);
|
| + // Some MockWebContents used in tests do not support the FaviconDriver.
|
| + if (faviconDriver) {
|
| + faviconDriverObserverBridge_.reset(
|
| + new FaviconDriverObserverBridge(self, faviconDriver));
|
| + }
|
| + } else {
|
| + faviconDriverObserverBridge_.reset();
|
| + }
|
| +}
|
| +
|
| +- (void)goBack {
|
| + if (self.navigationManager) {
|
| + DCHECK(self.navigationManager->CanGoBack());
|
| + base::RecordAction(UserMetricsAction("Back"));
|
| + self.navigationManager->GoBack();
|
| + }
|
| +}
|
| +
|
| +- (void)goForward {
|
| + if (self.navigationManager) {
|
| + DCHECK(self.navigationManager->CanGoForward());
|
| + base::RecordAction(UserMetricsAction("Forward"));
|
| + self.navigationManager->GoForward();
|
| + }
|
| +}
|
| +
|
| +- (BOOL)canGoBack {
|
| + return self.navigationManager && self.navigationManager->CanGoBack();
|
| +}
|
| +
|
| +- (BOOL)canGoForward {
|
| + return self.navigationManager && self.navigationManager->CanGoForward();
|
| +}
|
| +
|
| +- (void)goToEntry:(CRWSessionEntry*)entry {
|
| + DCHECK(entry);
|
| +
|
| + if (self.navigationManager) {
|
| + CRWSessionController* sessionController =
|
| + self.navigationManager->GetSessionController();
|
| + DCHECK([sessionController.entries containsObject:entry]);
|
| + NSUInteger index = [sessionController.entries indexOfObject:entry];
|
| + self.navigationManager->GoToIndex(index);
|
| + }
|
| +}
|
| +
|
| +- (void)openURLWithParams:(const web::WebState::OpenURLParams&)params {
|
| + switch (params.disposition) {
|
| + case WindowOpenDisposition::NEW_FOREGROUND_TAB:
|
| + case WindowOpenDisposition::NEW_BACKGROUND_TAB:
|
| + [parentTabModel_
|
| + insertOrUpdateTabWithURL:params.url
|
| + referrer:params.referrer
|
| + transition:params.transition
|
| + windowName:nil
|
| + opener:self
|
| + openedByDOM:NO
|
| + atIndex:TabModelConstants::kTabPositionAutomatically
|
| + inBackground:(params.disposition ==
|
| + WindowOpenDisposition::NEW_BACKGROUND_TAB)];
|
| + break;
|
| + case WindowOpenDisposition::CURRENT_TAB: {
|
| + web::NavigationManager::WebLoadParams loadParams(params.url);
|
| + loadParams.referrer = params.referrer;
|
| + loadParams.transition_type = params.transition;
|
| + loadParams.is_renderer_initiated = params.is_renderer_initiated;
|
| + self.navigationManager->LoadURLWithParams(loadParams);
|
| + } break;
|
| + default:
|
| + NOTIMPLEMENTED();
|
| + break;
|
| + };
|
| +}
|
| +
|
| +- (BOOL)openExternalURL:(const GURL&)url linkClicked:(BOOL)linkClicked {
|
| + if (!externalAppLauncher_.get())
|
| + externalAppLauncher_.reset([[ExternalAppLauncher alloc] init]);
|
| +
|
| + // This method may release CRWWebController which may cause a crash
|
| + // (crbug.com/393949).
|
| + [[self.webController retain] autorelease];
|
| +
|
| + // Make a local url copy for possible modification.
|
| + GURL finalURL = url;
|
| +
|
| + // Check if it's a direct FIDO U2F x-callback call. If so, do not open it, to
|
| + // prevent pages from spoofing requests with different origins.
|
| + if (finalURL.SchemeIs("u2f-x-callback")) {
|
| + return NO;
|
| + }
|
| +
|
| + // Check if it's a FIDO U2F call.
|
| + if (finalURL.SchemeIs("u2f")) {
|
| + // Create U2FController object lazily.
|
| + if (!U2FController_) {
|
| + U2FController_.reset([[U2FController alloc] init]);
|
| + }
|
| +
|
| + DCHECK([self navigationManager]);
|
| + GURL origin =
|
| + [self navigationManager]->GetLastCommittedItem()->GetURL().GetOrigin();
|
| +
|
| + // Compose u2f-x-callback URL and update urlToOpen.
|
| + finalURL = [U2FController_ XCallbackFromRequestURL:finalURL
|
| + originURL:origin
|
| + tabURL:self.url
|
| + tabID:self.tabId];
|
| +
|
| + if (!finalURL.is_valid()) {
|
| + return NO;
|
| + }
|
| + }
|
| +
|
| + if ([externalAppLauncher_ openURL:finalURL linkClicked:linkClicked]) {
|
| + // Clears pending navigation history after successfully launching the
|
| + // external app.
|
| + DCHECK([self navigationManager]);
|
| + [[self navigationManager]->GetSessionController()
|
| + discardNonCommittedEntries];
|
| + // Ensure the UI reflects the current entry, not the just-discarded pending
|
| + // entry.
|
| + [parentTabModel_ notifyTabChanged:self];
|
| + return YES;
|
| + }
|
| + return NO;
|
| +}
|
| +
|
| +- (void)webWillFinishHistoryNavigationFromEntry:(CRWSessionEntry*)fromEntry {
|
| + DCHECK(fromEntry);
|
| + [self updateDesktopUserAgentForEntry:self.currentSessionEntry
|
| + fromEntry:fromEntry];
|
| + [parentTabModel_ notifyTabChanged:self];
|
| +}
|
| +
|
| +- (void)webDidUpdateHistoryStateWithPageURL:(const GURL&)pageUrl {
|
| + favicon::FaviconDriver* faviconDriver =
|
| + favicon::WebFaviconDriver::FromWebState(self.webState);
|
| + if (faviconDriver) {
|
| + // Fetch the favicon for the new URL.
|
| + faviconDriver->FetchFavicon(pageUrl);
|
| + }
|
| + [parentTabModel_ notifyTabChanged:self];
|
| +}
|
| +
|
| +// Records the state (scroll position, form values, whatever can be
|
| +// harvested) from the current page into the current session entry.
|
| +- (void)recordStateInHistory {
|
| + // Link-loading prerender tab may not have correct zoom value during the load.
|
| + if (!self.isLinkLoadingPrerenderTab)
|
| + [self.webController recordStateInHistory];
|
| +}
|
| +
|
| +// Records metric for the interface's orientation.
|
| +- (void)recordInterfaceOrientation {
|
| + switch ([[UIApplication sharedApplication] statusBarOrientation]) {
|
| + case UIInterfaceOrientationPortrait:
|
| + case UIInterfaceOrientationPortraitUpsideDown:
|
| + UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", YES);
|
| + break;
|
| + case UIInterfaceOrientationLandscapeLeft:
|
| + case UIInterfaceOrientationLandscapeRight:
|
| + UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", NO);
|
| + break;
|
| + case UIInterfaceOrientationUnknown:
|
| + // TODO(crbug.com/228832): Convert from a boolean histogram to an
|
| + // enumerated histogram and log this case as well.
|
| + break;
|
| + }
|
| +}
|
| +
|
| +- (OpenInController*)openInController {
|
| + if (!openInController_) {
|
| + openInController_.reset([[OpenInController alloc]
|
| + initWithRequestContext:browserState_->GetRequestContext()
|
| + webController:self.webController]);
|
| + }
|
| + return openInController_.get();
|
| +}
|
| +
|
| +- (void)closeThisTab {
|
| + if (!parentTabModel_)
|
| + return;
|
| +
|
| + NSUInteger index = [parentTabModel_ indexOfTab:self];
|
| + if (index != NSNotFound)
|
| + [parentTabModel_ closeTabAtIndex:index];
|
| +}
|
| +
|
| +- (id<CRWNativeContent>)controllerForUnhandledContentAtURL:(const GURL&)url {
|
| + DownloadManagerController* downloadController =
|
| + [[[DownloadManagerController alloc]
|
| + initWithURL:url
|
| + requestContextGetter:browserState_->GetRequestContext()
|
| + storeKitLauncher:self.storeKitLauncher] autorelease];
|
| + [downloadController start];
|
| + return downloadController;
|
| +}
|
| +
|
| +- (void)handleExportableFile:(net::HttpResponseHeaders*)headers {
|
| + // Only "application/pdf" is supported for now.
|
| + if (self.webState->GetContentsMimeType() != "application/pdf")
|
| + return;
|
| +
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kTabIsShowingExportableNotificationForCrashReporting
|
| + object:self];
|
| + // Try to generate a filename by first looking at |content_disposition_|, then
|
| + // at the last component of |self.url| and if both of these fail use the
|
| + // default filename "document".
|
| + std::string contentDisposition;
|
| + if (headers)
|
| + headers->GetNormalizedHeader("content-disposition", &contentDisposition);
|
| + std::string defaultFilename =
|
| + l10n_util::GetStringUTF8(IDS_IOS_OPEN_IN_FILE_DEFAULT_TITLE);
|
| + base::string16 filename =
|
| + net::GetSuggestedFilename(self.url, contentDisposition,
|
| + "", // referrer-charset
|
| + "", // suggested-name
|
| + "application/pdf", // mime-type
|
| + defaultFilename);
|
| + [[self openInController]
|
| + enableWithDocumentURL:self.url
|
| + suggestedFilename:base::SysUTF16ToNSString(filename)];
|
| +}
|
| +
|
| +- (void)countMainFrameLoad {
|
| + if ([self isPrerenderTab] || [self url].SchemeIs(kChromeUIScheme)) {
|
| + return;
|
| + }
|
| + base::RecordAction(UserMetricsAction("MobilePageLoaded"));
|
| +}
|
| +
|
| +- (void)applicationDidBecomeActive {
|
| + if (requireReloadAfterBecomingActive_) {
|
| + if (visible_) {
|
| + [self.webController reload];
|
| + } else {
|
| + [self.webController requirePageReload];
|
| + }
|
| + requireReloadAfterBecomingActive_ = NO;
|
| + }
|
| +}
|
| +
|
| +#pragma mark -
|
| +#pragma mark FindInPageControllerDelegate
|
| +
|
| +- (void)willAdjustScrollPosition {
|
| + // Skip the next attempt to correct the scroll offset for the toolbar height.
|
| + // Used when programatically scrolling down the y offset.
|
| + [fullScreenController_ shouldSkipNextScrollOffsetForHeader];
|
| +}
|
| +
|
| +#pragma mark -
|
| +#pragma mark FullScreen
|
| +
|
| +- (void)updateFullscreenWithToolbarVisible:(BOOL)visible {
|
| + [fullScreenController_ moveHeaderToRestingPosition:visible];
|
| +}
|
| +
|
| +#pragma mark -
|
| +#pragma mark Reader mode
|
| +
|
| +- (UIView*)superviewForReaderModePanel {
|
| + return self.view;
|
| +}
|
| +
|
| +- (ReaderModeController*)readerModeController {
|
| + return readerModeController_.get();
|
| +}
|
| +
|
| +- (BOOL)canSwitchToReaderMode {
|
| + // Only if the page is loaded and the page passes suitability checks.
|
| + ReaderModeController* controller = self.readerModeController;
|
| + return controller && controller.checker->CanSwitchToReaderMode();
|
| +}
|
| +
|
| +- (void)switchToReaderMode {
|
| + DCHECK(self.view);
|
| + [self.readerModeController switchToReaderMode];
|
| +}
|
| +
|
| +- (void)loadReaderModeHTML:(NSString*)html forURL:(const GURL&)url {
|
| + // Before changing the HTML on the current page, this checks that the URL has
|
| + // not changed since reader mode was requested. This could happen for example
|
| + // if the page does a late redirect itself or if the user tapped on a link and
|
| + // triggered reader mode before the page load is detected by webState.
|
| + if (url == self.url)
|
| + [self.webController loadHTMLForCurrentURL:html];
|
| +
|
| + [self.readerModeController exitReaderMode];
|
| +}
|
| +
|
| +#pragma mark -
|
| +
|
| +- (void)openAppStore:(NSString*)appId {
|
| + [storeKitLauncher_ openAppStore:appId];
|
| +}
|
| +
|
| +- (std::vector<GURL>)currentRedirectedUrls {
|
| + DCHECK([self navigationManager]);
|
| + return
|
| + [[self navigationManager]->GetSessionController() currentRedirectedUrls];
|
| +}
|
| +
|
| +- (BOOL)useDesktopUserAgent {
|
| + web::NavigationItem* currentItem = self.currentSessionEntry.navigationItem;
|
| + return currentItem && currentItem->IsOverridingUserAgent();
|
| +}
|
| +
|
| +- (void)enableDesktopUserAgent {
|
| + DCHECK_EQ(self.useDesktopUserAgent, NO);
|
| + DCHECK([self navigationManager]);
|
| + [[self navigationManager]->GetSessionController()
|
| + useDesktopUserAgentForNextPendingEntry];
|
| +}
|
| +
|
| +- (void)reloadForDesktopUserAgent {
|
| + // |loadWithParams| will recreate the removed UIWebView.
|
| + [self.webController requirePageReconstruction];
|
| +
|
| + // TODO(crbug.com/228171): A hack in session_controller -addPendingEntry
|
| + // discusses making tab responsible for distinguishing history stack
|
| + // navigation from new navigations. Because we want a new navigation here, we
|
| + // use |PAGE_TRANSITION_FORM_SUBMIT|. When session_controller changes, so
|
| + // should this.
|
| + ui::PageTransition transition =
|
| + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORM_SUBMIT);
|
| + DCHECK([self navigationManager]);
|
| + CRWSessionController* sessionController =
|
| + [self navigationManager]->GetSessionController();
|
| + CRWSessionEntry* lastUserEntry = [sessionController lastUserEntry];
|
| + if (!lastUserEntry)
|
| + return;
|
| +
|
| + // |originalUrl| will be empty if a page was open by DOM.
|
| + GURL reloadURL(lastUserEntry.originalUrl);
|
| + if (reloadURL.is_empty()) {
|
| + DCHECK(sessionController.openedByDOM);
|
| + reloadURL = [lastUserEntry navigationItem]->GetVirtualURL();
|
| + }
|
| +
|
| + web::NavigationManager::WebLoadParams params(reloadURL);
|
| + params.referrer = lastUserEntry.navigationItem->GetReferrer();
|
| + params.transition_type = transition;
|
| + if (self.navigationManager)
|
| + self.navigationManager->LoadURLWithParams(params);
|
| +}
|
| +
|
| +- (id<SnapshotOverlayProvider>)snapshotOverlayProvider {
|
| + return snapshotOverlayProvider_.get();
|
| +}
|
| +
|
| +- (void)setSnapshotOverlayProvider:
|
| + (id<SnapshotOverlayProvider>)snapshotOverlayProvider {
|
| + snapshotOverlayProvider_.reset(snapshotOverlayProvider);
|
| +}
|
| +
|
| +- (BlockedPopupHandler*)popupHandler {
|
| + if (!popupHandler_.get()) {
|
| + popupHandler_.reset(new BlockedPopupHandler(self.browserState));
|
| + popupHandler_->SetDelegate(self);
|
| + }
|
| + return popupHandler_.get();
|
| +}
|
| +
|
| +- (void)evaluateU2FResultFromURL:(const GURL&)URL {
|
| + DCHECK(U2FController_);
|
| + [U2FController_ evaluateU2FResultFromU2FURL:URL webState:self.webState];
|
| +}
|
| +
|
| +- (NSString*)currentSessionID {
|
| + return [self tabId];
|
| +}
|
| +
|
| +#pragma mark - WebDelegate protocol methods.
|
| +
|
| +- (CRWWebController*)webPageOrderedOpen:(const GURL&)URL
|
| + referrer:(const web::Referrer&)referrer
|
| + windowName:(NSString*)windowName
|
| + inBackground:(BOOL)inBackground {
|
| + // Prerendered Tabs cannot open child windows.
|
| + // TODO(crbug.com/661673): Should we kill prerendering in this case?
|
| + if (!parentTabModel_)
|
| + return nil;
|
| + if (!inBackground)
|
| + [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
|
| + // Open a new tab or update an existing one. Tabs opened from a web page are
|
| + Tab* tab = [parentTabModel_
|
| + insertOrUpdateTabWithURL:URL
|
| + referrer:referrer
|
| + transition:ui::PAGE_TRANSITION_LINK
|
| + windowName:windowName
|
| + opener:self
|
| + openedByDOM:YES
|
| + atIndex:TabModelConstants::kTabPositionAutomatically
|
| + inBackground:inBackground];
|
| + return tab.webController;
|
| +}
|
| +
|
| +// This can be combined with the other versions once Tab loading is separated
|
| +// from creation.
|
| +- (CRWWebController*)webPageOrderedOpen {
|
| + [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
|
| +
|
| + Tab* tab = [parentTabModel_
|
| + insertBlankTabWithTransition:ui::PAGE_TRANSITION_LINK
|
| + opener:self
|
| + openedByDOM:YES
|
| + atIndex:TabModelConstants::kTabPositionAutomatically
|
| + inBackground:NO];
|
| + return tab.webController;
|
| +}
|
| +
|
| +- (void)webController:(CRWWebController*)webController
|
| + onFormResubmissionForRequest:(NSURLRequest*)request
|
| + continueBlock:(ProceduralBlock)continueBlock
|
| + cancelBlock:(ProceduralBlock)cancelBlock {
|
| + // Clear the resubmit data action sheet before loading a new request.
|
| + [self clearResubmitDataActionSheet];
|
| + resubmitDataController_.reset([[ResubmitDataController alloc]
|
| + initWithContinueBlock:continueBlock
|
| + cancelBlock:cancelBlock]);
|
| + [self showResubmitDataActionSheet];
|
| +}
|
| +
|
| +- (void)showResubmitDataActionSheet {
|
| + // Return early if the CRWWebController has been closed or web
|
| + // usage is disabled on it.
|
| + if (![self.webController webUsageEnabled])
|
| + return;
|
| + // Check to see if an action sheet can be shown.
|
| + if (self.webState && [self.webState->GetView() window]) {
|
| + // Display the action sheet with the arrow pointing at the top center of the
|
| + // web contents.
|
| + CGFloat xOrigin = CGRectGetMidX(self.webState->GetView().frame);
|
| + CGFloat yOrigin = CGRectGetMinY(self.webState->GetView().frame) +
|
| + [[self fullScreenControllerDelegate] headerHeight];
|
| + [resubmitDataController_
|
| + presentActionSheetFromRect:CGRectMake(xOrigin, yOrigin, 1, 1)
|
| + inView:self.webState->GetView()];
|
| + showResubmitDataActionSheetAttempt_ = 0;
|
| + return;
|
| + }
|
| +
|
| + // The resubmit data action cannot be presented as the |contentView_| was not
|
| + // yet added to the window. Retry after |kDelayBetweenAttemptsNanoSecs|.
|
| + // TODO(crbug.com/227868): The strategy to poll until the resubmit data action
|
| + // sheet can be presented is a temporary workaround. This needs to be
|
| + // refactored to match the Chromium implementation:
|
| + // * web_controller should notify/ the BVC once an action sheet should be
|
| + // shown.
|
| + // * BVC should present the action sheet and then trigger the reload
|
| + const NSUInteger kMaximumNumberAttempts = 10;
|
| + // 400 milliseconds
|
| + const int64_t kDelayBetweenAttemptsNanoSecs = 0.4 * NSEC_PER_SEC;
|
| + if (showResubmitDataActionSheetAttempt_ >= kMaximumNumberAttempts) {
|
| + NOTREACHED();
|
| + [self clearResubmitDataActionSheet];
|
| + return;
|
| + }
|
| + base::WeakNSObject<Tab> weakTab(self);
|
| + dispatch_after(
|
| + dispatch_time(DISPATCH_TIME_NOW, kDelayBetweenAttemptsNanoSecs),
|
| + dispatch_get_main_queue(), ^{
|
| + [weakTab showResubmitDataActionSheet];
|
| + });
|
| + showResubmitDataActionSheetAttempt_++;
|
| +}
|
| +
|
| +- (void)clearResubmitDataActionSheet {
|
| + [resubmitDataController_ dismissActionSheet];
|
| + resubmitDataController_.reset();
|
| + showResubmitDataActionSheetAttempt_ = 0;
|
| +}
|
| +
|
| +// The web page wants to close its own window.
|
| +- (void)webPageOrderedClose {
|
| + // Only allow a web page to close itself if it was opened by DOM, or if there
|
| + // are no navigation items.
|
| + DCHECK([[self navigationManager]->GetSessionController() isOpenedByDOM] ||
|
| + ![self navigationManager]->GetItemCount());
|
| + [self closeThisTab];
|
| +}
|
| +
|
| +// This method is invoked whenever the system believes the URL is about to
|
| +// change, or immediately after any unexpected change of the URL. The apparent
|
| +// destination URL is included in the |url| parameter.
|
| +// Warning: because of the present design it is possible for malicious websites
|
| +// to invoke superflous instances of this delegate with artibrary URLs.
|
| +// Ensure there is nothing here that could be a risk to the user beyond mild
|
| +// confusion in that event (e.g. progress bar starting unexpectedly).
|
| +- (void)webWillAddPendingURL:(const GURL&)url
|
| + transition:(ui::PageTransition)transition {
|
| + DCHECK(self.webController.loadPhase == web::LOAD_REQUESTED);
|
| + DCHECK([self navigationManager]);
|
| + [self clearResubmitDataActionSheet];
|
| +
|
| + // Move the toolbar to visible during page load.
|
| + [fullScreenController_ disableFullScreen];
|
| +
|
| + isUserNavigationEvent_ =
|
| + (transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) == 0;
|
| + // Check for link-follow clobbers. These are changes where there is no
|
| + // pending entry (since that means the change wasn't caused by this class),
|
| + // and where the URL changes (to avoid counting page resurrection).
|
| + // TODO(crbug.com/546401): Consider moving this into NavigationManager, or
|
| + // into a NavigationManager observer callback, so it doesn't need to be
|
| + // checked in several places.
|
| + if (isUserNavigationEvent_ && !isPrerenderTab_ &&
|
| + ![self navigationManager]->GetPendingItem() && url != self.url) {
|
| + base::RecordAction(UserMetricsAction("MobileTabClobbered"));
|
| + if ([parentTabModel_ tabUsageRecorder])
|
| + [parentTabModel_ tabUsageRecorder]->RecordPageLoadStart(self);
|
| + }
|
| + if (![self navigationManager]->GetPendingItem()) {
|
| + // Reset |isVoiceSearchResultsTab| since a new page is being navigated to.
|
| + self.isVoiceSearchResultsTab = NO;
|
| + }
|
| +}
|
| +
|
| +- (void)webDidAddPendingURL {
|
| + [parentTabModel_ notifyTabChanged:self];
|
| + [openInController_ disable];
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:
|
| + kTabClosingCurrentDocumentNotificationForCrashReporting
|
| + object:self];
|
| + [metricsClientManager_ pageLoadStarted:self.url];
|
| +}
|
| +
|
| +- (void)webCancelStartLoadingRequest {
|
| + DCHECK(self.webController.loadPhase == web::PAGE_LOADED);
|
| + [parentTabModel_ notifyTabChanged:self];
|
| +}
|
| +
|
| +// Called when the page URL has changed.
|
| +- (void)webDidStartLoadingURL:(const GURL&)currentUrl
|
| + shouldUpdateHistory:(BOOL)updateHistory {
|
| + DCHECK(self.webController.loadPhase == web::PAGE_LOADING);
|
| + DCHECK([self navigationManager]);
|
| + // |webWillStartLoading:| is not called for native page loads.
|
| + // TODO(crbug.com/381201): Move this call there once that bug is fixed so that
|
| + // |disableFullScreen| is called only from one place.
|
| + [fullScreenController_ disableFullScreen];
|
| + [findInPageController_ disableFindInPageWithCompletionHandler:nil];
|
| + [autoReloadBridge_ loadStartedForURL:currentUrl];
|
| +
|
| + if (isUserNavigationEvent_) {
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kTabModelUserNavigatedNotification
|
| + object:self];
|
| + }
|
| + if (parentTabModel_) {
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kTabModelTabWillStartLoadingNotification
|
| + object:parentTabModel_
|
| + userInfo:[NSDictionary
|
| + dictionaryWithObject:self
|
| + forKey:kTabModelTabKey]];
|
| + }
|
| + favicon::FaviconDriver* faviconDriver =
|
| + favicon::WebFaviconDriver::FromWebState(self.webState);
|
| + if (faviconDriver) {
|
| + faviconDriver->FetchFavicon(currentUrl);
|
| + }
|
| + [parentTabModel_ notifyTabChanged:self];
|
| + if (parentTabModel_) {
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kTabModelTabDidStartLoadingNotification
|
| + object:parentTabModel_
|
| + userInfo:[NSDictionary
|
| + dictionaryWithObject:self
|
| + forKey:kTabModelTabKey]];
|
| + }
|
| +
|
| + [parentTabModel_ navigationCommittedInTab:self];
|
| + if (updateHistory) {
|
| + [self addCurrentEntryToHistoryDB];
|
| + [self countMainFrameLoad];
|
| + }
|
| +
|
| + // Sending a notification about the url change for crash reporting.
|
| + // TODO(crbug.com/661675): Consider using the navigation entry committed
|
| + // notification now that it's in the right place.
|
| + NSString* urlString = base::SysUTF8ToNSString(currentUrl.spec());
|
| + if ([urlString length]) {
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kTabUrlStartedLoadingNotificationForCrashReporting
|
| + object:self
|
| + userInfo:[NSDictionary dictionaryWithObject:urlString
|
| + forKey:kTabUrlKey]];
|
| + }
|
| +}
|
| +
|
| +// Called when the page finishes loading, with the URL and a boolean indicating
|
| +// if the page was successfully loaded.
|
| +- (void)webDidFinishWithURL:(const GURL&)url loadSuccess:(BOOL)loadSuccess {
|
| + DCHECK(self.webController.loadPhase == web::PAGE_LOADED);
|
| +
|
| + // Cancel prerendering if response is "application/octet-stream". It can be a
|
| + // video file which should not be played from preload tab (crbug.com/436813).
|
| + if (isPrerenderTab_ &&
|
| + self.webState->GetContentsMimeType() == "application/octet-stream") {
|
| + [delegate_ discardPrerender];
|
| + }
|
| +
|
| + bool wasPost = false;
|
| + if (self.currentSessionEntry)
|
| + wasPost = self.currentSessionEntry.navigationItem->HasPostData();
|
| + if (loadSuccess)
|
| + [autoReloadBridge_ loadFinishedForURL:url wasPost:wasPost];
|
| + else
|
| + [autoReloadBridge_ loadFailedForURL:url wasPost:wasPost];
|
| + [webControllerSnapshotHelper_ setSnapshotCoalescingEnabled:YES];
|
| + if (!loadSuccess) {
|
| + [fullScreenController_ disableFullScreen];
|
| + }
|
| + [self recordInterfaceOrientation];
|
| + navigation_metrics::OriginsSeenService* originsSeenService =
|
| + IOSChromeOriginsSeenServiceFactory::GetForBrowserState(self.browserState);
|
| + bool already_seen = originsSeenService->Insert(url::Origin::Origin(url));
|
| + navigation_metrics::RecordMainFrameNavigation(
|
| + url, true, self.browserState->IsOffTheRecord(), already_seen);
|
| +
|
| + if (loadSuccess) {
|
| + scoped_refptr<net::HttpResponseHeaders> headers =
|
| + self.webStateImpl->GetHttpResponseHeaders();
|
| + [self handleExportableFile:headers.get()];
|
| + }
|
| +
|
| + [metricsClientManager_ pageLoadCompleted];
|
| + [parentTabModel_ notifyTabChanged:self];
|
| +
|
| + if (parentTabModel_) {
|
| + if ([parentTabModel_ tabUsageRecorder])
|
| + [parentTabModel_ tabUsageRecorder]->RecordPageLoadDone(self, loadSuccess);
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kTabModelTabDidFinishLoadingNotification
|
| + object:parentTabModel_
|
| + userInfo:[NSDictionary
|
| + dictionaryWithObjectsAndKeys:
|
| + self, kTabModelTabKey,
|
| + [NSNumber numberWithBool:loadSuccess],
|
| + kTabModelPageLoadSuccess, nil]];
|
| + }
|
| + [[OmniboxGeolocationController sharedInstance]
|
| + finishPageLoadForTab:self
|
| + loadSuccess:loadSuccess];
|
| +
|
| + // Always take snapshots on iPad if the tab switcher is enabled.
|
| + // If the tab switcher is not enabled, don't take snapshot of chrome scheme
|
| + // pages.
|
| + BOOL takeSnapshotOnIpad =
|
| + IsIPadIdiom() && (experimental_flags::IsTabSwitcherEnabled() ||
|
| + !web::GetWebClient()->IsAppSpecificURL(url));
|
| + // Always take snapshot on iPhone.
|
| + BOOL takeSnapshot = !IsIPadIdiom() || takeSnapshotOnIpad;
|
| + if (loadSuccess && takeSnapshot) {
|
| + [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
|
| + }
|
| + [webControllerSnapshotHelper_ setSnapshotCoalescingEnabled:NO];
|
| +}
|
| +
|
| +- (void)webLoadCancelled:(const GURL&)url {
|
| + // When a load is cancelled, this is the maximum that a page will ever load.
|
| + [fullScreenController_ enableFullScreen];
|
| + [parentTabModel_ notifyTabChanged:self];
|
| +}
|
| +
|
| +- (BOOL)webController:(CRWWebController*)webController
|
| + shouldOpenExternalURL:(const GURL&)URL {
|
| + if (isPrerenderTab_ && !isLinkLoadingPrerenderTab_) {
|
| + [delegate_ discardPrerender];
|
| + return NO;
|
| + }
|
| + return YES;
|
| +}
|
| +
|
| +- (void)webController:(CRWWebController*)webController
|
| + titleDidChange:(NSString*)title {
|
| + NSString* oldTitle = [self title];
|
| + BOOL isTitleChanged = (!oldTitle && title) || (oldTitle && !title) ||
|
| + (![oldTitle isEqualToString:title]);
|
| + if (isTitleChanged) {
|
| + [self updateTitle:title];
|
| + [parentTabModel_ notifyTabChanged:self];
|
| + }
|
| +}
|
| +
|
| +- (BOOL)urlTriggersNativeAppLaunch:(const GURL&)url
|
| + sourceURL:(const GURL&)sourceURL
|
| + linkClicked:(BOOL)linkClicked {
|
| + // Don't open any native app directly when prerendering or from Incognito.
|
| + if (isPrerenderTab_ || self.browserState->IsOffTheRecord())
|
| + return NO;
|
| +
|
| + base::scoped_nsprotocol<id<NativeAppMetadata>> metadata(
|
| + [ios::GetChromeBrowserProvider()->GetNativeAppWhitelistManager()
|
| + newNativeAppForURL:url]);
|
| + if (![metadata shouldAutoOpenLinks])
|
| + return NO;
|
| +
|
| + AuthenticationService* authenticationService =
|
| + AuthenticationServiceFactory::GetForBrowserState(self.browserState);
|
| + ChromeIdentity* identity = authenticationService->GetAuthenticatedIdentity();
|
| +
|
| + // Attempts to open external app without x-callback.
|
| + if ([self openExternalURL:[metadata launchURLWithURL:url identity:identity]
|
| + linkClicked:linkClicked]) {
|
| + return YES;
|
| + }
|
| +
|
| + // Auto-open didn't work. Reset the auto-open flag.
|
| + [metadata unsetShouldAutoOpenLinks];
|
| + return NO;
|
| +}
|
| +
|
| +- (double)lastVisitedTimestamp {
|
| + DCHECK([self navigationManager]);
|
| + return
|
| + [[self navigationManager]->GetSessionController() lastVisitedTimestamp];
|
| +}
|
| +
|
| +- (void)updateLastVisitedTimestamp {
|
| + // Stores this information in self.history and it will be written into disc
|
| + // with other information when needed.
|
| + DCHECK([self navigationManager]);
|
| + [[self navigationManager]->GetSessionController()
|
| + setLastVisitedTimestamp:[[NSDate date] timeIntervalSince1970]];
|
| +}
|
| +
|
| +- (infobars::InfoBarManager*)infoBarManager {
|
| + DCHECK(self.webState);
|
| + return InfoBarManagerImpl::FromWebState(self.webState);
|
| +}
|
| +
|
| +- (NSArray*)snapshotOverlays {
|
| + return [snapshotOverlayProvider_ snapshotOverlaysForTab:self];
|
| +}
|
| +
|
| +- (void)webViewRemoved {
|
| + [openInController_ disable];
|
| +}
|
| +
|
| +- (BOOL)webController:(CRWWebController*)webController
|
| + shouldOpenURL:(const GURL&)url
|
| + mainDocumentURL:(const GURL&)mainDocumentURL
|
| + linkClicked:(BOOL)linkClicked {
|
| + // chrome:// URLs are only allowed if the mainDocumentURL is also a chrome://
|
| + // URL.
|
| + if (url.SchemeIs(kChromeUIScheme) &&
|
| + !mainDocumentURL.SchemeIs(kChromeUIScheme)) {
|
| + return NO;
|
| + }
|
| +
|
| + // Always allow frame loads.
|
| + BOOL isFrameLoad = (url != mainDocumentURL);
|
| + if (isFrameLoad) {
|
| + return YES;
|
| + }
|
| +
|
| + // TODO(crbug.com/546402): If this turns out to be useful, find a less hacky
|
| + // hook point to send this from.
|
| + NSString* urlString = base::SysUTF8ToNSString(url.spec());
|
| + if ([urlString length]) {
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kTabUrlMayStartLoadingNotificationForCrashReporting
|
| + object:self
|
| + userInfo:[NSDictionary dictionaryWithObject:urlString
|
| + forKey:kTabUrlKey]];
|
| + }
|
| +
|
| + return YES;
|
| +}
|
| +
|
| +- (void)webController:(CRWWebController*)webController
|
| + retrievePlaceholderOverlayImage:(void (^)(UIImage*))block {
|
| + NSString* sessionID = [self currentSessionID];
|
| + // The snapshot is always grey, even if |useGreyImageCache_| is NO, as this
|
| + // overlay represents an out-of-date website and is shown only until the
|
| + // has begun loading. However, if |useGreyImageCache_| is YES, the grey image
|
| + // is already cached in memory for swiping, and a cache miss is acceptable.
|
| + // In other cases, such as during startup, either disk access or a greyspace
|
| + // conversion is required, as there will be no grey snapshots in memory.
|
| + if (useGreyImageCache_) {
|
| + [snapshotManager_ greyImageForSessionID:sessionID callback:block];
|
| + } else {
|
| + [webControllerSnapshotHelper_
|
| + retrieveGreySnapshotForWebController:webController
|
| + sessionID:sessionID
|
| + withOverlays:[self snapshotOverlays]
|
| + callback:block];
|
| + }
|
| +}
|
| +
|
| +- (UIImage*)updateSnapshotWithOverlay:(BOOL)shouldAddOverlay
|
| + visibleFrameOnly:(BOOL)visibleFrameOnly {
|
| + NSArray* overlays = shouldAddOverlay ? [self snapshotOverlays] : nil;
|
| + UIImage* snapshot = [webControllerSnapshotHelper_
|
| + updateSnapshotForWebController:self.webController
|
| + sessionID:self.tabId
|
| + withOverlays:overlays
|
| + visibleFrameOnly:visibleFrameOnly];
|
| + [parentTabModel_ notifyTabSnapshotChanged:self withImage:snapshot];
|
| + return snapshot;
|
| +}
|
| +
|
| +- (UIImage*)generateSnapshotWithOverlay:(BOOL)shouldAddOverlay
|
| + visibleFrameOnly:(BOOL)visibleFrameOnly {
|
| + NSArray* overlays = shouldAddOverlay ? [self snapshotOverlays] : nil;
|
| + return [webControllerSnapshotHelper_
|
| + generateSnapshotForWebController:self.webController
|
| + withOverlays:overlays
|
| + visibleFrameOnly:visibleFrameOnly];
|
| +}
|
| +
|
| +- (void)setSnapshotCoalescingEnabled:(BOOL)snapshotCoalescingEnabled {
|
| + [webControllerSnapshotHelper_
|
| + setSnapshotCoalescingEnabled:snapshotCoalescingEnabled];
|
| +}
|
| +
|
| +- (CGRect)snapshotContentArea {
|
| + CGRect snapshotContentArea = CGRectZero;
|
| + if (self.tabSnapshottingDelegate) {
|
| + snapshotContentArea =
|
| + [self.tabSnapshottingDelegate snapshotContentAreaForTab:self];
|
| + } else {
|
| + UIEdgeInsets visiblePageInsets = UIEdgeInsetsMake(
|
| + [self headerHeightForWebController:self.webController], 0.0, 0.0, 0.0);
|
| + snapshotContentArea = UIEdgeInsetsInsetRect(self.webController.view.bounds,
|
| + visiblePageInsets);
|
| + }
|
| + return snapshotContentArea;
|
| +}
|
| +
|
| +- (void)willUpdateSnapshot {
|
| + if ([[self.webController nativeController]
|
| + respondsToSelector:@selector(willUpdateSnapshot)]) {
|
| + [[self.webController nativeController] willUpdateSnapshot];
|
| + }
|
| + [overscrollActionsController_ clear];
|
| +}
|
| +
|
| +- (void)setWebUsageEnabled:(BOOL)webUsageEnabled {
|
| + [self.webController setWebUsageEnabled:webUsageEnabled];
|
| +}
|
| +
|
| +- (void)webControllerDidSuppressDialog:(id)webController {
|
| + DCHECK(isPrerenderTab_);
|
| + [delegate_ discardPrerender];
|
| +}
|
| +
|
| +- (BOOL)webController:(CRWWebController*)webController
|
| + shouldBlockPopupWithURL:(const GURL&)popupURL
|
| + sourceURL:(const GURL&)sourceURL {
|
| + ContentSetting setting =
|
| + ios::HostContentSettingsMapFactory::GetForBrowserState(browserState_)
|
| + ->GetContentSetting(sourceURL, sourceURL,
|
| + CONTENT_SETTINGS_TYPE_POPUPS, std::string());
|
| +
|
| + return setting != CONTENT_SETTING_ALLOW;
|
| +}
|
| +
|
| +- (void)webController:(CRWWebController*)webController
|
| + didBlockPopup:(const web::BlockedPopupInfo&)blockedPopupInfo {
|
| + [self popupHandler]->HandlePopup(blockedPopupInfo);
|
| +}
|
| +
|
| +- (CGFloat)headerHeightForWebController:(CRWWebController*)webController {
|
| + return [fullScreenControllerDelegate_ headerHeight];
|
| +}
|
| +
|
| +- (void)webControllerDidUpdateSSLStatusForCurrentNavigationItem:
|
| + (CRWWebController*)webController {
|
| + // Disable fullscreen if SSL cert is invalid.
|
| + web::NavigationItem* item = [self navigationManager]->GetTransientItem();
|
| + web::SecurityStyle securityStyle =
|
| + item ? item->GetSSL().security_style : web::SECURITY_STYLE_UNKNOWN;
|
| + if (securityStyle == web::SECURITY_STYLE_AUTHENTICATION_BROKEN) {
|
| + [fullScreenController_ disableFullScreen];
|
| + }
|
| +
|
| + [parentTabModel_ notifyTabChanged:self];
|
| + [self updateFullscreenWithToolbarVisible:YES];
|
| +}
|
| +
|
| +- (void)webControllerWebProcessDidCrash:(CRWWebController*)webController {
|
| + if (browserState_ && !browserState_->IsOffTheRecord()) {
|
| + // Report the crash.
|
| + GetApplicationContext()
|
| + ->GetMetricsServicesManager()
|
| + ->OnRendererProcessCrash();
|
| +
|
| + // Log the tab state for the termination.
|
| + RendererTerminationTabState tab_state =
|
| + visible_ ? RendererTerminationTabState::FOREGROUND_TAB_FOREGROUND_APP
|
| + : RendererTerminationTabState::BACKGROUND_TAB_FOREGROUND_APP;
|
| + if ([UIApplication sharedApplication].applicationState ==
|
| + UIApplicationStateBackground) {
|
| + tab_state =
|
| + visible_ ? RendererTerminationTabState::FOREGROUND_TAB_BACKGROUND_APP
|
| + : RendererTerminationTabState::BACKGROUND_TAB_BACKGROUND_APP;
|
| + }
|
| + UMA_HISTOGRAM_ENUMERATION(
|
| + kRendererTerminationStateHistogram, static_cast<int>(tab_state),
|
| + static_cast<int>(
|
| + RendererTerminationTabState::TERMINATION_TAB_STATE_COUNT));
|
| + if ([parentTabModel_ tabUsageRecorder])
|
| + [parentTabModel_ tabUsageRecorder]->RendererTerminated(self, visible_);
|
| + }
|
| +
|
| + BOOL applicationIsBackgrounded =
|
| + [UIApplication sharedApplication].applicationState ==
|
| + UIApplicationStateBackground;
|
| + if (visible_) {
|
| + if (!applicationIsBackgrounded) {
|
| + base::WeakNSObject<Tab> weakSelf(self);
|
| + base::scoped_nsobject<SadTabView> sadTabView(
|
| + [[SadTabView alloc] initWithReloadHandler:^{
|
| + base::scoped_nsobject<Tab> strongSelf([weakSelf retain]);
|
| + [strongSelf reload];
|
| + }]);
|
| + base::scoped_nsobject<CRWContentView> contentView(
|
| + [[CRWGenericContentView alloc] initWithView:sadTabView]);
|
| + self.webState->ShowTransientContentView(contentView);
|
| + [fullScreenController_ disableFullScreen];
|
| + }
|
| + } else {
|
| + [self.webController requirePageReload];
|
| + }
|
| + // Returning to the app (after the renderer crashed in the background) and
|
| + // having the page reload is much less confusing for the user.
|
| + // Note: Given that the tab is visible, calling |requirePageReload| will not
|
| + // work when the app becomes active because there is nothing to trigger
|
| + // a view redisplay in that scenario.
|
| + requireReloadAfterBecomingActive_ = visible_ && applicationIsBackgrounded;
|
| +}
|
| +
|
| +- (void)webController:(CRWWebController*)webController
|
| + didLoadPassKitObject:(NSData*)data {
|
| + [self.passKitDialogProvider presentPassKitDialog:data];
|
| +}
|
| +
|
| +#pragma mark - WebUserInterfaceDelegate methods.
|
| +
|
| +- (void)webController:(CRWWebController*)webController
|
| + runAuthDialogForProtectionSpace:(NSURLProtectionSpace*)protectionSpace
|
| + proposedCredential:(NSURLCredential*)credential
|
| + completionHandler:
|
| + (void (^)(NSString* user, NSString* password))handler {
|
| + if (self.dialogDelegate) {
|
| + [self.dialogDelegate tab:self
|
| + runAuthDialogForProtectionSpace:protectionSpace
|
| + proposedCredential:credential
|
| + completionHandler:handler];
|
| + } else if (handler) {
|
| + handler(nil, nil);
|
| + }
|
| +}
|
| +
|
| +- (void)cancelDialogsForWebController:(CRWWebController*)webController {
|
| + [self.dialogDelegate cancelDialogForTab:self];
|
| +}
|
| +
|
| +#pragma mark - PrerenderDelegate
|
| +
|
| +- (void)discardPrerender {
|
| + DCHECK(isPrerenderTab_);
|
| + [delegate_ discardPrerender];
|
| +}
|
| +
|
| +- (BOOL)isPrerenderTab {
|
| + return isPrerenderTab_;
|
| +}
|
| +
|
| +#pragma mark - ManageAccountsDelegate
|
| +
|
| +- (void)onManageAccounts {
|
| + if (isPrerenderTab_) {
|
| + [delegate_ discardPrerender];
|
| + return;
|
| + }
|
| + if (self != [parentTabModel_ currentTab])
|
| + return;
|
| +
|
| + signin_metrics::LogAccountReconcilorStateOnGaiaResponse(
|
| + ios::AccountReconcilorFactory::GetForBrowserState(browserState_)
|
| + ->GetState());
|
| + base::scoped_nsobject<GenericChromeCommand> command(
|
| + [[GenericChromeCommand alloc] initWithTag:IDC_SHOW_ACCOUNTS_SETTINGS]);
|
| + [self.view chromeExecuteCommand:command];
|
| +}
|
| +
|
| +- (void)onAddAccount {
|
| + if (isPrerenderTab_) {
|
| + [delegate_ discardPrerender];
|
| + return;
|
| + }
|
| + if (self != [parentTabModel_ currentTab])
|
| + return;
|
| +
|
| + signin_metrics::LogAccountReconcilorStateOnGaiaResponse(
|
| + ios::AccountReconcilorFactory::GetForBrowserState(browserState_)
|
| + ->GetState());
|
| + base::scoped_nsobject<GenericChromeCommand> command(
|
| + [[GenericChromeCommand alloc] initWithTag:IDC_SHOW_ADD_ACCOUNT]);
|
| + [self.view chromeExecuteCommand:command];
|
| +}
|
| +
|
| +- (void)onGoIncognito:(const GURL&)url {
|
| + if (isPrerenderTab_) {
|
| + [delegate_ discardPrerender];
|
| + return;
|
| + }
|
| + if (self != [parentTabModel_ currentTab])
|
| + return;
|
| +
|
| + // The user taps on go incognito from the mobile U-turn webpage (the web page
|
| + // that displays all users accounts available in the content area). As the
|
| + // user chooses to go to incognito, the mobile U-turn page is no longer
|
| + // neeeded. The current solution is to go back in history. This has the
|
| + // advantage of keeping the current browsing session and give a good user
|
| + // experience when the user comes back from incognito.
|
| + [self goBack];
|
| +
|
| + if (url.is_valid()) {
|
| + base::scoped_nsobject<OpenUrlCommand> command([[OpenUrlCommand alloc]
|
| + initWithURL:url
|
| + referrer:web::Referrer() // Strip referrer when switching modes.
|
| + windowName:nil
|
| + inIncognito:YES
|
| + inBackground:NO
|
| + appendTo:kLastTab]);
|
| + [self.view chromeExecuteCommand:command];
|
| + } else {
|
| + base::scoped_nsobject<GenericChromeCommand> chromeCommand(
|
| + [[GenericChromeCommand alloc] initWithTag:IDC_NEW_INCOGNITO_TAB]);
|
| + [self.view chromeExecuteCommand:chromeCommand];
|
| + }
|
| +}
|
| +
|
| +- (FindInPageController*)findInPageController {
|
| + return findInPageController_;
|
| +}
|
| +
|
| +- (NativeAppNavigationController*)nativeAppNavigationController {
|
| + return nativeAppNavigationController_;
|
| +}
|
| +
|
| +- (void)initNativeAppNavigationController {
|
| + if (browserState_->IsOffTheRecord())
|
| + return;
|
| + DCHECK(!nativeAppNavigationController_);
|
| + nativeAppNavigationController_.reset([[NativeAppNavigationController alloc]
|
| + initWithRequestContextGetter:browserState_->GetRequestContext()
|
| + tab:self]);
|
| + [self.webController addObserver:nativeAppNavigationController_];
|
| + DCHECK(nativeAppNavigationController_);
|
| +}
|
| +
|
| +- (id<PassKitDialogProvider>)passKitDialogProvider {
|
| + return passKitDialogProvider_.get();
|
| +}
|
| +
|
| +- (void)setPassKitDialogProvider:(id<PassKitDialogProvider>)provider {
|
| + passKitDialogProvider_.reset(provider);
|
| +}
|
| +
|
| +- (void)wasShown {
|
| + visible_ = YES;
|
| + [self updateFullscreenWithToolbarVisible:YES];
|
| + [self.webController wasShown];
|
| + [inputAccessoryViewController_ wasShown];
|
| +}
|
| +
|
| +- (void)wasHidden {
|
| + visible_ = NO;
|
| + [self updateFullscreenWithToolbarVisible:YES];
|
| + [self.webController wasHidden];
|
| + [inputAccessoryViewController_ wasHidden];
|
| +}
|
| +
|
| +- (BOOL)navigationIsBackwards:(const CRWSessionEntry*)fromEntry
|
| + toEntry:(const CRWSessionEntry*)toEntry {
|
| + DCHECK([self navigationManager]);
|
| + NSArray* entries = [self navigationManager]->GetSessionController().entries;
|
| + NSInteger fromIndex = [entries indexOfObject:fromEntry];
|
| + NSInteger toIndex = [entries indexOfObject:toEntry];
|
| + return (fromIndex != NSNotFound && toIndex != NSNotFound &&
|
| + fromIndex > toIndex);
|
| +}
|
| +
|
| +@end
|
| +
|
| +#pragma mark - TestingSupport
|
| +
|
| +@implementation Tab (TestingSupport)
|
| +
|
| +- (void)replaceWebStateImpl:(std::unique_ptr<web::WebStateImpl>)webState {
|
| + // Stop observing the old InfoBarManager and FaviconDriver since they will
|
| + // be deleted with the old web controller.
|
| + [self setShouldObserveInfoBarManager:NO];
|
| + [self setShouldObserveFaviconChanges:NO];
|
| + [self.webController setDelegate:nil];
|
| + // Set the new web state.
|
| + webStateImpl_.reset(webState.release());
|
| + [self.webController setDelegate:self];
|
| + // SessionTabHelper comes first because it sets up the tab ID, and other
|
| + // helpers may rely on that.
|
| + IOSChromeSessionTabHelper::CreateForWebState(webStateImpl_.get());
|
| + IOSChromeSyncedTabDelegate::CreateForWebState(webStateImpl_.get());
|
| + // Start observing the new web controller's InfoBarManager and FaviconDriver.
|
| + [self setShouldObserveInfoBarManager:YES];
|
| + [self setShouldObserveFaviconChanges:YES];
|
| + findInPageController_.reset();
|
| +}
|
| +
|
| +- (void)replaceExternalAppLauncher:(id)externalAppLauncher {
|
| + externalAppLauncher_.reset([externalAppLauncher retain]);
|
| +}
|
| +
|
| +- (TabModel*)parentTabModel {
|
| + return parentTabModel_;
|
| +}
|
| +
|
| +- (FormInputAccessoryViewController*)inputAccessoryViewController {
|
| + return inputAccessoryViewController_.get();
|
| +}
|
| +
|
| +@end
|
|
|