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

Unified Diff: ios/chrome/browser/find_in_page/find_in_page_controller.mm

Issue 1023813003: [iOS] Upstream ios/chrome/browser/find_in_page (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@upstreamAutofill
Patch Set: Created 5 years, 9 months 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/find_in_page/find_in_page_controller.mm
diff --git a/ios/chrome/browser/find_in_page/find_in_page_controller.mm b/ios/chrome/browser/find_in_page/find_in_page_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..0d1621e15ae12f2e5c816d8048dc24394cdec2ad
--- /dev/null
+++ b/ios/chrome/browser/find_in_page/find_in_page_controller.mm
@@ -0,0 +1,357 @@
+// 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/find_in_page/find_in_page_controller.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/ios/ios_util.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsobject.h"
+#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
+#import "ios/chrome/browser/find_in_page/js_findinpage_manager.h"
+#import "ios/chrome/browser/web/dom_altering_lock.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/js/crw_js_injection_receiver.h"
+#import "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state_observer_bridge.h"
+
+NSString* const kFindBarTextFieldWillBecomeFirstResponderNotification =
+ @"kFindBarTextFieldWillBecomeFirstResponderNotification";
+NSString* const kFindBarTextFieldDidResignFirstResponderNotification =
+ @"kFindBarTextFieldDidResignFirstResponderNotification";
+
+namespace {
+// The delay (in secs) after which the find in page string will be pumped again.
+const NSTimeInterval kRecurringPumpDelay = .01;
+}
+
+@interface FindInPageController () <DOMAltering, CRWWebStateObserver>
+// The find in page controller delegate.
+@property(nonatomic, readonly) id<FindInPageControllerDelegate> delegate;
+// The web view's scroll view.
+@property(nonatomic, readonly) CRWWebViewScrollViewProxy* webViewScrollView;
+
+// Convenience method to obtain UIPasteboardNameFind from UIPasteBoard.
+- (UIPasteboard*)findPasteboard;
+// Find in Page text field listeners.
+- (void)findBarTextFieldWillBecomeFirstResponder:(NSNotification*)note;
+- (void)findBarTextFieldDidResignFirstResponder:(NSNotification*)note;
+// Keyboard listeners.
+- (void)keyboardDidShow:(NSNotification*)note;
+- (void)keyboardWillHide:(NSNotification*)note;
+// Constantly injects the find string in page until
+// |disableFindInPageWithCompletionHandler:| is called or the find operation is
+// complete. Calls |completionHandler| if the find operation is complete.
+// |completionHandler| can be nil.
+- (void)startPumpingWithCompletionHandler:(ProceduralBlock)completionHandler;
+// Gives find in page more time to complete. Calls |completionHandler| with
+// a BOOL indicating if the find operation was successfull. |completionHandler|
+// can be nil.
+- (void)pumpFindStringInPageWithCompletionHandler:
+ (void (^)(BOOL))completionHandler;
+// Processes the result of a single find in page pump. Calls |completionHandler|
+// if pumping is done. Re-pumps if necessary.
+- (void)processPumpResult:(BOOL)finished
+ scrollPoint:(CGPoint)scrollPoint
+ completionHandler:(ProceduralBlock)completionHandler;
+// Returns the associated web state. May be null.
+- (web::WebState*)webState;
+@end
+
+@implementation FindInPageController {
+ @private
+ // Object that manages find_in_page.js injection into the web view.
+ JsFindinpageManager* _findInPageJsManager;
+ id<FindInPageControllerDelegate> _delegate;
+
+ // Access to the web view from the web state.
+ base::scoped_nsprotocol<id<CRWWebViewProxy>> _webViewProxy;
+
+ // True when a find is in progress. Used to avoid running JavaScript during
+ // disable when there is nothing to clear.
+ BOOL _findStringStarted;
+
+ // Bridge to observe the web state from Objective-C.
+ scoped_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
+}
+
+@synthesize delegate = _delegate;
+
+- (id)initWithWebState:(web::WebState*)webState
+ delegate:(id<FindInPageControllerDelegate>)delegate {
+ self = [super init];
+ if (self) {
+ DCHECK(delegate);
+ _findInPageJsManager = base::mac::ObjCCastStrict<JsFindinpageManager>(
+ [webState->GetJSInjectionReceiver()
+ instanceOfClass:[JsFindinpageManager class]]);
+ _delegate = delegate;
+ _webStateObserverBridge.reset(
+ new web::WebStateObserverBridge(webState, self));
+ _webViewProxy.reset([webState->GetWebViewProxy() retain]);
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(findBarTextFieldWillBecomeFirstResponder:)
+ name:kFindBarTextFieldWillBecomeFirstResponderNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(findBarTextFieldDidResignFirstResponder:)
+ name:kFindBarTextFieldDidResignFirstResponderNotification
+ object:nil];
+ DOMAlteringLock::CreateForWebState(webState);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [super dealloc];
+}
+
+- (FindInPageModel*)findInPageModel {
+ return [_findInPageJsManager findInPageModel];
+}
+
+- (BOOL)canFindInPage {
+ return [_webViewProxy hasSearchableTextContent];
+}
+
+- (void)initFindInPage {
+ [_findInPageJsManager inject];
+
+ // Initialize the module with our frame size.
+ CGRect frame = [_webViewProxy bounds];
+ [_findInPageJsManager setWidth:frame.size.width height:frame.size.height];
+}
+
+- (CRWWebViewScrollViewProxy*)webViewScrollView {
+ return [_webViewProxy scrollViewProxy];
+}
+
+- (void)processPumpResult:(BOOL)finished
+ scrollPoint:(CGPoint)scrollPoint
+ completionHandler:(ProceduralBlock)completionHandler {
+ if (finished) {
+ [_delegate willAdjustScrollPosition];
+ [[_webViewProxy scrollViewProxy] setContentOffset:scrollPoint animated:YES];
+ if (completionHandler)
+ completionHandler();
+ } else {
+ [self performSelector:@selector(startPumpingWithCompletionHandler:)
+ withObject:completionHandler
+ afterDelay:kRecurringPumpDelay];
+ }
+}
+
+- (void)findStringInPage:(NSString*)query
+ completionHandler:(ProceduralBlock)completionHandler {
+ ProceduralBlockWithBool lockAction = ^(BOOL hasLock) {
+ if (!hasLock) {
+ if (completionHandler) {
+ completionHandler();
+ }
+ return;
+ }
+ // Cancel any previous pumping.
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ [self initFindInPage];
+ // Keep track of whether a find is in progress so to avoid running
+ // JavaScript during disable if unnecessary.
+ _findStringStarted = YES;
+ base::WeakNSObject<FindInPageController> weakSelf(self);
+ [_findInPageJsManager findString:query
+ completionHandler:^(BOOL finished, CGPoint point) {
+ [weakSelf processPumpResult:finished
+ scrollPoint:point
+ completionHandler:completionHandler];
+ }];
+ };
+ DOMAlteringLock::FromWebState([self webState])->Acquire(self, lockAction);
+}
+
+- (void)startPumpingWithCompletionHandler:(ProceduralBlock)completionHandler {
+ base::WeakNSObject<FindInPageController> weakSelf(self);
+ id completionHandlerBlock = ^void(BOOL findFinished) {
+ if (findFinished) {
+ // Pumping complete. Nothing else to do.
+ if (completionHandler)
+ completionHandler();
+ return;
+ }
+ // Further pumping is required.
+ [weakSelf performSelector:@selector(startPumpingWithCompletionHandler:)
+ withObject:completionHandler
+ afterDelay:kRecurringPumpDelay];
+ };
+ [self pumpFindStringInPageWithCompletionHandler:completionHandlerBlock];
+}
+
+- (void)pumpFindStringInPageWithCompletionHandler:
+ (void (^)(BOOL))completionHandler {
+ base::WeakNSObject<FindInPageController> weakSelf(self);
+ [_findInPageJsManager pumpWithCompletionHandler:^(BOOL finished,
+ CGPoint point) {
+ base::scoped_nsobject<FindInPageController> strongSelf([weakSelf retain]);
+ if (finished) {
+ [[strongSelf delegate] willAdjustScrollPosition];
+ [[strongSelf webViewScrollView] setContentOffset:point animated:YES];
+ }
+ completionHandler(finished);
+ }];
+}
+
+- (void)findNextStringInPageWithCompletionHandler:
+ (ProceduralBlock)completionHandler {
+ [self initFindInPage];
+ base::WeakNSObject<FindInPageController> weakSelf(self);
+ [_findInPageJsManager nextMatchWithCompletionHandler:^(CGPoint point) {
+ base::scoped_nsobject<FindInPageController> strongSelf([weakSelf retain]);
+ [[strongSelf delegate] willAdjustScrollPosition];
+ CGFloat contentHeight = [strongSelf webViewScrollView].contentSize.height;
+ CGFloat frameHeight = [strongSelf webViewScrollView].frame.size.height;
+ CGFloat maxScroll = fmax(0, contentHeight - frameHeight);
+ if (point.y > maxScroll) {
+ point.y = maxScroll;
+ }
+ [[strongSelf webViewScrollView] setContentOffset:point animated:YES];
+ if (completionHandler)
+ completionHandler();
+ }];
+}
+
+// Highlight the previous search match, update model and scroll to match.
+- (void)findPreviousStringInPageWithCompletionHandler:
+ (ProceduralBlock)completionHandler {
+ [self initFindInPage];
+ base::WeakNSObject<FindInPageController> weakSelf(self);
+ [_findInPageJsManager previousMatchWithCompletionHandler:^(CGPoint point) {
+ base::scoped_nsobject<FindInPageController> strongSelf([weakSelf retain]);
+ [[strongSelf delegate] willAdjustScrollPosition];
+ [[strongSelf webViewScrollView] setContentOffset:point animated:YES];
+ if (completionHandler)
+ completionHandler();
+ }];
+}
+
+// Remove highlights from the page and disable the model.
+- (void)disableFindInPageWithCompletionHandler:
+ (ProceduralBlock)completionHandler {
+ if (![self canFindInPage])
+ return;
+ // Cancel any queued calls to |recurringPumpWithCompletionHandler|.
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ base::WeakNSObject<FindInPageController> weakSelf(self);
+ ProceduralBlock handler = ^{
+ web::WebState* webState = [self webState];
+ if (webState)
+ DOMAlteringLock::FromWebState(webState)->Release(self);
+ if (completionHandler)
+ completionHandler();
+ };
+ // Only run JSFindInPageManager disable if there is a string in progress to
+ // avoid WKWebView crash on deallocation due to outstanding completion
+ // handler.
+ if (_findStringStarted) {
+ [_findInPageJsManager disableWithCompletionHandler:handler];
+ _findStringStarted = NO;
+ } else {
+ handler();
+ }
+}
+
+- (void)saveSearchTerm {
+ [self findPasteboard].string = [[self findInPageModel] text];
+}
+
+- (void)restoreSearchTerm {
+ NSString* term = [self findPasteboard].string;
+ [[self findInPageModel] updateQuery:(term ? term : @"") matches:0];
+}
+
+- (UIPasteboard*)findPasteboard {
+ return [UIPasteboard pasteboardWithName:UIPasteboardNameFind create:NO];
+}
+
+- (web::WebState*)webState {
+ return _webStateObserverBridge ? _webStateObserverBridge->web_state()
+ : nullptr;
+}
+
+#pragma mark - Notification listeners
+
+- (void)findBarTextFieldWillBecomeFirstResponder:(NSNotification*)note {
+ // Listen to the keyboard appearance notifications.
+ NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver:self
+ selector:@selector(keyboardDidShow:)
+ name:UIKeyboardDidShowNotification
+ object:nil];
+ [defaultCenter addObserver:self
+ selector:@selector(keyboardWillHide:)
+ name:UIKeyboardWillHideNotification
+ object:nil];
+}
+
+- (void)findBarTextFieldDidResignFirstResponder:(NSNotification*)note {
+ // Resign from the keyboard appearance notifications on the next turn of the
+ // runloop.
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver:self
+ name:UIKeyboardDidShowNotification
+ object:nil];
+ [defaultCenter removeObserver:self
+ name:UIKeyboardWillHideNotification
+ object:nil];
+ });
+}
+
+- (void)keyboardDidShow:(NSNotification*)note {
+ NSDictionary* info = [note userInfo];
+ CGSize kbSize =
+ [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
+ UIInterfaceOrientation orientation =
+ [[UIApplication sharedApplication] statusBarOrientation];
+ CGFloat kbHeight = kbSize.height;
+ // Prior to iOS 8, the keyboard frame was not dependent on interface
+ // orientation, so height and width need to be swapped in landscape mode.
+ if (UIInterfaceOrientationIsLandscape(orientation) &&
+ !base::ios::IsRunningOnIOS8OrLater()) {
+ kbHeight = kbSize.width;
+ }
+
+ UIEdgeInsets insets = UIEdgeInsetsZero;
+ insets.bottom = kbHeight;
+ [_webViewProxy registerInsets:insets forCaller:self];
+}
+
+- (void)keyboardWillHide:(NSNotification*)note {
+ [_webViewProxy unregisterInsetsForCaller:self];
+}
+
+- (void)detachFromWebState {
+ _webStateObserverBridge.reset();
+}
+
+#pragma mark - CRWWebStateObserver Methods
+
+- (void)webStateDestroyed:(web::WebState*)webState {
+ [self detachFromWebState];
+}
+
+#pragma mark - DOMAltering Methods
+
+- (BOOL)canReleaseDOMLock {
+ return NO;
+}
+
+- (void)releaseDOMLockWithCompletionHandler:(ProceduralBlock)completionHandler {
+ NOTREACHED();
+}
+
+@end
« no previous file with comments | « ios/chrome/browser/find_in_page/find_in_page_controller.h ('k') | ios/chrome/browser/find_in_page/find_in_page_model.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698