Commit 9ff677af authored by Olivier Robin's avatar Olivier Robin Committed by Chromium LUCI CQ

QR code scanner: ignore non HTTP(s) URLs

If the result is a valid URL with a not HTTP(s) scheme, add
quotes so it triggers a search instead of a load.

Bug: 1153445
Change-Id: I5d9d79feafc6d2df756da5a9d35c434ce22393f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2611735
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Reviewed-by: default avatarStepan Khapugin <stkhapugin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841011}
parent 61d434d1
...@@ -90,10 +90,12 @@ source_set("eg_app_support+eg2") { ...@@ -90,10 +90,12 @@ source_set("eg_app_support+eg2") {
deps = [ deps = [
":qr_scanner", ":qr_scanner",
"//base", "//base",
"//components/search_engines",
"//components/version_info", "//components/version_info",
"//ios/chrome/app:app_internal", "//ios/chrome/app:app_internal",
"//ios/chrome/app/strings", "//ios/chrome/app/strings",
"//ios/chrome/browser/main", "//ios/chrome/browser/main",
"//ios/chrome/browser/search_engines",
"//ios/chrome/browser/ui/icons", "//ios/chrome/browser/ui/icons",
"//ios/chrome/browser/ui/location_bar", "//ios/chrome/browser/ui/location_bar",
"//ios/chrome/browser/ui/scanner", "//ios/chrome/browser/ui/scanner",
......
...@@ -30,13 +30,13 @@ ...@@ -30,13 +30,13 @@
// should not be called directly. // should not be called directly.
+ (id)cameraControllerSwizzleBlockWithMock:(id)cameraControllerMock; + (id)cameraControllerSwizzleBlockWithMock:(id)cameraControllerMock;
// Returns the block to use for swizzling the LocationBarCoordinator #pragma mark Search engine override
// loadGURLFromLocationBarBlock:transition: method to load |searchURL| instead
// of the generated search URL. // Overrides the default search engine with the |templateURL|.
// This block is only used for swizzling, which is why its type is opaque. It + (void)overrideSearchEngine:(NSString*)templateURL;
// should not be called directly.
+ (id)locationBarCoordinatorLoadGURLFromLocationBarSwizzleBlockForSearchURL: // Restored the Google default search engine.
(NSURL*)searchURL; + (void)resetSearchEngine;
#pragma mark Mocking and Expectations #pragma mark Mocking and Expectations
......
...@@ -7,9 +7,11 @@ ...@@ -7,9 +7,11 @@
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/search_engines/template_url_service.h"
#include "components/version_info/version_info.h" #include "components/version_info/version_info.h"
#import "ios/chrome/app/main_controller.h" #import "ios/chrome/app/main_controller.h"
#include "ios/chrome/browser/main/browser.h" #include "ios/chrome/browser/main/browser.h"
#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
#include "ios/chrome/browser/ui/icons/chrome_icon.h" #include "ios/chrome/browser/ui/icons/chrome_icon.h"
#import "ios/chrome/browser/ui/location_bar/location_bar_coordinator.h" #import "ios/chrome/browser/ui/location_bar/location_bar_coordinator.h"
#import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h" #import "ios/chrome/browser/ui/location_bar/location_bar_url_loader.h"
...@@ -23,6 +25,7 @@ ...@@ -23,6 +25,7 @@
#import "ios/chrome/test/app/chrome_test_util.h" #import "ios/chrome/test/app/chrome_test_util.h"
#import "ios/testing/nserror_util.h" #import "ios/testing/nserror_util.h"
#import "net/base/mac/url_conversions.h" #import "net/base/mac/url_conversions.h"
#include "net/base/mac/url_conversions.h"
#import "third_party/ocmock/OCMock/OCMock.h" #import "third_party/ocmock/OCMock/OCMock.h"
#import "ui/base/l10n/l10n_util.h" #import "ui/base/l10n/l10n_util.h"
#import "ui/base/l10n/l10n_util_mac.h" #import "ui/base/l10n/l10n_util_mac.h"
...@@ -56,20 +59,31 @@ using scanner::CameraState; ...@@ -56,20 +59,31 @@ using scanner::CameraState;
return swizzleCameraControllerBlock; return swizzleCameraControllerBlock;
} }
+ (id)locationBarCoordinatorLoadGURLFromLocationBarSwizzleBlockForSearchURL: #pragma mark Search engine override
(NSURL*)searchURL {
GURL searchGURL = net::GURLWithNSURL(searchURL); + (void)overrideSearchEngine:(NSString*)templateURL {
auto loadGURLFromLocationBarBlock = ^void( TemplateURLData data;
LocationBarCoordinator* self, TemplateURLRef::PostContent* postContent, data.SetShortName(base::ASCIIToUTF16("testSearchEngine"));
const GURL& url, ui::PageTransition transition) { data.SetKeyword(base::ASCIIToUTF16("testSearchEngine"));
web::NavigationManager::WebLoadParams params(searchGURL); GURL searchableURL(base::SysNSStringToUTF8(templateURL));
params.transition_type = transition; data.SetURL(searchableURL.possibly_invalid_spec());
UrlLoadingBrowserAgent::FromBrowser(self.browser) data.favicon_url = TemplateURL::GenerateFaviconURL(searchableURL);
->Load(UrlLoadParams::InCurrentTab(params)); data.last_visited = base::Time::Now();
[self cancelOmniboxEdit];
}; TemplateURLService* service =
ios::TemplateURLServiceFactory::GetForBrowserState(
chrome_test_util::GetOriginalBrowserState());
TemplateURL* url = service->Add(std::make_unique<TemplateURL>(data));
service->SetUserSelectedDefaultSearchProvider(url);
}
+ (void)resetSearchEngine {
TemplateURLService* service =
ios::TemplateURLServiceFactory::GetForBrowserState(
chrome_test_util::GetOriginalBrowserState());
return loadGURLFromLocationBarBlock; TemplateURL* templateURL = service->GetTemplateURLForHost("google.com");
service->SetUserSelectedDefaultSearchProvider(templateURL);
} }
#pragma mark Mocking and Expectations #pragma mark Mocking and Expectations
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h" #include "base/metrics/user_metrics_action.h"
#include "base/strings/sys_string_conversions.h"
#include "ios/chrome/browser/ui/commands/load_query_commands.h" #include "ios/chrome/browser/ui/commands/load_query_commands.h"
#import "ios/chrome/browser/ui/qr_scanner/qr_scanner_camera_controller.h" #import "ios/chrome/browser/ui/qr_scanner/qr_scanner_camera_controller.h"
#include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h" #include "ios/chrome/browser/ui/qr_scanner/qr_scanner_view.h"
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
#include "ios/chrome/browser/ui/scanner/scanner_view.h" #include "ios/chrome/browser/ui/scanner/scanner_view.h"
#include "ios/chrome/grit/ios_strings.h" #include "ios/chrome/grit/ios_strings.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
...@@ -82,6 +84,13 @@ using base::UserMetricsAction; ...@@ -82,6 +84,13 @@ using base::UserMetricsAction;
#pragma mark - QRScannerCameraControllerDelegate #pragma mark - QRScannerCameraControllerDelegate
- (void)receiveQRScannerResult:(NSString*)result loadImmediately:(BOOL)load { - (void)receiveQRScannerResult:(NSString*)result loadImmediately:(BOOL)load {
GURL url = GURL(base::SysNSStringToUTF8(result));
if (url.is_valid() && !url.SchemeIsHTTPOrHTTPS()) {
// Only HTTP(S) URLs are supported.
// For other URLs, add quotes so they are considered as search terms instead
// of URLs.
result = [NSString stringWithFormat:@"\"%@\"", result];
}
if ([self isVoiceOverActive]) { if ([self isVoiceOverActive]) {
// Post a notification announcing that a code was scanned. QR scanner will // Post a notification announcing that a code was scanned. QR scanner will
// be dismissed when the UIAccessibilityAnnouncementDidFinishNotification is // be dismissed when the UIAccessibilityAnnouncementDidFinishNotification is
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "ios/chrome/test/earl_grey/earl_grey_scoped_block_swizzler.h" #include "ios/chrome/test/earl_grey/earl_grey_scoped_block_swizzler.h"
#import "ios/testing/earl_grey/earl_grey_test.h" #import "ios/testing/earl_grey/earl_grey_test.h"
#import "net/base/mac/url_conversions.h" #import "net/base/mac/url_conversions.h"
#include "net/base/url_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h" #include "net/test/embedded_test_server/http_response.h"
...@@ -66,14 +67,18 @@ namespace { ...@@ -66,14 +67,18 @@ namespace {
char kTestURL[] = "/testurl"; char kTestURL[] = "/testurl";
char kTestURLResponse[] = "Test URL page"; char kTestURLResponse[] = "Test URL page";
char kTestQuery[] = "testquery";
char kTestQueryURL[] = "/searchurl/testquery";
char kTestQueryResponse[] = "Test query page";
char kTestURLEdited[] = "/testuredited"; char kTestURLEdited[] = "/testuredited";
char kTestURLEditedResponse[] = "Test URL edited page"; char kTestURLEditedResponse[] = "Test URL edited page";
char kTestQueryEditedURL[] = "/searchurl/testqueredited";
char kTestQueryEditedResponse[] = "Test query edited page"; char kTestQuery[] = "testquery";
char kTestQueryURL[] = "/search";
char kTestQueryURLParams[] = "?q={searchTerms}";
char kTestQueryResponse[] = "Query: testquery";
char kTestQueryEditedResponse[] = "Query: testqueredited";
char kTestDataURL[] = "data:dataURL";
char kTestSanitizedDataURL[] = "\"data:dataURL\"";
char kTestDataURLResponse[] = "Query: \"data:dataURL\"";
// The GREYCondition timeout used for calls to waitWithTimeout:pollInterval:. // The GREYCondition timeout used for calls to waitWithTimeout:pollInterval:.
CFTimeInterval kGREYConditionTimeout = 5; CFTimeInterval kGREYConditionTimeout = 5;
...@@ -168,19 +173,24 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -168,19 +173,24 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
std::make_unique<net::test_server::BasicHttpResponse>(); std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK); http_response->set_code(net::HTTP_OK);
char* body_content = nullptr; const char* body_content = nullptr;
if (base::StartsWith(request.relative_url, kTestURL, if (base::StartsWith(request.relative_url, kTestURL,
base::CompareCase::SENSITIVE)) { base::CompareCase::SENSITIVE)) {
body_content = kTestURLResponse; body_content = kTestURLResponse;
} else if (base::StartsWith(request.relative_url, kTestQueryURL,
base::CompareCase::SENSITIVE)) {
body_content = kTestQueryResponse;
} else if (base::StartsWith(request.relative_url, kTestURLEdited, } else if (base::StartsWith(request.relative_url, kTestURLEdited,
base::CompareCase::SENSITIVE)) { base::CompareCase::SENSITIVE)) {
body_content = kTestURLEditedResponse; body_content = kTestURLEditedResponse;
} else if (base::StartsWith(request.relative_url, kTestQueryEditedURL, } else if (base::StartsWith(request.relative_url, kTestQueryURL,
base::CompareCase::SENSITIVE)) { base::CompareCase::SENSITIVE)) {
body_content = kTestQueryEditedResponse; GURL url = request.GetURL();
std::string query;
bool found = net::GetValueForKeyInQuery(url, "q", &query);
if (found) {
std::string content = "Query: " + query;
body_content = content.c_str();
} else {
body_content = "No query";
}
} else { } else {
return nullptr; return nullptr;
} }
...@@ -201,7 +211,6 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -201,7 +211,6 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
GURL _testURL; GURL _testURL;
GURL _testURLEdited; GURL _testURLEdited;
GURL _testQuery; GURL _testQuery;
GURL _testQueryEdited;
} }
@end @end
...@@ -209,10 +218,6 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -209,10 +218,6 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
@implementation QRScannerViewControllerTestCase { @implementation QRScannerViewControllerTestCase {
// A swizzler for the CameraController method cameraControllerWithDelegate:. // A swizzler for the CameraController method cameraControllerWithDelegate:.
std::unique_ptr<EarlGreyScopedBlockSwizzler> _camera_controller_swizzler; std::unique_ptr<EarlGreyScopedBlockSwizzler> _camera_controller_swizzler;
// A swizzler for the LocationBarCoordinator method
// loadGURLFromLocationBar:transition:.
std::unique_ptr<EarlGreyScopedBlockSwizzler>
_load_GURL_from_location_bar_swizzler;
} }
- (void)setUp { - (void)setUp {
...@@ -225,12 +230,15 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -225,12 +230,15 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
_testURL = self.testServer->GetURL(kTestURL); _testURL = self.testServer->GetURL(kTestURL);
_testURLEdited = self.testServer->GetURL(kTestURLEdited); _testURLEdited = self.testServer->GetURL(kTestURLEdited);
_testQuery = self.testServer->GetURL(kTestQueryURL); _testQuery = self.testServer->GetURL(kTestQueryURL);
_testQueryEdited = self.testServer->GetURL(kTestQueryEditedURL);
NSString* templateURL =
base::SysUTF8ToNSString(_testQuery.spec() + kTestQueryURLParams);
[QRScannerAppInterface overrideSearchEngine:templateURL];
} }
- (void)tearDown { - (void)tearDown {
[super tearDown]; [super tearDown];
_load_GURL_from_location_bar_swizzler.reset(); [QRScannerAppInterface resetSearchEngine];
_camera_controller_swizzler.reset(); _camera_controller_swizzler.reset();
} }
...@@ -364,22 +372,6 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -364,22 +372,6 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
swizzleCameraControllerBlock); swizzleCameraControllerBlock);
} }
// Swizzles the LocationBarCoordinator loadGURLFromLocationBarBlock:transition:
// method to load |searchURL| instead of the generated search URL.
- (void)swizzleLocationBarCoordinatorLoadGURLFromLocationBar:
(const GURL&)replacementURL {
NSURL* replacementNSURL = net::NSURLWithGURL(replacementURL);
id loadGURLFromLocationBarBlock = [QRScannerAppInterface
locationBarCoordinatorLoadGURLFromLocationBarSwizzleBlockForSearchURL:
replacementNSURL];
_load_GURL_from_location_bar_swizzler =
std::make_unique<EarlGreyScopedBlockSwizzler>(
@"LocationBarCoordinator",
@"loadGURLFromLocationBar:postContent:transition:disposition:",
loadGURLFromLocationBarBlock);
}
// Checks that the modal presented by |viewController| is of class |klass| and // Checks that the modal presented by |viewController| is of class |klass| and
// waits for the modal's view to load. // waits for the modal's view to load.
- (void)waitForModalOfClass:(NSString*)klassString - (void)waitForModalOfClass:(NSString*)klassString
...@@ -684,6 +676,7 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -684,6 +676,7 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
// which can be appended to the response in the omnibox before the page is // which can be appended to the response in the omnibox before the page is
// loaded. // loaded.
- (void)doTestReceivingResult:(std::string)result - (void)doTestReceivingResult:(std::string)result
sanitizedResult:(std::string)sanitizedResult
response:(std::string)response response:(std::string)response
edit:(NSString*)editString { edit:(NSString*)editString {
id cameraControllerMock = id cameraControllerMock =
...@@ -707,11 +700,11 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -707,11 +700,11 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
[cameraControllerMock verify]; [cameraControllerMock verify];
// Optionally edit the text in the omnibox before pressing return. // Optionally edit the text in the omnibox before pressing return.
[self assertOmniboxIsVisibleWithText:result]; [self assertOmniboxIsVisibleWithText:sanitizedResult];
if (editString != nil) { if (editString != nil) {
EditOmniboxTextAndTapKeyboardReturn(result, editString); EditOmniboxTextAndTapKeyboardReturn(sanitizedResult, editString);
} else { } else {
TapKeyboardReturnKeyInOmniboxWithText(result); TapKeyboardReturnKeyInOmniboxWithText(sanitizedResult);
} }
[ChromeEarlGrey waitForWebStateContainingText:response]; [ChromeEarlGrey waitForWebStateContainingText:response];
...@@ -724,6 +717,15 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -724,6 +717,15 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
GREYAssertNil(error, error.localizedDescription); GREYAssertNil(error, error.localizedDescription);
} }
- (void)doTestReceivingResult:(std::string)result
response:(std::string)response
edit:(NSString*)editString {
[self doTestReceivingResult:result
sanitizedResult:result
response:response
edit:editString];
}
// Test that the correct page is loaded if the scanner result is a URL which is // Test that the correct page is loaded if the scanner result is a URL which is
// then manually edited when VoiceOver is enabled. // then manually edited when VoiceOver is enabled.
- (void)testReceivingQRScannerURLResultWithVoiceOver { - (void)testReceivingQRScannerURLResultWithVoiceOver {
...@@ -784,7 +786,6 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -784,7 +786,6 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
// Test that the correct page is loaded if the scanner result is a search query. // Test that the correct page is loaded if the scanner result is a search query.
- (void)testReceivingQRScannerSearchQueryResult { - (void)testReceivingQRScannerSearchQueryResult {
[self swizzleLocationBarCoordinatorLoadGURLFromLocationBar:_testQuery];
[self doTestReceivingResult:kTestQuery response:kTestQueryResponse edit:nil]; [self doTestReceivingResult:kTestQuery response:kTestQueryResponse edit:nil];
} }
...@@ -797,10 +798,18 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse( ...@@ -797,10 +798,18 @@ std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
EARL_GREY_TEST_DISABLED(@"Test disabled on iPad."); EARL_GREY_TEST_DISABLED(@"Test disabled on iPad.");
} }
[self swizzleLocationBarCoordinatorLoadGURLFromLocationBar:_testQueryEdited];
[self doTestReceivingResult:kTestQuery [self doTestReceivingResult:kTestQuery
response:kTestQueryEditedResponse response:kTestQueryEditedResponse
edit:@"\bedited"]; edit:@"\bedited"];
} }
// Test that the correct page is loaded if the scanner result is a not supported
// URL.
- (void)testReceivingQRScannerLoadDataResult {
[self doTestReceivingResult:kTestDataURL
sanitizedResult:kTestSanitizedDataURL
response:kTestDataURLResponse
edit:nil];
}
@end @end
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