diff options
author | kkhorimoto <kkhorimoto@chromium.org> | 2015-06-18 18:21:12 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-19 01:21:32 +0000 |
commit | c5bbbbfedd1369a7fddca5fde9a8b643befc9cd2 (patch) | |
tree | 40ee7b59ff4242cee66825cf28cffd1bb1634703 | |
parent | 29d8e3b989cb4938640639ce90de488e5ca90c12 (diff) | |
download | chromium_src-c5bbbbfedd1369a7fddca5fde9a8b643befc9cd2.zip chromium_src-c5bbbbfedd1369a7fddca5fde9a8b643befc9cd2.tar.gz chromium_src-c5bbbbfedd1369a7fddca5fde9a8b643befc9cd2.tar.bz2 |
Created CRWContentView.
This class consolidates the logic for displaying scrollable content and
hooking the scroll view up with the scroll view proxy.
Notable changes:
- Interstitials and the Sad Tab are now both implemented using
CRWContentViews.
- The CRWWebViewProxy API has been changed to take a single
CRWContentView instead of a web view and a scroll view.
- WebInterstitial::SetSize() has been removed since WebState already
handles resizing transient content views before display.
BUG=478181
Review URL: https://codereview.chromium.org/1183243002
Cr-Commit-Position: refs/heads/master@{#335188}
32 files changed, 690 insertions, 252 deletions
diff --git a/ios/web/interstitials/html_web_interstitial_impl.h b/ios/web/interstitials/html_web_interstitial_impl.h index 7bda180..8e469a9 100644 --- a/ios/web/interstitials/html_web_interstitial_impl.h +++ b/ios/web/interstitials/html_web_interstitial_impl.h @@ -32,12 +32,8 @@ class HtmlWebInterstitialImpl : public WebInterstitialImpl { // receives a JavaScript command. void CommandReceivedFromWebView(NSString* command); - // WebInterstitial implementation: - void SetSize(const gfx::Size& size) override; - // WebInterstitialImpl implementation: - UIView* GetView() const override; - UIScrollView* GetScrollView() const override; + CRWContentView* GetContentView() const override; protected: // WebInterstitialImpl implementation: @@ -56,6 +52,8 @@ class HtmlWebInterstitialImpl : public WebInterstitialImpl { // The CRWSimpleWebViewController that contains the web view used to show the // content. View needs to be resized by the caller. base::scoped_nsprotocol<id<CRWSimpleWebViewController>> web_view_controller_; + // The CRWContentView used to display |web_view_controller_|'s view. + base::scoped_nsobject<CRWContentView> content_view_; }; } // namespace web diff --git a/ios/web/interstitials/html_web_interstitial_impl.mm b/ios/web/interstitials/html_web_interstitial_impl.mm index 4a23631..4a2d091 100644 --- a/ios/web/interstitials/html_web_interstitial_impl.mm +++ b/ios/web/interstitials/html_web_interstitial_impl.mm @@ -8,6 +8,7 @@ #include "base/strings/sys_string_conversions.h" #include "ios/web/interstitials/web_interstitial_facade_delegate.h" #include "ios/web/public/interstitials/web_interstitial_delegate.h" +#include "ios/web/public/web_state/ui/crw_web_view_content_view.h" #import "ios/web/web_state/ui/crw_simple_web_view_controller.h" #include "ios/web/web_state/web_state_impl.h" #import "ios/web/web_state/web_view_creation_utils.h" @@ -82,22 +83,12 @@ void HtmlWebInterstitialImpl::CommandReceivedFromWebView(NSString* command) { delegate_->CommandReceived(base::SysNSStringToUTF8(command)); } -void HtmlWebInterstitialImpl::SetSize(const gfx::Size& size) { - CGRect frame = [web_view_controller_ view].frame; - frame.size = size.ToCGSize(); - [[web_view_controller_ view] setFrame:frame]; -} - -UIView* HtmlWebInterstitialImpl::GetView() const { - return [web_view_controller_ view]; -} - -UIScrollView* HtmlWebInterstitialImpl::GetScrollView() const { - return [web_view_controller_ scrollView]; +CRWContentView* HtmlWebInterstitialImpl::GetContentView() const { + return content_view_.get(); } void HtmlWebInterstitialImpl::PrepareForDisplay() { - if (!web_view_controller_) { + if (!content_view_) { web_view_controller_delegate_.reset( [[CRWWebInterstitialImplCRWSimpleWebViewDelegate alloc] initWithInterstitial:this]); @@ -111,6 +102,9 @@ void HtmlWebInterstitialImpl::PrepareForDisplay() { NSString* html = base::SysUTF8ToNSString(delegate_->GetHtmlContents()); [web_view_controller_ loadHTMLString:html baseURL:net::NSURLWithGURL(GetUrl())]; + content_view_.reset([[CRWWebViewContentView alloc] + initWithWebView:[web_view_controller_ view] + scrollView:[web_view_controller_ scrollView]]); } } diff --git a/ios/web/interstitials/native_web_interstitial_impl.h b/ios/web/interstitials/native_web_interstitial_impl.h index 87179d8..5957ea9 100644 --- a/ios/web/interstitials/native_web_interstitial_impl.h +++ b/ios/web/interstitials/native_web_interstitial_impl.h @@ -23,12 +23,8 @@ class NativeWebInterstitialImpl : public WebInterstitialImpl { scoped_ptr<NativeWebInterstitialDelegate> delegate); ~NativeWebInterstitialImpl() override; - // WebInterstitial implementation: - void SetSize(const gfx::Size& size) override; - // WebInterstitialImpl implementation: - UIView* GetView() const override; - UIScrollView* GetScrollView() const override; + CRWContentView* GetContentView() const override; protected: // WebInterstitialImpl implementation: @@ -40,12 +36,8 @@ class NativeWebInterstitialImpl : public WebInterstitialImpl { private: // The native interstitial delegate. scoped_ptr<NativeWebInterstitialDelegate> delegate_; - // The top-level view containing the scroll view. - base::scoped_nsobject<UIView> container_view_; - // The scroll view used to display |content_view_|. - base::scoped_nsobject<UIScrollView> scroll_view_; - // The content view provided by |delegate_|. - base::WeakNSObject<UIView> content_view_; + // The transient content view containing interstitial content. + base::scoped_nsobject<CRWContentView> content_view_; }; } // namespace web diff --git a/ios/web/interstitials/native_web_interstitial_impl.mm b/ios/web/interstitials/native_web_interstitial_impl.mm index f205d48..1a86b23 100644 --- a/ios/web/interstitials/native_web_interstitial_impl.mm +++ b/ios/web/interstitials/native_web_interstitial_impl.mm @@ -6,6 +6,7 @@ #include "base/logging.h" #include "ios/web/public/interstitials/web_interstitial_delegate.h" +#import "ios/web/public/web_state/ui/crw_generic_content_view.h" #include "ios/web/web_state/web_state_impl.h" #include "ui/gfx/geometry/size.h" @@ -31,37 +32,14 @@ NativeWebInterstitialImpl::NativeWebInterstitialImpl( NativeWebInterstitialImpl::~NativeWebInterstitialImpl() { } -void NativeWebInterstitialImpl::SetSize(const gfx::Size& size) { - if (!content_view_) - return; - - // Resize container and scroll view. - CGSize cgSize = size.ToCGSize(); - [container_view_ setFrame:{[container_view_ frame].origin, cgSize}]; - [scroll_view_ setFrame:[container_view_ bounds]]; - - // Resize content. - CGSize contentSize = [content_view_ sizeThatFits:cgSize]; - [content_view_ setFrame:{CGPointZero, contentSize}]; - [scroll_view_ setContentSize:contentSize]; -} - -UIView* NativeWebInterstitialImpl::GetView() const { - return container_view_.get(); -} - -UIScrollView* NativeWebInterstitialImpl::GetScrollView() const { - return scroll_view_.get(); +CRWContentView* NativeWebInterstitialImpl::GetContentView() const { + return content_view_.get(); } void NativeWebInterstitialImpl::PrepareForDisplay() { if (!content_view_) { - container_view_.reset([[UIView alloc] initWithFrame:CGRectZero]); - scroll_view_.reset([[UIScrollView alloc] initWithFrame:CGRectZero]); - content_view_.reset(delegate_->GetContentView()); - [scroll_view_ addSubview:content_view_]; - [scroll_view_ setBackgroundColor:delegate_->GetScrollViewBackgroundColor()]; - [container_view_ addSubview:scroll_view_]; + content_view_.reset([[CRWGenericContentView alloc] + initWithView:delegate_->GetContentView()]); } } diff --git a/ios/web/interstitials/web_interstitial_impl.h b/ios/web/interstitials/web_interstitial_impl.h index a438264..c1826ca 100644 --- a/ios/web/interstitials/web_interstitial_impl.h +++ b/ios/web/interstitials/web_interstitial_impl.h @@ -8,6 +8,7 @@ #import <UIKit/UIKit.h> #import "ios/web/public/interstitials/web_interstitial.h" +#include "ios/web/public/web_state/ui/crw_content_view.h" #include "ios/web/public/web_state/web_state_observer.h" #import "ios/web/web_state/ui/web_view_js_utils.h" #include "url/gurl.h" @@ -32,9 +33,8 @@ class WebInterstitialImpl : public WebInterstitial, public WebStateObserver { WebInterstitialImpl(WebStateImpl* web_state, const GURL& url); ~WebInterstitialImpl() override; - // Returns the view and scroll view used to display the interstitial content. - virtual UIView* GetView() const = 0; - virtual UIScrollView* GetScrollView() const = 0; + // Returns the transient content view used to display interstitial content. + virtual CRWContentView* GetContentView() const = 0; // Returns the url corresponding to this interstitial. const GURL& GetUrl() const; diff --git a/ios/web/interstitials/web_interstitial_impl.mm b/ios/web/interstitials/web_interstitial_impl.mm index 3e14856..b40a189 100644 --- a/ios/web/interstitials/web_interstitial_impl.mm +++ b/ios/web/interstitials/web_interstitial_impl.mm @@ -46,11 +46,11 @@ WebInterstitialFacadeDelegate* WebInterstitialImpl::GetFacadeDelegate() const { void WebInterstitialImpl::Show() { PrepareForDisplay(); - GetWebStateImpl()->DisplayWebInterstitial(this); + GetWebStateImpl()->ShowWebInterstitial(this); } void WebInterstitialImpl::Hide() { - GetWebStateImpl()->DismissWebInterstitial(); + GetWebStateImpl()->ClearTransientContentView(); } void WebInterstitialImpl::DontProceed() { diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp index 5b0f50b..7e7eddd 100644 --- a/ios/web/ios_web.gyp +++ b/ios/web/ios_web.gyp @@ -184,8 +184,11 @@ 'public/web_state/js/crw_js_injection_receiver.h', 'public/web_state/page_display_state.h', 'public/web_state/page_display_state.mm', + 'public/web_state/ui/crw_content_view.h', + 'public/web_state/ui/crw_generic_content_view.h', 'public/web_state/ui/crw_native_content.h', 'public/web_state/ui/crw_native_content_provider.h', + 'public/web_state/ui/crw_web_view_content_view.h', 'public/web_state/url_verification_constants.h', 'public/web_state/web_state.h', 'public/web_state/web_state_observer.h', @@ -232,6 +235,7 @@ 'web_state/ui/crw_context_menu_provider.mm', 'web_state/ui/crw_debug_web_view.h', 'web_state/ui/crw_debug_web_view.mm', + 'web_state/ui/crw_generic_content_view.mm', 'web_state/ui/crw_simple_web_view_controller.h', 'web_state/ui/crw_static_file_web_view.h', 'web_state/ui/crw_static_file_web_view.mm', @@ -247,6 +251,7 @@ 'web_state/ui/crw_web_controller.mm', 'web_state/ui/crw_web_controller_container_view.h', 'web_state/ui/crw_web_controller_container_view.mm', + 'web_state/ui/crw_web_view_content_view.mm', 'web_state/ui/crw_wk_simple_web_view_controller.h', 'web_state/ui/crw_wk_simple_web_view_controller.mm', 'web_state/ui/crw_wk_web_view_crash_detector.h', @@ -509,6 +514,8 @@ 'public/test/test_web_state.h', 'public/test/test_web_thread.h', 'public/test/test_web_thread_bundle.h', + 'public/test/test_web_view_content_view.h', + 'public/test/test_web_view_content_view.mm', 'public/test/web_test_util.h', 'test/crw_fake_web_controller_observer.h', 'test/crw_fake_web_controller_observer.mm', diff --git a/ios/web/public/interstitials/web_interstitial.h b/ios/web/public/interstitials/web_interstitial.h index 43629c1..8a573c3 100644 --- a/ios/web/public/interstitials/web_interstitial.h +++ b/ios/web/public/interstitials/web_interstitial.h @@ -63,9 +63,6 @@ class WebInterstitial { // the target URL. // Warning: 'this' has been deleted when this method returns. virtual void Proceed() = 0; - - // Sizes the view showing the actual interstitial page contents. - virtual void SetSize(const gfx::Size& size) = 0; }; } // namespace web diff --git a/ios/web/public/test/test_web_state.h b/ios/web/public/test/test_web_state.h index 4af4833..17f7a02 100644 --- a/ios/web/public/test/test_web_state.h +++ b/ios/web/public/test/test_web_state.h @@ -33,6 +33,7 @@ class TestWebState : public WebState { const GURL& GetVisibleURL() const override; const GURL& GetLastCommittedURL() const override; GURL GetCurrentURL(URLVerificationTrustLevel* trust_level) const override; + void ShowTransientContentView(CRWContentView* content_view) override {} void AddScriptCommandCallback(const ScriptCommandCallback& callback, const std::string& command_prefix) override {} void RemoveScriptCommandCallback(const std::string& command_prefix) override { diff --git a/ios/web/public/test/test_web_view_content_view.h b/ios/web/public/test/test_web_view_content_view.h new file mode 100644 index 0000000..560b9c7 --- /dev/null +++ b/ios/web/public/test/test_web_view_content_view.h @@ -0,0 +1,23 @@ +// 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. + +#ifndef IOS_WEB_PUBLIC_TEST_TEST_WEB_VIEW_CONTENT_VIEW_H_ +#define IOS_WEB_PUBLIC_TEST_TEST_WEB_VIEW_CONTENT_VIEW_H_ + +#import "ios/web/public/web_state/ui/crw_web_view_content_view.h" + +// A test version of CRWWebViewContentView. +@interface TestWebViewContentView : CRWWebViewContentView + +// Initializes the CRWTestWebContentView. Since |webView| and |scrollView| may +// be mock objects, they will not be added as subviews. +- (instancetype)initWithMockWebView:(id)webView scrollView:(id)scrollView; + +// CRWTestWebViewContentViews should be initialized via |-initWithMockWebView: +// scrollView:|. +- (instancetype)initWithWebView:(UIView*)webView + scrollView:(UIScrollView*)scrollView NS_UNAVAILABLE; +@end + +#endif // IOS_WEB_PUBLIC_TEST_TEST_WEB_VIEW_CONTENT_VIEW_H_ diff --git a/ios/web/public/test/test_web_view_content_view.mm b/ios/web/public/test/test_web_view_content_view.mm new file mode 100644 index 0000000..afccc98 --- /dev/null +++ b/ios/web/public/test/test_web_view_content_view.mm @@ -0,0 +1,40 @@ +// 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. + +#include "ios/web/public/test/test_web_view_content_view.h" + +#include "base/logging.h" +#include "base/mac/scoped_nsobject.h" + +@interface TestWebViewContentView () { + base::scoped_nsprotocol<id> _mockWebView; + base::scoped_nsprotocol<id> _mockScrollView; +} + +@end + +@implementation TestWebViewContentView + +- (instancetype)initWithMockWebView:(id)webView scrollView:(id)scrollView { + self = [super initWithFrame:CGRectZero]; + if (self) { + DCHECK(webView); + DCHECK(scrollView); + _mockWebView.reset([webView retain]); + _mockScrollView.reset([scrollView retain]); + } + return self; +} + +#pragma mark Accessors + +- (UIScrollView*)scrollView { + return static_cast<UIScrollView*>(_mockScrollView.get()); +} + +- (UIView*)webView { + return static_cast<UIView*>(_mockWebView.get()); +} + +@end diff --git a/ios/web/public/web_state/crw_web_view_proxy.h b/ios/web/public/web_state/crw_web_view_proxy.h index 784df4a..dc89d82 100644 --- a/ios/web/public/web_state/crw_web_view_proxy.h +++ b/ios/web/public/web_state/crw_web_view_proxy.h @@ -13,6 +13,7 @@ // Provides an interface for embedders to access the WebState's UIWebView in a // limited and controlled manner. +// TODO(kkhorimoto): rename protocol to CRWContentViewProxy. @protocol CRWWebViewProxy<NSObject> // The UIWebView's bounding rectangle (relative to its parent). diff --git a/ios/web/public/web_state/crw_web_view_scroll_view_proxy.h b/ios/web/public/web_state/crw_web_view_scroll_view_proxy.h index 9f9aadb..d46735e 100644 --- a/ios/web/public/web_state/crw_web_view_scroll_view_proxy.h +++ b/ios/web/public/web_state/crw_web_view_scroll_view_proxy.h @@ -20,6 +20,7 @@ // needed. // The class forwards some of the methods onto the UIScrollView. For more // information look at the UIScrollView documentation. +// TODO(kkhorimoto): rename class to CRWContentViewScrollViewProxy. @interface CRWWebViewScrollViewProxy : NSObject<UIScrollViewDelegate> @property(nonatomic, assign) CGPoint contentOffset; @property(nonatomic, assign) UIEdgeInsets contentInset; diff --git a/ios/web/public/web_state/ui/crw_content_view.h b/ios/web/public/web_state/ui/crw_content_view.h new file mode 100644 index 0000000..d84ecc2 --- /dev/null +++ b/ios/web/public/web_state/ui/crw_content_view.h @@ -0,0 +1,28 @@ +// 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. + +#ifndef IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_ +#define IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_ + +#import <UIKit/UIKit.h> + +// UIViews conforming to CRWScrollableContent (i.e. CRWContentViews) are used +// to display content within a WebState. +@protocol CRWScrollableContent<NSObject> + +// The scroll view used to display the content. If |scrollView| is non-nil, +// it will be used to back the CRWContentViewScrollViewProxy and is expected to +// be a subview of the CRWContentView. +@property(nonatomic, retain, readonly) UIScrollView* scrollView; + +// Returns YES if content is being displayed in the scroll view. +// TODO(stuartmorgan): See if this can be removed from the public interface. +- (BOOL)isViewAlive; + +@end + +// Convenience type for content views. +typedef UIView<CRWScrollableContent> CRWContentView; + +#endif // IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_CONTENT_VIEW_H_ diff --git a/ios/web/public/web_state/ui/crw_generic_content_view.h b/ios/web/public/web_state/ui/crw_generic_content_view.h new file mode 100644 index 0000000..985dbb9 --- /dev/null +++ b/ios/web/public/web_state/ui/crw_generic_content_view.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_GENERIC_CONTENT_VIEW_H_ +#define IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_GENERIC_CONTENT_VIEW_H_ + +#include "ios/web/public/web_state/ui/crw_content_view.h" + +// Wraps an arbitrary native UIView in a CRWContentView. +@interface CRWGenericContentView : CRWContentView + +// The view that was passed to |-initWithContentView:|. This is the view that +// is displayed in |self.scrollView|. +@property(nonatomic, retain, readonly) UIView* view; + +// Initializes the CRWNativeContentContainerView to display |view|, which +// will be added to the scroll view. +- (instancetype)initWithView:(UIView*)view NS_DESIGNATED_INITIALIZER; + +// CRWGenericContentViews should be initialized via |-initWithView:|. +- (instancetype)init NS_UNAVAILABLE; + +@end + +#endif // IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_GENERIC_CONTENT_VIEW_H_ diff --git a/ios/web/public/web_state/ui/crw_web_view_content_view.h b/ios/web/public/web_state/ui/crw_web_view_content_view.h new file mode 100644 index 0000000..e2de7bd --- /dev/null +++ b/ios/web/public/web_state/ui/crw_web_view_content_view.h @@ -0,0 +1,27 @@ +// 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. + +#ifndef IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_WEB_VIEW_CONTENT_VIEW_H_ +#define IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_WEB_VIEW_CONTENT_VIEW_H_ + +#import "ios/web/public/web_state/ui/crw_content_view.h" + +// Wraps a web vew in a CRWContentView. +@interface CRWWebViewContentView : CRWContentView + +// The webView passed to |-initWithWebView|. +@property(nonatomic, retain, readonly) UIView* webView; + +// Initializes the CRWWebViewContentView to display |webView|. +- (instancetype)initWithWebView:(UIView*)webView + scrollView:(UIScrollView*)scrollView + NS_DESIGNATED_INITIALIZER; + +// CRWWebViewContentViews should be initialized via |-initWithWebView: +// scrollView:|. +- (instancetype)init NS_UNAVAILABLE; + +@end + +#endif // IOS_WEB_PUBLIC_WEB_STATE_UI_CRW_WEB_VIEW_CONTENT_VIEW_H_ diff --git a/ios/web/public/web_state/web_state.h b/ios/web/public/web_state/web_state.h index f4cd765..6cdc4b0 100644 --- a/ios/web/public/web_state/web_state.h +++ b/ios/web/public/web_state/web_state.h @@ -23,11 +23,14 @@ class SkBitmap; #if defined(__OBJC__) @class CRWJSInjectionReceiver; +@protocol CRWScrollableContent; @protocol CRWWebViewProxy; typedef id<CRWWebViewProxy> CRWWebViewProxyType; @class UIView; +typedef UIView<CRWScrollableContent> CRWContentView; #else class CRWJSInjectionReceiver; +typedef void CRWContentView; typedef void* CRWWebViewProxyType; class UIView; #endif // defined(__OBJC__) @@ -137,6 +140,10 @@ class WebState : public base::SupportsUserData { // See http://crbug.com/457679 virtual GURL GetCurrentURL(URLVerificationTrustLevel* trust_level) const = 0; + // Resizes |content_view| to the content area's size and adds it to the + // hierarchy. A navigation will remove the view from the hierarchy. + virtual void ShowTransientContentView(CRWContentView* content_view) = 0; + // Returns true if a WebInterstitial is currently displayed. virtual bool IsShowingWebInterstitial() const = 0; diff --git a/ios/web/test/web_test.mm b/ios/web/test/web_test.mm index c521209..b286b96 100644 --- a/ios/web/test/web_test.mm +++ b/ios/web/test/web_test.mm @@ -114,6 +114,7 @@ void WebTestBase::LoadURL(const GURL& url) { while ([webController_ loadPhase] != PAGE_LOADED) WaitForBackgroundTasks(); webController_.get().delegate = existingDelegate; + [[webController_ view] layoutIfNeeded]; } void WebTestBase::WaitForBackgroundTasks() { diff --git a/ios/web/web_state/crw_web_view_proxy_impl.h b/ios/web/web_state/crw_web_view_proxy_impl.h index 7a8386c..3589106 100644 --- a/ios/web/web_state/crw_web_view_proxy_impl.h +++ b/ios/web/web_state/crw_web_view_proxy_impl.h @@ -8,21 +8,24 @@ #import <UIKit/UIKit.h> #include "ios/web/public/web_state/crw_web_view_proxy.h" +#include "ios/web/public/web_state/ui/crw_content_view.h" @class CRWWebController; +// TODO(kkhorimoto): Rename class to CRWContentViewProxyImpl. @interface CRWWebViewProxyImpl : NSObject<CRWWebViewProxy> +// Used by CRWWebController to set the content view being managed. +// |contentView|'s scroll view property will be managed by the +// WebViewScrollViewProxy. +@property(nonatomic, weak) CRWContentView* contentView; + // TODO(justincohen): It would be better if we could use something like a // ScrollPositionController instead of passing in all of web controller. // crbug.com/227744 // Init with a weak reference of web controller, used for passing thru calls. - (instancetype)initWithWebController:(CRWWebController*)webController; -// Used by the CRWWebController to set the web view to be managed. -// Also sets the UIScrollView to be managed inside the WebViewScrollViewProxy. -- (void)setWebView:(UIView*)webView scrollView:(UIScrollView*)scrollView; - @end #endif // IOS_WEB_WEB_STATE_CRW_WEB_VIEW_PROXY_IMPL_H_ diff --git a/ios/web/web_state/crw_web_view_proxy_impl.mm b/ios/web/web_state/crw_web_view_proxy_impl.mm index adb13c8..c8d6142 100644 --- a/ios/web/web_state/crw_web_view_proxy_impl.mm +++ b/ios/web/web_state/crw_web_view_proxy_impl.mm @@ -7,6 +7,7 @@ #include "base/ios/weak_nsobject.h" #include "base/mac/scoped_nsobject.h" #import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h" +#import "ios/web/public/web_state/ui/crw_content_view.h" #import "ios/web/web_state/ui/crw_web_controller.h" namespace { @@ -64,12 +65,12 @@ UIView* GetFirstResponderSubview(UIView* view) { @end @implementation CRWWebViewProxyImpl { - base::WeakNSObject<UIView> _webView; + base::WeakNSObject<CRWContentView> _contentView; base::WeakNSObject<CRWWebController> _webController; base::scoped_nsobject<NSMutableDictionary> _registeredInsets; // The WebViewScrollViewProxy is a wrapper around the UIWebView's // UIScrollView to give components access in a limited and controlled manner. - base::scoped_nsobject<CRWWebViewScrollViewProxy> _webViewScrollViewProxy; + base::scoped_nsobject<CRWWebViewScrollViewProxy> _contentViewScrollViewProxy; } - (instancetype)initWithWebController:(CRWWebController*)webController { @@ -78,21 +79,21 @@ UIView* GetFirstResponderSubview(UIView* view) { DCHECK(webController); _registeredInsets.reset([[NSMutableDictionary alloc] init]); _webController.reset(webController); - _webViewScrollViewProxy.reset([[CRWWebViewScrollViewProxy alloc] init]); + _contentViewScrollViewProxy.reset([[CRWWebViewScrollViewProxy alloc] init]); } return self; } - (CRWWebViewScrollViewProxy*)scrollViewProxy { - return _webViewScrollViewProxy.get(); + return _contentViewScrollViewProxy.get(); } - (CGRect)bounds { - return [_webView bounds]; + return [_contentView bounds]; } - (NSArray*)gestureRecognizers { - return [_webView gestureRecognizers]; + return [_contentView gestureRecognizers]; } - (web::WebViewType)webViewType { @@ -115,25 +116,27 @@ UIView* GetFirstResponderSubview(UIView* view) { [_registeredInsets removeObjectForKey:callerValue]; } -- (void)setWebView:(UIView*)webView scrollView:(UIScrollView*)scrollView { - _webView.reset(webView); - if (webView) - DCHECK(scrollView); - [_webViewScrollViewProxy setScrollView:scrollView]; +- (CRWContentView*)contentView { + return _contentView.get(); +} + +- (void)setContentView:(CRWContentView*)contentView { + _contentView.reset(contentView); + [_contentViewScrollViewProxy setScrollView:contentView.scrollView]; } - (void)addSubview:(UIView*)view { - return [_webView addSubview:view]; + return [_contentView addSubview:view]; } - (BOOL)hasSearchableTextContent { - return _webView != nil && [_webController contentIsHTML]; + return _contentView != nil && [_webController contentIsHTML]; } - (UIView*)getKeyboardAccessory { - if (!_webView) + if (!_contentView) return nil; - UIView* firstResponder = GetFirstResponderSubview(_webView); + UIView* firstResponder = GetFirstResponderSubview(_contentView); return firstResponder.inputAccessoryView; } diff --git a/ios/web/web_state/ui/crw_generic_content_view.mm b/ios/web/web_state/ui/crw_generic_content_view.mm new file mode 100644 index 0000000..2602313 --- /dev/null +++ b/ios/web/web_state/ui/crw_generic_content_view.mm @@ -0,0 +1,74 @@ +// 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/web/public/web_state/ui/crw_generic_content_view.h" + +#include "base/logging.h" +#include "base/mac/scoped_nsobject.h" + +@interface CRWGenericContentView () { + // Backing objectect for |self.scrollView|. + base::scoped_nsobject<UIScrollView> _scrollView; + // Backing object for |self.view|. + base::scoped_nsobject<UIView> _view; +} + +@end + +@implementation CRWGenericContentView + +- (instancetype)initWithView:(UIView*)view { + self = [super initWithFrame:CGRectZero]; + if (self) { + DCHECK(view); + _view.reset([view retain]); + _scrollView.reset([[UIScrollView alloc] initWithFrame:CGRectZero]); + [self addSubview:_scrollView]; + [_scrollView addSubview:_view]; + [_scrollView setBackgroundColor:[_view backgroundColor]]; + } + return self; +} + +#pragma mark Accessors + +- (UIScrollView*)scrollView { + if (!_scrollView) { + _scrollView.reset([[UIScrollView alloc] initWithFrame:CGRectZero]); + } + return _scrollView.get(); +} + +- (UIView*)view { + return _view.get(); +} + +#pragma mark Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + + // scrollView layout. + self.scrollView.frame = self.bounds; + + // view layout. + CGRect contentRect = + UIEdgeInsetsInsetRect(self.bounds, self.scrollView.contentInset); + CGSize viewSize = [self.view sizeThatFits:contentRect.size]; + self.view.frame = CGRectMake(0.0, 0.0, viewSize.width, viewSize.height); + + // UIScrollViews only scroll vertically if the content size's height is + // creater than that of its content rect. + if (viewSize.height <= CGRectGetHeight(contentRect)) { + CGFloat singlePixel = 1.0f / [[UIScreen mainScreen] scale]; + viewSize.height = CGRectGetHeight(contentRect) + singlePixel; + } + self.scrollView.contentSize = viewSize; +} + +- (BOOL)isViewAlive { + return YES; +} + +@end diff --git a/ios/web/web_state/ui/crw_ui_web_view_web_controller.mm b/ios/web/web_state/ui/crw_ui_web_view_web_controller.mm index 467607f..465a776 100644 --- a/ios/web/web_state/ui/crw_ui_web_view_web_controller.mm +++ b/ios/web/web_state/ui/crw_ui_web_view_web_controller.mm @@ -24,6 +24,7 @@ #include "ios/web/net/request_group_util.h" #include "ios/web/public/url_scheme_util.h" #include "ios/web/public/web_client.h" +#import "ios/web/public/web_state/ui/crw_web_view_content_view.h" #import "ios/web/ui_web_view_util.h" #include "ios/web/web_state/frame_info.h" #import "ios/web/web_state/js/crw_js_invoke_parameter_queue.h" @@ -439,9 +440,9 @@ const size_t kMaxMessageQueueSize = 262144; #pragma mark - #pragma mark Testing-Only Methods -- (void)injectWebView:(id)webView { - [super injectWebView:webView]; - [self setWebView:webView]; +- (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView { + [super injectWebViewContentView:webViewContentView]; + [self setWebView:static_cast<UIWebView*>(webViewContentView.webView)]; } #pragma mark CRWJSInjectionEvaluatorMethods diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h index a6be7b5..2282fe4 100644 --- a/ios/web/web_state/ui/crw_web_controller.h +++ b/ios/web/web_state/ui/crw_web_controller.h @@ -52,6 +52,7 @@ extern NSString* const kContainerViewID; @protocol CRWNativeContentProvider; @protocol CRWSwipeRecognizerProvider; @protocol CRWWebControllerObserver; +@class CRWWebViewContentView; @protocol CRWWebViewProxy; class GURL; @@ -94,10 +95,8 @@ class WebStateImpl; @property(nonatomic, readonly) web::WebState* webState; @property(nonatomic, readonly) web::WebStateImpl* webStateImpl; -// If on a regular webpage, the UIWebView responsible for rendering it. If -// on an internal page, the native view implementing the functionality. If the -// view has been purged due to low memory, this will re-create it. It is up -// to the caller to size the view. +// The container view used to display content. If the view has been purged due +// to low memory, this will recreate it. @property(nonatomic, readonly) UIView* view; // The web view proxy associated with this controller. @@ -130,10 +129,12 @@ class WebStateImpl; // Return an image to use as replacement of a missing snapshot. + (UIImage*)defaultSnapshotImage; -// Adds |interstitialView| to the content view, copying the current scroll -// offsets to |scrollView|. -- (void)displayInterstitialView:(UIView*)interstitialView - withScrollView:(UIScrollView*)scrollView; +// Replaces the currently displayed content with |contentView|. The content +// view will be dismissed for the next navigation. +- (void)showTransientContentView:(CRWContentView*)contentView; + +// Clear the transient content view, if one is shown. +- (void)clearTransientContentView; // Give the unload listeners a chance to fire. Returns YES if they complete // and the CRWWebController is in a state it may be closed. @@ -314,9 +315,10 @@ class WebStateImpl; @interface CRWWebController (UsedOnlyForTesting) // Testing or internal API. -// Injects a web view for testing. Takes ownership of the |webView|. -- (void)injectWebView:(id)webView; -- (void)resetInjectedWebView; +// Injects a CRWWebViewContentView for testing. Takes ownership of +// |webViewContentView|. +- (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView; +- (void)resetInjectedWebViewContentView; // Returns the number of observers registered for this CRWWebController. - (NSUInteger)observerCount; - (NSString*)windowId; diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 1b630ee..73aa287 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm @@ -53,8 +53,10 @@ #import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h" #import "ios/web/public/web_state/js/crw_js_injection_manager.h" #import "ios/web/public/web_state/js/crw_js_injection_receiver.h" +#import "ios/web/public/web_state/ui/crw_content_view.h" #import "ios/web/public/web_state/ui/crw_native_content.h" #import "ios/web/public/web_state/ui/crw_native_content_provider.h" +#import "ios/web/public/web_state/ui/crw_web_view_content_view.h" #include "ios/web/public/web_state/url_verification_constants.h" #include "ios/web/public/web_state/web_state.h" #include "ios/web/web_state/blocked_popup_info.h" @@ -259,16 +261,26 @@ void CancelAllTouches(UIScrollView* web_scroll_view) { base::scoped_nsobject<CRWJSInjectionReceiver> _jsInjectionReceiver; } +// The container view. The container view should be accessed through this +// property rather than |self.view| from within this class, as |self.view| +// triggers creation while |self.containerView| will return nil if the view +// hasn't been instantiated. +@property(nonatomic, retain, readonly) + CRWWebControllerContainerView* containerView; // The current page state of the web view. Writing to this property // asynchronously applies the passed value to the current web view. @property(nonatomic, readwrite) web::PageDisplayState pageDisplayState; +// The currently displayed native controller, if any. +@property(nonatomic, readwrite) id<CRWNativeContent> nativeController; +// Removes the container view from the hierarchy and resets the ivar. +- (void)resetContainerView; // Resets any state that is associated with a specific document object (e.g., // page interaction tracking). - (void)resetDocumentSpecificState; // Returns YES if the URL looks like it is one CRWWebController can show. + (BOOL)webControllerCanShow:(const GURL&)url; -// Clear any interstitials being displayed. -- (void)clearInterstitials; +// Clears the currently-displayed transient content view. +- (void)clearTransientContentView; // Returns a lazily created CRWTouchTrackingRecognizer. - (CRWTouchTrackingRecognizer*)touchTrackingRecognizer; // Shows placeholder overlay. @@ -503,11 +515,6 @@ enum { WebKitErrorPlugInLoadFailed = 204, }; -// Tag for the interstitial view so we can find it and dismiss it later. -enum { - kInterstitialViewTag = 1000, -}; - // URLs that are fed into UIWebView as history push/replace get escaped, // potentially changing their format. Code that attempts to determine whether a // URL hasn't changed can be confused by those differences though, so method @@ -636,24 +643,24 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; return _webStateImpl.get(); } -// WebStateImpl will delete the interstitial page object, which will in turn -// remove its view from |_contentView|. -- (void)clearInterstitials { - [_webViewProxy setWebView:self.webView scrollView:self.webScrollView]; +- (void)clearTransientContentView { + // Early return if there is no transient content view. + if (!self.containerView.transientContentView) + return; + + // Remove the transient content view from the hierarchy. + [self.containerView clearTransientContentView]; + + // Notify the WebState so it can perform any required state cleanup. if (_webStateImpl) - _webStateImpl->ClearWebInterstitialForNavigation(); + _webStateImpl->ClearTransientContentView(); } -// Attaches |interstitialView| to |_contentView|. Note that this class never -// explicitly removes the interstitial from |_contentView|; -// web::WebStateImpl::DismissWebInterstitial() takes care of that. -- (void)displayInterstitialView:(UIView*)interstitialView - withScrollView:(UIScrollView*)scrollView { - DCHECK(interstitialView); - DCHECK(scrollView); - [_webViewProxy setWebView:interstitialView scrollView:scrollView]; - interstitialView.tag = kInterstitialViewTag; - [_containerView addSubview:interstitialView]; +- (void)showTransientContentView:(CRWContentView*)contentView { + DCHECK(contentView); + DCHECK(contentView.scrollView); + DCHECK([contentView.scrollView isDescendantOfView:contentView]); + [self.containerView displayTransientContent:contentView]; } - (id<CRWWebDelegate>)delegate { @@ -662,11 +669,11 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; - (void)setDelegate:(id<CRWWebDelegate>)delegate { _delegate.reset(delegate); - if ([_nativeController respondsToSelector:@selector(setDelegate:)]) { + if ([self.nativeController respondsToSelector:@selector(setDelegate:)]) { if ([_delegate respondsToSelector:@selector(webController:titleDidChange:)]) - [_nativeController setDelegate:self]; + [self.nativeController setDelegate:self]; else - [_nativeController setDelegate:nil]; + [self.nativeController setDelegate:nil]; } } @@ -694,10 +701,8 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; [self abortLoad]; [self.webView removeFromSuperview]; - [_webViewProxy setWebView:nil scrollView:nil]; + [self.containerView resetContent]; [self resetWebView]; - // Remove the web toolbars. - [_containerView removeAllToolbars]; } - (void)dealloc { @@ -718,24 +723,24 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; - (void)dismissKeyboard { [self.webView endEditing:YES]; - if ([_nativeController respondsToSelector:@selector(dismissKeyboard)]) - [_nativeController dismissKeyboard]; + if ([self.nativeController respondsToSelector:@selector(dismissKeyboard)]) + [self.nativeController dismissKeyboard]; } - (id<CRWNativeContent>)nativeController { - return _nativeController.get(); + return self.containerView.nativeController; } - (void)setNativeController:(id<CRWNativeContent>)nativeController { // Check for pointer equality. - if (_nativeController.get() == nativeController) + if (self.nativeController == nativeController) return; // Unset the delegate on the previous instance. - if ([_nativeController respondsToSelector:@selector(setDelegate:)]) - [_nativeController setDelegate:nil]; + if ([self.nativeController respondsToSelector:@selector(setDelegate:)]) + [self.nativeController setDelegate:nil]; - _nativeController.reset([nativeController retain]); + [self.containerView displayNativeContent:nativeController]; [self setNativeControllerWebUsageEnabled:_webUsageEnabled]; } @@ -746,8 +751,9 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } - (void)setNativeControllerWebUsageEnabled:(BOOL)webUsageEnabled { - if ([_nativeController respondsToSelector:@selector(setWebUsageEnabled:)]) { - [_nativeController setWebUsageEnabled:webUsageEnabled]; + if ([self.nativeController + respondsToSelector:@selector(setWebUsageEnabled:)]) { + [self.nativeController setWebUsageEnabled:webUsageEnabled]; } } @@ -764,11 +770,11 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; if (enabled) { // Don't create the web view; let it be lazy created as needed. } else { - [self clearInterstitials]; + [self clearTransientContentView]; [self removeWebViewAllowingCachedReconstruction:YES]; _touchTrackingRecognizer.get().touchTrackingDelegate = nil; _touchTrackingRecognizer.reset(); - _containerView.reset(); + [self resetContainerView]; } } } @@ -777,12 +783,16 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; [self removeWebViewAllowingCachedReconstruction:NO]; } +- (void)resetContainerView { + [self.containerView removeFromSuperview]; + _containerView.reset(); +} + - (void)handleLowMemory { [self removeWebViewAllowingCachedReconstruction:YES]; - [self setNativeController:nil]; _touchTrackingRecognizer.get().touchTrackingDelegate = nil; _touchTrackingRecognizer.reset(); - _containerView.reset(); + [self resetContainerView]; _usePlaceholderOverlay = YES; } @@ -796,7 +806,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; _usePlaceholderOverlay = YES; _touchTrackingRecognizer.get().touchTrackingDelegate = nil; _touchTrackingRecognizer.reset(); - _containerView.reset(); + [self resetContainerView]; } } } @@ -806,7 +816,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } - (BOOL)isViewAlive { - return self.webView || [_nativeController isViewAlive]; + return [self.containerView isViewAlive]; } - (BOOL)contentIsHTML { @@ -826,16 +836,16 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } - (void)dismissModals { - if ([_nativeController respondsToSelector:@selector(dismissModals)]) - [_nativeController dismissModals]; + if ([self.nativeController respondsToSelector:@selector(dismissModals)]) + [self.nativeController dismissModals]; } // Caller must reset the delegate before calling. - (void)close { self.nativeProvider = nil; self.swipeRecognizerProvider = nil; - if ([_nativeController respondsToSelector:@selector(close)]) - [_nativeController close]; + if ([self.nativeController respondsToSelector:@selector(close)]) + [self.nativeController close]; base::scoped_nsobject<NSSet> observers([_observers copy]); for (id it in observers.get()) { @@ -979,8 +989,8 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } // Any non-web URL source is trusted. *trustLevel = web::URLVerificationTrustLevel::kAbsolute; - if (_nativeController) - return [_nativeController url]; + if (self.nativeController) + return [self.nativeController url]; return [self currentNavigationURL]; } @@ -1159,18 +1169,15 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; [webView addGestureRecognizer:recognizer]; } - webView.frame = [_containerView bounds]; - _URLOnStartLoading = _defaultURL; - // Do final view setup. - CGPoint initialOffset = CGPointMake(0, 0 - [self headerHeight]); - [self.webScrollView setContentOffset:initialOffset]; - [_containerView addToolbars:_webViewToolbars]; - - [_webViewProxy setWebView:self.webView scrollView:self.webScrollView]; + // Add the web toolbars. + [self.containerView addToolbars:_webViewToolbars]; - [_containerView addSubview:webView]; + base::scoped_nsobject<CRWWebViewContentView> webViewContentView( + [[CRWWebViewContentView alloc] initWithWebView:self.webView + scrollView:self.webScrollView]); + [self.containerView displayWebViewContentView:webViewContentView]; } - (CRWWebController*)createChildWebControllerWithReferrerURL: @@ -1184,14 +1191,18 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } - (BOOL)canUseViewForGeneratingOverlayPlaceholderView { - return _containerView != nil; + return self.containerView != nil; } - (UIView*)view { // Kick off the process of lazily creating the view and starting the load if - // necessary; this creates _contentView if it doesn't exist. + // necessary; this creates _containerView if it doesn't exist. [self triggerPendingLoad]; - DCHECK(_containerView); + DCHECK(self.containerView); + return self.containerView; +} + +- (CRWWebControllerContainerView*)containerView { return _containerView.get(); } @@ -1308,9 +1319,6 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; // See crbug.com/228397. [self registerUserAgent]; - // Freeing the native controller removes its view from the view hierarchy. - [self setNativeController:nil]; - // Clear the set of URLs opened in external applications. _openedApplicationURL.reset([[NSMutableSet alloc] init]); @@ -1325,7 +1333,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; DCHECK(!targetURL.SchemeIs(url::kJavaScriptScheme)); [self ensureWebViewCreated]; - DCHECK(self.webView && !_nativeController); + DCHECK(self.webView && !self.nativeController); NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:net::NSURLWithGURL(targetURL)]; const web::Referrer referrer([self currentSessionEntryReferrer]); @@ -1393,9 +1401,6 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } - (void)loadNativeViewWithSuccess:(BOOL)loadSuccess { - [_nativeController view].frame = [self visibleFrame]; - [_containerView addSubview:[_nativeController view]]; - [[_nativeController view] setNeedsUpdateConstraints]; const GURL currentURL([self currentURL]); [self didStartLoadingURL:currentURL updateHistory:loadSuccess]; _loadPhase = web::PAGE_LOADED; @@ -1405,14 +1410,14 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; // Inform the embedder the title changed. if ([_delegate respondsToSelector:@selector(webController:titleDidChange:)]) { - NSString* title = [_nativeController title]; + NSString* title = [self.nativeController title]; // If a title is present, notify the delegate. if (title) [_delegate webController:self titleDidChange:title]; // If the controller handles title change notification, route those to the // delegate. - if ([_nativeController respondsToSelector:@selector(setDelegate:)]) { - [_nativeController setDelegate:self]; + if ([self.nativeController respondsToSelector:@selector(setDelegate:)]) { + [self.nativeController setDelegate:self]; } } } @@ -1518,7 +1523,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; - (void)loadCurrentURL { // If the content view doesn't exist, the tab has either been evicted, or // never displayed. Bail, and let the URL be loaded when the tab is shown. - if (!_containerView) + if (!self.containerView) return; // Reset current WebUI if one exists. @@ -1535,8 +1540,8 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; [self abortLoad]; DCHECK(!_isHalted); - // Remove the interstitial before doing anything else. - [self clearInterstitials]; + // Remove the transient content view. + [self clearTransientContentView]; const GURL currentURL = [self currentNavigationURL]; // If it's a chrome URL, but not a native one, create the WebUI instance. @@ -1566,16 +1571,17 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } - (void)triggerPendingLoad { - if (!_containerView) { + if (!self.containerView) { DCHECK(!_isBeingDestroyed); // Create the top-level parent view, which will contain the content (whether // native or web). Note, this needs to be created with a non-zero size // to allow for (native) subviews with autosize constraints to be correctly // processed. _containerView.reset([[CRWWebControllerContainerView alloc] - initWithFrame:[[UIScreen mainScreen] bounds]]); - [_containerView addGestureRecognizer:[self touchTrackingRecognizer]]; - [_containerView setAccessibilityIdentifier:web::kContainerViewID]; + initWithContentViewProxy:_webViewProxy]); + self.containerView.frame = [[UIScreen mainScreen] bounds]; + [self.containerView addGestureRecognizer:[self touchTrackingRecognizer]]; + [self.containerView setAccessibilityIdentifier:web::kContainerViewID]; // Is |currentUrl| a web scheme or native chrome scheme. BOOL isChromeScheme = web::GetWebClient()->IsAppSpecificURL([self currentNavigationURL]); @@ -1622,7 +1628,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; // This ensures state processing and delegate calls are consistent. [self loadCurrentURL]; } else { - [_nativeController reload]; + [self.nativeController reload]; } } @@ -1885,7 +1891,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; return; [_webViewToolbars addObject:toolbarView]; if (self.webView) - [_containerView addToolbar:toolbarView]; + [self.containerView addToolbar:toolbarView]; } - (void)removeToolbarViewFromWebView:(UIView*)toolbarView { @@ -1893,7 +1899,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; return; [_webViewToolbars removeObject:toolbarView]; if (self.webView) - [_containerView removeToolbar:toolbarView]; + [self.containerView removeToolbar:toolbarView]; } - (CRWJSInjectionReceiver*)jsInjectionReceiver { @@ -2567,18 +2573,17 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; #pragma mark - - (BOOL)wantsKeyboardShield { - if (_nativeController && - [_nativeController respondsToSelector:@selector(wantsKeyboardShield)]) { - return [_nativeController wantsKeyboardShield]; + if ([self.nativeController + respondsToSelector:@selector(wantsKeyboardShield)]) { + return [self.nativeController wantsKeyboardShield]; } return YES; } - (BOOL)wantsLocationBarHintText { - if (_nativeController && - [_nativeController + if ([self.nativeController respondsToSelector:@selector(wantsLocationBarHintText)]) { - return [_nativeController wantsLocationBarHintText]; + return [self.nativeController wantsLocationBarHintText]; } return YES; } @@ -2632,18 +2637,16 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } - (void)wasShown { - if (_nativeController && - [_nativeController respondsToSelector:@selector(wasShown)]) { - [_nativeController wasShown]; + if ([self.nativeController respondsToSelector:@selector(wasShown)]) { + [self.nativeController wasShown]; } } - (void)wasHidden { if (_isHalted) return; - if (_nativeController && - [_nativeController respondsToSelector:@selector(wasHidden)]) { - [_nativeController wasHidden]; + if ([self.nativeController respondsToSelector:@selector(wasHidden)]) { + [self.nativeController wasHidden]; } } @@ -3183,7 +3186,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; [_placeholderOverlayView setContentMode:UIViewContentModeScaleAspectFill]; - [_containerView addSubview:_placeholderOverlayView]; + [self.containerView addSubview:_placeholderOverlayView]; id callback = ^(UIImage* image) { [_placeholderOverlayView setImage:image]; @@ -3223,7 +3226,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; // If we were showing the preview, remove it. if (!_overlayPreviewMode && _placeholderOverlayView) { - _containerView.reset(); + [self resetContainerView]; // Reset |_placeholderOverlayView| directly instead of calling // -removePlaceholderOverlay, which removes |_placeholderOverlayView| in an // animation. @@ -3601,7 +3604,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; #pragma mark Fullscreen - (CGRect)visibleFrame { - CGRect frame = [_containerView bounds]; + CGRect frame = self.containerView.bounds; CGFloat headerHeight = [self headerHeight]; frame.origin.y = headerHeight; frame.size.height -= headerHeight; @@ -3725,8 +3728,8 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; } - (void)loadHTML:(NSString*)html forURL:(const GURL&)url { - // Remove the interstitial before doing anything else. - [self clearInterstitials]; + // Remove the transient content view. + [self clearTransientContentView]; DLOG_IF(WARNING, !self.webView) << "self.webView null while trying to load HTML"; @@ -3747,7 +3750,7 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; [self abortLoad]; // If discarding the non-committed entries results in an app-specific URL, // reload it in its native view. - if (!_nativeController && + if (!self.nativeController && [self shouldLoadURLInNativeView:[self currentNavigationURL]]) { [self loadCurrentURLInNativeView]; } @@ -3762,17 +3765,16 @@ const NSTimeInterval kSnapshotOverlayTransition = 0.5; #pragma mark - #pragma mark Testing-Only Methods -- (void)injectWebView:(id)webView { +- (void)injectWebViewContentView:(id)webViewContentView { [self removeWebViewAllowingCachedReconstruction:NO]; _lastRegisteredRequestURL = _defaultURL; - CHECK([webView respondsToSelector:@selector(scrollView)]); - [_webViewProxy setWebView:webView - scrollView:[static_cast<id>(webView) scrollView]]; + [self.containerView displayWebViewContentView:webViewContentView]; } -- (void)resetInjectedWebView { +- (void)resetInjectedWebViewContentView { [self resetWebView]; + [self resetContainerView]; } - (void)addObserver:(id<CRWWebControllerObserver>)observer { diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.h b/ios/web/web_state/ui/crw_web_controller_container_view.h index 7eb61f1..bac9967 100644 --- a/ios/web/web_state/ui/crw_web_controller_container_view.h +++ b/ios/web/web_state/ui/crw_web_controller_container_view.h @@ -7,10 +7,55 @@ #import <UIKit/UIKit.h> +#include "ios/web/public/web_state/ui/crw_content_view.h" + +@class CRWWebViewContentView; +@class CRWWebViewProxyImpl; +@protocol CRWNativeContent; + // Container view class that manages the display of content within // CRWWebController. @interface CRWWebControllerContainerView : UIView +#pragma mark Content Views +// The web view content view being displayed. +@property(nonatomic, retain, readonly) + CRWWebViewContentView* webViewContentView; +// The native controller whose content is being displayed. +@property(nonatomic, retain, readonly) id<CRWNativeContent> nativeController; +// The currently displayed transient content view. +@property(nonatomic, retain, readonly) CRWContentView* transientContentView; + +// Designated initializer. |proxy|'s content view will be updated as different +// content is added to the container. +- (instancetype)initWithContentViewProxy:(CRWWebViewProxyImpl*)proxy + NS_DESIGNATED_INITIALIZER; + +// CRWWebControllerContainerView should be initialized via +// |-initWithContentViewProxy:|. +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; + +// Returns YES if the container view is currently displaying content. +- (BOOL)isViewAlive; + +// Removes all subviews and resets state to default. +- (void)resetContent; + +// Replaces the currently displayed content with |webViewContentView|. +- (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView; + +// Replaces the currently displayed content with |nativeController|'s view. +- (void)displayNativeContent:(id<CRWNativeContent>)nativeController; + +// Adds |transientContentView| as a subview above previously displayed content. +- (void)displayTransientContent:(CRWContentView*)transientContentView; + +// Removes the transient content view, if one is displayed. +- (void)clearTransientContentView; + +#pragma mark Toolbars + // |toolbar| will be resized to the container width, bottom-aligned, and added // as the topmost subview. - (void)addToolbar:(UIView*)toolbar; diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.mm b/ios/web/web_state/ui/crw_web_controller_container_view.mm index e54e303..f7c4849 100644 --- a/ios/web/web_state/ui/crw_web_controller_container_view.mm +++ b/ios/web/web_state/ui/crw_web_controller_container_view.mm @@ -4,8 +4,13 @@ #import "ios/web/web_state/ui/crw_web_controller_container_view.h" +#include "base/ios/weak_nsobject.h" #include "base/logging.h" #include "base/mac/scoped_nsobject.h" +#import "ios/web/public/web_state/ui/crw_content_view.h" +#import "ios/web/public/web_state/ui/crw_native_content.h" +#import "ios/web/public/web_state/ui/crw_web_view_content_view.h" +#import "ios/web/web_state/crw_web_view_proxy_impl.h" #pragma mark - CRWToolbarContainerView @@ -84,10 +89,22 @@ #pragma mark - CRWWebControllerContainerView @interface CRWWebControllerContainerView () { - // Backing object for |self.toolbarContainerView|. + // The proxy for the content added to the container. It is owned by the web + // controller. + base::WeakNSObject<CRWWebViewProxyImpl> _webViewProxy; + // Backing objects for corresponding properties. + base::scoped_nsobject<CRWWebViewContentView> _webViewContentView; + base::scoped_nsprotocol<id<CRWNativeContent>> _nativeController; + base::scoped_nsobject<CRWContentView> _transientContentView; base::scoped_nsobject<CRWToolbarContainerView> _toolbarContainerView; } +// Redefine properties as readwrite. +@property(nonatomic, retain, readwrite) + CRWWebViewContentView* webViewContentView; +@property(nonatomic, retain, readwrite) id<CRWNativeContent> nativeController; +@property(nonatomic, retain, readwrite) CRWContentView* transientContentView; + // Container view that displays any added toolbars. It is always the top-most // subview, and is bottom aligned with the CRWWebControllerContainerView. @property(nonatomic, retain, readonly) @@ -97,9 +114,11 @@ @implementation CRWWebControllerContainerView -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; +- (instancetype)initWithContentViewProxy:(CRWWebViewProxyImpl*)proxy { + self = [super initWithFrame:CGRectZero]; if (self) { + DCHECK(proxy); + _webViewProxy.reset(proxy); self.backgroundColor = [UIColor whiteColor]; self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; @@ -107,13 +126,57 @@ return self; } -- (instancetype)init { - NOTREACHED(); - return nil; +- (void)dealloc { + [_webViewProxy setContentView:nil]; + [super dealloc]; } #pragma mark Accessors +- (CRWWebViewContentView*)webViewContentView { + return _webViewContentView.get(); +} + +- (void)setWebViewContentView:(CRWWebViewContentView*)webViewContentView { + if (![_webViewContentView isEqual:webViewContentView]) { + [_webViewContentView removeFromSuperview]; + _webViewContentView.reset([webViewContentView retain]); + [self addSubview:_webViewContentView]; + } +} + +- (id<CRWNativeContent>)nativeController { + return _nativeController.get(); +} + +- (void)setNativeController:(id<CRWNativeContent>)nativeController { + if (![_nativeController isEqual:nativeController]) { + // TODO(kkhorimoto): This line isn't strictly necessary since all native + // controllers currently inherit from NativeContentController, which removes + // its view upon deallocation. Consider moving NativeContentController into + // web/ so this behavior can be depended upon from within web/ without + // making assumptions about chrome/ code. + base::WeakNSProtocol<id> oldController(_nativeController); + [[_nativeController view] removeFromSuperview]; + _nativeController.reset([nativeController retain]); + [self addSubview:[_nativeController view]]; + [[_nativeController view] setNeedsUpdateConstraints]; + DCHECK(!oldController); + } +} + +- (CRWContentView*)transientContentView { + return _transientContentView.get(); +} + +- (void)setTransientContentView:(CRWContentView*)transientContentView { + if (![_transientContentView isEqual:transientContentView]) { + [_transientContentView removeFromSuperview]; + _transientContentView.reset([transientContentView retain]); + [self addSubview:_transientContentView]; + } +} + - (void)setToolbarContainerView:(CRWToolbarContainerView*)toolbarContainerView { if (![_toolbarContainerView isEqual:toolbarContainerView]) { [_toolbarContainerView removeFromSuperview]; @@ -131,6 +194,12 @@ - (void)layoutSubviews { [super layoutSubviews]; + // Resize displayed content to the container's bounds. + self.webViewContentView.frame = self.bounds; + [self.nativeController view].frame = self.bounds; + self.transientContentView.frame = self.bounds; + + // Bottom align the toolbars with the bottom of the container. if (self.toolbarContainerView) { [self bringSubviewToFront:self.toolbarContainerView]; CGSize toolbarContainerSize = @@ -142,6 +211,51 @@ } } +- (BOOL)isViewAlive { + return self.webViewContentView || self.transientContentView || + [self.nativeController isViewAlive]; +} + +#pragma mark Content Setters + +- (void)resetContent { + self.webViewContentView = nil; + self.nativeController = nil; + self.transientContentView = nil; + [self removeAllToolbars]; + [_webViewProxy setContentView:nil]; +} + +- (void)displayWebViewContentView:(CRWWebViewContentView*)webViewContentView { + DCHECK(webViewContentView); + self.webViewContentView = webViewContentView; + self.nativeController = nil; + self.transientContentView = nil; + [_webViewProxy setContentView:self.webViewContentView]; + [self setNeedsLayout]; +} + +- (void)displayNativeContent:(id<CRWNativeContent>)nativeController { + DCHECK(nativeController); + self.webViewContentView = nil; + self.nativeController = nativeController; + self.transientContentView = nil; + [_webViewProxy setContentView:nil]; + [self setNeedsLayout]; +} + +- (void)displayTransientContent:(CRWContentView*)transientContentView { + DCHECK(transientContentView); + self.transientContentView = transientContentView; + [_webViewProxy setContentView:self.transientContentView]; + [self setNeedsLayout]; +} + +- (void)clearTransientContentView { + self.transientContentView = nil; + [_webViewProxy setContentView:self.webViewContentView]; +} + #pragma mark Toolbars - (void)addToolbar:(UIView*)toolbar { diff --git a/ios/web/web_state/ui/crw_web_controller_container_view_unittest.mm b/ios/web/web_state/ui/crw_web_controller_container_view_unittest.mm index 69f49c3..6afb6b16 100644 --- a/ios/web/web_state/ui/crw_web_controller_container_view_unittest.mm +++ b/ios/web/web_state/ui/crw_web_controller_container_view_unittest.mm @@ -3,10 +3,13 @@ // found in the LICENSE file. #import "base/mac/scoped_nsobject.h" +#import "ios/web/web_state/crw_web_view_proxy_impl.h" #import "ios/web/web_state/ui/crw_web_controller_container_view.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" #include "testing/platform_test.h" +#include "third_party/ocmock/gtest_support.h" +#import "third_party/ocmock/OCMock/OCMock.h" namespace { // The frame of CRWWebControllerContainerViewTest's |container_view_|. @@ -31,10 +34,15 @@ class CRWWebControllerContainerViewTest : public PlatformTest { protected: void SetUp() override { PlatformTest::SetUp(); + mock_web_view_proxy_.reset( + [[OCMockObject niceMockForClass:[CRWWebViewProxyImpl class]] retain]); container_view_.reset([[CRWWebControllerContainerView alloc] - initWithFrame:kContainerViewFrame]); + initWithContentViewProxy:mock_web_view_proxy_]); + [container_view_ setFrame:kContainerViewFrame]; } + // The web view proxy object (required for designated initializer). + base::scoped_nsobject<id> mock_web_view_proxy_; // The container view being tested. base::scoped_nsobject<CRWWebControllerContainerView> container_view_; }; diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm index b7b97cf..4e7a4ed 100644 --- a/ios/web/web_state/ui/crw_web_controller_unittest.mm +++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm @@ -18,8 +18,11 @@ #include "ios/web/navigation/navigation_item_impl.h" #import "ios/web/navigation/navigation_manager_impl.h" #include "ios/web/public/referrer.h" +#include "ios/web/public/test/test_web_view_content_view.h" #include "ios/web/public/test/web_test_util.h" #import "ios/web/public/web_state/crw_web_controller_observer.h" +#import "ios/web/public/web_state/ui/crw_content_view.h" +#import "ios/web/public/web_state/ui/crw_web_view_content_view.h" #include "ios/web/public/web_state/url_verification_constants.h" #include "ios/web/test/web_test.h" #import "ios/web/test/wk_web_view_crash_utils.h" @@ -27,6 +30,7 @@ #import "ios/web/web_state/js/crw_js_invoke_parameter_queue.h" #import "ios/web/web_state/ui/crw_ui_web_view_web_controller.h" #import "ios/web/web_state/ui/crw_web_controller+protected.h" +#import "ios/web/web_state/ui/crw_web_controller_container_view.h" #import "ios/web/web_state/web_state_impl.h" #import "net/base/mac/url_conversions.h" #include "net/ssl/ssl_info.h" @@ -46,6 +50,7 @@ using web::NavigationManagerImpl; @interface CRWWebController (PrivateAPI) @property(nonatomic, readwrite) web::PageDisplayState pageDisplayState; +@property(nonatomic, readonly) CRWWebControllerContainerView* containerView; - (void)setJsMessageQueueThrottled:(BOOL)throttle; - (void)removeDocumentLoadCommandsFromQueue; - (GURL)updateURLForHistoryNavigationFromURL:(const GURL&)startURL @@ -293,7 +298,10 @@ class WebControllerTest : public WebTestT { mockDelegate_.reset([[MockInteractionLoader alloc] initWithRepresentedObject:originalMockDelegate]); [WebTestT::webController_ setDelegate:mockDelegate_]; - [WebTestT::webController_ injectWebView:(UIView*)mockWebView_]; + base::scoped_nsobject<TestWebViewContentView> webViewContentView( + [[TestWebViewContentView alloc] initWithMockWebView:mockWebView_ + scrollView:mockScrollView_]); + [WebTestT::webController_ injectWebViewContentView:webViewContentView]; NavigationManagerImpl& navigationManager = [WebTestT::webController_ webStateImpl]->GetNavigationManagerImpl(); @@ -315,7 +323,7 @@ class WebControllerTest : public WebTestT { EXPECT_OCMOCK_VERIFY(mockDelegate_); EXPECT_OCMOCK_VERIFY(mockChildWebController_.get()); EXPECT_OCMOCK_VERIFY(mockWebView_); - [WebTestT::webController_ resetInjectedWebView]; + [WebTestT::webController_ resetInjectedWebViewContentView]; [WebTestT::webController_ setDelegate:nil]; WebTestT::TearDown(); } @@ -1224,8 +1232,8 @@ TEST_F(WebControllerKeyboardTest, DismissKeyboard) { @"</body></html>"); // Get the webview. - UIWebView* webView = - (UIWebView*)[[[webController_ view] subviews] objectAtIndex:0]; + UIWebView* webView = static_cast<UIWebView*>( + [webController_ containerView].webViewContentView.webView); EXPECT_TRUE(webView); // Create the window and add the webview. @@ -1473,7 +1481,11 @@ class CRWWKWebControllerWebProcessTest : public web::WKWebViewWebTest { CR_TEST_REQUIRES_WK_WEB_VIEW(); WKWebViewWebTest::SetUp(); webView_.reset(web::CreateTerminatedWKWebView()); - [webController_ injectWebView:webView_]; + base::scoped_nsobject<TestWebViewContentView> webViewContentView( + [[TestWebViewContentView alloc] + initWithMockWebView:webView_ + scrollView:[webView_ scrollView]]); + [webController_ injectWebViewContentView:webViewContentView]; } base::scoped_nsobject<WKWebView> webView_; }; diff --git a/ios/web/web_state/ui/crw_web_view_content_view.mm b/ios/web/web_state/ui/crw_web_view_content_view.mm new file mode 100644 index 0000000..e3c085e --- /dev/null +++ b/ios/web/web_state/ui/crw_web_view_content_view.mm @@ -0,0 +1,56 @@ +// 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/web/public/web_state/ui/crw_web_view_content_view.h" + +#include "base/logging.h" +#include "base/mac/scoped_nsobject.h" + +@interface CRWWebViewContentView () { + // The web view being shown. + base::scoped_nsobject<UIView> _webView; + // The web view's scroll view. + base::scoped_nsobject<UIScrollView> _scrollView; +} + +@end + +@implementation CRWWebViewContentView + +- (instancetype)initWithWebView:(UIView*)webView + scrollView:(UIScrollView*)scrollView { + self = [super initWithFrame:CGRectZero]; + if (self) { + DCHECK(webView); + DCHECK(scrollView); + DCHECK([scrollView isDescendantOfView:webView]); + _webView.reset([webView retain]); + _scrollView.reset([scrollView retain]); + [self addSubview:_webView]; + } + return self; +} + +#pragma mark Accessors + +- (UIScrollView*)scrollView { + return _scrollView.get(); +} + +- (UIView*)webView { + return _webView.get(); +} + +#pragma mark Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + self.webView.frame = self.bounds; +} + +- (BOOL)isViewAlive { + return YES; +} + +@end diff --git a/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm b/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm index 87b2713..c77f15b 100644 --- a/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm +++ b/ios/web/web_state/ui/crw_wk_web_view_web_controller.mm @@ -19,6 +19,7 @@ #include "ios/web/public/web_client.h" #import "ios/web/public/web_state/js/crw_js_injection_manager.h" #import "ios/web/public/web_state/ui/crw_native_content_provider.h" +#import "ios/web/public/web_state/ui/crw_web_view_content_view.h" #import "ios/web/ui_web_view_util.h" #include "ios/web/web_state/blocked_popup_info.h" #include "ios/web/web_state/frame_info.h" @@ -268,9 +269,9 @@ NSString* const kScriptImmediateName = @"crwebinvokeimmediate"; #pragma mark - #pragma mark Testing-Only Methods -- (void)injectWebView:(id)webView { - [super injectWebView:webView]; - [self setWebView:webView]; +- (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView { + [super injectWebViewContentView:webViewContentView]; + [self setWebView:static_cast<WKWebView*>(webViewContentView.webView)]; } #pragma mark - Protected property implementations diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h index ba236ce..d5386b2 100644 --- a/ios/web/web_state/web_state_impl.h +++ b/ios/web/web_state/web_state_impl.h @@ -222,6 +222,7 @@ class WebStateImpl : public WebState, public NavigationManagerDelegate { const GURL& GetVisibleURL() const override; const GURL& GetLastCommittedURL() const override; GURL GetCurrentURL(URLVerificationTrustLevel* trust_level) const override; + void ShowTransientContentView(CRWContentView* content_view) override; bool IsShowingWebInterstitial() const override; WebInterstitial* GetWebInterstitial() const override; void AddScriptCommandCallback(const ScriptCommandCallback& callback, @@ -234,15 +235,11 @@ class WebStateImpl : public WebState, public NavigationManagerDelegate { bool bypass_cache, const ImageDownloadCallback& callback) override; - // Called before navigation events to clear the currently-displayed - // WebInterstitials. - void ClearWebInterstitialForNavigation(); - // Adds |interstitial|'s view to the web controller's content view. - void DisplayWebInterstitial(WebInterstitialImpl* interstitial); + void ShowWebInterstitial(WebInterstitialImpl* interstitial); - // Called to dismiss the currently-displayed WebInterstitial. - void DismissWebInterstitial(); + // Called to dismiss the currently-displayed transient content view. + void ClearTransientContentView(); // NavigationManagerDelegate: void NavigateToPendingEntry() override; @@ -294,7 +291,7 @@ class WebStateImpl : public WebState, public NavigationManagerDelegate { std::string mime_type_; std::string content_language_header_; - // Weak pointer to the he interstital page being displayed, if any. + // Weak pointer to the interstitial page being displayed, if any. WebInterstitialImpl* interstitial_; // Returned by reference. diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm index 7450809..fd6aa9a 100644 --- a/ios/web/web_state/web_state_impl.mm +++ b/ios/web/web_state/web_state_impl.mm @@ -15,8 +15,10 @@ #include "ios/web/public/url_util.h" #include "ios/web/public/web_client.h" #include "ios/web/public/web_state/credential.h" +#include "ios/web/public/web_state/ui/crw_content_view.h" #include "ios/web/public/web_state/web_state_observer.h" #import "ios/web/web_state/ui/crw_web_controller.h" +#import "ios/web/web_state/ui/crw_web_controller_container_view.h" #include "ios/web/web_state/web_state_facade_delegate.h" #import "ios/web/webui/web_ui_ios_controller_factory_registry.h" #import "ios/web/webui/web_ui_ios_impl.h" @@ -265,6 +267,14 @@ const base::string16& WebStateImpl::GetTitle() const { return empty_string16_; } +void WebStateImpl::ShowTransientContentView(CRWContentView* content_view) { + DCHECK(Configured()); + DCHECK(content_view); + DCHECK(content_view.scrollView); + DCHECK([content_view.scrollView isDescendantOfView:content_view]); + [web_controller_ showTransientContentView:content_view]; +} + bool WebStateImpl::IsShowingWebInterstitial() const { // Technically we could have |interstitial_| set but its view isn't // being displayed, but there's no code path where that could occur. @@ -320,16 +330,15 @@ void WebStateImpl::UpdateHttpResponseHeaders(const GURL& url) { content_language_header_ = content_language; } -void WebStateImpl::ClearWebInterstitialForNavigation() { +void WebStateImpl::ShowWebInterstitial(WebInterstitialImpl* interstitial) { + DCHECK(Configured()); + DCHECK(interstitial); + interstitial_ = interstitial; + ShowTransientContentView(interstitial_->GetContentView()); +} + +void WebStateImpl::ClearTransientContentView() { if (interstitial_) { - // DontProceed() dismisses the interstitial page in the same way as if it - // was closed by user action. Clearing interstitial_ early makes - // IsShowingWebInterstitial() return false so the code that is triggered in - // DontProceed knows that the interstitial page is not visible anymore. - WebInterstitialImpl* interstitial = interstitial_; - DismissWebInterstitial(); - // In this case, DontProceed() may not remove an unsafe page from the nav - // history. CRWSessionController* sessionController = navigation_manager_.GetSessionController(); web::NavigationItem* currentItem = @@ -342,27 +351,17 @@ void WebStateImpl::ClearWebInterstitialForNavigation() { [sessionController goBack]; } [sessionController discardNonCommittedEntries]; + // Store the currently displayed interstitial in a local variable and reset + // |interstitial_| early. This is to prevent an infinite loop, as + // |DontProceed()| internally calls |ClearTransientContentView()|. + web::WebInterstitial* interstitial = interstitial_; + interstitial_ = nullptr; interstitial->DontProceed(); - } -} - -void WebStateImpl::DisplayWebInterstitial(WebInterstitialImpl* interstitial) { - DCHECK(Configured()); - DCHECK(interstitial); - interstitial_ = interstitial; - CGSize content_view_size = [web_controller_ view].bounds.size; - interstitial_->SetSize( - gfx::Size(content_view_size.width, content_view_size.height)); - [web_controller_ displayInterstitialView:interstitial_->GetView() - withScrollView:interstitial_->GetScrollView()]; -} - -void WebStateImpl::DismissWebInterstitial() { - if (interstitial_) { + // Don't access |interstitial| after calling |DontProceed()|, as it triggers + // deletion. FOR_EACH_OBSERVER(WebStateObserver, observers_, InsterstitialDismissed()); - [interstitial_->GetView() removeFromSuperview]; - interstitial_ = nullptr; } + [web_controller_ clearTransientContentView]; } WebUIIOS* WebStateImpl::CreateWebUIIOS(const GURL& url) { @@ -454,7 +453,7 @@ BrowserState* WebStateImpl::GetBrowserState() const { void WebStateImpl::OpenURL(const WebState::OpenURLParams& params) { DCHECK(Configured()); - ClearWebInterstitialForNavigation(); + ClearTransientContentView(); [[web_controller_ delegate] openURLWithParams:params]; } |