зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1643205 - Navigator's share() must consume user activation r=edgar
navigator.share() must consume the user activation. Differential Revision: https://phabricator.services.mozilla.com/D79640
This commit is contained in:
Родитель
180b52ef6f
Коммит
338877b3cb
|
@ -1374,6 +1374,17 @@ Promise* Navigator::Share(const ShareData& aData, ErrorResult& aRv) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// null checked above
|
||||
auto* doc = mWindow->GetExtantDoc();
|
||||
|
||||
if (StaticPrefs::dom_webshare_requireinteraction() &&
|
||||
!doc->ConsumeTransientUserGestureActivation()) {
|
||||
aRv.ThrowNotAllowedError(
|
||||
"User activation was already consumed "
|
||||
"or share() was not activated by a user gesture.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If none of data's members title, text, or url are present, reject p with
|
||||
// TypeError, and abort these steps.
|
||||
bool someMemberPassed = aData.mTitle.WasPassed() || aData.mText.WasPassed() ||
|
||||
|
@ -1384,9 +1395,6 @@ Promise* Navigator::Share(const ShareData& aData, ErrorResult& aRv) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// null checked above
|
||||
auto doc = mWindow->GetExtantDoc();
|
||||
|
||||
// If data's url member is present, try to resolve it...
|
||||
nsCOMPtr<nsIURI> url;
|
||||
if (aData.mUrl.WasPassed()) {
|
||||
|
@ -1415,16 +1423,6 @@ Promise* Navigator::Share(const ShareData& aData, ErrorResult& aRv) {
|
|||
text.SetIsVoid(true);
|
||||
}
|
||||
|
||||
// The spec does the "triggered by user activation" after the data checks.
|
||||
// Unfortunately, both Chrome and Safari behave this way, so interop wins.
|
||||
// https://github.com/w3c/web-share/pull/118
|
||||
if (StaticPrefs::dom_webshare_requireinteraction() &&
|
||||
!UserActivation::IsHandlingUserInput()) {
|
||||
NS_WARNING("Attempt to share not triggered by user activation");
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Let mSharePromise be a new promise.
|
||||
mSharePromise = Promise::Create(mWindow->AsGlobal(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
|
|
|
@ -86,6 +86,7 @@ DIRS += [
|
|||
'url',
|
||||
'webauthn',
|
||||
'webidl',
|
||||
'webshare',
|
||||
'xml',
|
||||
'xslt',
|
||||
'xul',
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "DOM: Core & HTML")
|
||||
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
[DEFAULT]
|
||||
prefs =
|
||||
dom.webshare.enabled=true
|
||||
scheme = https
|
||||
[test_navigator_share_consume_user_activation.html]
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Web Share: consume transient activation</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
|
||||
<body>
|
||||
<button id="share">Share</button>
|
||||
</body>
|
||||
<script>
|
||||
// TODO: add a task that tests share() consume the transient user activation.
|
||||
// Because OS-level prompt can't be cancelled, it's currently not possible to
|
||||
// test this. We need to add a Web share Mocking service:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1646229
|
||||
|
||||
// test that share() would be blocked with an already-consumed-activation.
|
||||
add_task(async function blockedIfAlreadyConsumed() {
|
||||
const wrappedDoc = SpecialPowers.wrap(document);
|
||||
const button = document.getElementById("share");
|
||||
|
||||
// Kick off transient activation
|
||||
synthesizeMouseAtCenter(button, {});
|
||||
|
||||
ok(
|
||||
wrappedDoc.hasValidTransientUserGestureActivation,
|
||||
"Activated by a gesture"
|
||||
);
|
||||
|
||||
wrappedDoc.consumeTransientUserGestureActivation();
|
||||
|
||||
try {
|
||||
const sharePromise = navigator.share({ title: "test" });
|
||||
await sharePromise;
|
||||
ok(false, "must throw because activation was already consumed");
|
||||
} catch (err) {
|
||||
is(err.name, "NotAllowedError", "Expected NotAllowedError DOMException");
|
||||
} finally {
|
||||
ok(
|
||||
!wrappedDoc.hasValidTransientUserGestureActivation,
|
||||
"share() must consume the activation"
|
||||
);
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebShare Test: consume user activation</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/manual-helper.js"></script>
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="/resources/testdriver.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
// Not activated by user gesture, so not allowed!
|
||||
await promise_rejects_dom(t, "NotAllowedError", navigator.share());
|
||||
|
||||
await test_driver.bless("web share");
|
||||
|
||||
// We have a gesture, but the URL is invalid - so TypeError!
|
||||
await promise_rejects_js(
|
||||
t,
|
||||
TypeError,
|
||||
navigator.share({ url: "http://example.com:65536" })
|
||||
);
|
||||
|
||||
// The activation has been consumed, so calling share() again would require
|
||||
// a new gesture.
|
||||
await promise_rejects_dom(t, "NotAllowedError", navigator.share());
|
||||
}, "Calling share consumes user activation");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,34 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="utf-8" />
|
||||
<title>WebShare Test: Share no known fields</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/manual-helper.js"></script>
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="/resources/testdriver.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(t => {
|
||||
promise_test(async t => {
|
||||
await test_driver.bless("web share", () => {
|
||||
return promise_rejects_js(t, TypeError, navigator.share());
|
||||
}, 'share with no arguments (same as empty dictionary)');
|
||||
});
|
||||
}, "share with no arguments (same as empty dictionary)");
|
||||
|
||||
promise_test(t => {
|
||||
promise_test(async t => {
|
||||
await test_driver.bless("web share", () => {
|
||||
return promise_rejects_js(t, TypeError, navigator.share({}));
|
||||
}, 'share with an empty dictionary');
|
||||
});
|
||||
}, "share with an empty dictionary");
|
||||
|
||||
promise_test(t => {
|
||||
promise_test(async t => {
|
||||
await test_driver.bless("web share", () => {
|
||||
return promise_rejects_js(t, TypeError, navigator.share(undefined));
|
||||
}, 'share with a undefined argument (same as empty dictionary)');
|
||||
});
|
||||
}, "share with a undefined argument (same as empty dictionary)");
|
||||
|
||||
promise_test(t => {
|
||||
promise_test(async t => {
|
||||
await test_driver.bless("web share", () => {
|
||||
return promise_rejects_js(t, TypeError, navigator.share(null));
|
||||
}, 'share with a null argument (same as empty dictionary)');
|
||||
});
|
||||
}, "share with a null argument (same as empty dictionary)");
|
||||
|
||||
promise_test(t => {
|
||||
return promise_rejects_js(t,
|
||||
TypeError, navigator.share({unused: 'unexpected field'}));
|
||||
}, 'share with a dictionary containing only surplus fields');
|
||||
promise_test(async t => {
|
||||
await test_driver.bless("web share", () => {
|
||||
return promise_rejects_js(
|
||||
t,
|
||||
TypeError,
|
||||
navigator.share({ unused: "unexpected field" })
|
||||
);
|
||||
});
|
||||
}, "share with a dictionary containing only surplus fields");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="utf-8" />
|
||||
<title>WebShare Test: Share with an invalid URL</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="/resources/testdriver.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(t => {
|
||||
promise_test(async t => {
|
||||
// URL is invalid in that the URL Parser returns failure (port is too
|
||||
// large).
|
||||
const url = 'http://example.com:65536';
|
||||
return promise_rejects_js(
|
||||
t, TypeError, navigator.share({url}));
|
||||
}, 'share with an invalid URL');
|
||||
const url = "http://example.com:65536";
|
||||
await test_driver.bless(
|
||||
"web share",
|
||||
() => {
|
||||
return promise_rejects_js(t, TypeError, navigator.share({ url }));
|
||||
},
|
||||
"share with an invalid URL"
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Загрузка…
Ссылка в новой задаче