| Index: ios/chrome/browser/ui/reader_mode/reader_mode_controller.mm
|
| diff --git a/ios/chrome/browser/ui/reader_mode/reader_mode_controller.mm b/ios/chrome/browser/ui/reader_mode/reader_mode_controller.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f04917f4ca09773eacf1ab08739aa0b30269fae7
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/reader_mode/reader_mode_controller.mm
|
| @@ -0,0 +1,309 @@
|
| +// Copyright 2015 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/ui/reader_mode/reader_mode_controller.h"
|
| +
|
| +#include <memory>
|
| +#include <utility>
|
| +
|
| +#include "base/ios/weak_nsobject.h"
|
| +#include "base/mac/bind_objc_block.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "components/infobars/core/infobar.h"
|
| +#include "components/infobars/core/infobar_manager.h"
|
| +#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
|
| +#include "ios/chrome/browser/dom_distiller/distiller_viewer.h"
|
| +#include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
|
| +#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
|
| +#import "ios/chrome/browser/ui/reader_mode/reader_mode_checker.h"
|
| +#import "ios/chrome/browser/ui/reader_mode/reader_mode_infobar_delegate.h"
|
| +#import "ios/chrome/browser/ui/reader_mode/reader_mode_view.h"
|
| +#include "ios/web/public/browser_state.h"
|
| +#include "ios/web/public/web_state/web_state.h"
|
| +
|
| +@protocol ReaderModeCheckerObserverBridgeProtocol
|
| +- (void)pageIsDistillable;
|
| +@end
|
| +
|
| +@protocol InfoBarManagerObserverBridgeProtocol
|
| +- (void)infoBarRemoved:(infobars::InfoBar*)infobar;
|
| +@end
|
| +
|
| +namespace {
|
| +// Used to find the ReaderModeView in the view hierarchy.
|
| +const NSInteger kReaderModeViewTag = 42;
|
| +const CGFloat kReaderModeAnimationDuration = .5;
|
| +
|
| +class ReaderModeCheckerObserverBridge : ReaderModeCheckerObserver {
|
| + public:
|
| + ReaderModeCheckerObserverBridge(
|
| + ReaderModeChecker* readerModeChecker,
|
| + id<ReaderModeCheckerObserverBridgeProtocol> observer)
|
| + : ReaderModeCheckerObserver(readerModeChecker), observer_(observer) {
|
| + DCHECK(observer);
|
| + };
|
| +
|
| + void PageIsDistillable() override { [observer_ pageIsDistillable]; };
|
| +
|
| + private:
|
| + id<ReaderModeCheckerObserverBridgeProtocol> observer_;
|
| +};
|
| +
|
| +class InfoBarManagerObserverBridge : infobars::InfoBarManager::Observer {
|
| + public:
|
| + InfoBarManagerObserverBridge(
|
| + infobars::InfoBarManager* infoBarManager,
|
| + id<InfoBarManagerObserverBridgeProtocol> observer)
|
| + : infobars::InfoBarManager::Observer(),
|
| + manager_(infoBarManager),
|
| + observer_(observer) {
|
| + DCHECK(infoBarManager);
|
| + DCHECK(observer);
|
| + manager_->AddObserver(this);
|
| + };
|
| +
|
| + ~InfoBarManagerObserverBridge() override {
|
| + if (manager_)
|
| + manager_->RemoveObserver(this);
|
| + }
|
| +
|
| + void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override {
|
| + [observer_ infoBarRemoved:infobar];
|
| + }
|
| +
|
| + void OnManagerShuttingDown(infobars::InfoBarManager* manager) override {
|
| + manager_->RemoveObserver(this);
|
| + manager_ = nullptr;
|
| + }
|
| +
|
| + private:
|
| + infobars::InfoBarManager* manager_;
|
| + id<InfoBarManagerObserverBridgeProtocol> observer_;
|
| +};
|
| +} // namespace
|
| +
|
| +@interface ReaderModeController ()<ReaderModeViewDelegate,
|
| + ReaderModeCheckerObserverBridgeProtocol,
|
| + InfoBarManagerObserverBridgeProtocol> {
|
| + std::unique_ptr<ReaderModeChecker> _checker;
|
| + std::unique_ptr<ReaderModeCheckerObserverBridge> _checkerBridge;
|
| + std::unique_ptr<InfoBarManagerObserverBridge> _infoBarBridge;
|
| + std::unique_ptr<dom_distiller::DistillerViewer> _viewer;
|
| + // The currently displayed infobar.
|
| + infobars::InfoBar* infobar_;
|
| + web::WebState* _webState;
|
| +}
|
| +@property(readonly, nonatomic) id<ReaderModeControllerDelegate> delegate;
|
| +
|
| +// Triggers a distillation and returns a DistillerViewer to keep as a handle to
|
| +// the running distillation.
|
| +- (std::unique_ptr<dom_distiller::DistillerViewer>)startDistillation
|
| + WARN_UNUSED_RESULT;
|
| +- (void)distillationFinished:(const std::string&)html forURL:(const GURL&)url;
|
| +
|
| +// Returns a ReaderModeView that presents a waiting UI while the distillation
|
| +// is taking place. Releasing this view will stop the distillation in progress.
|
| +- (ReaderModeView*)readerModeViewWithFrame:(CGRect)frame;
|
| +
|
| +- (void)showInfoBar:(const std::string&)html forURL:(const GURL&)url;
|
| +- (void)removeInfoBar;
|
| +
|
| +@end
|
| +
|
| +@implementation ReaderModeController
|
| +@synthesize delegate = _delegate;
|
| +
|
| +- (instancetype)initWithWebState:(web::WebState*)webState
|
| + delegate:(id<ReaderModeControllerDelegate>)delegate {
|
| + DCHECK(webState);
|
| + self = [super init];
|
| + if (self) {
|
| + _webState = webState;
|
| + _checker = base::MakeUnique<ReaderModeChecker>(_webState);
|
| + _delegate = delegate;
|
| + _checkerBridge =
|
| + base::MakeUnique<ReaderModeCheckerObserverBridge>(_checker.get(), self);
|
| + infobars::InfoBarManager* infobar_manager =
|
| + InfoBarManagerImpl::FromWebState(_webState);
|
| + _infoBarBridge =
|
| + base::MakeUnique<InfoBarManagerObserverBridge>(infobar_manager, self);
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (instancetype)init {
|
| + NOTREACHED();
|
| + return nil;
|
| +}
|
| +
|
| +- (void)dealloc {
|
| + if (_webState)
|
| + [self detachFromWebState];
|
| + [super dealloc];
|
| +}
|
| +
|
| +- (void)detachFromWebState {
|
| + [self removeInfoBar];
|
| + _webState = nullptr;
|
| +}
|
| +
|
| +// Property accessor.
|
| +- (ReaderModeChecker*)checker {
|
| + return _checker.get();
|
| +}
|
| +
|
| +#pragma mark - Private methods.
|
| +#pragma mark distillation
|
| +
|
| +- (std::unique_ptr<dom_distiller::DistillerViewer>)startDistillation {
|
| + DCHECK(_webState);
|
| + base::WeakNSObject<ReaderModeController> weakSelf(self);
|
| + GURL pageURL = _webState->GetLastCommittedURL();
|
| + ios::ChromeBrowserState* browserState =
|
| + ios::ChromeBrowserState::FromBrowserState(_webState->GetBrowserState());
|
| + return base::MakeUnique<dom_distiller::DistillerViewer>(
|
| + dom_distiller::DomDistillerServiceFactory::GetForBrowserState(
|
| + browserState),
|
| + browserState->GetPrefs(), pageURL,
|
| + base::BindBlock(^(
|
| + const GURL& pageURL, const std::string& html,
|
| + const std::vector<dom_distiller::DistillerViewer::ImageInfo>& images,
|
| + const std::string& title) {
|
| + [weakSelf distillationFinished:html forURL:pageURL];
|
| + }));
|
| +}
|
| +
|
| +- (void)distillationFinished:(const std::string&)html forURL:(const GURL&)url {
|
| + UIView* superview = [self.delegate superviewForReaderModePanel];
|
| + DCHECK(_viewer || [superview viewWithTag:kReaderModeViewTag]);
|
| + if ([superview viewWithTag:kReaderModeViewTag]) {
|
| + [self.delegate loadReaderModeHTML:base::SysUTF8ToNSString(html) forURL:url];
|
| + } else if (_viewer) {
|
| + [self showInfoBar:html forURL:url];
|
| + }
|
| +}
|
| +
|
| +#pragma mark view creation
|
| +
|
| +- (ReaderModeView*)readerModeViewWithFrame:(CGRect)frame {
|
| + DCHECK(_checker->CanSwitchToReaderMode());
|
| + ReaderModeView* view =
|
| + [[[ReaderModeView alloc] initWithFrame:frame delegate:self] autorelease];
|
| + [view assignDistillerViewer:[self startDistillation]];
|
| + return view;
|
| +}
|
| +
|
| +#pragma mark infobar.
|
| +
|
| +- (void)showInfoBar:(const std::string&)html forURL:(const GURL&)url {
|
| + DCHECK(_webState);
|
| + base::WeakNSProtocol<id<ReaderModeControllerDelegate>> weakDelegate(
|
| + self.delegate);
|
| +
|
| + // Non reference version of the variables needed.
|
| + const std::string html_non_ref(html);
|
| + const GURL url_non_ref(url);
|
| + auto infoBarDelegate =
|
| + base::MakeUnique<ReaderModeInfoBarDelegate>(base::BindBlock(^{
|
| + [weakDelegate loadReaderModeHTML:base::SysUTF8ToNSString(html_non_ref)
|
| + forURL:url_non_ref];
|
| + }));
|
| +
|
| + infobars::InfoBarManager* infobar_manager =
|
| + InfoBarManagerImpl::FromWebState(_webState);
|
| +
|
| + std::unique_ptr<infobars::InfoBar> infobar =
|
| + infobar_manager->CreateConfirmInfoBar(std::move(infoBarDelegate));
|
| + if (infobar_)
|
| + infobar_ = infobar_manager->ReplaceInfoBar(infobar_, std::move(infobar));
|
| + else
|
| + infobar_ = infobar_manager->AddInfoBar(std::move(infobar));
|
| +}
|
| +
|
| +- (void)removeInfoBar {
|
| + DCHECK(_webState);
|
| + if (infobar_) {
|
| + infobars::InfoBarManager* infobar_manager =
|
| + InfoBarManagerImpl::FromWebState(_webState);
|
| + infobar_manager->RemoveInfoBar(infobar_);
|
| + infobar_ = nullptr;
|
| + }
|
| +}
|
| +
|
| +#pragma mark - public methods.
|
| +
|
| +- (void)switchToReaderMode {
|
| + UIView* superview = [self.delegate superviewForReaderModePanel];
|
| + if ([superview viewWithTag:kReaderModeViewTag])
|
| + return; // There is already a reader mode waiting view visible.
|
| +
|
| + [self removeInfoBar];
|
| +
|
| + // Get the view.
|
| + ReaderModeView* readerView = [self readerModeViewWithFrame:superview.bounds];
|
| + readerView.tag = kReaderModeViewTag;
|
| + [superview addSubview:readerView];
|
| +
|
| + // Animate the view in. First the view is animated in (via transparency) and
|
| + // the the animation on the view itself is started.
|
| + readerView.alpha = 0.0;
|
| +
|
| + [UIView animateWithDuration:kReaderModeAnimationDuration
|
| + delay:0.0
|
| + options:UIViewAnimationOptionCurveEaseIn
|
| + animations:^{
|
| + readerView.alpha = 1.0;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + if (finished)
|
| + [readerView start]; // Starts the waiting animation on the view.
|
| + }];
|
| +}
|
| +
|
| +#pragma mark - ReaderModeViewDelegate.
|
| +
|
| +- (void)exitReaderMode {
|
| + UIView* superview = [self.delegate superviewForReaderModePanel];
|
| + if (![superview viewWithTag:kReaderModeViewTag]) {
|
| + DCHECK(_viewer);
|
| + return;
|
| + }
|
| +
|
| + ReaderModeView* readerView =
|
| + static_cast<ReaderModeView*>([superview viewWithTag:kReaderModeViewTag]);
|
| + if (!readerView)
|
| + return;
|
| +
|
| + // First stop the view waiting animation (if any) and then remove the view.
|
| + [readerView stopWaitingWithCompletion:^{
|
| + [UIView animateWithDuration:kReaderModeAnimationDuration
|
| + delay:0.0
|
| + options:UIViewAnimationOptionCurveEaseIn
|
| + animations:^{
|
| + readerView.alpha = 0.0;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + [readerView removeFromSuperview];
|
| + }];
|
| + }];
|
| +}
|
| +
|
| +#pragma mark - ReaderModeCheckerObserverBridgeProtocol
|
| +
|
| +- (void)pageIsDistillable {
|
| + _viewer = [self startDistillation];
|
| +}
|
| +
|
| +#pragma mark - InfoBarManagerObserverBridgeProtocol.
|
| +
|
| +- (void)infoBarRemoved:(infobars::InfoBar*)infobar {
|
| + if (infobar == infobar_) {
|
| + _viewer.reset();
|
| + infobar_ = nullptr;
|
| + }
|
| +}
|
| +
|
| +@end
|
|
|