blob: 1aa3b7ed137e9c564f8cc7cd66581364b26b5a12 [file] [log] [blame]
// Copyright (c) 2012 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 <map>
#include "base/bind.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/sessions/content/session_tab_helper.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/extension_api_frame_id_map.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_navigation_ui_data.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/manifest_handlers/background_info.h"
namespace extensions {
namespace {
content::WebContents* GetActiveWebContents(const Browser* browser) {
return browser->tab_strip_model()->GetActiveWebContents();
}
// Saves ExtensionNavigationUIData for each render frame which completes
// navigation.
class ExtensionNavigationUIDataObserver : public content::WebContentsObserver {
public:
explicit ExtensionNavigationUIDataObserver(content::WebContents* web_contents)
: WebContentsObserver(web_contents) {}
const ExtensionNavigationUIData* GetExtensionNavigationUIData(
content::RenderFrameHost* rfh) const {
auto iter = navigation_ui_data_map_.find(rfh);
if (iter == navigation_ui_data_map_.end())
return nullptr;
return iter->second.get();
}
private:
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override {
if (!navigation_handle->HasCommitted())
return;
content::RenderFrameHost* rfh = navigation_handle->GetRenderFrameHost();
const auto* data = static_cast<const ChromeNavigationUIData*>(
navigation_handle->GetNavigationUIData());
navigation_ui_data_map_[rfh] =
data->GetExtensionNavigationUIData()->DeepCopy();
}
std::map<content::RenderFrameHost*,
std::unique_ptr<ExtensionNavigationUIData>>
navigation_ui_data_map_;
DISALLOW_COPY_AND_ASSIGN(ExtensionNavigationUIDataObserver);
};
} // namespace
// Tests that we can load extension pages into the tab area and they can call
// extension APIs.
IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WebContents) {
ASSERT_TRUE(LoadExtension(
test_data_dir_.AppendASCII("good").AppendASCII("Extensions")
.AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
.AppendASCII("1.0.0.0")));
ui_test_utils::NavigateToURL(
browser(),
GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html"));
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
GetActiveWebContents(browser()), "testTabsAPI()", &result));
EXPECT_TRUE(result);
// There was a bug where we would crash if we navigated to a page in the same
// extension because no new render view was getting created, so we would not
// do some setup.
ui_test_utils::NavigateToURL(
browser(),
GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html"));
result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
GetActiveWebContents(browser()), "testTabsAPI()", &result));
EXPECT_TRUE(result);
}
// Ensure that platform app frames can't be loaded in a tab even on a redirect.
// Regression test for crbug.com/1110551.
IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TabNavigationToPlatformApp) {
ASSERT_TRUE(embedded_test_server()->Start());
const Extension* extension = LoadExtension(
test_data_dir_.AppendASCII("platform_apps").AppendASCII("minimal"));
ASSERT_TRUE(extension);
const GURL test_cases[] = {extension->GetResourceURL("main.html"),
BackgroundInfo::GetBackgroundURL(extension)};
for (const GURL& app_url : test_cases) {
GURL redirect_to_platform_app =
embedded_test_server()->GetURL("/server-redirect?" + app_url.spec());
content::WebContents* web_contents = GetActiveWebContents(browser());
content::TestNavigationObserver observer(web_contents,
net::ERR_BLOCKED_BY_CLIENT);
ui_test_utils::NavigateToURL(browser(), redirect_to_platform_app);
observer.Wait();
EXPECT_FALSE(observer.last_navigation_succeeded());
}
}
// Ensure that the extension's background page can't be navigated away.
// Regression test for crbug.com/1130083.
IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, BackgroundPageNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
const Extension* extension = LoadExtension(
test_data_dir_.AppendASCII("common").AppendASCII("background_page"));
ASSERT_TRUE(extension);
ExtensionHost* host =
ProcessManager::Get(profile())->GetBackgroundHostForExtension(
extension->id());
ASSERT_TRUE(host);
content::WebContents* background_contents = host->web_contents();
// Navigation to a different url should be disallowed.
{
GURL target_url = embedded_test_server()->GetURL("/body1.html");
content::TestNavigationManager navigation_observer(background_contents,
target_url);
constexpr char kScript[] = "window.location.href = '%s'";
ASSERT_TRUE(ExecuteScriptInBackgroundPageNoWait(
extension->id(),
base::StringPrintf(kScript, target_url.spec().c_str())));
navigation_observer.WaitForNavigationFinished();
EXPECT_FALSE(navigation_observer.was_committed());
EXPECT_EQ(extension->GetResourceURL("background.html"),
background_contents->GetLastCommittedURL());
}
// A same-document navigation is still permitted.
{
GURL target_url = extension->GetResourceURL("background.html#fragment");
content::TestNavigationManager navigation_observer(background_contents,
target_url);
constexpr char kScript[] = "window.location.href = '%s'";
ASSERT_TRUE(ExecuteScriptInBackgroundPageNoWait(
extension->id(),
base::StringPrintf(kScript, target_url.spec().c_str())));
navigation_observer.WaitForNavigationFinished();
EXPECT_TRUE(navigation_observer.was_committed());
EXPECT_EQ(target_url, background_contents->GetLastCommittedURL());
}
// Another same-document navigation case.
{
GURL target_url = extension->GetResourceURL("bar.html");
content::TestNavigationManager navigation_observer(background_contents,
target_url);
constexpr char kScript[] = "history.pushState({}, '', '%s')";
ASSERT_TRUE(ExecuteScriptInBackgroundPageNoWait(
extension->id(),
base::StringPrintf(kScript, target_url.spec().c_str())));
navigation_observer.WaitForNavigationFinished();
EXPECT_TRUE(navigation_observer.was_committed());
EXPECT_EQ(target_url, background_contents->GetLastCommittedURL());
}
}
// Test that we correctly set up the ExtensionNavigationUIData for each
// navigation.
IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ExtensionNavigationUIData) {
ASSERT_TRUE(embedded_test_server()->Start());
content::WebContents* web_contents = GetActiveWebContents(browser());
ExtensionNavigationUIDataObserver observer(web_contents);
// Load a page with an iframe.
const GURL url = embedded_test_server()->GetURL("/iframe.html");
ui_test_utils::NavigateToURL(browser(), url);
sessions::SessionTabHelper* session_tab_helper =
sessions::SessionTabHelper::FromWebContents(web_contents);
ASSERT_TRUE(session_tab_helper);
int expected_tab_id = session_tab_helper->session_id().id();
int expected_window_id = session_tab_helper->window_id().id();
// Test ExtensionNavigationUIData for the main frame.
{
const auto* extension_navigation_ui_data =
observer.GetExtensionNavigationUIData(web_contents->GetMainFrame());
ASSERT_TRUE(extension_navigation_ui_data);
EXPECT_FALSE(extension_navigation_ui_data->is_web_view());
ExtensionApiFrameIdMap::FrameData frame_data =
extension_navigation_ui_data->frame_data();
EXPECT_EQ(ExtensionApiFrameIdMap::kTopFrameId, frame_data.frame_id);
EXPECT_EQ(ExtensionApiFrameIdMap::kInvalidFrameId,
frame_data.parent_frame_id);
EXPECT_EQ(expected_tab_id, frame_data.tab_id);
EXPECT_EQ(expected_window_id, frame_data.window_id);
}
// Test ExtensionNavigationUIData for the sub-frame.
{
const auto* extension_navigation_ui_data =
observer.GetExtensionNavigationUIData(
content::ChildFrameAt(web_contents->GetMainFrame(), 0));
ASSERT_TRUE(extension_navigation_ui_data);
EXPECT_FALSE(extension_navigation_ui_data->is_web_view());
ExtensionApiFrameIdMap::FrameData frame_data =
extension_navigation_ui_data->frame_data();
EXPECT_NE(ExtensionApiFrameIdMap::kInvalidFrameId, frame_data.frame_id);
EXPECT_NE(ExtensionApiFrameIdMap::kTopFrameId, frame_data.frame_id);
EXPECT_EQ(ExtensionApiFrameIdMap::kTopFrameId, frame_data.parent_frame_id);
EXPECT_EQ(expected_tab_id, frame_data.tab_id);
EXPECT_EQ(expected_window_id, frame_data.window_id);
}
}
} // namespace extensions