blob: ab3b9767bb08b92a79d422381982e0bec3d01ea2 [file] [log] [blame]
// Copyright 2017 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/web/page_placeholder_tab_helper.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
#import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Placeholder will not be displayed longer than this time.
const double kPlaceholderMaxDisplayTimeInSeconds = 1.5;
// Placeholder removal will include a fade-out animation of this length.
const NSTimeInterval kPlaceholderFadeOutAnimationLengthInSeconds = 0.5;
} // namespace
PagePlaceholderTabHelper::PagePlaceholderTabHelper(web::WebState* web_state)
: web_state_(web_state), weak_factory_(this) {
web_state_->AddObserver(this);
}
PagePlaceholderTabHelper::~PagePlaceholderTabHelper() {
RemovePlaceholder();
}
void PagePlaceholderTabHelper::AddPlaceholderForNextNavigation() {
add_placeholder_for_next_navigation_ = true;
}
void PagePlaceholderTabHelper::CancelPlaceholderForNextNavigation() {
add_placeholder_for_next_navigation_ = false;
if (displaying_placeholder_) {
RemovePlaceholder();
}
}
void PagePlaceholderTabHelper::DidStartNavigation(
web::WebState* web_state,
web::NavigationContext* navigation_context) {
DCHECK_EQ(web_state_, web_state);
if (add_placeholder_for_next_navigation_) {
add_placeholder_for_next_navigation_ = false;
AddPlaceholder();
}
}
void PagePlaceholderTabHelper::PageLoaded(web::WebState* web_state,
web::PageLoadCompletionStatus) {
DCHECK_EQ(web_state_, web_state);
RemovePlaceholder();
}
void PagePlaceholderTabHelper::WebStateDestroyed(web::WebState* web_state) {
DCHECK_EQ(web_state_, web_state);
web_state_->RemoveObserver(this);
web_state_ = nullptr;
RemovePlaceholder();
}
void PagePlaceholderTabHelper::AddPlaceholder() {
if (displaying_placeholder_)
return;
displaying_placeholder_ = true;
// Lazily create the placeholder view.
if (!placeholder_view_) {
placeholder_view_ = [[UIImageView alloc] init];
placeholder_view_.contentMode = UIViewContentModeScaleAspectFit;
placeholder_view_.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
// Update placeholder view's image and display it on top of WebState's view.
__weak UIImageView* weak_placeholder_view = placeholder_view_;
__weak UIView* weak_web_state_view = web_state_->GetView();
__weak id<CRWWebViewProxy> web_view_proxy = web_state_->GetWebViewProxy();
SnapshotTabHelper::FromWebState(web_state_)
->RetrieveGreySnapshot(^(UIImage* snapshot) {
CGRect frame = weak_web_state_view.frame;
UIEdgeInsets inset = web_view_proxy.contentInset;
frame.origin.x += inset.left;
frame.origin.y += inset.top;
frame.size.width -= (inset.right + inset.left);
frame.size.height -= (inset.bottom + inset.top);
weak_placeholder_view.frame = frame;
weak_placeholder_view.image = snapshot;
[weak_web_state_view addSubview:weak_placeholder_view];
});
// Remove placeholder if it takes too long to load the page.
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&PagePlaceholderTabHelper::RemovePlaceholder,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromSecondsD(kPlaceholderMaxDisplayTimeInSeconds));
}
void PagePlaceholderTabHelper::RemovePlaceholder() {
if (!displaying_placeholder_)
return;
displaying_placeholder_ = false;
// Remove placeholder view with a fade-out animation.
__weak UIImageView* weak_placeholder_view = placeholder_view_;
[UIView animateWithDuration:kPlaceholderFadeOutAnimationLengthInSeconds
animations:^{
weak_placeholder_view.alpha = 0.0f;
}
completion:^(BOOL finished) {
[weak_placeholder_view removeFromSuperview];
}];
}