зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1791079 - Implement User Activation API r=dom-core,webidl,saschanaz,edgar
Rewrote internal user activation tree (spreading state to other elements) logic to match HTML spec: https://html.spec.whatwg.org/multipage/interaction.html#user-activation-processing-model Added navigator.userActivation API to expose internal user activation. Also fixed a WPT test to conform to spec (siblings are not activated), see also spec issue: https://github.com/whatwg/html/issues/9831 Co-authored-by: Tom Schuster <evilpies@gmail.com> Differential Revision: https://phabricator.services.mozilla.com/D185348
This commit is contained in:
Родитель
a21d68d939
Коммит
953794b454
|
@ -16734,36 +16734,43 @@ BrowsingContext* Document::GetBrowsingContext() const {
|
|||
}
|
||||
|
||||
void Document::NotifyUserGestureActivation() {
|
||||
if (RefPtr<BrowsingContext> bc = GetBrowsingContext()) {
|
||||
bc->PreOrderWalk([&](BrowsingContext* aBC) {
|
||||
WindowContext* windowContext = aBC->GetCurrentWindowContext();
|
||||
if (!windowContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocShell* docShell = aBC->GetDocShell();
|
||||
if (!docShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
Document* document = docShell->GetDocument();
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
// XXXedgar we probably could just check `IsInProcess()` after fission
|
||||
// enable.
|
||||
if (NodePrincipal()->Equals(document->NodePrincipal())) {
|
||||
windowContext->NotifyUserGestureActivation();
|
||||
}
|
||||
});
|
||||
|
||||
for (bc = bc->GetParent(); bc; bc = bc->GetParent()) {
|
||||
if (WindowContext* windowContext = bc->GetCurrentWindowContext()) {
|
||||
windowContext->NotifyUserGestureActivation();
|
||||
}
|
||||
}
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#activation-notification
|
||||
// 1. "Assert: document is fully active."
|
||||
RefPtr<BrowsingContext> currentBC = GetBrowsingContext();
|
||||
if (!currentBC) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<WindowContext> currentWC = GetWindowContext();
|
||||
|
||||
// 2. "Let windows be « document's relevant global object"
|
||||
// Instead of assembling a list, we just call notify for wanted windows as we
|
||||
// find them
|
||||
currentWC->NotifyUserGestureActivation();
|
||||
|
||||
// 3. "...windows with the active window of each of document's ancestor
|
||||
// navigables."
|
||||
for (WindowContext* wc = currentWC; wc; wc = wc->GetParentWindowContext()) {
|
||||
wc->NotifyUserGestureActivation();
|
||||
}
|
||||
|
||||
// 4. "windows with the active window of each of document's descendant
|
||||
// navigables, filtered to include only those navigables whose active
|
||||
// document's origin is same origin with document's origin"
|
||||
currentBC->PreOrderWalk([&](BrowsingContext* bc) {
|
||||
WindowContext* wc = bc->GetCurrentWindowContext();
|
||||
if (!wc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check same-origin as current document
|
||||
WindowGlobalChild* wgc = wc->GetWindowGlobalChild();
|
||||
if (!wgc || !wgc->IsSameOriginWith(currentWC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
wc->NotifyUserGestureActivation();
|
||||
});
|
||||
}
|
||||
|
||||
bool Document::HasBeenUserGestureActivated() {
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "mozilla/dom/StorageManager.h"
|
||||
#include "mozilla/dom/TCPSocket.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
#include "mozilla/dom/UserActivation.h"
|
||||
#include "mozilla/dom/VRDisplay.h"
|
||||
#include "mozilla/dom/VRDisplayEvent.h"
|
||||
#include "mozilla/dom/VRServiceTest.h"
|
||||
|
@ -159,6 +160,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddonManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebGpu)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocks)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserActivation)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
|
||||
|
@ -245,6 +247,8 @@ void Navigator::Invalidate() {
|
|||
mLocks = nullptr;
|
||||
}
|
||||
|
||||
mUserActivation = nullptr;
|
||||
|
||||
mSharePromise = nullptr;
|
||||
}
|
||||
|
||||
|
@ -2288,4 +2292,11 @@ AutoplayPolicy Navigator::GetAutoplayPolicy(AudioContext& aContext) {
|
|||
return media::AutoplayPolicy::GetAutoplayPolicy(aContext);
|
||||
}
|
||||
|
||||
already_AddRefed<UserActivation> Navigator::UserActivation() {
|
||||
if (!mUserActivation) {
|
||||
mUserActivation = new dom::UserActivation(GetWindow());
|
||||
}
|
||||
return do_AddRef(mUserActivation);
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -85,6 +85,7 @@ class XRSystem;
|
|||
class StorageManager;
|
||||
class MediaCapabilities;
|
||||
class MediaSession;
|
||||
class UserActivation;
|
||||
struct ShareData;
|
||||
class WindowGlobalChild;
|
||||
|
||||
|
@ -249,6 +250,8 @@ class Navigator final : public nsISupports, public nsWrapperCache {
|
|||
AutoplayPolicy GetAutoplayPolicy(HTMLMediaElement& aElement);
|
||||
AutoplayPolicy GetAutoplayPolicy(AudioContext& aContext);
|
||||
|
||||
already_AddRefed<UserActivation> UserActivation();
|
||||
|
||||
private:
|
||||
void ValidateShareData(const ShareData& aData, ErrorResult& aRv);
|
||||
RefPtr<MediaKeySystemAccessManager> mMediaKeySystemAccessManager;
|
||||
|
@ -296,6 +299,7 @@ class Navigator final : public nsISupports, public nsWrapperCache {
|
|||
RefPtr<webgpu::Instance> mWebGpu;
|
||||
RefPtr<Promise> mSharePromise; // Web Share API related
|
||||
RefPtr<dom::LockManager> mLocks;
|
||||
RefPtr<dom::UserActivation> mUserActivation;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -4,12 +4,50 @@
|
|||
* 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/. */
|
||||
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/dom/UserActivation.h"
|
||||
#include "mozilla/dom/UserActivationBinding.h"
|
||||
#include "mozilla/dom/WindowGlobalChild.h"
|
||||
|
||||
#include "mozilla/TextEvents.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(UserActivation, mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(UserActivation)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(UserActivation)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UserActivation)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
UserActivation::UserActivation(nsPIDOMWindowInner* aWindow)
|
||||
: mWindow(aWindow) {}
|
||||
|
||||
JSObject* UserActivation::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return UserActivation_Binding::Wrap(aCx, this, aGivenProto);
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#dom-useractivation-hasbeenactive
|
||||
bool UserActivation::HasBeenActive() const {
|
||||
// The hasBeenActive getter steps are to return true if this's relevant global
|
||||
// object has sticky activation, and false otherwise.
|
||||
|
||||
WindowContext* wc = mWindow->GetWindowContext();
|
||||
return wc && wc->HasBeenUserGestureActivated();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#dom-useractivation-isactive
|
||||
bool UserActivation::IsActive() const {
|
||||
// The isActive getter steps are to return true if this's relevant global
|
||||
// object has transient activation, and false otherwise.
|
||||
|
||||
WindowContext* wc = mWindow->GetWindowContext();
|
||||
return wc && wc->HasValidTransientUserGestureActivation();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// The current depth of user and keyboard inputs. sUserInputEventDepth
|
||||
|
|
|
@ -4,16 +4,34 @@
|
|||
* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_UserAcitvation_h
|
||||
#define mozilla_dom_UserAcitvation_h
|
||||
#ifndef mozilla_dom_UserActivation_h
|
||||
#define mozilla_dom_UserActivation_h
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class UserActivation final {
|
||||
class UserActivation final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
// WebIDL UserActivation
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(UserActivation)
|
||||
|
||||
explicit UserActivation(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
|
||||
|
||||
bool HasBeenActive() const;
|
||||
bool IsActive() const;
|
||||
|
||||
// End of WebIDL UserActivation
|
||||
|
||||
enum class State : uint8_t {
|
||||
// Not activated.
|
||||
None,
|
||||
|
@ -64,6 +82,11 @@ class UserActivation final {
|
|||
* the epoch.
|
||||
*/
|
||||
static TimeStamp LatestUserInputStart();
|
||||
|
||||
private:
|
||||
~UserActivation() = default;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -83,4 +106,4 @@ class MOZ_RAII AutoHandlingUserInputStatePusher final {
|
|||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // mozilla_dom_UserAcitvation_h
|
||||
#endif // mozilla_dom_UserActivation_h
|
||||
|
|
|
@ -1407,6 +1407,8 @@ let interfaceNamesInGlobalScope = [
|
|||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "URLSearchParams", insecureContext: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "UserActivation", insecureContext: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "UserProximityEvent", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "ValidityState", insecureContext: true },
|
||||
|
|
|
@ -373,3 +373,8 @@ partial interface Navigator {
|
|||
[Pref="dom.media.autoplay-policy-detection.enabled"]
|
||||
AutoplayPolicy getAutoplayPolicy(AudioContext context);
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#the-useractivation-interface
|
||||
partial interface Navigator {
|
||||
[SameObject] readonly attribute UserActivation userActivation;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://html.spec.whatwg.org/multipage/interaction.html#the-useractivation-interface
|
||||
*/
|
||||
|
||||
[Exposed=Window]
|
||||
interface UserActivation {
|
||||
readonly attribute boolean hasBeenActive;
|
||||
readonly attribute boolean isActive;
|
||||
};
|
|
@ -981,6 +981,7 @@ WEBIDL_FILES = [
|
|||
"UIEvent.webidl",
|
||||
"URL.webidl",
|
||||
"URLSearchParams.webidl",
|
||||
"UserActivation.webidl",
|
||||
"ValidityState.webidl",
|
||||
"VideoColorSpace.webidl",
|
||||
"VideoDecoder.webidl",
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[element-ready-check-containing-iframe.html]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[Element ready check for containing iframe]
|
||||
expected: FAIL
|
|
@ -1,5 +0,0 @@
|
|||
[element-request-fullscreen-consume-user-activation.html]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[Element#requestFullscreen() consumes user activation]
|
||||
expected: FAIL
|
|
@ -300,36 +300,6 @@ prefs: [dom.security.featurePolicy.experimental.enabled:true, dom.security.featu
|
|||
[SVGElement interface: attribute onbeforematch]
|
||||
expected: FAIL
|
||||
|
||||
[UserActivation interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[UserActivation interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[UserActivation interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[UserActivation interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[UserActivation interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[UserActivation interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[UserActivation interface: attribute hasBeenActive]
|
||||
expected: FAIL
|
||||
|
||||
[UserActivation interface: attribute isActive]
|
||||
expected: FAIL
|
||||
|
||||
[Navigator interface: attribute userActivation]
|
||||
expected: FAIL
|
||||
|
||||
[Navigator interface: window.navigator must inherit property "userActivation" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[SVGElement interface: attribute onbeforetoggle]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
[activation-trigger-pointerevent.html?touch]
|
||||
# A webdriver bug (Bug 1856991) does not emit touch click events internally as expected
|
||||
bug: 1856991
|
||||
expected: TIMEOUT
|
||||
[Activation through touch pointerevent click]
|
||||
expected: TIMEOUT
|
||||
|
||||
|
||||
[activation-trigger-pointerevent.html?pen]
|
||||
# Pen touch type is not supported by webdriver
|
||||
[Activation through pen pointerevent click]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[activation-trigger-pointerevent.html?mouse]
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
[chained-setTimeout.html]
|
||||
[Call-depth=1: initial activation states are false]
|
||||
expected: FAIL
|
||||
|
||||
[Call-depth=2: initial activation states are false]
|
||||
expected: FAIL
|
||||
|
||||
[Call-depth=3: initial activation states are false]
|
||||
expected: FAIL
|
||||
|
||||
[Call-depth=1: after-click activation states are true]
|
||||
expected: FAIL
|
||||
|
||||
[Call-depth=2: after-click activation states are true]
|
||||
expected: FAIL
|
||||
|
||||
[Call-depth=3: after-click activation states are true]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[detached-iframe.html]
|
||||
[navigator.userActivation retains state even if global is removed]
|
||||
expected: FAIL
|
|
@ -2,3 +2,5 @@
|
|||
[MessageEventInit user activation not set]
|
||||
expected: FAIL
|
||||
|
||||
[MessageEventInit user activation set]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[navigation-state-reset-crossorigin.sub.html]
|
||||
expected: TIMEOUT
|
||||
[Post-navigation state reset.]
|
||||
expected: TIMEOUT
|
||||
# There is a webdriver bug (Bug 1856989) which breaks cross-process iframe clicks
|
||||
expected:
|
||||
if fission: TIMEOUT
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[navigation-state-reset-sameorigin.html]
|
||||
expected: TIMEOUT
|
||||
[Post-navigation state reset.]
|
||||
expected: TIMEOUT
|
|
@ -1,3 +0,0 @@
|
|||
[no-activation-thru-escape-key.html]
|
||||
['Escape' key doesn't activate a page.]
|
||||
expected: FAIL
|
|
@ -1,4 +1,4 @@
|
|||
[propagation-crossorigin.sub.html]
|
||||
expected: TIMEOUT
|
||||
[Propagation test]
|
||||
expected: NOTRUN
|
||||
# There is a webdriver bug (Bug 1856989) which breaks cross-process iframe clicks
|
||||
expected:
|
||||
if fission: TIMEOUT
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
[propagation-same-and-cross-origin.sub.html]
|
||||
expected: TIMEOUT
|
||||
[Check Initial states of user activation are all false]
|
||||
expected: NOTRUN
|
||||
|
||||
[Check that activating a same-origin navigable doesn't activate a cross origin navigable]
|
||||
expected: NOTRUN
|
||||
|
||||
[Clicking on the cross-origin navigable activates parent navigable.]
|
||||
expected: NOTRUN
|
||||
# There is a webdriver bug (Bug 1856989) which breaks cross-process iframe clicks
|
||||
expected:
|
||||
if fission: TIMEOUT
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[propagation-sameorigin.html]
|
||||
expected: TIMEOUT
|
||||
[Propagation test]
|
||||
expected: NOTRUN
|
|
@ -1,3 +0,0 @@
|
|||
[user-activation-interface.html]
|
||||
[navigator.userActivation shows correct states before/after a click]
|
||||
expected: FAIL
|
|
@ -62,9 +62,11 @@
|
|||
assert_false(msg.hasBeenActive);
|
||||
}, "Grandchild frame initial state");
|
||||
} else if (msg.type == 'child-one-report') {
|
||||
// Siblings (same or cross origin) should not be activated per spec
|
||||
// Spec issue discussing: https://github.com/whatwg/html/issues/9831
|
||||
test(() => {
|
||||
assert_true(msg.isActive);
|
||||
assert_true(msg.hasBeenActive);
|
||||
assert_false(msg.isActive);
|
||||
assert_false(msg.hasBeenActive);
|
||||
}, "Child1 frame final state");
|
||||
} else if (msg.type == 'child-sameorigin-report') {
|
||||
// This msg was triggered by a user click.
|
||||
|
|
Загрузка…
Ссылка в новой задаче