1// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "ios/web/navigation/wk_based_navigation_manager_impl.h"
6
7#include <WebKit/WebKit.h>
8#include <memory>
9
10#include "base/strings/stringprintf.h"
11#include "base/strings/sys_string_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/test/metrics/histogram_tester.h"
14#include "ios/web/common/features.h"
15#import "ios/web/navigation/navigation_manager_delegate.h"
16#import "ios/web/navigation/navigation_manager_impl.h"
17#import "ios/web/navigation/wk_navigation_util.h"
18#include "ios/web/public/navigation/navigation_item.h"
19#include "ios/web/public/navigation/reload_type.h"
20#include "ios/web/public/test/fakes/test_browser_state.h"
21#import "ios/web/public/web_client.h"
22#import "ios/web/test/fakes/crw_fake_back_forward_list.h"
23#include "ios/web/test/test_url_constants.h"
24#include "net/base/escape.h"
25#import "net/base/mac/url_conversions.h"
26#include "testing/gmock/include/gmock/gmock.h"
27#include "testing/gtest/include/gtest/gtest.h"
28#include "testing/platform_test.h"
29#include "third_party/ocmock/OCMock/OCMock.h"
30#include "ui/base/page_transition_types.h"
31#include "url/scheme_host_port.h"
32#include "url/url_util.h"
33
34#if !defined(__has_feature) || !__has_feature(objc_arc)
35#error "This file requires ARC support."
36#endif
37
38namespace web {
39
40// Query parameter that will be appended by AppendingUrlRewriter if it is
41// installed into NavigationManager by a test case.
42const char kRewrittenQueryParam[] = "wknavigationmanagerrewrittenquery";
43
44// Appends |kRewrittenQueryParam| to |url|.
45bool AppendingUrlRewriter(GURL* url, BrowserState* browser_state) {
46  GURL::Replacements query_replacements;
47  query_replacements.SetQueryStr(kRewrittenQueryParam);
48  *url = url->ReplaceComponents(query_replacements);
49  return false;
50}
51
52// URL scheme that will be rewritten by WebUIUrlRewriter.
53const char kSchemeToRewrite[] = "wknavigationmanagerschemetorewrite";
54
55// Replaces |kSchemeToRewrite| scheme with |kTestWebUIScheme|.
56bool WebUIUrlRewriter(GURL* url, BrowserState* browser_state) {
57  if (url->scheme() == kSchemeToRewrite) {
58    GURL::Replacements scheme_replacements;
59    scheme_replacements.SetSchemeStr(kTestWebUIScheme);
60    *url = url->ReplaceComponents(scheme_replacements);
61    return true;
62  }
63  return false;
64}
65
66class MockNavigationManagerDelegate : public NavigationManagerDelegate {
67 public:
68  void SetWebViewNavigationProxy(id web_view) { mock_web_view_ = web_view; }
69  void RemoveWebView() override {
70    // Simulate removing the web view.
71    mock_web_view_ = nil;
72  }
73
74  MOCK_METHOD0(ClearTransientContent, void());
75  MOCK_METHOD0(ClearDialogs, void());
76  MOCK_METHOD0(RecordPageStateInNavigationItem, void());
77  MOCK_METHOD2(OnGoToIndexSameDocumentNavigation,
78               void(NavigationInitiationType type, bool has_user_gesture));
79  MOCK_METHOD1(LoadCurrentItem, void(NavigationInitiationType type));
80  MOCK_METHOD0(LoadIfNecessary, void());
81  MOCK_METHOD0(Reload, void());
82  MOCK_METHOD1(OnNavigationItemsPruned, void(size_t));
83  MOCK_METHOD1(OnNavigationItemCommitted, void(NavigationItem* item));
84  MOCK_METHOD1(SetWebStateUserAgent, void(UserAgentType user_agent_type));
85  MOCK_METHOD4(GoToBackForwardListItem,
86               void(WKBackForwardListItem*,
87                    NavigationItem*,
88                    NavigationInitiationType,
89                    bool));
90  MOCK_METHOD0(GetPendingItem, NavigationItemImpl*());
91
92 private:
93  WebState* GetWebState() override { return nullptr; }
94
95  id<CRWWebViewNavigationProxy> GetWebViewNavigationProxy() const override {
96    return mock_web_view_;
97  }
98
99  id mock_web_view_;
100};
101
102// Test fixture for WKBasedNavigationManagerImpl.
103class WKBasedNavigationManagerTest : public PlatformTest {
104 protected:
105  WKBasedNavigationManagerTest() : manager_(new WKBasedNavigationManagerImpl) {
106    mock_web_view_ = OCMClassMock([WKWebView class]);
107    mock_wk_list_ = [[CRWFakeBackForwardList alloc] init];
108    OCMStub([mock_web_view_ backForwardList]).andReturn(mock_wk_list_);
109    delegate_.SetWebViewNavigationProxy(mock_web_view_);
110
111    manager_->SetDelegate(&delegate_);
112    manager_->SetBrowserState(&browser_state_);
113
114    BrowserURLRewriter::GetInstance()->AddURLRewriter(WebUIUrlRewriter);
115    url::AddStandardScheme(kSchemeToRewrite, url::SCHEME_WITH_HOST);
116  }
117
118  // Returns the value of the "#session=" URL hash component from |url|.
119  static std::string ExtractRestoredSession(const GURL& url) {
120    std::string decoded = net::UnescapeBinaryURLComponent(url.ref());
121    return decoded.substr(
122        strlen(wk_navigation_util::kRestoreSessionSessionHashPrefix));
123  }
124
125  std::unique_ptr<NavigationManagerImpl> manager_;
126  CRWFakeBackForwardList* mock_wk_list_;
127  id mock_web_view_;
128  MockNavigationManagerDelegate delegate_;
129  base::HistogramTester histogram_tester_;
130
131 private:
132  TestBrowserState browser_state_;
133  url::ScopedSchemeRegistryForTests scoped_registry_;
134};
135
136// Tests that GetItemAtIndex() on an empty manager will sync navigation items to
137// WKBackForwardList using default properties.
138TEST_F(WKBasedNavigationManagerTest, SyncAfterItemAtIndex) {
139  EXPECT_EQ(0, manager_->GetItemCount());
140  EXPECT_EQ(nullptr, manager_->GetItemAtIndex(0));
141
142  [mock_wk_list_ setCurrentURL:@"http://www.0.com"];
143  EXPECT_EQ(1, manager_->GetItemCount());
144  EXPECT_EQ(0, manager_->GetLastCommittedItemIndex());
145
146  NavigationItem* item = manager_->GetItemAtIndex(0);
147  ASSERT_NE(item, nullptr);
148  EXPECT_EQ(GURL("http://www.0.com"), item->GetURL());
149  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(ui::PAGE_TRANSITION_LINK,
150                                           item->GetTransitionType()));
151  EXPECT_EQ(UserAgentType::NONE, item->GetUserAgentType());
152  EXPECT_FALSE(item->GetTimestamp().is_null());
153}
154
155// Tests that Referrer is inferred from the previous WKBackForwardListItem.
156TEST_F(WKBasedNavigationManagerTest, SyncAfterItemAtIndexWithPreviousItem) {
157  [mock_wk_list_ setCurrentURL:@"http://www.1.com"
158                  backListURLs:@[ @"http://www.0.com" ]
159               forwardListURLs:@[ @"http://www.2.com" ]];
160  EXPECT_EQ(3, manager_->GetItemCount());
161  EXPECT_EQ(1, manager_->GetLastCommittedItemIndex());
162
163  // The out-of-order access is intentionall to test that syncing doesn't rely
164  // on the previous WKBackForwardListItem having an associated NavigationItem.
165  NavigationItem* item2 = manager_->GetItemAtIndex(2);
166  ASSERT_NE(item2, nullptr);
167  EXPECT_EQ(GURL("http://www.2.com"), item2->GetURL());
168  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(ui::PAGE_TRANSITION_LINK,
169                                           item2->GetTransitionType()));
170  EXPECT_EQ(UserAgentType::NONE, item2->GetUserAgentType());
171  EXPECT_EQ(GURL("http://www.1.com"), item2->GetReferrer().url);
172  EXPECT_FALSE(item2->GetTimestamp().is_null());
173
174  NavigationItem* item1 = manager_->GetItemAtIndex(1);
175  ASSERT_NE(item1, nullptr);
176  EXPECT_EQ(GURL("http://www.1.com"), item1->GetURL());
177  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(ui::PAGE_TRANSITION_LINK,
178                                           item1->GetTransitionType()));
179  EXPECT_EQ(UserAgentType::NONE, item1->GetUserAgentType());
180  EXPECT_EQ(GURL("http://www.0.com"), item1->GetReferrer().url);
181  EXPECT_FALSE(item1->GetTimestamp().is_null());
182
183  NavigationItem* item0 = manager_->GetItemAtIndex(0);
184  ASSERT_NE(item0, nullptr);
185  EXPECT_EQ(GURL("http://www.0.com"), item0->GetURL());
186  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(ui::PAGE_TRANSITION_LINK,
187                                           item0->GetTransitionType()));
188  EXPECT_EQ(UserAgentType::NONE, item0->GetUserAgentType());
189  EXPECT_FALSE(item0->GetTimestamp().is_null());
190}
191
192// Tests that GetLastCommittedItem() creates a default NavigationItem when the
193// last committed item in WKWebView does not have a linked entry.
194TEST_F(WKBasedNavigationManagerTest, SyncInGetLastCommittedItem) {
195  [mock_wk_list_ setCurrentURL:@"http://www.0.com"];
196  EXPECT_EQ(1, manager_->GetItemCount());
197
198  NavigationItem* item = manager_->GetLastCommittedItem();
199  ASSERT_NE(item, nullptr);
200  EXPECT_EQ("http://www.0.com/", item->GetURL().spec());
201  EXPECT_FALSE(item->GetTimestamp().is_null());
202}
203
204// Tests that GetLastCommittedItem() creates a default NavigationItem when the
205// last committed item in WKWebView is an app-specific URL.
206TEST_F(WKBasedNavigationManagerTest,
207       SyncInGetLastCommittedItemForAppSpecificURL) {
208  GURL url(url::SchemeHostPort(kSchemeToRewrite, "test", 0).Serialize());
209
210  // Verifies that the test URL is rewritten into an app-specific URL.
211  manager_->AddPendingItem(url, Referrer(), ui::PAGE_TRANSITION_TYPED,
212                           web::NavigationInitiationType::BROWSER_INITIATED);
213  NavigationItem* pending_item = manager_->GetPendingItem();
214  ASSERT_TRUE(pending_item);
215  ASSERT_TRUE(web::GetWebClient()->IsAppSpecificURL(pending_item->GetURL()));
216
217  [mock_wk_list_ setCurrentURL:base::SysUTF8ToNSString(url.spec())];
218  NavigationItem* item = manager_->GetLastCommittedItem();
219
220  ASSERT_NE(item, nullptr);
221  EXPECT_EQ(url, item->GetURL());
222  EXPECT_EQ(1, manager_->GetItemCount());
223}
224
225// Tests that CommitPendingItem() will sync navigation items to
226// WKBackForwardList and the pending item NavigationItemImpl will be used.
227TEST_F(WKBasedNavigationManagerTest, GetItemAtIndexAfterCommitPending) {
228  // Simulate a main frame navigation.
229  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
230                           ui::PAGE_TRANSITION_TYPED,
231                           web::NavigationInitiationType::BROWSER_INITIATED);
232  NavigationItem* pending_item0 = manager_->GetPendingItem();
233
234  [mock_wk_list_ setCurrentURL:@"http://www.0.com"];
235  manager_->CommitPendingItem();
236
237  EXPECT_EQ(1, manager_->GetItemCount());
238  NavigationItem* item = manager_->GetLastCommittedItem();
239  EXPECT_EQ(pending_item0, item);
240  EXPECT_EQ(GURL("http://www.0.com"), item->GetURL());
241  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(ui::PAGE_TRANSITION_TYPED,
242                                           item->GetTransitionType()));
243
244  // Simulate a second main frame navigation.
245  manager_->AddPendingItem(GURL("http://www.2.com"), Referrer(),
246                           ui::PAGE_TRANSITION_TYPED,
247                           web::NavigationInitiationType::BROWSER_INITIATED);
248  NavigationItem* pending_item2 = manager_->GetPendingItem();
249
250  // Simulate an iframe navigation between the two main frame navigations.
251  [mock_wk_list_ setCurrentURL:@"http://www.2.com"
252                  backListURLs:@[ @"http://www.0.com", @"http://www.1.com" ]
253               forwardListURLs:nil];
254  manager_->CommitPendingItem();
255
256  EXPECT_EQ(3, manager_->GetItemCount());
257  EXPECT_EQ(2, manager_->GetLastCommittedItemIndex());
258
259  // This item is created by syncing.
260  NavigationItem* item1 = manager_->GetItemAtIndex(1);
261  EXPECT_EQ(GURL("http://www.1.com"), item1->GetURL());
262  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(ui::PAGE_TRANSITION_LINK,
263                                           item1->GetTransitionType()));
264  EXPECT_EQ(GURL("http://www.0.com"), item1->GetReferrer().url);
265
266  // This item is created by CommitPendingItem.
267  NavigationItem* item2 = manager_->GetItemAtIndex(2);
268  EXPECT_EQ(pending_item2, item2);
269  EXPECT_EQ(GURL("http://www.2.com"), item2->GetURL());
270  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(ui::PAGE_TRANSITION_TYPED,
271                                           item2->GetTransitionType()));
272  EXPECT_EQ(GURL(""), item2->GetReferrer().url);
273}
274
275// Tests that AddPendingItem does not create a new NavigationItem if the new
276// pending item is a back forward navigation or when reloading a redirect page.
277TEST_F(WKBasedNavigationManagerTest, ReusePendingItemForHistoryNavigation) {
278  // Simulate two regular navigations.
279  [mock_wk_list_ setCurrentURL:@"http://www.1.com"
280                  backListURLs:@[ @"http://www.0.com" ]
281               forwardListURLs:nil];
282
283  // Force sync NavigationItems.
284  NavigationItem* original_item0 = manager_->GetItemAtIndex(0);
285  manager_->GetItemAtIndex(1);
286
287  // Simulate a back-forward navigation. Manually shuffle the objects in
288  // mock_wk_list_ to avoid creating new WKBackForwardListItem mocks and
289  // preserve existing NavigationItem associations.
290  WKBackForwardListItem* wk_item0 = [mock_wk_list_ itemAtIndex:-1];
291  WKBackForwardListItem* wk_item1 = [mock_wk_list_ itemAtIndex:0];
292  mock_wk_list_.currentItem = wk_item0;
293  mock_wk_list_.backList = nil;
294  mock_wk_list_.forwardList = @[ wk_item1 ];
295  OCMStub([mock_web_view_ URL])
296      .andReturn([[NSURL alloc] initWithString:@"http://www.0.com"]);
297  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
298                           ui::PAGE_TRANSITION_TYPED,
299                           web::NavigationInitiationType::BROWSER_INITIATED);
300
301  EXPECT_EQ(original_item0, manager_->GetPendingItem());
302
303  // Simulate reloading a redirect url.  This happens when one restores while
304  // offline.
305  GURL redirect_url = wk_navigation_util::CreateRedirectUrl(
306      manager_->GetPendingItem()->GetURL());
307  [mock_wk_list_ setCurrentURL:base::SysUTF8ToNSString(redirect_url.spec())
308                  backListURLs:nil
309               forwardListURLs:nil];
310  original_item0 = manager_->GetItemAtIndex(0);
311  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
312                           ui::PAGE_TRANSITION_RELOAD,
313                           web::NavigationInitiationType::BROWSER_INITIATED);
314  EXPECT_EQ(original_item0, manager_->GetPendingItem());
315}
316
317// Tests that AddPendingItem does not create a new NavigationItem if the new
318// pending item is a reload of app-specific URL.
319TEST_F(WKBasedNavigationManagerTest, ReusePendingItemForReloadAppSpecificURL) {
320  // Simulate a previous app-specific navigation.
321  NSString* url = @"about:blank?for=chrome%3A%2F%2Fnewtab";
322  [mock_wk_list_ setCurrentURL:url];
323  NavigationItem* original_item = manager_->GetItemAtIndex(0);
324
325  OCMExpect([mock_web_view_ URL]).andReturn([[NSURL alloc] initWithString:url]);
326
327  manager_->AddPendingItem(GURL("chrome://newtab"), Referrer(),
328                           ui::PAGE_TRANSITION_RELOAD,
329                           web::NavigationInitiationType::BROWSER_INITIATED);
330
331  EXPECT_EQ(original_item, manager_->GetPendingItem());
332}
333
334// Tests that transient URL rewriters are only applied to a new pending item.
335TEST_F(WKBasedNavigationManagerTest,
336       TransientURLRewritersOnlyUsedForPendingItem) {
337  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
338                           ui::PAGE_TRANSITION_TYPED,
339                           NavigationInitiationType::BROWSER_INITIATED);
340
341  // Install transient URL rewriters.
342  manager_->AddTransientURLRewriter(&AppendingUrlRewriter);
343  [mock_wk_list_ setCurrentURL:@"http://www.0.com"];
344
345  // Transient URL rewriters do not apply to lazily synced items.
346  NavigationItem* item0 = manager_->GetItemAtIndex(0);
347  EXPECT_EQ(GURL("http://www.0.com"), item0->GetURL());
348
349  // Transient URL rewriters do not apply to transient items.
350  manager_->AddTransientItem(GURL("http://www.1.com"));
351  EXPECT_EQ(GURL("http://www.1.com"), manager_->GetTransientItem()->GetURL());
352
353  // Transient URL rewriters are applied to a new pending item.
354  manager_->AddPendingItem(GURL("http://www.2.com"), Referrer(),
355                           ui::PAGE_TRANSITION_TYPED,
356                           NavigationInitiationType::BROWSER_INITIATED);
357  EXPECT_EQ(kRewrittenQueryParam, manager_->GetPendingItem()->GetURL().query());
358}
359
360// Tests DiscardNonCommittedItems discards both pending and transient items.
361TEST_F(WKBasedNavigationManagerTest, DiscardNonCommittedItems) {
362  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
363                           ui::PAGE_TRANSITION_TYPED,
364                           web::NavigationInitiationType::BROWSER_INITIATED);
365  manager_->AddTransientItem(GURL("http://www.1.com"));
366
367  EXPECT_NE(nullptr, manager_->GetPendingItem());
368  EXPECT_NE(nullptr, manager_->GetTransientItem());
369
370  manager_->DiscardNonCommittedItems();
371  EXPECT_EQ(nullptr, manager_->GetPendingItem());
372  EXPECT_EQ(nullptr, manager_->GetTransientItem());
373}
374
375// Tests that in the absence of a transient item, going back is delegated to the
376// underlying WKWebView.
377TEST_F(WKBasedNavigationManagerTest, GoBackWithoutTransientItem) {
378  ASSERT_FALSE(manager_->CanGoBack());
379
380  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
381                           ui::PAGE_TRANSITION_TYPED,
382                           web::NavigationInitiationType::BROWSER_INITIATED);
383  [mock_wk_list_ setCurrentURL:@"http://www.0.com"];
384
385  manager_->AddPendingItem(GURL("http://www.1.com"), Referrer(),
386                           ui::PAGE_TRANSITION_TYPED,
387                           web::NavigationInitiationType::BROWSER_INITIATED);
388  [mock_wk_list_ setCurrentURL:@"http://www.1.com"
389                  backListURLs:@[ @"http://www.0.com" ]
390               forwardListURLs:nil];
391
392  ASSERT_TRUE(manager_->CanGoBack());
393
394  EXPECT_CALL(delegate_,
395              GoToBackForwardListItem(
396                  mock_wk_list_.backList[0], manager_->GetItemAtIndex(0),
397                  NavigationInitiationType::BROWSER_INITIATED,
398                  /*has_user_gesture=*/true));
399  manager_->GoBack();
400  [mock_web_view_ verify];
401}
402
403// Tests that going back from a transient item will discard the transient item
404// and the pending item associated with it.
405TEST_F(WKBasedNavigationManagerTest, GoBackFromTransientItem) {
406  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
407                           ui::PAGE_TRANSITION_TYPED,
408                           web::NavigationInitiationType::BROWSER_INITIATED);
409  [mock_wk_list_ setCurrentURL:@"http://www.0.com"];
410  manager_->CommitPendingItem();
411
412  manager_->AddPendingItem(GURL("http://www.1.com"), Referrer(),
413                           ui::PAGE_TRANSITION_TYPED,
414                           web::NavigationInitiationType::BROWSER_INITIATED);
415  manager_->AddTransientItem(GURL("http://www.1.com/transient"));
416
417  ASSERT_TRUE(manager_->CanGoBack());
418  EXPECT_CALL(delegate_,
419              GoToBackForwardListItem(
420                  mock_wk_list_.currentItem, manager_->GetItemAtIndex(0),
421                  NavigationInitiationType::BROWSER_INITIATED,
422                  /*has_user_gesture=*/true));
423  manager_->GoBack();
424  [mock_web_view_ verify];
425
426  EXPECT_EQ(nullptr, manager_->GetPendingItem());
427  EXPECT_EQ(nullptr, manager_->GetTransientItem());
428}
429
430// Tests that going forward is always delegated to the underlying WKWebView
431// without any sanity checks such as whether any forward history exists.
432TEST_F(WKBasedNavigationManagerTest, GoForward) {
433  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
434                           ui::PAGE_TRANSITION_TYPED,
435                           web::NavigationInitiationType::BROWSER_INITIATED);
436  [mock_wk_list_ setCurrentURL:@"http://www.0.com"];
437  manager_->CommitPendingItem();
438
439  manager_->AddPendingItem(GURL("http://www.1.com"), Referrer(),
440                           ui::PAGE_TRANSITION_TYPED,
441                           web::NavigationInitiationType::BROWSER_INITIATED);
442  [mock_wk_list_ setCurrentURL:@"http://www.1.com"
443                  backListURLs:@[ @"http://www.0.com" ]
444               forwardListURLs:nil];
445
446  [mock_wk_list_ moveCurrentToIndex:0];
447  ASSERT_TRUE(manager_->CanGoForward());
448
449  EXPECT_CALL(delegate_,
450              GoToBackForwardListItem(
451                  mock_wk_list_.forwardList[0], manager_->GetItemAtIndex(1),
452                  NavigationInitiationType::BROWSER_INITIATED,
453                  /*has_user_gesture=*/true));
454  manager_->GoForward();
455  [mock_web_view_ verify];
456}
457
458// Tests that going forward clears uncommitted items.
459TEST_F(WKBasedNavigationManagerTest, GoForwardShouldDiscardsUncommittedItems) {
460  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
461                           ui::PAGE_TRANSITION_TYPED,
462                           web::NavigationInitiationType::BROWSER_INITIATED);
463  [mock_wk_list_ setCurrentURL:@"http://www.0.com"];
464  manager_->CommitPendingItem();
465
466  manager_->AddPendingItem(GURL("http://www.1.com"), Referrer(),
467                           ui::PAGE_TRANSITION_TYPED,
468                           web::NavigationInitiationType::BROWSER_INITIATED);
469  [mock_wk_list_ setCurrentURL:@"http://www.1.com"
470                  backListURLs:@[ @"http://www.0.com" ]
471               forwardListURLs:nil];
472
473  [mock_wk_list_ moveCurrentToIndex:0];
474  ASSERT_TRUE(manager_->CanGoForward());
475
476  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
477                           ui::PAGE_TRANSITION_TYPED,
478                           web::NavigationInitiationType::BROWSER_INITIATED);
479  manager_->AddTransientItem(GURL("http://www.1.com"));
480
481  EXPECT_NE(nullptr, manager_->GetPendingItem());
482  EXPECT_NE(nullptr, manager_->GetTransientItem());
483
484  EXPECT_CALL(delegate_,
485              GoToBackForwardListItem(
486                  mock_wk_list_.forwardList[0], manager_->GetItemAtIndex(1),
487                  NavigationInitiationType::BROWSER_INITIATED,
488                  /*has_user_gesture=*/true));
489  manager_->GoForward();
490  [mock_web_view_ verify];
491
492  EXPECT_EQ(nullptr, manager_->GetPendingItem());
493  EXPECT_EQ(nullptr, manager_->GetTransientItem());
494}
495
496// Tests CanGoToOffset API for positive, negative and zero delta.
497TEST_F(WKBasedNavigationManagerTest, CanGoToOffset) {
498  manager_->AddPendingItem(GURL("http://www.url.com/0"), Referrer(),
499                           ui::PAGE_TRANSITION_LINK,
500                           web::NavigationInitiationType::BROWSER_INITIATED);
501
502  [mock_wk_list_ setCurrentURL:@"http://www.url.com/0"];
503  manager_->CommitPendingItem();
504
505  manager_->AddPendingItem(GURL("http://www.url.com/1"), Referrer(),
506                           ui::PAGE_TRANSITION_LINK,
507                           web::NavigationInitiationType::BROWSER_INITIATED);
508
509  [mock_wk_list_ setCurrentURL:@"http://www.url.com/1"
510                  backListURLs:@[ @"http://www.url.com/0" ]
511               forwardListURLs:nil];
512  manager_->CommitPendingItem();
513
514  manager_->AddPendingItem(GURL("http://www.url.com/2"), Referrer(),
515                           ui::PAGE_TRANSITION_LINK,
516                           web::NavigationInitiationType::BROWSER_INITIATED);
517
518  [mock_wk_list_
519        setCurrentURL:@"http://www.url.com/2"
520         backListURLs:@[ @"http://www.url.com/0", @"http://www.url.com/1" ]
521      forwardListURLs:nil];
522  manager_->CommitPendingItem();
523
524  ASSERT_EQ(3, manager_->GetItemCount());
525  ASSERT_EQ(2, manager_->GetLastCommittedItemIndex());
526
527  // Go to entry at index 1 and test API from that state.
528  [mock_wk_list_ moveCurrentToIndex:1];
529  ASSERT_EQ(1, manager_->GetLastCommittedItemIndex());
530  ASSERT_EQ(-1, manager_->GetPendingItemIndex());
531  EXPECT_TRUE(manager_->CanGoToOffset(-1));
532  EXPECT_EQ(0, manager_->GetIndexForOffset(-1));
533  EXPECT_FALSE(manager_->CanGoToOffset(-2));
534  EXPECT_TRUE(manager_->CanGoToOffset(1));
535  EXPECT_EQ(2, manager_->GetIndexForOffset(1));
536  EXPECT_FALSE(manager_->CanGoToOffset(2));
537  // Test with large values
538  EXPECT_FALSE(manager_->CanGoToOffset(INT_MAX));
539  EXPECT_FALSE(manager_->CanGoToOffset(INT_MIN));
540
541  // Go to entry at index 0 and test API from that state.
542  [mock_wk_list_ moveCurrentToIndex:0];
543  ASSERT_EQ(0, manager_->GetLastCommittedItemIndex());
544  ASSERT_EQ(-1, manager_->GetPendingItemIndex());
545  EXPECT_FALSE(manager_->CanGoToOffset(-1));
546  EXPECT_TRUE(manager_->CanGoToOffset(1));
547  EXPECT_EQ(1, manager_->GetIndexForOffset(1));
548  EXPECT_TRUE(manager_->CanGoToOffset(2));
549  EXPECT_EQ(2, manager_->GetIndexForOffset(2));
550  EXPECT_FALSE(manager_->CanGoToOffset(3));
551  // Test with large values
552  EXPECT_FALSE(manager_->CanGoToOffset(INT_MAX));
553  EXPECT_FALSE(manager_->CanGoToOffset(INT_MIN));
554
555  // Go to entry at index 2 and test API from that state.
556  [mock_wk_list_ moveCurrentToIndex:2];
557  ASSERT_EQ(2, manager_->GetLastCommittedItemIndex());
558  ASSERT_EQ(-1, manager_->GetPendingItemIndex());
559  EXPECT_TRUE(manager_->CanGoToOffset(-1));
560  EXPECT_EQ(1, manager_->GetIndexForOffset(-1));
561  EXPECT_TRUE(manager_->CanGoToOffset(-2));
562  EXPECT_EQ(0, manager_->GetIndexForOffset(-2));
563  EXPECT_FALSE(manager_->CanGoToOffset(1));
564  // Test with large values
565  EXPECT_FALSE(manager_->CanGoToOffset(INT_MAX));
566  EXPECT_FALSE(manager_->CanGoToOffset(INT_MIN));
567
568  // Test with transient entry.
569  manager_->AddPendingItem(GURL("http://www.url.com/3"), Referrer(),
570                           ui::PAGE_TRANSITION_LINK,
571                           web::NavigationInitiationType::BROWSER_INITIATED);
572  manager_->AddTransientItem(GURL("http://www.url.com/3"));
573  ASSERT_EQ(3, manager_->GetItemCount());
574  ASSERT_EQ(2, manager_->GetLastCommittedItemIndex());
575  EXPECT_TRUE(manager_->CanGoToOffset(-1));
576  EXPECT_EQ(2, manager_->GetIndexForOffset(-1));
577  EXPECT_TRUE(manager_->CanGoToOffset(-3));
578  EXPECT_EQ(0, manager_->GetIndexForOffset(-3));
579  EXPECT_FALSE(manager_->CanGoToOffset(-4));
580  EXPECT_FALSE(manager_->CanGoToOffset(1));
581
582  // Simulate a history navigation pending item.
583  [mock_wk_list_ moveCurrentToIndex:1];
584  OCMExpect([mock_web_view_ URL])
585      .andReturn([[NSURL alloc] initWithString:@"http://www.url.com/1"]);
586  manager_->AddPendingItem(GURL("http://www.url.com/1"), Referrer(),
587                           ui::PAGE_TRANSITION_LINK,
588                           web::NavigationInitiationType::BROWSER_INITIATED);
589
590  EXPECT_EQ(3, manager_->GetItemCount());
591  EXPECT_EQ(2, manager_->GetLastCommittedItemIndex());
592  EXPECT_EQ(1, manager_->GetPendingItemIndex());
593  EXPECT_TRUE(manager_->CanGoToOffset(-1));
594  EXPECT_EQ(0, manager_->GetIndexForOffset(-1));
595  EXPECT_FALSE(manager_->CanGoToOffset(-2));
596  EXPECT_TRUE(manager_->CanGoToOffset(1));
597  EXPECT_EQ(2, manager_->GetIndexForOffset(1));
598  EXPECT_FALSE(manager_->CanGoToOffset(2));
599}
600
601// Tests that non-empty session history can be restored, and are re-written if
602// necessary.
603TEST_F(WKBasedNavigationManagerTest, RestoreSessionWithHistory) {
604  manager_->AddTransientURLRewriter(&WebUIUrlRewriter);
605  auto item0 = std::make_unique<NavigationItemImpl>();
606  GURL url(url::SchemeHostPort(kSchemeToRewrite, "test", 0).Serialize());
607  item0->SetURL(url);
608  item0->SetTitle(base::ASCIIToUTF16("Test Website 0"));
609  auto item1 = std::make_unique<NavigationItemImpl>();
610  item1->SetURL(GURL("http://www.1.com"));
611
612  std::vector<std::unique_ptr<NavigationItem>> items;
613  items.push_back(std::move(item0));
614  items.push_back(std::move(item1));
615
616  ASSERT_FALSE(manager_->IsRestoreSessionInProgress());
617  manager_->Restore(1 /* last_committed_item_index */, std::move(items));
618  EXPECT_TRUE(manager_->IsRestoreSessionInProgress());
619
620  ASSERT_FALSE(manager_->GetPendingItem());
621  NavigationItem* pending_item =
622      manager_->GetPendingItemInCurrentOrRestoredSession();
623  ASSERT_TRUE(pending_item);
624  GURL pending_url = pending_item->GetURL();
625  EXPECT_TRUE(pending_url.SchemeIsFile());
626  EXPECT_EQ("restore_session.html", pending_url.ExtractFileName());
627  EXPECT_EQ(url.spec(), pending_item->GetVirtualURL());
628  EXPECT_EQ("Test Website 0", base::UTF16ToUTF8(pending_item->GetTitle()));
629
630  EXPECT_EQ("{\"offset\":0,\"titles\":[\"Test Website 0\",\"\"],"
631            "\"urls\":[\"testwebui://test/\","
632            "\"http://www.1.com/\"]}",
633            ExtractRestoredSession(pending_url));
634
635  // Check that cached visible item is returned.
636  EXPECT_EQ("http://www.1.com/", manager_->GetVisibleItem()->GetURL());
637
638  histogram_tester_.ExpectTotalCount(kRestoreNavigationItemCount, 1);
639  histogram_tester_.ExpectBucketCount(kRestoreNavigationItemCount, 2, 1);
640}
641
642// Tests that restoring session replaces existing history in navigation manager.
643TEST_F(WKBasedNavigationManagerTest, RestoreSessionResetsHistory) {
644  EXPECT_EQ(-1, manager_->GetPendingItemIndex());
645  EXPECT_EQ(-1, manager_->GetLastCommittedItemIndex());
646
647  // Sets up the navigation history with 2 entries, and a pending back-forward
648  // navigation so that last_committed_item_index is 1, pending_item_index is 0,
649  // and previous_item_index is 0. Basically, none of them is -1.
650  manager_->AddPendingItem(GURL("http://www.url.com/0"), Referrer(),
651                           ui::PAGE_TRANSITION_TYPED,
652                           web::NavigationInitiationType::BROWSER_INITIATED);
653  [mock_wk_list_ setCurrentURL:@"http://www.url.com/0"];
654  manager_->CommitPendingItem();
655
656  manager_->AddPendingItem(GURL("http://www.url.com/1"), Referrer(),
657                           ui::PAGE_TRANSITION_TYPED,
658                           web::NavigationInitiationType::BROWSER_INITIATED);
659  [mock_wk_list_ setCurrentURL:@"http://www.url.com/1"
660                  backListURLs:@[ @"http://www.url.com/0" ]
661               forwardListURLs:nil];
662  manager_->CommitPendingItem();
663
664  [mock_wk_list_ moveCurrentToIndex:0];
665  OCMStub([mock_web_view_ URL])
666      .andReturn([NSURL URLWithString:@"http://www.url.com/0"]);
667  manager_->AddPendingItem(GURL("http://www.url.com/0"), Referrer(),
668                           ui::PAGE_TRANSITION_TYPED,
669                           web::NavigationInitiationType::BROWSER_INITIATED);
670
671  EXPECT_EQ(1, manager_->GetLastCommittedItemIndex());
672  EXPECT_EQ(0, manager_->GetPendingItemIndex());
673  EXPECT_TRUE(manager_->GetPendingItem() != nullptr);
674
675  // Restores a fake session.
676  auto restored_item = std::make_unique<NavigationItemImpl>();
677  restored_item->SetURL(GURL("http://restored.com"));
678  std::vector<std::unique_ptr<NavigationItem>> items;
679  items.push_back(std::move(restored_item));
680  ASSERT_FALSE(manager_->IsRestoreSessionInProgress());
681  manager_->Restore(0 /* last_committed_item_index */, std::move(items));
682  EXPECT_TRUE(manager_->IsRestoreSessionInProgress());
683
684  // Check that last_committed_index, previous_item_index and pending_item_index
685  // are all reset to -1. Note that last_committed_item_index will change to the
686  // value in the restored session (i.e. 0) once restore_session.html finishes
687  // loading in the web view. This is not tested here because this test doesn't
688  // use real WKWebView.
689  EXPECT_EQ(-1, manager_->GetLastCommittedItemIndex());
690  EXPECT_EQ(-1, manager_->GetPendingItemIndex());
691
692  // Check that the only pending item is restore_session.html.
693  ASSERT_FALSE(manager_->GetPendingItem());
694  NavigationItem* pending_item =
695      manager_->GetPendingItemInCurrentOrRestoredSession();
696  ASSERT_TRUE(pending_item != nullptr);
697  GURL pending_url = pending_item->GetURL();
698  EXPECT_TRUE(pending_url.SchemeIsFile());
699  EXPECT_EQ("restore_session.html", pending_url.ExtractFileName());
700
701  // Check that cached visible item is returned.
702  EXPECT_EQ("http://restored.com/", manager_->GetVisibleItem()->GetURL());
703}
704
705// Tests that Restore() accepts empty session history and performs no-op.
706TEST_F(WKBasedNavigationManagerTest, RestoreSessionWithEmptyHistory) {
707  manager_->Restore(-1 /* last_committed_item_index */,
708                    std::vector<std::unique_ptr<NavigationItem>>());
709
710  ASSERT_EQ(nullptr, manager_->GetPendingItem());
711}
712
713// Tests that the virtual URL of a restore_session redirect item is updated to
714// the target URL.
715TEST_F(WKBasedNavigationManagerTest, HideInternalRedirectUrl) {
716  GURL target_url = GURL("http://www.1.com?query=special%26chars");
717  GURL url = wk_navigation_util::CreateRedirectUrl(target_url);
718  NSString* url_spec = base::SysUTF8ToNSString(url.spec());
719  [mock_wk_list_ setCurrentURL:url_spec];
720  NavigationItem* item = manager_->GetItemAtIndex(0);
721  ASSERT_TRUE(item);
722  EXPECT_EQ(target_url, item->GetVirtualURL());
723  EXPECT_EQ(url, item->GetURL());
724}
725
726// Tests that the virtual URL of a placeholder item is updated to the original
727// URL.
728TEST_F(WKBasedNavigationManagerTest, HideInternalPlaceholderUrl) {
729  if (base::FeatureList::IsEnabled(web::features::kUseJSForErrorPage))
730    return;
731
732  GURL original_url = GURL("http://www.1.com?query=special%26chars");
733  GURL url = wk_navigation_util::CreatePlaceholderUrlForUrl(original_url);
734  NSString* url_spec = base::SysUTF8ToNSString(url.spec());
735  [mock_wk_list_ setCurrentURL:url_spec];
736  NavigationItem* item = manager_->GetItemAtIndex(0);
737  ASSERT_TRUE(item);
738  EXPECT_EQ(original_url, item->GetVirtualURL());
739  EXPECT_EQ(url, item->GetURL());
740}
741
742// Tests that all NavigationManager APIs return reasonable values in the Empty
743// Window Open Navigation edge case. See comments in header file for details.
744TEST_F(WKBasedNavigationManagerTest, EmptyWindowOpenNavigation) {
745  // Set up the precondition for an empty window open item.
746  OCMExpect([mock_web_view_ URL])
747      .andReturn(net::NSURLWithGURL(GURL(url::kAboutBlankURL)));
748  mock_wk_list_.currentItem = nil;
749
750  manager_->AddPendingItem(GURL(url::kAboutBlankURL), Referrer(),
751                           ui::PAGE_TRANSITION_LINK,
752                           web::NavigationInitiationType::RENDERER_INITIATED);
753
754  const NavigationItem* pending_item = manager_->GetPendingItem();
755  ASSERT_TRUE(pending_item);
756  EXPECT_EQ(-1, manager_->GetPendingItemIndex());
757  EXPECT_EQ(url::kAboutBlankURL, pending_item->GetURL().spec());
758
759  manager_->CommitPendingItem();
760
761  const NavigationItem* last_committed_item = manager_->GetLastCommittedItem();
762  ASSERT_EQ(pending_item, last_committed_item);
763  EXPECT_EQ(last_committed_item, manager_->GetVisibleItem());
764
765  EXPECT_EQ(0, manager_->GetIndexForOffset(0));
766  EXPECT_EQ(1, manager_->GetIndexForOffset(1));
767  EXPECT_EQ(-1, manager_->GetIndexForOffset(-1));
768
769  EXPECT_EQ(1, manager_->GetItemCount());
770  EXPECT_EQ(last_committed_item, manager_->GetItemAtIndex(0));
771  EXPECT_FALSE(manager_->GetItemAtIndex(1));
772
773  EXPECT_EQ(0, manager_->GetIndexOfItem(last_committed_item));
774  EXPECT_EQ(-1, manager_->GetPendingItemIndex());
775  EXPECT_EQ(0, manager_->GetLastCommittedItemIndex());
776
777  EXPECT_FALSE(manager_->CanGoBack());
778  EXPECT_FALSE(manager_->CanGoForward());
779  EXPECT_TRUE(manager_->CanGoToOffset(0));
780  EXPECT_FALSE(manager_->CanGoToOffset(-1));
781  EXPECT_FALSE(manager_->CanGoToOffset(1));
782
783  // This is allowed on an empty window open item.
784  manager_->GoToIndex(0);
785
786  // Add another navigation and verify that it replaces the empty window open
787  // item.
788  manager_->AddPendingItem(GURL("http://www.2.com"), Referrer(),
789                           ui::PAGE_TRANSITION_TYPED,
790                           web::NavigationInitiationType::BROWSER_INITIATED);
791
792  const NavigationItem* pending_item_2 = manager_->GetPendingItem();
793  ASSERT_TRUE(pending_item_2);
794  EXPECT_EQ("http://www.2.com/", pending_item_2->GetURL().spec());
795
796  [mock_wk_list_ setCurrentURL:@"http://www.2.com"];
797  manager_->CommitPendingItem();
798  OCMExpect([mock_web_view_ URL])
799      .andReturn([[NSURL alloc] initWithString:@"http://www.2.com"]);
800
801  const NavigationItem* last_committed_item_2 =
802      manager_->GetLastCommittedItem();
803  ASSERT_EQ(pending_item_2, last_committed_item_2);
804  EXPECT_EQ(last_committed_item_2, manager_->GetVisibleItem());
805
806  EXPECT_EQ(0, manager_->GetIndexForOffset(0));
807  EXPECT_EQ(1, manager_->GetIndexForOffset(1));
808  EXPECT_EQ(-1, manager_->GetIndexForOffset(-1));
809
810  EXPECT_EQ(1, manager_->GetItemCount());
811  EXPECT_EQ(last_committed_item_2, manager_->GetItemAtIndex(0));
812  EXPECT_FALSE(manager_->GetItemAtIndex(1));
813
814  EXPECT_EQ(-1, manager_->GetIndexOfItem(last_committed_item));
815  EXPECT_EQ(0, manager_->GetIndexOfItem(last_committed_item_2));
816  EXPECT_EQ(-1, manager_->GetPendingItemIndex());
817  EXPECT_EQ(0, manager_->GetLastCommittedItemIndex());
818
819  EXPECT_FALSE(manager_->CanGoBack());
820  EXPECT_FALSE(manager_->CanGoForward());
821  EXPECT_TRUE(manager_->CanGoToOffset(0));
822  EXPECT_FALSE(manager_->CanGoToOffset(-1));
823  EXPECT_FALSE(manager_->CanGoToOffset(1));
824
825  // This is still allowed on a length-1 navigation history.
826  manager_->GoToIndex(0);
827}
828
829// Test fixture for detach from web view mode for WKBasedNavigationManagerImpl.
830class WKBasedNavigationManagerDetachedModeTest
831    : public WKBasedNavigationManagerTest {
832 protected:
833  void SetUp() override {
834    // Sets up each test case with a session history of 3 items. The middle item
835    // is the current item.
836    url0_ = GURL("http://www.0.com");
837    url1_ = GURL("http://www.1.com");
838    url2_ = GURL("http://www.2.com");
839
840    [mock_wk_list_ setCurrentURL:@"http://www.1.com"
841                    backListURLs:@[ @"http://www.0.com" ]
842                 forwardListURLs:@[ @"http://www.2.com" ]];
843
844    ASSERT_EQ(url0_, manager_->GetItemAtIndex(0)->GetURL());
845    ASSERT_EQ(url1_, manager_->GetItemAtIndex(1)->GetURL());
846    ASSERT_EQ(url2_, manager_->GetItemAtIndex(2)->GetURL());
847  }
848
849  NSString* CreateRedirectUrlForWKList(GURL url) {
850    GURL redirect_url = wk_navigation_util::CreateRedirectUrl(url);
851    return base::SysUTF8ToNSString(redirect_url.spec());
852  }
853
854  GURL url0_;
855  GURL url1_;
856  GURL url2_;
857};
858
859// Tests that all getters return the expected value in detached mode.
860TEST_F(WKBasedNavigationManagerDetachedModeTest, CachedSessionHistory) {
861  manager_->DetachFromWebView();
862  delegate_.RemoveWebView();
863
864  EXPECT_EQ(url1_, manager_->GetVisibleItem()->GetURL());
865  EXPECT_EQ(3, manager_->GetItemCount());
866
867  EXPECT_EQ(url0_, manager_->GetItemAtIndex(0)->GetURL());
868  EXPECT_EQ(url1_, manager_->GetItemAtIndex(1)->GetURL());
869  EXPECT_EQ(url2_, manager_->GetItemAtIndex(2)->GetURL());
870
871  EXPECT_EQ(0, manager_->GetIndexOfItem(manager_->GetItemAtIndex(0)));
872  EXPECT_EQ(1, manager_->GetIndexOfItem(manager_->GetItemAtIndex(1)));
873  EXPECT_EQ(2, manager_->GetIndexOfItem(manager_->GetItemAtIndex(2)));
874
875  EXPECT_EQ(-1, manager_->GetPendingItemIndex());
876  EXPECT_EQ(nullptr, manager_->GetPendingItem());
877
878  EXPECT_EQ(1, manager_->GetLastCommittedItemIndex());
879  EXPECT_EQ(url1_, manager_->GetLastCommittedItem()->GetURL());
880
881  EXPECT_TRUE(manager_->CanGoBack());
882  EXPECT_TRUE(manager_->CanGoForward());
883  EXPECT_TRUE(manager_->CanGoToOffset(0));
884  EXPECT_TRUE(manager_->CanGoToOffset(-1));
885  EXPECT_TRUE(manager_->CanGoToOffset(1));
886
887  EXPECT_EQ(0, manager_->GetIndexForOffset(-1));
888  EXPECT_EQ(1, manager_->GetIndexForOffset(0));
889  EXPECT_EQ(2, manager_->GetIndexForOffset(1));
890
891  NavigationItemList backward_items = manager_->GetBackwardItems();
892  EXPECT_EQ(1UL, backward_items.size());
893  EXPECT_EQ(url0_, backward_items[0]->GetURL());
894
895  NavigationItemList forward_items = manager_->GetForwardItems();
896  EXPECT_EQ(1UL, forward_items.size());
897  EXPECT_EQ(url2_, forward_items[0]->GetURL());
898}
899
900// Tests that detaching from an empty WKWebView works.
901TEST_F(WKBasedNavigationManagerDetachedModeTest, NothingToCache) {
902  delegate_.RemoveWebView();
903  manager_->DetachFromWebView();
904
905  EXPECT_EQ(0, manager_->GetItemCount());
906  EXPECT_EQ(nullptr, manager_->GetVisibleItem());
907  EXPECT_EQ(nullptr, manager_->GetItemAtIndex(0));
908  EXPECT_EQ(nullptr, manager_->GetPendingItem());
909  EXPECT_EQ(-1, manager_->GetLastCommittedItemIndex());
910
911  manager_->Reload(web::ReloadType::NORMAL, false /* check_for_repost */);
912  EXPECT_EQ(nullptr, manager_->GetPendingItem());
913}
914
915// Tests that Reload from detached mode restores cached history.
916TEST_F(WKBasedNavigationManagerDetachedModeTest, Reload) {
917  manager_->DetachFromWebView();
918  delegate_.RemoveWebView();
919
920  manager_->Reload(web::ReloadType::NORMAL, false /* check_for_repost */);
921  NavigationItem* pending_item =
922      manager_->GetPendingItemInCurrentOrRestoredSession();
923  EXPECT_EQ(
924      "{\"offset\":-1,\"titles\":[\"\",\"\",\"\"],\"urls\":[\"http://www.0.com/"
925      "\",\"http://www.1.com/\",\"http://www.2.com/\"]}",
926      ExtractRestoredSession(pending_item->GetURL()));
927
928  EXPECT_EQ(url0_, pending_item->GetVirtualURL());
929  EXPECT_EQ(url1_, manager_->GetVisibleItem()->GetURL());
930
931  histogram_tester_.ExpectTotalCount(kRestoreNavigationItemCount, 1);
932  histogram_tester_.ExpectBucketCount(kRestoreNavigationItemCount, 3, 1);
933}
934
935// Tests that GoToIndex from detached mode restores cached history with updated
936// current item offset.
937TEST_F(WKBasedNavigationManagerDetachedModeTest, GoToIndex) {
938  manager_->DetachFromWebView();
939  delegate_.RemoveWebView();
940
941  manager_->GoToIndex(0);
942  NavigationItem* pending_item =
943      manager_->GetPendingItemInCurrentOrRestoredSession();
944
945  EXPECT_EQ(
946      "{\"offset\":-2,\"titles\":[\"\",\"\",\"\"],\"urls\":[\"http://www.0.com/"
947      "\",\"http://www.1.com/\",\"http://www.2.com/\"]}",
948      ExtractRestoredSession(pending_item->GetURL()));
949  EXPECT_EQ(url0_, pending_item->GetVirtualURL());
950  EXPECT_EQ(url0_, manager_->GetVisibleItem()->GetURL());
951
952  histogram_tester_.ExpectTotalCount(kRestoreNavigationItemCount, 1);
953  histogram_tester_.ExpectBucketCount(kRestoreNavigationItemCount, 3, 1);
954}
955
956// Tests that LoadIfNecessary from detached mode restores cached history.
957TEST_F(WKBasedNavigationManagerDetachedModeTest, LoadIfNecessary) {
958  manager_->DetachFromWebView();
959  delegate_.RemoveWebView();
960
961  manager_->LoadIfNecessary();
962  NavigationItem* pending_item =
963      manager_->GetPendingItemInCurrentOrRestoredSession();
964
965  EXPECT_EQ(
966      "{\"offset\":-1,\"titles\":[\"\",\"\",\"\"],\"urls\":[\"http://www.0.com/"
967      "\",\"http://www.1.com/\",\"http://www.2.com/\"]}",
968      ExtractRestoredSession(pending_item->GetURL()));
969  EXPECT_EQ(url0_, pending_item->GetVirtualURL());
970  EXPECT_EQ(url1_, manager_->GetVisibleItem()->GetURL());
971
972  histogram_tester_.ExpectTotalCount(kRestoreNavigationItemCount, 1);
973  histogram_tester_.ExpectBucketCount(kRestoreNavigationItemCount, 3, 1);
974}
975
976// Tests that LoadURLWithParams from detached mode restores backward history and
977// adds the new item at the end.
978TEST_F(WKBasedNavigationManagerDetachedModeTest, LoadURLWithParams) {
979  manager_->DetachFromWebView();
980  delegate_.RemoveWebView();
981
982  GURL url("http://www.3.com");
983  NavigationManager::WebLoadParams params(url);
984  manager_->LoadURLWithParams(params);
985  NavigationItem* pending_item =
986      manager_->GetPendingItemInCurrentOrRestoredSession();
987  EXPECT_EQ(
988      "{\"offset\":0,\"titles\":[\"\",\"\",\"\"],\"urls\":[\"http://www.0.com/"
989      "\",\"http://www.1.com/\",\"http://www.3.com/\"]}",
990      ExtractRestoredSession(pending_item->GetURL()));
991  EXPECT_EQ(url0_, pending_item->GetVirtualURL());
992  EXPECT_EQ(url, manager_->GetVisibleItem()->GetURL());
993
994  histogram_tester_.ExpectTotalCount(kRestoreNavigationItemCount, 1);
995  histogram_tester_.ExpectBucketCount(kRestoreNavigationItemCount, 3, 1);
996}
997
998// Tests that detaching placeholder urls are cleaned before being cached.
999TEST_F(WKBasedNavigationManagerDetachedModeTest, CachedPlaceholders) {
1000  [mock_wk_list_ setCurrentURL:CreateRedirectUrlForWKList(url1_)
1001                  backListURLs:@[ CreateRedirectUrlForWKList(url0_) ]
1002               forwardListURLs:@[ CreateRedirectUrlForWKList(url2_) ]];
1003  manager_->DetachFromWebView();
1004
1005  EXPECT_EQ(url0_, manager_->GetNavigationItemImplAtIndex(0)->GetURL());
1006  EXPECT_EQ(url1_, manager_->GetNavigationItemImplAtIndex(1)->GetURL());
1007  EXPECT_EQ(url2_, manager_->GetNavigationItemImplAtIndex(2)->GetURL());
1008}
1009
1010// Tests that pending item is set to serializable when appropriate.
1011TEST_F(WKBasedNavigationManagerDetachedModeTest, NotSerializable) {
1012  manager_->AddPendingItem(GURL("http://www.0.com"), Referrer(),
1013                           ui::PAGE_TRANSITION_TYPED,
1014                           web::NavigationInitiationType::BROWSER_INITIATED);
1015  EXPECT_FALSE(manager_->GetPendingItemInCurrentOrRestoredSession()
1016                   ->ShouldSkipSerialization());
1017
1018  manager_->SetWKWebViewNextPendingUrlNotSerializable(GURL("http://www.1.com"));
1019  manager_->AddPendingItem(GURL("http://www.1.com"), Referrer(),
1020                           ui::PAGE_TRANSITION_TYPED,
1021                           web::NavigationInitiationType::BROWSER_INITIATED);
1022  EXPECT_TRUE(manager_->GetPendingItemInCurrentOrRestoredSession()
1023                  ->ShouldSkipSerialization());
1024
1025  manager_->AddPendingItem(GURL("http://www.1.com"), Referrer(),
1026                           ui::PAGE_TRANSITION_TYPED,
1027                           web::NavigationInitiationType::BROWSER_INITIATED);
1028  EXPECT_FALSE(manager_->GetPendingItemInCurrentOrRestoredSession()
1029                   ->ShouldSkipSerialization());
1030}
1031
1032}  // namespace web
1033