Commit 52aef044 authored by Mike Dougherty's avatar Mike Dougherty Committed by Commit Bot

Reland: Restrict web share feature to URLs without file protocol

This is a reland of c02e4409

These EarlGrey tests fail on iOS 12.4. (Presenting the iOS share sheet
crashes the test even without any JS changes.) Update the tests to run
only on iOS 13+.

The feature works in Chrome even on iOS 12.4 without crashing.

Original change's description:
> Restrict web share feature to URLs with http/https protocols
>
> Bug: 1122059
> Change-Id: Id9644b9dff7e9854f20218f09750fbf191d5a318
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2378274
> Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
> Reviewed-by: Eugene But <eugenebut@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#803970}

Bug: 1122059
Change-Id: Ia7c94020287330c918a3a9d50c545fca52306d6d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2392979
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804487}
parent dd6b0b15
......@@ -438,6 +438,7 @@ source_set("eg2_tests") {
"stop_loading_egtest.mm",
"tab_order_egtest.mm",
"visible_url_egtest.mm",
"web_share_egtest.mm",
"window_open_by_dom_egtest.mm",
]
......
// 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 "base/ios/ios_util.h"
#include "base/strings/stringprintf.h"
#import "ios/chrome/test/earl_grey/chrome_actions.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/testing/earl_grey/earl_grey_test.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_response.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
const char kWebShareButtonId[] = "shareButton";
const char kWebShareStatusSuccess[] = "success";
const char kWebShareStatusFailure[] = "failure";
const char kWebShareValidLinkUrl[] = "/share_link.html";
const char kWebShareFileUrl[] = "/share_file.html";
const char kWebShareRelativeLinkUrl[] = "/share_relative_link.html";
const char kWebShareRelativeFilenameFileUrl[] = "/share_filename_file.html";
const char kWebSharePageContents[] =
"<html>"
"<head>"
"<script>"
"async function tryUrl() {"
" document.getElementById(\"result\").innerHTML = '';"
" try {"
" var opts = {url: \"%s\"};"
" navigator.share(opts);"
" document.getElementById(\"result\").innerHTML = 'success';"
" } catch {"
" document.getElementById(\"result\").innerHTML = 'failure';"
" }"
"}"
"</script>"
"</head><body>"
"<button id=\"shareButton\" onclick=\"tryUrl()\">Share</button>"
"<div id=\"result\"></div>"
"</body></html>";
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->set_content_type("text/html");
if (request.relative_url == kWebShareValidLinkUrl) {
std::string content =
base::StringPrintf(kWebSharePageContents, "https://example.com");
http_response->set_content(content);
} else if (request.relative_url == kWebShareFileUrl) {
std::string content =
base::StringPrintf(kWebSharePageContents, "file:///Users/u/data");
http_response->set_content(content);
} else if (request.relative_url == kWebShareRelativeLinkUrl) {
std::string content =
base::StringPrintf(kWebSharePageContents, "/something.png");
http_response->set_content(content);
} else if (request.relative_url == kWebShareRelativeFilenameFileUrl) {
std::string content =
base::StringPrintf(kWebSharePageContents, "filename.zip");
http_response->set_content(content);
} else {
return nullptr;
}
return std::move(http_response);
}
} // namespace
@interface WebShareTestCase : ChromeTestCase
@end
@implementation WebShareTestCase
- (void)setUp {
[super setUp];
self.testServer->RegisterRequestHandler(base::BindRepeating(&HandleRequest));
GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
}
// Tests that a fully specified url can be shared.
- (void)testShareUrl {
if (!base::ios::IsRunningOnIOS13OrLater()) {
// Test is unsupported on iOS 12 because presenting the share sheet in this
// EarlGrey test crashes the app.
EARL_GREY_TEST_SKIPPED(@"Skipped for iOS 12.");
}
const GURL pageURL = self.testServer->GetURL(kWebShareValidLinkUrl);
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kWebShareButtonId)];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Copy")]
performAction:grey_tap()];
[ChromeEarlGrey waitForWebStateContainingText:kWebShareStatusSuccess];
}
// Tests that a relative url can be shared.
- (void)testShareRelativeUrl {
if (!base::ios::IsRunningOnIOS13OrLater()) {
// Test is unsupported on iOS 12 because presenting the share sheet in this
// EarlGrey test crashes the app.
EARL_GREY_TEST_SKIPPED(@"Skipped for iOS 12.");
}
const GURL pageURL = self.testServer->GetURL(kWebShareRelativeLinkUrl);
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kWebShareButtonId)];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Copy")]
performAction:grey_tap()];
[ChromeEarlGrey waitForWebStateContainingText:kWebShareStatusSuccess];
}
// Tests that a relative url can be shared when the filename starts with "file".
- (void)testShareRelativeFilenameUrl {
if (!base::ios::IsRunningOnIOS13OrLater()) {
// Test is unsupported on iOS 12 because presenting the share sheet in this
// EarlGrey test crashes the app.
EARL_GREY_TEST_SKIPPED(@"Skipped for iOS 12.");
}
const GURL pageURL =
self.testServer->GetURL(kWebShareRelativeFilenameFileUrl);
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kWebShareButtonId)];
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Copy")]
performAction:grey_tap()];
[ChromeEarlGrey waitForWebStateContainingText:kWebShareStatusSuccess];
}
// Tests that a "file://" url can not be shared.
- (void)testShareFileUrl {
if (!base::ios::IsRunningOnIOS13OrLater()) {
// Test is unsupported on iOS 12 because presenting the share sheet in this
// EarlGrey test crashes the app.
EARL_GREY_TEST_SKIPPED(@"Skipped for iOS 12.");
}
const GURL pageURL = self.testServer->GetURL(kWebShareFileUrl);
[ChromeEarlGrey loadURL:pageURL];
[[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
performAction:chrome_test_util::TapWebElementWithId(kWebShareButtonId)];
[ChromeEarlGrey waitForWebStateContainingText:kWebShareStatusFailure];
// Share sheet should not display.
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"Copy")]
assertWithMatcher:grey_nil()];
}
@end
......@@ -606,6 +606,7 @@ js_compile_bundle("all_frames_web_bundle") {
"web_state/js/resources/common.js",
"web_state/js/resources/cookie.js",
"web_state/js/resources/find_in_page.js",
"web_state/js/resources/share_workaround.js",
]
}
......
......@@ -11,3 +11,4 @@ goog.require('__crWeb.common');
goog.require('__crWeb.cookie');
goog.require('__crWeb.findInPage');
goog.require('__crWeb.message');
goog.require('__crWeb.shareWorkaround');
// 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.
/**
* @fileoverview Workaround for preventing the leaking of local file contents.
* See crbug.com/1122059.
*/
goog.provide('__crWeb.shareWorkaround');
/** Beginning of anonymous object */
(function() {
/** @private */
// Store originals to prevent calling a modified version later.
const originalNavigator_ = navigator;
const originalNavigatorShare_ = Navigator.prototype.share;
const originalReflectApply_ = Reflect.apply;
const originalObjectDefineProperty_ = Object.defineProperty;
/**
* Wraps navigator.share() to prevent sharing URLs with "file:" scheme.
* NOTE: This code is sensitive and easy to break. See comments in
* crbug.com/1122059 and review comments in crrev.com/c/2378274.
* TODO:(crbug.com/1123689): Remove this workaround once WebKit fix is released.
*/
Navigator.prototype.share = function(data) {
// Copy values to a new Object to prevent functions returning different
// data from data.url. crbug.com/1122059#c23
const validatedData = {};
if (data.hasOwnProperty('files')) {
originalObjectDefineProperty_(validatedData, 'files',
{ value: data.files, configurable: false, writable: false })
}
if (data.hasOwnProperty('text')) {
originalObjectDefineProperty_(validatedData, 'text',
{ value: data.text, configurable: false, writable: false })
}
if (data.hasOwnProperty('title')) {
originalObjectDefineProperty_(validatedData, 'title',
{ value: data.title, configurable: false, writable: false })
}
let url = undefined;
if (data.hasOwnProperty('url')) {
url = data['url'];
let proceed = false;
if (url === undefined) {
// Allow url key to be set without value.
proceed = true;
} else if (typeof url === "string") {
// file: URLs are not allowed.
if (url.length >= 5 &&
(url[0] == 'f' || url[0] == 'F') &&
(url[1] == 'i' || url[1] == 'I') &&
(url[2] == 'l' || url[2] == 'L') &&
(url[3] == 'e' || url[3] == 'E') &&
url[4] == ':') {
proceed = false;
} else {
proceed = true;
}
}
if (!proceed) {
throw new Error("Sharing is not supported for this type of url.");
}
}
originalObjectDefineProperty_(validatedData, 'url',
{ value: url, configurable: false, writable: false })
return originalReflectApply_(originalNavigatorShare_,
originalNavigator_,
[validatedData]);
};
}()); // End of anonymous object
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