// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/webui/crw_web_ui_scheme_handler.h"
#import "base/run_loop.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "ios/web/public/test/web_test.h"
#import "ios/web/public/webui/web_ui_ios_controller.h"
#import "ios/web/public/webui/web_ui_ios_controller_factory.h"
#import "ios/web/test/test_url_constants.h"
#import "net/base/apple/url_conversions.h"
#import "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#import "services/network/test/test_url_loader_factory.h"
#import "third_party/ocmock/OCMock/OCMock.h"
namespace {
const char kOfflineHost[] = "offline";
const char kChromeScheme[] = "chrome";
} // namespace
@interface FakeSchemeTask : NSObject <WKURLSchemeTask>
// Override from the protocol to have it readwrite.
@property(nonatomic, readwrite, copy) NSURLRequest* request;
// Response.
@property(nonatomic, readwrite, copy) NSURLResponse* response;
// The error received.
@property(nonatomic, strong) NSError* error;
@property(nonatomic, assign) BOOL receivedData;
@property(nonatomic, assign) BOOL receivedError;
@end
@implementation FakeSchemeTask
@synthesize request = _request;
- (void)didReceiveResponse:(NSURLResponse*)response {
self.response = response;
}
- (void)didReceiveData:(NSData*)data {
self.receivedData = YES;
}
- (void)didFinish {
}
- (void)didFailWithError:(NSError*)error {
self.receivedError = YES;
self.error = error;
}
#pragma mark - test utils
- (BOOL)responseHasMimetype:(NSString*)mimeType {
if (![self.response isKindOfClass:NSHTTPURLResponse.class])
return NO;
NSHTTPURLResponse* httpResponse =
static_cast<NSHTTPURLResponse*>(self.response);
return
[httpResponse.allHeaderFields[@"Content-Type"] containsString:mimeType];
}
@end
namespace web {
namespace {
class FakeWebUIIOSControllerFactory : public WebUIIOSControllerFactory {
NSInteger GetErrorCodeForWebUIURL(const GURL& url) const override {
if (!url.SchemeIs(kTestWebUIScheme) && !url.SchemeIs(kChromeScheme))
return NSURLErrorUnsupportedURL;
if (url.host() == kOfflineHost)
return NSURLErrorNotConnectedToInternet;
return 0;
}
std::unique_ptr<WebUIIOSController> CreateWebUIIOSControllerForURL(
WebUIIOS* web_ui,
const GURL& url) const override {
return nullptr;
}
};
} // namespace
// Test fixture for testing CRWWebUISchemeManager.
class CRWWebUISchemeManagerTest : public WebTest {
public:
CRWWebUISchemeManagerTest()
: test_shared_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)) {
WebUIIOSControllerFactory::RegisterFactory(&factory_);
}
~CRWWebUISchemeManagerTest() override {
WebUIIOSControllerFactory::DeregisterFactory(&factory_);
}
protected:
CRWWebUISchemeHandler* CreateSchemeHandler() {
return [[CRWWebUISchemeHandler alloc]
initWithURLLoaderFactory:GetSharedURLLoaderFactory()];
}
NSURL* GetWebUIURL() {
NSString* url = [base::SysUTF8ToNSString(kTestWebUIScheme)
stringByAppendingString:@"://testpage"];
return [NSURL URLWithString:url];
}
scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory() {
return test_shared_loader_factory_;
}
network::TestURLLoaderFactory* GetURLLoaderFactory() {
return &test_url_loader_factory_;
}
void RespondWithData(const GURL& url, const std::string& data) {
GetURLLoaderFactory()->AddResponse(url.spec(), data);
base::RunLoop().RunUntilIdle();
}
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
FakeWebUIIOSControllerFactory factory_;
};
// Tests that calling start on the scheme handler returns some data when the URL
// is a WebUI URL.
TEST_F(CRWWebUISchemeManagerTest, StartTaskWithCorrectURL) {
CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
id web_view = OCMClassMock([WKWebView class]);
FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
NSMutableURLRequest* request =
[NSMutableURLRequest requestWithURL:GetWebUIURL()];
request.mainDocumentURL = GetWebUIURL();
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_TRUE(url_scheme_task.receivedData);
EXPECT_FALSE(url_scheme_task.receivedError);
}
// Tests that the error returned is the same as the error from the factory.
TEST_F(CRWWebUISchemeManagerTest, ErrorReceived) {
CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
id web_view = OCMClassMock([WKWebView class]);
FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
NSURLComponents* components_url = [[NSURLComponents alloc] init];
components_url.scheme = base::SysUTF8ToNSString(kTestWebUIScheme);
components_url.host = base::SysUTF8ToNSString(kOfflineHost);
NSMutableURLRequest* request =
[NSMutableURLRequest requestWithURL:components_url.URL];
request.mainDocumentURL = request.URL;
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_FALSE(url_scheme_task.receivedData);
EXPECT_TRUE(url_scheme_task.receivedError);
EXPECT_EQ(NSURLErrorNotConnectedToInternet, url_scheme_task.error.code);
// Check with a different URL.
request.mainDocumentURL = [NSURL URLWithString:@"invalidScheme://page"];
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_FALSE(url_scheme_task.receivedData);
EXPECT_TRUE(url_scheme_task.receivedError);
EXPECT_EQ(NSURLErrorUnsupportedURL, url_scheme_task.error.code);
}
// Tests that calling start on the scheme handler returns some data when the URL
// is *not* a WebUI URL but the main document URL is.
TEST_F(CRWWebUISchemeManagerTest, StartTaskWithCorrectMainURL) {
CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
id web_view = OCMClassMock([WKWebView class]);
FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
NSMutableURLRequest* request = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:@"https://notAWebUIURL"]];
request.mainDocumentURL = GetWebUIURL();
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_TRUE(url_scheme_task.receivedData);
EXPECT_FALSE(url_scheme_task.receivedError);
}
// Tests that calling start on the scheme handler returns an error when the URL
// is correct but the mainDocumentURL is wrong.
TEST_F(CRWWebUISchemeManagerTest, StartTaskWithWrongMainDocumentURL) {
CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
id web_view = OCMClassMock([WKWebView class]);
FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
NSMutableURLRequest* request =
[NSMutableURLRequest requestWithURL:GetWebUIURL()];
request.mainDocumentURL = [NSURL URLWithString:@"https://notAWebUIURL"];
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_FALSE(url_scheme_task.receivedData);
EXPECT_TRUE(url_scheme_task.receivedError);
}
// Tests that calling stop right after start prevent the handler from returning
// data.
TEST_F(CRWWebUISchemeManagerTest, StopTask) {
CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
id web_view = OCMClassMock([WKWebView class]);
FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
NSMutableURLRequest* request =
[NSMutableURLRequest requestWithURL:GetWebUIURL()];
request.mainDocumentURL = GetWebUIURL();
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
[scheme_handler webView:web_view stopURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_FALSE(url_scheme_task.receivedData);
EXPECT_FALSE(url_scheme_task.receivedError);
}
// Tests that proper mime-type is returned for a given chrome:// request.
TEST_F(CRWWebUISchemeManagerTest, CheckMimetypeOfChromeScheme) {
CRWWebUISchemeHandler* scheme_handler = CreateSchemeHandler();
id web_view = OCMClassMock([WKWebView class]);
FakeSchemeTask* url_scheme_task = [[FakeSchemeTask alloc] init];
// Check javascript
NSMutableURLRequest* request = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:@"chrome://clown/res/clown.js"]];
request.mainDocumentURL = [NSURL URLWithString:@"chrome://clown/"];
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_TRUE([url_scheme_task responseHasMimetype:@"text/javascript"]);
EXPECT_TRUE(url_scheme_task.receivedData);
EXPECT_FALSE(url_scheme_task.receivedError);
// Check css.
request = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:@"chrome://clown/res/clown.css"]];
request.mainDocumentURL = [NSURL URLWithString:@"chrome://clown/"];
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_TRUE([url_scheme_task responseHasMimetype:@"text/css"]);
EXPECT_TRUE(url_scheme_task.receivedData);
EXPECT_FALSE(url_scheme_task.receivedError);
// Check svg.
request = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:@"chrome://clown/res/clown.svg"]];
request.mainDocumentURL = [NSURL URLWithString:@"chrome://clown/"];
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_TRUE([url_scheme_task responseHasMimetype:@"image/svg+xml"]);
EXPECT_TRUE(url_scheme_task.receivedData);
EXPECT_FALSE(url_scheme_task.receivedError);
// Anything else, is 'html'.
request = [NSMutableURLRequest
requestWithURL:[NSURL
URLWithString:@"chrome://clown/res/clown.anything"]];
request.mainDocumentURL = [NSURL URLWithString:@"chrome://clown/"];
url_scheme_task.request = request;
[scheme_handler webView:web_view startURLSchemeTask:url_scheme_task];
RespondWithData(net::GURLWithNSURL(request.URL), "{}");
EXPECT_TRUE([url_scheme_task responseHasMimetype:@"text/html"]);
EXPECT_TRUE(url_scheme_task.receivedData);
EXPECT_FALSE(url_scheme_task.receivedError);
}
} // namespace web