Commit 7fb377bd authored by Jiajun Ou's avatar Jiajun Ou Committed by Commit Bot

Support backForwardList APIs in ChromeWebView

Details:
- Implement CWVBackForwardList: a equivalent of https://developer.apple.com/documentation/webkit/wkbackforwardlist
- Implement CWVBackForwardListItem: a equivalent of https://developer.apple.com/documentation/webkit/wkbackforwardlistitem
- Implement |backForwardList| property in CWVWebView and -[CWVWebView goToBackForwardListItem:]

Bug: 1021830
Change-Id: Ia04f5197fb1eb03b1f59955068f00af1022fc2a9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1928460
Commit-Queue: Jiajun Ou <garzonou@google.com>
Reviewed-by: default avatarHiroshi Ichikawa <ichikawa@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/master@{#723651}
parent a12fc5c5
...@@ -44,6 +44,8 @@ config("config") { ...@@ -44,6 +44,8 @@ config("config") {
# These are defined as vars so they can be shared with different targets below. # These are defined as vars so they can be shared with different targets below.
ios_web_view_public_headers = [ ios_web_view_public_headers = [
"public/cwv_back_forward_list.h",
"public/cwv_back_forward_list_item.h",
"public/cwv_download_task.h", "public/cwv_download_task.h",
"public/cwv_export.h", "public/cwv_export.h",
"public/cwv_favicon.h", "public/cwv_favicon.h",
...@@ -115,6 +117,10 @@ ios_web_view_sources = [ ...@@ -115,6 +117,10 @@ ios_web_view_sources = [
"internal/content_settings/web_view_cookie_settings_factory.mm", "internal/content_settings/web_view_cookie_settings_factory.mm",
"internal/content_settings/web_view_host_content_settings_map_factory.h", "internal/content_settings/web_view_host_content_settings_map_factory.h",
"internal/content_settings/web_view_host_content_settings_map_factory.mm", "internal/content_settings/web_view_host_content_settings_map_factory.mm",
"internal/cwv_back_forward_list.mm",
"internal/cwv_back_forward_list_internal.h",
"internal/cwv_back_forward_list_item.mm",
"internal/cwv_back_forward_list_item_internal.h",
"internal/cwv_download_task.mm", "internal/cwv_download_task.mm",
"internal/cwv_download_task_internal.h", "internal/cwv_download_task_internal.h",
"internal/cwv_favicon.mm", "internal/cwv_favicon.mm",
......
// Copyright 2019 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_view/internal/cwv_back_forward_list_internal.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "net/base/mac/url_conversions.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation CWVBackForwardListItemArray {
BOOL _isBackList;
}
- (instancetype)initWithBackForwardList:(CWVBackForwardList*)list
isBackList:(BOOL)isBackList {
self = [super init];
if (self) {
_list = list;
_isBackList = isBackList;
}
return self;
}
- (NSUInteger)count {
if (!self.list.navigationManager) {
return 0;
}
int currentIndex = self.list.navigationManager->GetLastCommittedItemIndex();
if (currentIndex == -1) {
// If there is no item in the list, currentIndex will be -1 so at that time
// |self| should be empty array.
return 0;
}
if (_isBackList) {
return currentIndex;
}
// |self| is forwardList.
int count = self.list.navigationManager->GetItemCount();
DCHECK(count >= currentIndex + 1);
return count - currentIndex - 1;
}
- (CWVBackForwardListItem*)objectAtIndexedSubscript:(NSUInteger)index {
if (index >= self.count) {
[NSException raise:NSRangeException
format:@"The index of %@ is out of boundary",
_isBackList ? @"backList" : @"forwardList"];
}
int internalIndex;
if (_isBackList) {
internalIndex = index;
} else {
internalIndex =
self.list.navigationManager->GetLastCommittedItemIndex() + 1 + index;
}
web::NavigationItem* item =
self.list.navigationManager->GetItemAtIndex(internalIndex);
DCHECK(item);
return [[CWVBackForwardListItem alloc] initWithNavigationItem:item];
}
@end
@implementation CWVBackForwardList
@synthesize backList = _backList;
@synthesize forwardList = _forwardList;
- (instancetype)initWithNavigationManager:
(const web::NavigationManager*)navigationManager {
self = [super init];
if (self) {
DCHECK(navigationManager);
_navigationManager = navigationManager;
_forwardList =
[[CWVBackForwardListItemArray alloc] initWithBackForwardList:self
isBackList:NO];
_backList =
[[CWVBackForwardListItemArray alloc] initWithBackForwardList:self
isBackList:YES];
}
return self;
}
- (CWVBackForwardListItem*)currentItem {
if (!self.navigationManager) {
return nil;
}
web::NavigationItem* item = self.navigationManager->GetLastCommittedItem();
if (!item) {
return nil;
}
return [[CWVBackForwardListItem alloc] initWithNavigationItem:item];
}
- (CWVBackForwardListItem*)backItem {
if (self.backList.count == 0) {
return nil;
}
return self.backList[self.backList.count - 1];
}
- (CWVBackForwardListItem*)forwardItem {
if (self.forwardList.count == 0) {
return nil;
}
return self.forwardList[0];
}
- (CWVBackForwardListItem*)itemAtIndex:(NSInteger)index {
if (index == 0) {
return self.currentItem;
} else if (index < 0) {
NSInteger internalIndex = self.backList.count + index;
if (internalIndex < 0) {
return nil;
}
return self.backList[internalIndex];
} else {
NSUInteger internalIndex = index - 1;
if (internalIndex >= self.forwardList.count) {
return nil;
}
return self.forwardList[internalIndex];
}
}
- (int)internalIndexOfItem:(CWVBackForwardListItem*)item {
int count = self.navigationManager->GetItemCount();
for (int i = 0; i < count; i++) {
if (item.uniqueID ==
self.navigationManager->GetItemAtIndex(i)->GetUniqueID()) {
return i;
}
}
return -1;
}
@end
// Copyright 2019 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_VIEW_INTERNAL_CWV_BACK_FORWARD_LIST_INTERNAL_H_
#define IOS_WEB_VIEW_INTERNAL_CWV_BACK_FORWARD_LIST_INTERNAL_H_
#import "ios/web_view/public/cwv_back_forward_list.h"
#import "ios/web_view/internal/cwv_back_forward_list_item_internal.h"
NS_ASSUME_NONNULL_BEGIN
namespace web {
class NavigationManager;
} // namespace web
// This class exists to avoid recreating a new NSArray<CWVBackForwardListItem*>
// when |backForwardList.{back|forward}List| is called. The content of the list
// might silently be changed, so if it were a real array it must be recreated
// every time, which might result in an extra O(n) factor unexpectedly on some
// normal code like this:
//
// list = webView.backForwardList;
// for (i = 0; i < list.backList.count; i++) {
// <DO SOMETHING WITH list.backList[i]>; // It silently generates a new list.
// }
// And this would be O(n^2) which surely is not something expected.
//
// This class is an array-like wrapper (provides .count and [] operator).
@interface CWVBackForwardListItemArray ()
// The CWVBackForwardList that |self| belongs to.
@property(nonatomic, weak, readonly) CWVBackForwardList* list;
// |self| is backList if |isBackList| == YES, otherwise it is forwardList.
- (instancetype)initWithBackForwardList:(CWVBackForwardList*)list
isBackList:(BOOL)isBackList
NS_DESIGNATED_INITIALIZER;
@end
@interface CWVBackForwardList ()
// The lower-level information provider of backForwardListItem.
@property(nonatomic, nullable) const web::NavigationManager* navigationManager;
// Returns the index of the |item| in the lower level web::NavigationManager.
// -1 means item not found.
- (int)internalIndexOfItem:(CWVBackForwardListItem*)item;
- (instancetype)initWithNavigationManager:
(const web::NavigationManager*)navigationManager NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_INTERNAL_CWV_BACK_FORWARD_LIST_INTERNAL_H_
// Copyright 2019 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_view/internal/cwv_back_forward_list_item_internal.h"
#import "base/mac/foundation_util.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "net/base/mac/url_conversions.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation CWVBackForwardListItem
- (instancetype)initWithNavigationItem:
(const web::NavigationItem*)navigationItem {
// WARNING: |navigationItem| should not be stored since it can be freed by
// owner (the lower level web::NavigationManager) anytime.
self = [super init];
if (self) {
_uniqueID = navigationItem->GetUniqueID();
_title = base::SysUTF16ToNSString(navigationItem->GetTitle());
_URL = net::NSURLWithGURL(navigationItem->GetURL());
}
return self;
}
- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
}
if (![other isKindOfClass:self.class]) {
return NO;
}
return self.uniqueID ==
base::mac::ObjCCastStrict<CWVBackForwardListItem>(other).uniqueID;
}
- (NSUInteger)hash {
return static_cast<NSUInteger>(self.uniqueID);
}
@end
// Copyright 2019 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_VIEW_INTERNAL_CWV_BACK_FORWARD_LIST_ITEM_INTERNAL_H_
#define IOS_WEB_VIEW_INTERNAL_CWV_BACK_FORWARD_LIST_ITEM_INTERNAL_H_
#import "ios/web_view/public/cwv_back_forward_list_item.h"
NS_ASSUME_NONNULL_BEGIN
namespace web {
class NavigationItem;
} // namespace web
@interface CWVBackForwardListItem ()
// An unique ID generated by lower level |web::NavigationItem::GetUniqueID()|.
@property(nonatomic, readonly) int uniqueID;
- (instancetype)initWithNavigationItem:
(const web::NavigationItem*)navigationItem NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_INTERNAL_CWV_BACK_FORWARD_LIST_ITEM_INTERNAL_H_
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#import "ios/web/public/web_view_only/wk_web_view_configuration_util.h" #import "ios/web/public/web_view_only/wk_web_view_configuration_util.h"
#include "ios/web_view/cwv_web_view_buildflags.h" #include "ios/web_view/cwv_web_view_buildflags.h"
#import "ios/web_view/internal/autofill/cwv_autofill_controller_internal.h" #import "ios/web_view/internal/autofill/cwv_autofill_controller_internal.h"
#import "ios/web_view/internal/cwv_back_forward_list_internal.h"
#import "ios/web_view/internal/cwv_favicon_internal.h" #import "ios/web_view/internal/cwv_favicon_internal.h"
#import "ios/web_view/internal/cwv_html_element_internal.h" #import "ios/web_view/internal/cwv_html_element_internal.h"
#import "ios/web_view/internal/cwv_navigation_action_internal.h" #import "ios/web_view/internal/cwv_navigation_action_internal.h"
...@@ -140,7 +141,7 @@ WEB_STATE_USER_DATA_KEY_IMPL(WebViewHolder) ...@@ -140,7 +141,7 @@ WEB_STATE_USER_DATA_KEY_IMPL(WebViewHolder)
#endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
// Updates the availability of the back/forward navigation properties exposed // Updates the availability of the back/forward navigation properties exposed
// through |canGoBack| and |canGoForward|. // through |canGoBack| and |canGoForward|, and also updates |backForwardList|.
- (void)updateNavigationAvailability; - (void)updateNavigationAvailability;
// Updates the URLs exposed through |lastCommittedURL| and |visibleURL|. // Updates the URLs exposed through |lastCommittedURL| and |visibleURL|.
- (void)updateCurrentURLs; - (void)updateCurrentURLs;
...@@ -164,6 +165,7 @@ static NSString* gUserAgentProduct = nil; ...@@ -164,6 +165,7 @@ static NSString* gUserAgentProduct = nil;
#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
@synthesize autofillController = _autofillController; @synthesize autofillController = _autofillController;
#endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #endif // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
@synthesize backForwardList = _backForwardList;
@synthesize canGoBack = _canGoBack; @synthesize canGoBack = _canGoBack;
@synthesize canGoForward = _canGoForward; @synthesize canGoForward = _canGoForward;
@synthesize configuration = _configuration; @synthesize configuration = _configuration;
...@@ -271,6 +273,26 @@ static NSString* gUserAgentProduct = nil; ...@@ -271,6 +273,26 @@ static NSString* gUserAgentProduct = nil;
_webState->GetNavigationManager()->GoForward(); _webState->GetNavigationManager()->GoForward();
} }
- (BOOL)goToBackForwardListItem:(CWVBackForwardListItem*)item {
if (!_backForwardList) {
return NO; // Do nothing if |_backForwardList| is not generated yet.
}
if ([item isEqual:_backForwardList.currentItem]) {
return NO;
}
int index = [_backForwardList internalIndexOfItem:item];
if (index == -1) {
return NO;
}
DCHECK(_webState);
web::NavigationManager* navigationManager = _webState->GetNavigationManager();
navigationManager->GoToIndex(index);
return YES;
}
- (void)reload { - (void)reload {
// |check_for_repost| is false because CWVWebView does not support repost form // |check_for_repost| is false because CWVWebView does not support repost form
// dialogs. // dialogs.
...@@ -673,6 +695,9 @@ static NSString* gUserAgentProduct = nil; ...@@ -673,6 +695,9 @@ static NSString* gUserAgentProduct = nil;
} }
_webState->AddObserver(_webStateObserver.get()); _webState->AddObserver(_webStateObserver.get());
if (_backForwardList) {
_backForwardList.navigationManager = _webState->GetNavigationManager();
}
[self updateWebStateVisibility]; [self updateWebStateVisibility];
_webStateDelegate = std::make_unique<web::WebStateDelegateBridge>(self); _webStateDelegate = std::make_unique<web::WebStateDelegateBridge>(self);
...@@ -740,10 +765,20 @@ static NSString* gUserAgentProduct = nil; ...@@ -740,10 +765,20 @@ static NSString* gUserAgentProduct = nil;
[self addSubview:subview]; [self addSubview:subview];
} }
- (CWVBackForwardList*)backForwardList {
if (!_backForwardList) {
_backForwardList = [[CWVBackForwardList alloc]
initWithNavigationManager:_webState->GetNavigationManager()];
}
return _backForwardList;
}
- (void)updateNavigationAvailability { - (void)updateNavigationAvailability {
self.canGoBack = _webState && _webState->GetNavigationManager()->CanGoBack(); self.canGoBack = _webState && _webState->GetNavigationManager()->CanGoBack();
self.canGoForward = self.canGoForward =
_webState && _webState->GetNavigationManager()->CanGoForward(); _webState && _webState->GetNavigationManager()->CanGoForward();
self.backForwardList.navigationManager = _webState->GetNavigationManager();
} }
- (void)updateCurrentURLs { - (void)updateCurrentURLs {
......
// Copyright 2019 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_VIEW_PUBLIC_CWV_BACK_FORWARD_LIST_H_
#define IOS_WEB_VIEW_PUBLIC_CWV_BACK_FORWARD_LIST_H_
#import <UIKit/UIKit.h>
#import "cwv_export.h"
#import "ios/web_view/public/cwv_back_forward_list_item.h"
NS_ASSUME_NONNULL_BEGIN
@class CWVWebView;
// This just behaves like a NSArray<CWVBackForwardListItem*>.
CWV_EXPORT
@interface CWVBackForwardListItemArray : NSObject
// The number of items in this array-like |self|.
@property(nonatomic, readonly) NSUInteger count;
// This overloads the [] array-style subscripting operator.
- (CWVBackForwardListItem*)objectAtIndexedSubscript:(NSUInteger)index;
// Instances are supposed to be created only internally.
- (instancetype)init NS_UNAVAILABLE;
@end
// A equivalent of
// https://developer.apple.com/documentation/webkit/wkbackforwardlist
CWV_EXPORT
@interface CWVBackForwardList : NSObject
// A NSArray of CWVBackForwardListItem. The item with the greatest index
// will be closest to current item.
@property(nonatomic, readonly) CWVBackForwardListItemArray* backList;
// A NSArray of CWVBackForwardListItem. The item with index 0 will be
// closest to current item.
@property(nonatomic, readonly) CWVBackForwardListItemArray* forwardList;
// The current item, or nil if there isn't one.
@property(nonatomic, readonly, nullable) CWVBackForwardListItem* currentItem;
// The item in |backList| immediately preceding the current item, or nil if
// there isn't one.
@property(nonatomic, readonly, nullable) CWVBackForwardListItem* backItem;
// The item in |forwardList| immediately following the current item, or nil if
// there isn't one.
@property(nonatomic, readonly, nullable) CWVBackForwardListItem* forwardItem;
// Returns the item at a specified distance from the current item. For the
// meaning of |index|: 0 for the current item, -1 for the immediately preceding
// item, 1 for the immediately following item, and so on (which means |index|
// here is a OFFSET in fact). Returns nil if there isn't one.
- (nullable CWVBackForwardListItem*)itemAtIndex:(NSInteger)index;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_PUBLIC_CWV_BACK_FORWARD_LIST_H_
// Copyright 2019 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_VIEW_PUBLIC_CWV_BACK_FORWARD_LIST_ITEM_H_
#define IOS_WEB_VIEW_PUBLIC_CWV_BACK_FORWARD_LIST_ITEM_H_
#import <UIKit/UIKit.h>
#import "cwv_export.h"
NS_ASSUME_NONNULL_BEGIN
// A equivalent of
// https://developer.apple.com/documentation/webkit/wkbackforwardlistitem
CWV_EXPORT
@interface CWVBackForwardListItem : NSObject
// The URL of the item.
@property(nonatomic, readonly) NSURL* URL;
// The title of the item.
@property(nonatomic, readonly) NSString* title;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_PUBLIC_CWV_BACK_FORWARD_LIST_ITEM_H_
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class CWVBackForwardList;
@class CWVBackForwardListItem;
@class CWVScriptCommand; @class CWVScriptCommand;
@class CWVScrollView; @class CWVScrollView;
@class CWVTranslationController; @class CWVTranslationController;
...@@ -113,6 +115,10 @@ CWV_EXPORT ...@@ -113,6 +115,10 @@ CWV_EXPORT
// back-forward list navigations. // back-forward list navigations.
@property(nonatomic) BOOL allowsBackForwardNavigationGestures; @property(nonatomic) BOOL allowsBackForwardNavigationGestures;
// An equivalent of
// https://developer.apple.com/documentation/webkit/wkwebview/1414977-backforwardlist
@property(nonatomic, readonly, nonnull) CWVBackForwardList* backForwardList;
// The User Agent product string used to build the full User Agent. // The User Agent product string used to build the full User Agent.
+ (NSString*)userAgentProduct; + (NSString*)userAgentProduct;
...@@ -164,6 +170,11 @@ CWV_EXPORT ...@@ -164,6 +170,11 @@ CWV_EXPORT
- (void)goBack; - (void)goBack;
- (void)goForward; - (void)goForward;
// Navigates to the specified |item| in the |self.backForwardList| and returns
// YES. Does nothing and returns NO when |item| is the current item, or it
// belongs to an expired list, or the list does not contain |item|.
- (BOOL)goToBackForwardListItem:(CWVBackForwardListItem*)item;
// Reloads the current page. // Reloads the current page.
- (void)reload; - (void)reload;
......
...@@ -118,6 +118,9 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier = ...@@ -118,6 +118,9 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier =
[_backButton addTarget:self [_backButton addTarget:self
action:@selector(back) action:@selector(back)
forControlEvents:UIControlEventTouchUpInside]; forControlEvents:UIControlEventTouchUpInside];
[_backButton addTarget:self
action:@selector(logBackStack)
forControlEvents:UIControlEventTouchDragOutside];
[_backButton setAccessibilityLabel:kWebViewShellBackButtonAccessibilityLabel]; [_backButton setAccessibilityLabel:kWebViewShellBackButtonAccessibilityLabel];
[_forwardButton setImage:[UIImage imageNamed:@"ic_forward"] [_forwardButton setImage:[UIImage imageNamed:@"ic_forward"]
...@@ -126,6 +129,9 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier = ...@@ -126,6 +129,9 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier =
[_forwardButton addTarget:self [_forwardButton addTarget:self
action:@selector(forward) action:@selector(forward)
forControlEvents:UIControlEventTouchUpInside]; forControlEvents:UIControlEventTouchUpInside];
[_forwardButton addTarget:self
action:@selector(logForwardStack)
forControlEvents:UIControlEventTouchDragOutside];
[_forwardButton [_forwardButton
setAccessibilityLabel:kWebViewShellForwardButtonAccessibilityLabel]; setAccessibilityLabel:kWebViewShellForwardButtonAccessibilityLabel];
...@@ -280,12 +286,38 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier = ...@@ -280,12 +286,38 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibilityIdentifier =
} }
} }
- (void)logBackStack {
if (!_webView.canGoBack) {
return;
}
CWVBackForwardList* list = _webView.backForwardList;
CWVBackForwardListItemArray* backList = list.backList;
for (size_t i = 0; i < backList.count; i++) {
CWVBackForwardListItem* item = backList[i];
NSLog(@"BackStack Item #%ld: <URL='%@', title='%@'>", i, item.URL,
item.title);
}
}
- (void)forward { - (void)forward {
if ([_webView canGoForward]) { if ([_webView canGoForward]) {
[_webView goForward]; [_webView goForward];
} }
} }
- (void)logForwardStack {
if (!_webView.canGoForward) {
return;
}
CWVBackForwardList* list = _webView.backForwardList;
CWVBackForwardListItemArray* forwardList = list.forwardList;
for (size_t i = 0; i < forwardList.count; i++) {
CWVBackForwardListItem* item = forwardList[i];
NSLog(@"ForwardStack Item #%ld: <URL='%@', title='%@'>", i, item.URL,
item.title);
}
}
- (void)reloadOrStop { - (void)reloadOrStop {
if (_webView.loading) { if (_webView.loading) {
[_webView stopLoading]; [_webView stopLoading];
......
...@@ -13,6 +13,7 @@ source_set("inttests") { ...@@ -13,6 +13,7 @@ source_set("inttests") {
"scroll_view_kvo_inttest.mm", "scroll_view_kvo_inttest.mm",
"ui_delegate_inttest.mm", "ui_delegate_inttest.mm",
"web_view_autofill_inttest.mm", "web_view_autofill_inttest.mm",
"web_view_back_forward_list_inttest.mm",
"web_view_from_wk_web_view_configuration_inttest.mm", "web_view_from_wk_web_view_configuration_inttest.mm",
"web_view_inttest.mm", "web_view_inttest.mm",
"web_view_inttest_base.h", "web_view_inttest_base.h",
......
// Copyright 2019 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 <ChromeWebView/ChromeWebView.h>
#import <Foundation/Foundation.h>
#import "base/test/ios/wait_util.h"
#import "ios/web_view/test/observer.h"
#import "ios/web_view/test/web_view_inttest_base.h"
#import "ios/web_view/test/web_view_test_util.h"
#import "net/base/mac/url_conversions.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest_mac.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface CWVBackForwardListTestNavigationObserver
: NSObject <CWVNavigationDelegate>
// Whether |webViewDidFinishNavigation| has been called. Initiated as NO.
@property(nonatomic, assign, readonly) BOOL pageLoaded;
- (void)webViewDidFinishNavigation:(CWVWebView*)webView;
@end
@implementation CWVBackForwardListTestNavigationObserver
- (void)webViewDidFinishNavigation:(CWVWebView*)webView {
_pageLoaded = YES;
}
@end
namespace ios_web_view {
// Tests all CWVBackForwardList-related functionalities.
class WebViewBackForwardListTest : public WebViewInttestBase {
protected:
// Lets test_server_ produce some html pages and return the URLs.
void GenerateTestPageUrls() {
page1_url_ = GetUrlForPageWithHtml(
"<html><header><title>page1</title></header><body>1</body></html>");
page2_url_ = GetUrlForPageWithHtml(
"<html><header><title>page2</title></header><body>2</body></html>");
page3_url_ = GetUrlForPageWithHtml(
"<html><header><title>page3</title></header><body>3</body></html>");
page4_url_ = GetUrlForPageWithHtml(
"<html><header><title>page4</title></header><body>4</body></html>");
}
// Waits until web_view_ has loaded a page.
bool WaitUntilPageLoaded() {
CWVBackForwardListTestNavigationObserver* observer =
[[CWVBackForwardListTestNavigationObserver alloc] init];
web_view_.navigationDelegate = observer;
bool result = base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForPageLoadTimeout, ^{
return observer.pageLoaded;
});
web_view_.navigationDelegate = nil;
return result;
}
GURL page1_url_;
GURL page2_url_;
GURL page3_url_;
GURL page4_url_;
};
// Tests if a CWVBackForwardList can be correctly created from CWVWebView, and
// tests if it can go to the correct page by the generated list items.
TEST_F(WebViewBackForwardListTest,
GenerateBackForwardListItemAndGoToBackForwardListItem) {
ASSERT_TRUE(test_server_->Start());
GenerateTestPageUrls();
// Go to page3
ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page1_url_)));
ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page2_url_)));
ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page3_url_)));
// Now it should be in page3
ASSERT_NSEQ(@"page3",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
CWVBackForwardList* list = web_view_.backForwardList;
// Tests |backList|
ASSERT_EQ(2UL, list.backList.count);
CWVBackForwardListItem* lastPageItem = list.backList[1];
EXPECT_NSEQ(net::NSURLWithGURL(page2_url_), lastPageItem.URL);
EXPECT_NSEQ(@"page2", lastPageItem.title);
EXPECT_NSEQ(net::NSURLWithGURL(page1_url_), list.backList[0].URL);
EXPECT_NSEQ(@"page1", list.backList[0].title);
// Tests |backItem|
EXPECT_NSEQ(lastPageItem, list.backItem);
// Tests |currentItem|
EXPECT_NSEQ(net::NSURLWithGURL(page3_url_), list.currentItem.URL);
EXPECT_NSEQ(@"page3", list.currentItem.title);
// Tests |forwardList|
EXPECT_EQ(0UL, list.forwardList.count);
// Tests |forwardItem|
EXPECT_FALSE(list.forwardItem);
// Go to page2 by |goToBackForwardListItem:|
ASSERT_TRUE([web_view_ goToBackForwardListItem:lastPageItem]);
ASSERT_TRUE(WaitUntilPageLoaded());
// Now it should be in page2
ASSERT_NSEQ(@"page2",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
// The |list| should always be same as |web_view_.backForwardList|, to be
// consistent with the API in WKWebView. Instead, the properties of |list|
// will be changed after navigation operations.
ASSERT_EQ(web_view_.backForwardList, list);
// Tests if it is no-op when going to current item.
EXPECT_FALSE([web_view_ goToBackForwardListItem:list.currentItem]);
ASSERT_EQ(web_view_.backForwardList, list);
// Tests |backList|
ASSERT_EQ(1UL, list.backList.count);
EXPECT_NSEQ(net::NSURLWithGURL(page1_url_), list.backList[0].URL);
EXPECT_NSEQ(@"page1", list.backList[0].title);
// Tests |forwardList|
ASSERT_EQ(1UL, list.forwardList.count);
EXPECT_NSEQ(net::NSURLWithGURL(page3_url_), list.forwardList[0].URL);
EXPECT_NSEQ(@"page3", list.forwardList[0].title);
// Tests |currentItem|
EXPECT_NSEQ(net::NSURLWithGURL(page2_url_), list.currentItem.URL);
EXPECT_NSEQ(@"page2", list.currentItem.title);
// Go to page1
ASSERT_TRUE([web_view_ canGoBack]);
[web_view_ goBack];
ASSERT_TRUE(WaitUntilPageLoaded());
// Now it should be in page1
ASSERT_NSEQ(@"page1",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
ASSERT_EQ(web_view_.backForwardList, list);
EXPECT_FALSE([web_view_ canGoBack]);
// Tests |backList|
EXPECT_EQ(0UL, list.backList.count);
// Tests |backItem|
EXPECT_FALSE(list.backItem);
// Tests |currentItem|
EXPECT_NSEQ(net::NSURLWithGURL(page1_url_), list.currentItem.URL);
EXPECT_NSEQ(@"page1", list.currentItem.title);
// Tests |forwardList|
ASSERT_EQ(2UL, list.forwardList.count);
CWVBackForwardListItem* topPageItem = list.forwardList[1];
EXPECT_NSEQ(net::NSURLWithGURL(page3_url_), topPageItem.URL);
EXPECT_NSEQ(@"page3", topPageItem.title);
// Tests |forwardItem|
CWVBackForwardListItem* nextPageItem = list.forwardList[0];
EXPECT_NSEQ(nextPageItem, list.forwardItem);
EXPECT_NSEQ(net::NSURLWithGURL(page2_url_), nextPageItem.URL);
EXPECT_NSEQ(@"page2", nextPageItem.title);
// Go to page3 and tests going forward by
// |goToBackForwardListItem:|
ASSERT_TRUE([web_view_ goToBackForwardListItem:topPageItem]);
ASSERT_TRUE(WaitUntilPageLoaded());
// Now it should be in page3
ASSERT_NSEQ(@"page3",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
// Go back to page1 and then go to page4 to make the items of page2 and page3
// exipred
ASSERT_TRUE([web_view_ goToBackForwardListItem:list.backList[0]]);
ASSERT_TRUE(WaitUntilPageLoaded());
// Now it should be in page1
ASSERT_NSEQ(@"page1",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
// Go to page4 then
ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page4_url_)));
// Now it should be in page4
ASSERT_NSEQ(@"page4",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
EXPECT_EQ(1UL, list.backList.count);
EXPECT_EQ(0UL, list.forwardList.count);
// The page2 is expired now so |goToBackForwardListItem:| should do nothing
// and return NO in this case.
EXPECT_FALSE([web_view_ goToBackForwardListItem:lastPageItem]);
EXPECT_NSEQ(@"page4",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
}
// Tests if a CWVBackForwardList can be correctly created from CWVWebView, and
// to see if |itemAtIndex:| works fine.
TEST_F(WebViewBackForwardListTest, TestBackForwardListItemAtIndex) {
ASSERT_TRUE(test_server_->Start());
GenerateTestPageUrls();
// Go to page3
ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page1_url_)));
ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page2_url_)));
ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(page3_url_)));
// Now it should be in page3
ASSERT_NSEQ(@"page3",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
CWVBackForwardList* list = web_view_.backForwardList;
ASSERT_EQ(2UL, list.backList.count);
EXPECT_EQ(0UL, list.forwardList.count);
EXPECT_FALSE([list itemAtIndex:-3]);
EXPECT_NSEQ(list.backList[0], [list itemAtIndex:-2]);
EXPECT_NSEQ(list.backList[1], [list itemAtIndex:-1]);
EXPECT_NSEQ(list.currentItem, [list itemAtIndex:0]);
EXPECT_FALSE([list itemAtIndex:1]);
// Go to page2
ASSERT_TRUE([web_view_ goToBackForwardListItem:list.backList[1]]);
ASSERT_TRUE(WaitUntilPageLoaded());
// Now it should be in page2
ASSERT_NSEQ(@"page2",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
list = web_view_.backForwardList;
EXPECT_EQ(1UL, list.backList.count);
EXPECT_EQ(1UL, list.forwardList.count);
EXPECT_FALSE([list itemAtIndex:-2]);
ASSERT_TRUE([list itemAtIndex:-1]);
EXPECT_NSEQ(@"page1", [list itemAtIndex:-1].title);
ASSERT_TRUE([list itemAtIndex:0]);
EXPECT_NSEQ(@"page2", [list itemAtIndex:0].title);
ASSERT_TRUE([list itemAtIndex:1]);
EXPECT_NSEQ(@"page3", [list itemAtIndex:1].title);
EXPECT_FALSE([list itemAtIndex:2]);
// Go to page1
ASSERT_TRUE([web_view_ canGoBack]);
[web_view_ goBack];
ASSERT_TRUE(WaitUntilPageLoaded());
// Now it should be in page1
ASSERT_NSEQ(@"page1",
test::EvaluateJavaScript(web_view_, @"document.title", nil));
list = web_view_.backForwardList;
ASSERT_EQ(2UL, list.forwardList.count);
EXPECT_EQ(0UL, list.backList.count);
EXPECT_FALSE([list itemAtIndex:-1]);
EXPECT_NSEQ(list.currentItem, [list itemAtIndex:0]);
EXPECT_NSEQ(list.forwardList[0], [list itemAtIndex:1]);
EXPECT_NSEQ(list.forwardList[1], [list itemAtIndex:2]);
EXPECT_FALSE([list itemAtIndex:3]);
}
} // namespace ios_web_view
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment