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 |