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

Unified Diff: ios/chrome/browser/ui/contextual_search/contextual_search_results_view.mm

Issue 2588713002: Upstream Chrome on iOS source code [4/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/contextual_search/contextual_search_results_view.mm
diff --git a/ios/chrome/browser/ui/contextual_search/contextual_search_results_view.mm b/ios/chrome/browser/ui/contextual_search/contextual_search_results_view.mm
new file mode 100644
index 0000000000000000000000000000000000000000..50ba220b849dfc0585a366ed1211e093fe719cdf
--- /dev/null
+++ b/ios/chrome/browser/ui/contextual_search/contextual_search_results_view.mm
@@ -0,0 +1,362 @@
+// Copyright 2016 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.
+
+#include "ios/chrome/browser/ui/contextual_search/contextual_search_results_view.h"
+
+#include <memory>
+
+#import "base/ios/weak_nsobject.h"
+#include "base/mac/scoped_nsobject.h"
+#import "ios/chrome/browser/tabs/tab.h"
+#import "ios/chrome/browser/ui/contextual_search/contextual_search_metrics.h"
+#import "ios/chrome/browser/ui/contextual_search/contextual_search_panel_view.h"
+#import "ios/chrome/browser/ui/contextual_search/contextual_search_web_state_observer.h"
+#import "ios/chrome/common/material_timing.h"
+#include "ios/web/public/load_committed_details.h"
+#import "ios/web/public/web_state/crw_web_view_proxy.h"
+#import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h"
+#import "ios/web/public/web_state/ui/crw_native_content_provider.h"
+#import "ios/web/web_state/ui/crw_web_controller.h"
+
+namespace {
+enum SearchResultsViewVisibility { OFFSCREEN, PRELOAD, VISIBLE };
+}
+
+@interface ContextualSearchResultsView ()<ContextualSearchWebStateDelegate,
+ CRWNativeContentProvider>
+
+// Can the search results be scrolled currently?
+@property(nonatomic, assign, getter=isScrollEnabled) BOOL scrollEnabled;
+@end
+
+@implementation ContextualSearchResultsView {
+ base::WeakNSProtocol<id<ContextualSearchTabPromoter>> _promoter;
+ base::WeakNSProtocol<id<ContextualSearchPreloadChecker>> _preloadChecker;
+ std::unique_ptr<ContextualSearchWebStateObserver> _webStateObserver;
+
+ // Tab that loads the search results.
+ base::scoped_nsobject<Tab> _tab;
+
+ // Access to the search tab's web view proxy.
+ base::scoped_nsprotocol<id<CRWWebViewProxy>> _webViewProxy;
+
+ BOOL _loaded;
+ BOOL _displayed;
+ BOOL _loadingError;
+ BOOL _waitingForInitialSearchTabLoad;
+ BOOL _loadInProgress;
+ BOOL _shouldScroll;
+
+ BOOL _preloadEnabled;
+
+ SearchResultsViewVisibility _visibility;
+
+ // Time of starting a search results page load.
+ base::Time _loadStartTime;
+}
+
+@synthesize active = _active;
+@synthesize opener = _opener;
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ if ((self = [super initWithFrame:frame])) {
+ self.translatesAutoresizingMaskIntoConstraints = NO;
+ self.backgroundColor = [UIColor whiteColor];
+ self.accessibilityIdentifier = @"contextualSearchResultsView";
+ self.clipsToBounds = YES;
+ _webStateObserver.reset(new ContextualSearchWebStateObserver(self));
+ _visibility = OFFSCREEN;
+ }
+ return self;
+}
+
+#pragma mark - private properties
+
+- (BOOL)isScrollEnabled {
+ return [[_webViewProxy scrollViewProxy] isScrollEnabled];
+}
+
+- (void)setScrollEnabled:(BOOL)scrollEnabled {
+ [[_webViewProxy scrollViewProxy] setScrollEnabled:scrollEnabled];
+ [[_webViewProxy scrollViewProxy] setBounces:NO];
+}
+
+#pragma mark - public properties
+
+- (id<ContextualSearchTabPromoter>)promoter {
+ return _promoter;
+}
+
+- (void)setPromoter:(id<ContextualSearchTabPromoter>)promoter {
+ _promoter.reset(promoter);
+}
+
+- (id<ContextualSearchPreloadChecker>)preloadChecker {
+ return _preloadChecker;
+}
+
+- (void)setPreloadChecker:(id<ContextualSearchPreloadChecker>)preloadChecker {
+ _preloadChecker.reset(preloadChecker);
+}
+
+- (BOOL)contentVisible {
+ return _loaded && _visibility == VISIBLE;
+}
+
+- (void)setActive:(BOOL)active {
+ if (active) {
+ // Start watching the embedded Tab's web activity.
+ _webStateObserver->ObserveWebState([_tab webState]);
+ [[_tab webController] setShouldSuppressDialogs:NO];
+ _webViewProxy.reset([[[_tab webController] webViewProxy] retain]);
+ [[_webViewProxy scrollViewProxy] setBounces:NO];
+ } else {
+ // Stop watching the embedded Tab's web activity.
+ _webStateObserver->ObserveWebState(nullptr);
+ _webViewProxy.reset(nil);
+ }
+
+ _active = active;
+}
+
+#pragma mark - public methods
+
+- (void)scrollToTopAnimated:(BOOL)animated {
+ [[_webViewProxy scrollViewProxy] setBounces:YES];
+ // Scroll the search tab's view to the top.
+ [[_webViewProxy scrollViewProxy] setContentOffset:CGPointZero
+ animated:animated];
+}
+
+- (void)createTabForSearch:(GURL)url preloadEnabled:(BOOL)preloadEnabled {
+ DCHECK(self.opener);
+ if (_tab)
+ [self cancelLoad];
+
+ void (^searchTabConfiguration)(Tab*) = ^(Tab* tab) {
+ [tab setIsLinkLoadingPrerenderTab:YES];
+ [[tab webController] setDelegate:tab];
+ };
+
+ ui::PageTransition transition = ui::PAGE_TRANSITION_FROM_ADDRESS_BAR;
+ Tab* tab = [Tab newPreloadingTabWithBrowserState:self.opener.browserState
+ url:url
+ referrer:web::Referrer()
+ transition:transition
+ provider:self
+ opener:self.opener
+ desktopUserAgent:false
+ configuration:searchTabConfiguration];
+ _tab.reset([tab retain]);
+ // Don't actually start the page load yet -- that happens in -loadTab
+
+ _preloadEnabled = preloadEnabled;
+ [self loadPendingSearchIfPossible];
+}
+
+- (Tab*)releaseTab {
+ [self disconnectTab];
+ // Allow the search tab to be sized by autoresizing mask again.
+ [[_tab view] setTranslatesAutoresizingMaskIntoConstraints:YES];
+ return [_tab.release() autorelease];
+}
+
+- (void)recordFinishedSearchChained:(BOOL)chained {
+ base::TimeDelta duration = base::Time::Now() - _loadStartTime;
+ ContextualSearch::RecordDuration(self.contentVisible, chained, duration);
+}
+
+#pragma mark - private methods
+
+- (void)disconnectTab {
+ [[_tab view] removeFromSuperview];
+ [[_tab webController] setNativeProvider:nil];
+ self.active = NO;
+ _webViewProxy.reset();
+}
+
+- (void)cancelLoad {
+ [self disconnectTab];
+ _loadInProgress = NO;
+ _loaded = NO;
+ [_tab close];
+ _tab.reset();
+}
+
+- (void)loadPendingSearchIfPossible {
+ // If the search tab hasn't been created, or if it's already loaded, no-op.
+ if (!_tab.get() || _loadInProgress || self.active || _visibility == OFFSCREEN)
+ return;
+
+ // If this view is in a position where loading would be "preloading", check
+ // if that's allowed.
+ if (_visibility == PRELOAD &&
+ !([_preloadChecker canPreloadSearchResults] && _preloadEnabled)) {
+ return;
+ }
+ [self loadTab];
+}
+
+- (void)loadTab {
+ DCHECK(_tab.get());
+ // Start observing the search tab.
+ self.active = YES;
+ // TODO(crbug.com/546223): See if |_waitingForInitialSearchTabLoad| and
+ // |_loadInProgress| can be consolidated.
+ _waitingForInitialSearchTabLoad = YES;
+ // Mark the start of the time for the search.
+ _loadStartTime = base::Time::Now();
+ // Start the load by asking for the tab's view, but making it hidden.
+ [_tab view].hidden = YES;
+ [self addSubview:[_tab view]];
+ _loadInProgress = YES;
+}
+
+- (void)displayTab {
+ [_tab view].frame = self.bounds;
+ _loadInProgress = NO;
+
+ void (^insertTab)(void) = ^{
+ [_tab view].hidden = NO;
+ self.scrollEnabled = _shouldScroll;
+ };
+
+ if (_visibility == VISIBLE) {
+ // Fade it in quickly.
+ UIViewAnimationOptions options =
+ UIViewAnimationOptionTransitionCrossDissolve;
+ [UIView cr_transitionWithView:self
+ duration:ios::material::kDuration2
+ curve:ios::material::CurveEaseInOut
+ options:options
+ animations:insertTab
+ completion:nil];
+ } else {
+ // Swap it in.
+ insertTab();
+ }
+}
+
+#pragma mark - ContextualSearchScrollSynchronizer
+
+- (UIGestureRecognizer*)scrollRecognizer {
+ if (_webViewProxy) {
+ UIGestureRecognizer* recognizer =
+ [[_webViewProxy scrollViewProxy] panGestureRecognizer];
+ if ([recognizer isEnabled])
+ return recognizer;
+ }
+ return nil;
+}
+
+- (BOOL)scrolled {
+ if (!_webViewProxy)
+ return NO;
+ CGPoint offset = [[_webViewProxy scrollViewProxy] contentOffset];
+ return !CGPointEqualToPoint(offset, CGPointZero);
+}
+
+#pragma mark - ContextualSearchWebStateDelegate methods
+
+- (void)webState:(web::WebState*)webState
+ navigatedWithDetails:(const web::LoadCommittedDetails&)details {
+ if (_loaded) {
+ if (self.active && !details.is_in_page && !_loadingError) {
+ // Use async dispatch so the rest of the OnNavigationItemCommitted()
+ // handlers can complete before the tab changes owners.
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [_promoter promoteTabHeaderPressed:NO];
+ });
+ }
+ _loadingError = NO;
+ }
+}
+
+- (void)webState:(web::WebState*)webState
+ pageLoadedWithStatus:(web::PageLoadCompletionStatus)loadStatus {
+ if (!_loaded) {
+ _loaded = YES;
+ if (_waitingForInitialSearchTabLoad) {
+ _waitingForInitialSearchTabLoad = NO;
+ [self displayTab];
+ }
+ }
+}
+
+#pragma mark - ContextualSearchPanelMotionObserver
+
+- (void)panel:(ContextualSearchPanelView*)panel
+ didChangeToState:(ContextualSearch::PanelState)toState
+ fromState:(ContextualSearch::PanelState)fromState {
+ // Update visibility.
+ switch (toState) {
+ case ContextualSearch::DISMISSED:
+ _visibility = OFFSCREEN;
+ break;
+ case ContextualSearch::PEEKING:
+ _visibility = PRELOAD;
+ break;
+ default:
+ _visibility = VISIBLE;
+ break;
+ }
+
+ // Load any pending search if now visible.
+ if (_visibility != OFFSCREEN) {
+ [self loadPendingSearchIfPossible];
+ }
+
+ // If the drag takes the panel down from covering, and the search tab's
+ // scrolling is enabled, then disable it and reset any scrolling.
+ if (toState <= ContextualSearch::PREVIEWING &&
+ fromState == ContextualSearch::COVERING && self.isScrollEnabled) {
+ self.scrollEnabled = NO;
+ [self scrollToTopAnimated:YES];
+ _shouldScroll = NO;
+ } else if (toState == ContextualSearch::COVERING) {
+ self.scrollEnabled = YES;
+ _shouldScroll = YES;
+ }
+}
+
+- (void)panelWillPromote:(ContextualSearchPanelView*)panel {
+ [panel removeMotionObserver:self];
+}
+
+#pragma mark - CRWNativeContentProvider methods
+
+// Native pages should never be loaded this way.
+- (BOOL)hasControllerForURL:(const GURL&)url {
+ NOTREACHED();
+ [self cancelLoad];
+ return NO;
+}
+
+// Native pages should never be loaded this way.
+- (id<CRWNativeContent>)controllerForURL:(const GURL&)url {
+ NOTREACHED();
+ [self cancelLoad];
+ return nil;
+}
+
+- (id<CRWNativeContent>)controllerForURL:(const GURL&)url
+ withError:(NSError*)error
+ isPost:(BOOL)isPost {
+ // Display the error page in the contextual search tab.
+ _loadingError = YES;
+ [self displayTab];
+
+ [[_webViewProxy scrollViewProxy] setScrollEnabled:NO];
+ id errorController =
+ [self.opener.webController.nativeProvider controllerForURL:url
+ withError:error
+ isPost:isPost];
+
+ if ([errorController respondsToSelector:@selector(setScrollEnabled:)]) {
+ [errorController setScrollEnabled:NO];
+ }
+ return errorController;
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698