blob: ed3c374539884af3d0f1c60981daf0b09accd403 [file] [log] [blame]
// Copyright 2014 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 "base/bind.h"
#include "base/strings/stringprintf.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
class NavigationControllerBrowserTest : public ContentBrowserTest {
protected:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
}
};
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, LoadDataWithBaseURL) {
const GURL base_url("http://baseurl");
const GURL history_url("http://historyurl");
const std::string data = "<html><body>foo</body></html>";
const NavigationController& controller =
shell()->web_contents()->GetController();
// Load data. Blocks until it is done.
content::LoadDataWithBaseURL(shell(), history_url, data, base_url);
// We should use history_url instead of the base_url as the original url of
// this navigation entry, because base_url is only used for resolving relative
// paths in the data, or enforcing same origin policy.
EXPECT_EQ(controller.GetVisibleEntry()->GetOriginalRequestURL(), history_url);
}
// The renderer uses the position in the history list as a clue to whether a
// navigation is stale. In the case where the entry limit is reached and the
// history list is pruned, make sure that there is no mismatch that would cause
// it to start incorrectly rejecting navigations as stale. See
// http://crbug.com/89798.
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
DontIgnoreBackAfterNavEntryLimit) {
NavigationController& controller =
shell()->web_contents()->GetController();
const int kMaxEntryCount =
static_cast<int>(NavigationControllerImpl::max_entry_count());
// Load up to the max count, all entries should be there.
for (int url_index = 0; url_index < kMaxEntryCount; ++url_index) {
GURL url(base::StringPrintf("data:text/html,page%d", url_index));
EXPECT_TRUE(NavigateToURL(shell(), url));
}
EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
// Navigate twice more more.
for (int url_index = kMaxEntryCount;
url_index < kMaxEntryCount + 2; ++url_index) {
GURL url(base::StringPrintf("data:text/html,page%d", url_index));
EXPECT_TRUE(NavigateToURL(shell(), url));
}
// We expect http://www.a.com/0 and /1 to be gone.
EXPECT_EQ(kMaxEntryCount, controller.GetEntryCount());
EXPECT_EQ(GURL("data:text/html,page2"),
controller.GetEntryAtIndex(0)->GetURL());
// Now try to go back. This should not hang.
ASSERT_TRUE(controller.CanGoBack());
controller.GoBack();
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
// This should have successfully gone back.
EXPECT_EQ(GURL(base::StringPrintf("data:text/html,page%d", kMaxEntryCount)),
controller.GetLastCommittedEntry()->GetURL());
}
namespace {
int RendererHistoryLength(Shell* shell) {
int value = 0;
EXPECT_TRUE(ExecuteScriptAndExtractInt(
shell->web_contents(),
"domAutomationController.send(history.length)",
&value));
return value;
}
// Similar to the ones from content_browser_test_utils.
bool NavigateToURLAndReplace(Shell* shell, const GURL& url) {
WebContents* web_contents = shell->web_contents();
WaitForLoadStop(web_contents);
TestNavigationObserver same_tab_observer(web_contents, 1);
NavigationController::LoadURLParams params(url);
params.should_replace_current_entry = true;
web_contents->GetController().LoadURLWithParams(params);
web_contents->Focus();
same_tab_observer.Wait();
if (!IsLastCommittedEntryOfPageType(web_contents, PAGE_TYPE_NORMAL))
return false;
return web_contents->GetLastCommittedURL() == url;
}
} // namespace
// When loading a new page to replace an old page in the history list, make sure
// that the browser and renderer agree, and that both get it right.
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
CorrectLengthWithCurrentItemReplacement) {
NavigationController& controller =
shell()->web_contents()->GetController();
EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page1")));
EXPECT_EQ(1, controller.GetEntryCount());
EXPECT_EQ(1, RendererHistoryLength(shell()));
EXPECT_TRUE(NavigateToURLAndReplace(shell(), GURL("data:text/html,page2")));
EXPECT_EQ(1, controller.GetEntryCount());
EXPECT_EQ(1, RendererHistoryLength(shell()));
// Note that there's no way to access the renderer's notion of the history
// offset via JavaScript. Checking just the history length, though, is enough;
// if the replacement failed, there would be a new history entry and thus an
// incorrect length.
}
namespace {
struct FrameNavigateParamsCapturer : public WebContentsObserver {
public:
explicit FrameNavigateParamsCapturer(FrameTreeNode* node)
: WebContentsObserver(
node->current_frame_host()->delegate()->GetAsWebContents()),
frame_tree_node_id_(node->frame_tree_node_id()),
message_loop_runner_(new MessageLoopRunner) {}
void Wait() {
message_loop_runner_->Run();
}
const FrameNavigateParams& params() const {
return params_;
}
const LoadCommittedDetails& details() const {
return details_;
}
private:
void DidNavigateAnyFrame(RenderFrameHost* render_frame_host,
const LoadCommittedDetails& details,
const FrameNavigateParams& params) override {
RenderFrameHostImpl* rfh =
static_cast<RenderFrameHostImpl*>(render_frame_host);
if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
return;
params_ = params;
details_ = details;
message_loop_runner_->Quit();
}
// The id of the FrameTreeNode whose navigations to observe.
int frame_tree_node_id_;
// The params of the last navigation.
FrameNavigateParams params_;
// The details of the last navigation.
LoadCommittedDetails details_;
// The MessageLoopRunner used to spin the message loop.
scoped_refptr<MessageLoopRunner> message_loop_runner_;
};
} // namespace
// Verify that the distinction between manual and auto subframes is properly set
// for subframe navigations. TODO(avi): It's rather bogus that the same info is
// in two different enums; http://crbug.com/453555.
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
ManualAndAutoSubframeNavigationClassification) {
GURL main_url(
embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
NavigateToURL(shell(), main_url);
// It is safe to obtain the root frame tree node here, as it doesn't change.
FrameTreeNode* root =
static_cast<WebContentsImpl*>(shell()->web_contents())->
GetFrameTree()->root();
ASSERT_EQ(1U, root->child_count());
ASSERT_NE(nullptr, root->child_at(0));
{
// Navigate the iframe to a new URL; expect a manual subframe transition.
FrameNavigateParamsCapturer capturer(root->child_at(0));
GURL frame_url(
embedded_test_server()->GetURL("/frame_tree/2-1.html"));
NavigateFrameToURL(root->child_at(0), frame_url);
capturer.Wait();
EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
capturer.params().transition);
EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
}
{
// History navigations should result in an auto subframe transition.
FrameNavigateParamsCapturer capturer(root->child_at(0));
shell()->web_contents()->GetController().GoBack();
capturer.Wait();
EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
}
{
// History navigations should result in an auto subframe transition.
FrameNavigateParamsCapturer capturer(root->child_at(0));
shell()->web_contents()->GetController().GoForward();
capturer.Wait();
EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
}
{
// Navigate the iframe to a new URL; expect a manual subframe transition.
FrameNavigateParamsCapturer capturer(root->child_at(0));
GURL frame_url(
embedded_test_server()->GetURL("/frame_tree/2-3.html"));
NavigateFrameToURL(root->child_at(0), frame_url);
capturer.Wait();
EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
capturer.params().transition);
EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
}
}
} // namespace content