[go: nahoru, domu]

blob: 4870d5bad389290a10988d7e239843148090cd4d [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer.h"
#import "base/apple/foundation_util.h"
#import "base/ios/ios_util.h"
#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/ui/fullscreen/fullscreen_model.h"
#import "ios/web/common/features.h"
#import "ios/web/public/ui/crw_web_view_proxy.h"
#import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
#import "ios/web/public/web_state.h"
@interface FullscreenWebViewResizer ()
// The fullscreen model, used to get the information about the state of
// fullscreen.
@property(nonatomic, assign) FullscreenModel* model;
@end
@implementation FullscreenWebViewResizer {
BOOL _installedObserver;
}
@synthesize model = _model;
@synthesize webState = _webState;
- (instancetype)initWithModel:(FullscreenModel*)model {
self = [super init];
if (self) {
_installedObserver = NO;
_model = model;
_compensateFrameChangeByOffset = YES;
}
return self;
}
- (void)dealloc {
[self stopObservingWebStateViewFrame];
}
#pragma mark - Properties
- (void)setWebState:(web::WebState*)webState {
if (_webState == webState)
return;
[self stopObservingWebStateViewFrame];
_webState = webState;
if (webState) {
[self observeWebStateViewFrame:webState];
self.compensateFrameChangeByOffset = NO;
[self updateForCurrentState];
self.compensateFrameChangeByOffset = YES;
}
}
#pragma mark - Public
- (void)updateForCurrentState {
if (!self.webState)
return;
[self updateForFullscreenProgress:self.model->progress()];
}
- (void)forceToUpdateToProgress:(CGFloat)progress {
if (!self.webState)
return;
[self stopObservingWebStateViewFrame];
[self updateForFullscreenProgress:progress];
[self observeWebStateViewFrame:self.webState];
}
#pragma mark - Private
// Updates the WebView of the current webState to adjust it to the current
// fullscreen `progress`. `progress` should be between 0 and 1, 0 meaning that
// the application is in fullscreen, 1 that it is out of fullscreen.
- (void)updateForFullscreenProgress:(CGFloat)progress {
if (!self.webState || !self.webState->GetView().superview)
return;
[self updateForInsets:self.model->GetToolbarInsetsAtProgress(progress)];
self.model->SetWebViewSafeAreaInsets(self.webState->GetView().safeAreaInsets);
}
// Updates the WebState view, resizing it such as `insets` is the insets between
// the WebState view and its superview.
- (void)updateForInsets:(UIEdgeInsets)insets {
UIView* webView = self.webState->GetView();
id<CRWWebViewProxy> webViewProxy = self.webState->GetWebViewProxy();
CRWWebViewScrollViewProxy* scrollViewProxy = webViewProxy.scrollViewProxy;
if (self.webState->GetContentsMimeType() == "application/pdf") {
scrollViewProxy.contentInset = insets;
if (!CGRectEqualToRect(webView.frame, webView.superview.bounds)) {
webView.frame = webView.superview.bounds;
}
return;
}
CGRect newFrame = UIEdgeInsetsInsetRect(webView.superview.bounds, insets);
if (base::FeatureList::IsEnabled(web::features::kSmoothScrollingDefault)) {
// Make sure the frame has changed to avoid a loop as the frame property is
// actually monitored by this object.
if (std::fabs(newFrame.origin.x - webView.frame.origin.x) < 0.01 &&
std::fabs(newFrame.origin.y - webView.frame.origin.y) < 0.01 &&
std::fabs(newFrame.size.width - webView.frame.size.width) < 0.01 &&
std::fabs(newFrame.size.height - webView.frame.size.height) < 0.01) {
return;
}
}
// Update the content offset of the scroll view to match the padding
// that will be included in the frame.
CGFloat currentTopInset = webView.frame.origin.y;
CGPoint newContentOffset = scrollViewProxy.contentOffset;
newContentOffset.y += insets.top - currentTopInset;
if (self.compensateFrameChangeByOffset) {
scrollViewProxy.contentOffset = newContentOffset;
}
webView.frame = newFrame;
// Setting WKWebView frame can mistakenly reset contentOffset. Change it
// back to the initial value if necessary.
// TODO(crbug.com/645857): Remove this workaround once WebKit bug is
// fixed.
if (self.compensateFrameChangeByOffset &&
[scrollViewProxy contentOffset].y != newContentOffset.y) {
[scrollViewProxy setContentOffset:newContentOffset];
}
}
// Observes the frame property of the view of the `webState` using KVO.
- (void)observeWebStateViewFrame:(web::WebState*)webState {
if (_installedObserver || !webState->GetView()) {
return;
}
NSKeyValueObservingOptions options = 0;
if (!base::FeatureList::IsEnabled(web::features::kSmoothScrollingDefault)) {
options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
}
[webState->GetView() addObserver:self
forKeyPath:@"frame"
options:options
context:nil];
_installedObserver = YES;
}
// Callback for the KVO.
- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
if (![keyPath isEqualToString:@"frame"] || object != _webState->GetView())
return;
if (!base::FeatureList::IsEnabled(web::features::kSmoothScrollingDefault)) {
NSValue* oldValue =
base::apple::ObjCCast<NSValue>(change[NSKeyValueChangeOldKey]);
NSValue* newValue =
base::apple::ObjCCast<NSValue>(change[NSKeyValueChangeNewKey]);
// If the value is unchanged -- if the old and new values are equal --
// then return without notifying observers.
if (oldValue && newValue && [newValue isEqualToValue:oldValue]) {
return;
}
}
[self updateForCurrentState];
}
- (void)stopObservingWebStateViewFrame {
if (!_installedObserver) {
return;
}
DCHECK(_webState);
DCHECK(_webState->GetView());
[_webState->GetView() removeObserver:self forKeyPath:@"frame"];
_installedObserver = NO;
}
@end