Commit ff9f977e authored by Mike Dougherty's avatar Mike Dougherty Committed by Commit Bot

[iOS] Allow navigation between app specific URL pages

Messaging from chrome url pages was broken when the page was loaded by
navigating from chrome://chrome-urls because the webUI object was being
cleared in
|CRWWKNavigationHandler webView:didStartProvisionalNavigation|.

Navigating to an app specific URL is only allowed in certain
situations, one of which is when the page is loaded by clicking a link
on another app specific URL. So, update the logic to handle this
scenario.

Bug: 1140780
Change-Id: I9fbdca65487ce7c3e287922f1c98fa11da1c2d0a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2518192
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Reviewed-by: default avatarJustin Cohen <justincohen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#826898}
parent ae84ee84
...@@ -574,6 +574,7 @@ test("ios_web_inttests") { ...@@ -574,6 +574,7 @@ test("ios_web_inttests") {
"web_state/http_auth_inttest.mm", "web_state/http_auth_inttest.mm",
"web_state/keep_render_process_alive_inttest.mm", "web_state/keep_render_process_alive_inttest.mm",
"web_state/web_state_observer_inttest.mm", "web_state/web_state_observer_inttest.mm",
"webui/web_ui_inttest.mm",
"webui/web_ui_mojo_inttest.mm", "webui/web_ui_mojo_inttest.mm",
] ]
......
...@@ -642,11 +642,13 @@ void ReportOutOfSyncURLInDidStartProvisionalNavigation( ...@@ -642,11 +642,13 @@ void ReportOutOfSyncURLInDidStartProvisionalNavigation(
// should be registered. // should be registered.
// When using WKBasedNavigationManager, renderer-initiated app-specific loads // When using WKBasedNavigationManager, renderer-initiated app-specific loads
// should be allowed in two specific cases: // should only be allowed in these specific cases:
// 1) if |backForwardList.currentItem| is a placeholder URL for the // 1) if |backForwardList.currentItem| is a placeholder URL for the
// provisional load URL (i.e. webView.URL), then this is an in-progress // provisional load URL (i.e. webView.URL), then this is an in-progress
// app-specific load and should not be restarted. // app-specific load and should not be restarted.
// 2) back/forward navigation to an app-specific URL should be allowed. // 2) back/forward navigation to an app-specific URL should be allowed.
// 3) navigation to an app-specific URL should be allowed from other
// app-specific URLs
bool exemptedAppSpecificLoad = false; bool exemptedAppSpecificLoad = false;
bool currentItemIsPlaceholder = bool currentItemIsPlaceholder =
!base::FeatureList::IsEnabled(web::features::kUseJSForErrorPage) && !base::FeatureList::IsEnabled(web::features::kUseJSForErrorPage) &&
...@@ -655,8 +657,8 @@ void ReportOutOfSyncURLInDidStartProvisionalNavigation( ...@@ -655,8 +657,8 @@ void ReportOutOfSyncURLInDidStartProvisionalNavigation(
bool isBackForward = bool isBackForward =
self.pendingNavigationInfo.navigationType == WKNavigationTypeBackForward; self.pendingNavigationInfo.navigationType == WKNavigationTypeBackForward;
bool isRestoringSession = IsRestoreSessionUrl(self.documentURL); bool isRestoringSession = IsRestoreSessionUrl(self.documentURL);
exemptedAppSpecificLoad = exemptedAppSpecificLoad = currentItemIsPlaceholder || isBackForward ||
currentItemIsPlaceholder || isBackForward || isRestoringSession; isRestoringSession || self.webStateImpl->HasWebUI();
if (!web::GetWebClient()->IsAppSpecificURL(webViewURL) || if (!web::GetWebClient()->IsAppSpecificURL(webViewURL) ||
!exemptedAppSpecificLoad) { !exemptedAppSpecificLoad) {
......
<!doctype html>
<html>
<body>
WebUI page
<a id="link" href="testwebui://testwebui2">Link to WebUI</a>
</body>
</html>
<!doctype html>
<html>
<body>
WebUI page 2
</body>
</html>
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
<include name="IDR_MOJO_TEST_HTML" file="data/mojo_test.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> <include name="IDR_MOJO_TEST_HTML" file="data/mojo_test.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
<include name="IDR_MOJO_TEST_JS" file="data/mojo_test.js" type="BINDATA" compress="gzip" /> <include name="IDR_MOJO_TEST_JS" file="data/mojo_test.js" type="BINDATA" compress="gzip" />
<include name="IDR_MOJO_TEST_MOJO_JS" file="${root_gen_dir}/ios/web/test/mojo_test.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" /> <include name="IDR_MOJO_TEST_MOJO_JS" file="${root_gen_dir}/ios/web/test/mojo_test.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
<include name="IDR_WEBUI_TEST_HTML" file="data/webui_test.html" flattenhtml="true" type="BINDATA" compress="gzip" />
<include name="IDR_WEBUI_TEST_HTML_2" file="data/webui_test_2.html" flattenhtml="true" type="BINDATA" compress="gzip" />
</includes> </includes>
</release> </release>
</grit> </grit>
// Copyright 2020 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 <memory>
#include <string>
#include "base/bind.h"
#include "base/run_loop.h"
#import "base/test/ios/wait_util.h"
#include "ios/web/grit/ios_web_resources.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/test/navigation_test_util.h"
#import "ios/web/public/test/web_test_with_web_state.h"
#import "ios/web/public/test/web_view_content_test_util.h"
#import "ios/web/public/test/web_view_interaction_test_util.h"
#include "ios/web/public/webui/web_ui_ios_controller.h"
#include "ios/web/public/webui/web_ui_ios_controller_factory.h"
#include "ios/web/public/webui/web_ui_ios_data_source.h"
#include "ios/web/test/grit/test_resources.h"
#include "ios/web/test/test_url_constants.h"
#import "ios/web/web_state/web_state_impl.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using base::test::ios::kWaitForPageLoadTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;
using web::test::TapWebViewElementWithId;
using web::test::WaitForWebViewContainingText;
namespace web {
namespace {
// Hostname for test WebUI page.
const char kTestWebUIURLHost[] = "testwebui";
const char kTestWebUIURLHost2[] = "testwebui2";
// The element id of the link to load a webUI page.
const char kWebUIPageLinkID[] = "link";
// Text present on the sample WebUI page.
const char kWebUIPageText[] = "WebUI page";
// Controller for test WebUI.
class TestUI : public WebUIIOSController {
public:
// Constructs controller from |web_ui| and |ui_handler| which will communicate
// with test WebUI page.
TestUI(WebUIIOS* web_ui, const std::string& host, int resource_id)
: WebUIIOSController(web_ui, host) {
web::WebUIIOSDataSource* source =
web::WebUIIOSDataSource::Create(kTestWebUIURLHost);
source->SetDefaultResource(resource_id);
web::WebState* web_state = web_ui->GetWebState();
web::WebUIIOSDataSource::Add(web_state->GetBrowserState(), source);
}
~TestUI() override = default;
};
// Factory that creates TestUI controller.
class TestWebUIControllerFactory : public WebUIIOSControllerFactory {
public:
// Constructs a controller factory.
TestWebUIControllerFactory() {}
// WebUIIOSControllerFactory overrides.
std::unique_ptr<WebUIIOSController> CreateWebUIIOSControllerForURL(
WebUIIOS* web_ui,
const GURL& url) const override {
if (!url.SchemeIs(kTestWebUIScheme))
return nullptr;
if (url.host() == kTestWebUIURLHost) {
return std::make_unique<TestUI>(web_ui, url.host(), IDR_WEBUI_TEST_HTML);
}
DCHECK_EQ(url.host(), kTestWebUIURLHost2);
return std::make_unique<TestUI>(web_ui, url.host(), IDR_WEBUI_TEST_HTML_2);
}
NSInteger GetErrorCodeForWebUIURL(const GURL& url) const override {
if (url.SchemeIs(kTestWebUIScheme))
return 0;
return NSURLErrorUnsupportedURL;
}
};
} // namespace
// A test fixture for verifying WebUI.
class WebUITest : public WebTestWithWebState {
protected:
WebUITest() : WebTestWithWebState() {}
void SetUp() override {
WebTestWithWebState::SetUp();
factory_ = std::make_unique<TestWebUIControllerFactory>();
WebUIIOSControllerFactory::RegisterFactory(factory_.get());
url::SchemeHostPort tuple(kTestWebUIScheme, kTestWebUIURLHost, 0);
GURL url(tuple.Serialize());
test::LoadUrl(web_state(), url);
// LoadIfNecessary is needed because the view is not created (but needed)
// when loading the page. TODO(crbug.com/705819): Remove this call.
web_state()->GetNavigationManager()->LoadIfNecessary();
ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{
base::RunLoop().RunUntilIdle();
return !web_state()->IsLoading();
}));
ASSERT_EQ(url.spec(), BaseUrl());
}
void TearDown() override {
WebUIIOSControllerFactory::DeregisterFactory(factory_.get());
WebTestWithWebState::TearDown();
}
private:
std::unique_ptr<TestWebUIControllerFactory> factory_;
};
// Tests that a web UI page is loaded and that the WebState correctly reports
// |WebStateImpl::HasWebUI|.
TEST_F(WebUITest, LoadWebUIPage) {
ASSERT_TRUE(static_cast<WebStateImpl*>(web_state())->HasWebUI());
EXPECT_TRUE(WaitForWebViewContainingText(web_state(), kWebUIPageText));
}
// Tests that a web UI page is correctly loaded when navigated to from another
// webUI page.
TEST_F(WebUITest, LoadWebUIPageLoadViaLinkClick) {
ASSERT_TRUE(TapWebViewElementWithId(web_state(), kWebUIPageLinkID));
url::SchemeHostPort tuple(kTestWebUIScheme, kTestWebUIURLHost2, 0);
GURL url(tuple.Serialize());
ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{
base::RunLoop().RunUntilIdle();
return !web_state()->IsLoading() && url.spec() == BaseUrl();
}));
ASSERT_TRUE(static_cast<WebStateImpl*>(web_state())->HasWebUI());
}
} // namespace web
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