Skip to content

Commit

Permalink
Bug 1791079 - Implement User Activation API r=dom-core,webidl,saschan…
Browse files Browse the repository at this point in the history
…az,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: whatwg/html#9831

Co-authored-by: Tom Schuster <[email protected]>

Differential Revision: https://phabricator.services.mozilla.com/D185348
  • Loading branch information
CanadaHonk committed Oct 7, 2023
1 parent 90b9ecb commit 1e445f8
Show file tree
Hide file tree
Showing 23 changed files with 164 additions and 121 deletions.
60 changes: 35 additions & 25 deletions dom/base/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16734,36 +16734,46 @@ BrowsingContext* Document::GetBrowsingContext() const {
}

void Document::NotifyUserGestureActivation() {
if (RefPtr<BrowsingContext> bc = GetBrowsingContext()) {
bc->PreOrderWalk([&](BrowsingContext* aBC) {
WindowContext* windowContext = aBC->GetCurrentWindowContext();
if (!windowContext) {
return;
}
// https://html.spec.whatwg.org/multipage/interaction.html#activation-notification
// 1. "Assert: document is fully active."
RefPtr<BrowsingContext> currentBC = GetBrowsingContext();
if (!currentBC) {
return;
}

nsIDocShell* docShell = aBC->GetDocShell();
if (!docShell) {
return;
}
RefPtr<WindowContext> currentWC = GetWindowContext();
if (!currentWC) {
return;
}

Document* document = docShell->GetDocument();
if (!document) {
return;
}
// 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();

// XXXedgar we probably could just check `IsInProcess()` after fission
// enable.
if (NodePrincipal()->Equals(document->NodePrincipal())) {
windowContext->NotifyUserGestureActivation();
}
});
// 3. "...windows with the active window of each of document's ancestor
// navigables."
for (WindowContext* wc = currentWC; wc; wc = wc->GetParentWindowContext()) {
wc->NotifyUserGestureActivation();
}

for (bc = bc->GetParent(); bc; bc = bc->GetParent()) {
if (WindowContext* windowContext = bc->GetCurrentWindowContext()) {
windowContext->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() {
Expand Down
11 changes: 11 additions & 0 deletions dom/base/Navigator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -245,6 +247,8 @@ void Navigator::Invalidate() {
mLocks = nullptr;
}

mUserActivation = nullptr;

mSharePromise = nullptr;
}

Expand Down Expand Up @@ -2288,4 +2292,11 @@ AutoplayPolicy Navigator::GetAutoplayPolicy(AudioContext& aContext) {
return media::AutoplayPolicy::GetAutoplayPolicy(aContext);
}

already_AddRefed<dom::UserActivation> Navigator::UserActivation() {
if (!mUserActivation) {
mUserActivation = new dom::UserActivation(GetWindow());
}
return do_AddRef(mUserActivation);
}

} // namespace mozilla::dom
4 changes: 4 additions & 0 deletions dom/base/Navigator.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class XRSystem;
class StorageManager;
class MediaCapabilities;
class MediaSession;
class UserActivation;
struct ShareData;
class WindowGlobalChild;

Expand Down Expand Up @@ -249,6 +250,8 @@ class Navigator final : public nsISupports, public nsWrapperCache {
AutoplayPolicy GetAutoplayPolicy(HTMLMediaElement& aElement);
AutoplayPolicy GetAutoplayPolicy(AudioContext& aContext);

already_AddRefed<dom::UserActivation> UserActivation();

private:
void ValidateShareData(const ShareData& aData, ErrorResult& aRv);
RefPtr<MediaKeySystemAccessManager> mMediaKeySystemAccessManager;
Expand Down Expand Up @@ -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
Expand Down
38 changes: 38 additions & 0 deletions dom/base/UserActivation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 27 additions & 4 deletions dom/base/UserActivation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -64,6 +82,11 @@ class UserActivation final {
* the epoch.
*/
static TimeStamp LatestUserInputStart();

private:
~UserActivation() = default;

nsCOMPtr<nsPIDOMWindowInner> mWindow;
};

/**
Expand All @@ -83,4 +106,4 @@ class MOZ_RAII AutoHandlingUserInputStatePusher final {

} // namespace mozilla::dom

#endif // mozilla_dom_UserAcitvation_h
#endif // mozilla_dom_UserActivation_h
2 changes: 2 additions & 0 deletions dom/tests/mochitest/general/test_interfaces.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
5 changes: 5 additions & 0 deletions dom/webidl/Navigator.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
14 changes: 14 additions & 0 deletions dom/webidl/UserActivation.webidl
Original file line number Diff line number Diff line change
@@ -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;
};
1 change: 1 addition & 0 deletions dom/webidl/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,7 @@ WEBIDL_FILES = [
"UIEvent.webidl",
"URL.webidl",
"URLSearchParams.webidl",
"UserActivation.webidl",
"ValidityState.webidl",
"VideoColorSpace.webidl",
"VideoDecoder.webidl",
Expand Down

This file was deleted.

30 changes: 0 additions & 30 deletions testing/web-platform/meta/html/dom/idlharness.https.html.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
[activation-trigger-pointerevent.html?touch]
expected: TIMEOUT
# A webdriver bug (Bug 1856991) does not emit touch click events internally as expected
bug: 1856991
expected:
if os == "android": [OK, TIMEOUT]
if os == "linux": [OK, TIMEOUT]
TIMEOUT

[Activation through touch pointerevent click]
expected: TIMEOUT
expected:
if os == "android": [FAIL, TIMEOUT]
if os == "linux": [FAIL, TIMEOUT]
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]

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
[MessageEventInit user activation not set]
expected: FAIL

[MessageEventInit user activation set]
expected: FAIL
Original file line number Diff line number Diff line change
@@ -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

This file was deleted.

This file was deleted.

Loading

0 comments on commit 1e445f8

Please sign in to comment.