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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ios/chrome/browser/ui/contextual_search/contextual_search_results_view .h"
6
7 #include <memory>
8
9 #import "base/ios/weak_nsobject.h"
10 #include "base/mac/scoped_nsobject.h"
11 #import "ios/chrome/browser/tabs/tab.h"
12 #import "ios/chrome/browser/ui/contextual_search/contextual_search_metrics.h"
13 #import "ios/chrome/browser/ui/contextual_search/contextual_search_panel_view.h"
14 #import "ios/chrome/browser/ui/contextual_search/contextual_search_web_state_obs erver.h"
15 #import "ios/chrome/common/material_timing.h"
16 #include "ios/web/public/load_committed_details.h"
17 #import "ios/web/public/web_state/crw_web_view_proxy.h"
18 #import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h"
19 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
20 #import "ios/web/web_state/ui/crw_web_controller.h"
21
22 namespace {
23 enum SearchResultsViewVisibility { OFFSCREEN, PRELOAD, VISIBLE };
24 }
25
26 @interface ContextualSearchResultsView ()<ContextualSearchWebStateDelegate,
27 CRWNativeContentProvider>
28
29 // Can the search results be scrolled currently?
30 @property(nonatomic, assign, getter=isScrollEnabled) BOOL scrollEnabled;
31 @end
32
33 @implementation ContextualSearchResultsView {
34 base::WeakNSProtocol<id<ContextualSearchTabPromoter>> _promoter;
35 base::WeakNSProtocol<id<ContextualSearchPreloadChecker>> _preloadChecker;
36 std::unique_ptr<ContextualSearchWebStateObserver> _webStateObserver;
37
38 // Tab that loads the search results.
39 base::scoped_nsobject<Tab> _tab;
40
41 // Access to the search tab's web view proxy.
42 base::scoped_nsprotocol<id<CRWWebViewProxy>> _webViewProxy;
43
44 BOOL _loaded;
45 BOOL _displayed;
46 BOOL _loadingError;
47 BOOL _waitingForInitialSearchTabLoad;
48 BOOL _loadInProgress;
49 BOOL _shouldScroll;
50
51 BOOL _preloadEnabled;
52
53 SearchResultsViewVisibility _visibility;
54
55 // Time of starting a search results page load.
56 base::Time _loadStartTime;
57 }
58
59 @synthesize active = _active;
60 @synthesize opener = _opener;
61
62 - (instancetype)initWithFrame:(CGRect)frame {
63 if ((self = [super initWithFrame:frame])) {
64 self.translatesAutoresizingMaskIntoConstraints = NO;
65 self.backgroundColor = [UIColor whiteColor];
66 self.accessibilityIdentifier = @"contextualSearchResultsView";
67 self.clipsToBounds = YES;
68 _webStateObserver.reset(new ContextualSearchWebStateObserver(self));
69 _visibility = OFFSCREEN;
70 }
71 return self;
72 }
73
74 #pragma mark - private properties
75
76 - (BOOL)isScrollEnabled {
77 return [[_webViewProxy scrollViewProxy] isScrollEnabled];
78 }
79
80 - (void)setScrollEnabled:(BOOL)scrollEnabled {
81 [[_webViewProxy scrollViewProxy] setScrollEnabled:scrollEnabled];
82 [[_webViewProxy scrollViewProxy] setBounces:NO];
83 }
84
85 #pragma mark - public properties
86
87 - (id<ContextualSearchTabPromoter>)promoter {
88 return _promoter;
89 }
90
91 - (void)setPromoter:(id<ContextualSearchTabPromoter>)promoter {
92 _promoter.reset(promoter);
93 }
94
95 - (id<ContextualSearchPreloadChecker>)preloadChecker {
96 return _preloadChecker;
97 }
98
99 - (void)setPreloadChecker:(id<ContextualSearchPreloadChecker>)preloadChecker {
100 _preloadChecker.reset(preloadChecker);
101 }
102
103 - (BOOL)contentVisible {
104 return _loaded && _visibility == VISIBLE;
105 }
106
107 - (void)setActive:(BOOL)active {
108 if (active) {
109 // Start watching the embedded Tab's web activity.
110 _webStateObserver->ObserveWebState([_tab webState]);
111 [[_tab webController] setShouldSuppressDialogs:NO];
112 _webViewProxy.reset([[[_tab webController] webViewProxy] retain]);
113 [[_webViewProxy scrollViewProxy] setBounces:NO];
114 } else {
115 // Stop watching the embedded Tab's web activity.
116 _webStateObserver->ObserveWebState(nullptr);
117 _webViewProxy.reset(nil);
118 }
119
120 _active = active;
121 }
122
123 #pragma mark - public methods
124
125 - (void)scrollToTopAnimated:(BOOL)animated {
126 [[_webViewProxy scrollViewProxy] setBounces:YES];
127 // Scroll the search tab's view to the top.
128 [[_webViewProxy scrollViewProxy] setContentOffset:CGPointZero
129 animated:animated];
130 }
131
132 - (void)createTabForSearch:(GURL)url preloadEnabled:(BOOL)preloadEnabled {
133 DCHECK(self.opener);
134 if (_tab)
135 [self cancelLoad];
136
137 void (^searchTabConfiguration)(Tab*) = ^(Tab* tab) {
138 [tab setIsLinkLoadingPrerenderTab:YES];
139 [[tab webController] setDelegate:tab];
140 };
141
142 ui::PageTransition transition = ui::PAGE_TRANSITION_FROM_ADDRESS_BAR;
143 Tab* tab = [Tab newPreloadingTabWithBrowserState:self.opener.browserState
144 url:url
145 referrer:web::Referrer()
146 transition:transition
147 provider:self
148 opener:self.opener
149 desktopUserAgent:false
150 configuration:searchTabConfiguration];
151 _tab.reset([tab retain]);
152 // Don't actually start the page load yet -- that happens in -loadTab
153
154 _preloadEnabled = preloadEnabled;
155 [self loadPendingSearchIfPossible];
156 }
157
158 - (Tab*)releaseTab {
159 [self disconnectTab];
160 // Allow the search tab to be sized by autoresizing mask again.
161 [[_tab view] setTranslatesAutoresizingMaskIntoConstraints:YES];
162 return [_tab.release() autorelease];
163 }
164
165 - (void)recordFinishedSearchChained:(BOOL)chained {
166 base::TimeDelta duration = base::Time::Now() - _loadStartTime;
167 ContextualSearch::RecordDuration(self.contentVisible, chained, duration);
168 }
169
170 #pragma mark - private methods
171
172 - (void)disconnectTab {
173 [[_tab view] removeFromSuperview];
174 [[_tab webController] setNativeProvider:nil];
175 self.active = NO;
176 _webViewProxy.reset();
177 }
178
179 - (void)cancelLoad {
180 [self disconnectTab];
181 _loadInProgress = NO;
182 _loaded = NO;
183 [_tab close];
184 _tab.reset();
185 }
186
187 - (void)loadPendingSearchIfPossible {
188 // If the search tab hasn't been created, or if it's already loaded, no-op.
189 if (!_tab.get() || _loadInProgress || self.active || _visibility == OFFSCREEN)
190 return;
191
192 // If this view is in a position where loading would be "preloading", check
193 // if that's allowed.
194 if (_visibility == PRELOAD &&
195 !([_preloadChecker canPreloadSearchResults] && _preloadEnabled)) {
196 return;
197 }
198 [self loadTab];
199 }
200
201 - (void)loadTab {
202 DCHECK(_tab.get());
203 // Start observing the search tab.
204 self.active = YES;
205 // TODO(crbug.com/546223): See if |_waitingForInitialSearchTabLoad| and
206 // |_loadInProgress| can be consolidated.
207 _waitingForInitialSearchTabLoad = YES;
208 // Mark the start of the time for the search.
209 _loadStartTime = base::Time::Now();
210 // Start the load by asking for the tab's view, but making it hidden.
211 [_tab view].hidden = YES;
212 [self addSubview:[_tab view]];
213 _loadInProgress = YES;
214 }
215
216 - (void)displayTab {
217 [_tab view].frame = self.bounds;
218 _loadInProgress = NO;
219
220 void (^insertTab)(void) = ^{
221 [_tab view].hidden = NO;
222 self.scrollEnabled = _shouldScroll;
223 };
224
225 if (_visibility == VISIBLE) {
226 // Fade it in quickly.
227 UIViewAnimationOptions options =
228 UIViewAnimationOptionTransitionCrossDissolve;
229 [UIView cr_transitionWithView:self
230 duration:ios::material::kDuration2
231 curve:ios::material::CurveEaseInOut
232 options:options
233 animations:insertTab
234 completion:nil];
235 } else {
236 // Swap it in.
237 insertTab();
238 }
239 }
240
241 #pragma mark - ContextualSearchScrollSynchronizer
242
243 - (UIGestureRecognizer*)scrollRecognizer {
244 if (_webViewProxy) {
245 UIGestureRecognizer* recognizer =
246 [[_webViewProxy scrollViewProxy] panGestureRecognizer];
247 if ([recognizer isEnabled])
248 return recognizer;
249 }
250 return nil;
251 }
252
253 - (BOOL)scrolled {
254 if (!_webViewProxy)
255 return NO;
256 CGPoint offset = [[_webViewProxy scrollViewProxy] contentOffset];
257 return !CGPointEqualToPoint(offset, CGPointZero);
258 }
259
260 #pragma mark - ContextualSearchWebStateDelegate methods
261
262 - (void)webState:(web::WebState*)webState
263 navigatedWithDetails:(const web::LoadCommittedDetails&)details {
264 if (_loaded) {
265 if (self.active && !details.is_in_page && !_loadingError) {
266 // Use async dispatch so the rest of the OnNavigationItemCommitted()
267 // handlers can complete before the tab changes owners.
268 dispatch_async(dispatch_get_main_queue(), ^{
269 [_promoter promoteTabHeaderPressed:NO];
270 });
271 }
272 _loadingError = NO;
273 }
274 }
275
276 - (void)webState:(web::WebState*)webState
277 pageLoadedWithStatus:(web::PageLoadCompletionStatus)loadStatus {
278 if (!_loaded) {
279 _loaded = YES;
280 if (_waitingForInitialSearchTabLoad) {
281 _waitingForInitialSearchTabLoad = NO;
282 [self displayTab];
283 }
284 }
285 }
286
287 #pragma mark - ContextualSearchPanelMotionObserver
288
289 - (void)panel:(ContextualSearchPanelView*)panel
290 didChangeToState:(ContextualSearch::PanelState)toState
291 fromState:(ContextualSearch::PanelState)fromState {
292 // Update visibility.
293 switch (toState) {
294 case ContextualSearch::DISMISSED:
295 _visibility = OFFSCREEN;
296 break;
297 case ContextualSearch::PEEKING:
298 _visibility = PRELOAD;
299 break;
300 default:
301 _visibility = VISIBLE;
302 break;
303 }
304
305 // Load any pending search if now visible.
306 if (_visibility != OFFSCREEN) {
307 [self loadPendingSearchIfPossible];
308 }
309
310 // If the drag takes the panel down from covering, and the search tab's
311 // scrolling is enabled, then disable it and reset any scrolling.
312 if (toState <= ContextualSearch::PREVIEWING &&
313 fromState == ContextualSearch::COVERING && self.isScrollEnabled) {
314 self.scrollEnabled = NO;
315 [self scrollToTopAnimated:YES];
316 _shouldScroll = NO;
317 } else if (toState == ContextualSearch::COVERING) {
318 self.scrollEnabled = YES;
319 _shouldScroll = YES;
320 }
321 }
322
323 - (void)panelWillPromote:(ContextualSearchPanelView*)panel {
324 [panel removeMotionObserver:self];
325 }
326
327 #pragma mark - CRWNativeContentProvider methods
328
329 // Native pages should never be loaded this way.
330 - (BOOL)hasControllerForURL:(const GURL&)url {
331 NOTREACHED();
332 [self cancelLoad];
333 return NO;
334 }
335
336 // Native pages should never be loaded this way.
337 - (id<CRWNativeContent>)controllerForURL:(const GURL&)url {
338 NOTREACHED();
339 [self cancelLoad];
340 return nil;
341 }
342
343 - (id<CRWNativeContent>)controllerForURL:(const GURL&)url
344 withError:(NSError*)error
345 isPost:(BOOL)isPost {
346 // Display the error page in the contextual search tab.
347 _loadingError = YES;
348 [self displayTab];
349
350 [[_webViewProxy scrollViewProxy] setScrollEnabled:NO];
351 id errorController =
352 [self.opener.webController.nativeProvider controllerForURL:url
353 withError:error
354 isPost:isPost];
355
356 if ([errorController respondsToSelector:@selector(setScrollEnabled:)]) {
357 [errorController setScrollEnabled:NO];
358 }
359 return errorController;
360 }
361
362 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698