mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1766582 - Expose last user gesture timestamp; r=sgalich,smaug
Differential Revision: https://phabricator.services.mozilla.com/D144590
This commit is contained in:
parent
102a6112e6
commit
b838774cf3
@ -340,10 +340,11 @@ void WindowContext::DidSet(FieldIndex<IDX_UserActivationState>) {
|
||||
USER_ACTIVATION_LOG(
|
||||
"Set user gesture start time for %s browsing context 0x%08" PRIx64,
|
||||
XRE_IsParentProcess() ? "Parent" : "Child", Id());
|
||||
mUserGestureStart =
|
||||
(GetUserActivationState() == UserActivation::State::FullActivated)
|
||||
? TimeStamp::Now()
|
||||
: TimeStamp();
|
||||
if (GetUserActivationState() == UserActivation::State::FullActivated) {
|
||||
mUserGestureStart = TimeStamp::Now();
|
||||
} else if (GetUserActivationState() == UserActivation::State::None) {
|
||||
mUserGestureStart = TimeStamp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -459,13 +460,26 @@ bool WindowContext::HasBeenUserGestureActivated() {
|
||||
return GetUserActivationState() != UserActivation::State::None;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp WindowContext::LastUserGestureTimeStamp() {
|
||||
MOZ_ASSERT(IsInProcess());
|
||||
if (nsGlobalWindowInner* innerWindow = GetInnerWindow()) {
|
||||
if (Performance* perf = innerWindow->GetPerformance()) {
|
||||
return perf->GetDOMTiming()->TimeStampToDOMHighRes(mUserGestureStart);
|
||||
}
|
||||
}
|
||||
NS_WARNING(
|
||||
"Unable to calculate DOMHighResTimeStamp for LastUserGestureTimeStamp");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool WindowContext::HasValidTransientUserGestureActivation() {
|
||||
MOZ_ASSERT(IsInProcess());
|
||||
|
||||
if (GetUserActivationState() != UserActivation::State::FullActivated) {
|
||||
MOZ_ASSERT(mUserGestureStart.IsNull(),
|
||||
"mUserGestureStart should be null if the document hasn't ever "
|
||||
"been activated by user gesture");
|
||||
// mUserGestureStart should be null if the document hasn't ever been
|
||||
// activated by user gesture
|
||||
MOZ_ASSERT_IF(GetUserActivationState() == UserActivation::State::None,
|
||||
mUserGestureStart.IsNull());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/dom/MaybeDiscarded.h"
|
||||
#include "mozilla/dom/SyncedContext.h"
|
||||
#include "mozilla/dom/UserActivation.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsILoadInfo.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
@ -188,6 +189,10 @@ class WindowContext : public nsISupports, public nsWrapperCache {
|
||||
// out.
|
||||
bool HasValidTransientUserGestureActivation();
|
||||
|
||||
// Reture timestamp of last user gesture in milliseconds relative to
|
||||
// navigation start timestamp.
|
||||
DOMHighResTimeStamp LastUserGestureTimeStamp();
|
||||
|
||||
// Return true if the corresponding window has valid transient user gesture
|
||||
// activation and the transient user gesture activation had been consumed
|
||||
// successfully.
|
||||
|
@ -16261,6 +16261,13 @@ bool Document::HasBeenUserGestureActivated() {
|
||||
return wc && wc->HasBeenUserGestureActivated();
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp Document::LastUserGestureTimeStamp() {
|
||||
if (RefPtr<WindowContext> wc = GetWindowContext()) {
|
||||
return wc->LastUserGestureTimeStamp();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Document::ClearUserGestureActivation() {
|
||||
if (RefPtr<BrowsingContext> bc = GetBrowsingContext()) {
|
||||
bc = bc->Top();
|
||||
|
@ -3669,6 +3669,10 @@ class Document : public nsINode,
|
||||
// document in the document tree.
|
||||
bool HasBeenUserGestureActivated();
|
||||
|
||||
// Reture timestamp of last user gesture in milliseconds relative to
|
||||
// navigation start timestamp.
|
||||
DOMHighResTimeStamp LastUserGestureTimeStamp();
|
||||
|
||||
// Return true if there is transient user gesture activation and it hasn't yet
|
||||
// timed out.
|
||||
bool HasValidTransientUserGestureActivation() const;
|
||||
|
@ -12,7 +12,8 @@ onmessage = function(e) {
|
||||
if (e.data === "get") {
|
||||
parent.postMessage({
|
||||
isActivated: SpecialPowers.wrap(document).hasValidTransientUserGestureActivation,
|
||||
hasBeenActivated: SpecialPowers.wrap(document).hasBeenUserGestureActivated
|
||||
hasBeenActivated: SpecialPowers.wrap(document).hasBeenUserGestureActivated,
|
||||
lastActivationTimestamp: SpecialPowers.wrap(document).lastUserGestureTimeStamp,
|
||||
}, "*");
|
||||
}
|
||||
};
|
||||
|
@ -22,25 +22,32 @@ function waitForEvent(aTarget, aEvent, aCallback) {
|
||||
|
||||
let [iframe0, iframe1] = document.querySelectorAll("iframe");
|
||||
|
||||
function doCheck(aDocument, aName, aHasBeenUserGestureActivated) {
|
||||
is(SpecialPowers.wrap(aDocument).hasBeenUserGestureActivated,
|
||||
aHasBeenUserGestureActivated,
|
||||
`check has-been-user-activated on the ${aName}`);
|
||||
if (aHasBeenUserGestureActivated) {
|
||||
ok(SpecialPowers.wrap(aDocument).lastUserGestureTimeStamp > 0,
|
||||
`check last-user-gesture-timestamp on the ${aName}`);
|
||||
} else {
|
||||
is(SpecialPowers.wrap(aDocument).lastUserGestureTimeStamp, 0,
|
||||
`check last-user-gesture-timestamp on the ${aName}`);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function checkInitialStatus() {
|
||||
ok(!SpecialPowers.wrap(document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on top-level document");
|
||||
ok(!SpecialPowers.wrap(frames[0].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on first iframe");
|
||||
ok(!SpecialPowers.wrap(frames[1].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on second iframe");
|
||||
doCheck(document, "top-level document", false);
|
||||
doCheck(frames[0].document, "first iframe", false);
|
||||
doCheck(frames[1].document, "second iframe", false);
|
||||
});
|
||||
|
||||
add_task(async function triggerUserActivation() {
|
||||
// Trigger user activation on the first iframe.
|
||||
SpecialPowers.wrap(frames[0].document).notifyUserGestureActivation();
|
||||
// We should also propagate to all the ancestors.
|
||||
ok(SpecialPowers.wrap(document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the top-level document");
|
||||
ok(SpecialPowers.wrap(frames[0].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the first iframe");
|
||||
ok(!SpecialPowers.wrap(frames[1].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the second iframe");
|
||||
doCheck(document, "top-level document", true);
|
||||
doCheck(frames[0].document, "first iframe", true);
|
||||
doCheck(frames[1].document, "second iframe", false);
|
||||
});
|
||||
|
||||
add_task(async function iframeNavigation() {
|
||||
@ -48,26 +55,22 @@ add_task(async function iframeNavigation() {
|
||||
await waitForEvent(frames[0].frameElement, "load", () => {});
|
||||
// We should reset the flag on iframe that navigates away from current page,
|
||||
// but the flag on its ancestor isn't changed.
|
||||
ok(SpecialPowers.wrap(document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the top-level document");
|
||||
ok(!SpecialPowers.wrap(frames[0].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the first iframe");
|
||||
ok(!SpecialPowers.wrap(frames[1].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the second iframe");
|
||||
doCheck(document, "top-level document", true);
|
||||
doCheck(frames[0].document, "first iframe", false);
|
||||
doCheck(frames[1].document, "second iframe", false);
|
||||
});
|
||||
|
||||
add_task(async function triggerUserActivationOnCrossOriginFrame() {
|
||||
// Reset the activation flag.
|
||||
SpecialPowers.wrap(document).clearUserGestureActivation();
|
||||
doCheck(document, "top-level document", false);
|
||||
|
||||
// load cross-origin test page on iframe.
|
||||
frames[0].frameElement.src = "https://example.com/tests/dom/base/test/useractivation/file_iframe_user_activated.html";
|
||||
await waitForEvent(window, "message", (event) => {
|
||||
if (event.data === "done") {
|
||||
ok(SpecialPowers.wrap(document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the top-level document");
|
||||
ok(!SpecialPowers.wrap(frames[1].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the second iframe");
|
||||
doCheck(document, "top-level document", true);
|
||||
doCheck(frames[1].document, "second iframe", false);
|
||||
} else {
|
||||
ok(false, "receive unexpected message: " + event.data);
|
||||
}
|
||||
@ -88,15 +91,14 @@ add_task(async function propagateToSameOriginConnectedSubframe() {
|
||||
|
||||
// Trigger user activation on top-level document.
|
||||
SpecialPowers.wrap(document).notifyUserGestureActivation();
|
||||
ok(SpecialPowers.wrap(document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the top-level document");
|
||||
ok(SpecialPowers.wrap(iframe1.contentDocument).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the second iframe");
|
||||
doCheck(document, "top-level document", true);
|
||||
doCheck(iframe1.contentDocument, "second iframe", true);
|
||||
|
||||
iframe0.contentWindow.postMessage("get", "*");
|
||||
await waitForEvent(window, "message", (event) => {
|
||||
if (typeof event.data === "object") {
|
||||
ok(!event.data.hasBeenActivated, "check has-been-user-activated on the first iframe");
|
||||
is(event.data.lastActivationTimestamp, 0, "check last-user-gesture-timestamp on the first iframe");
|
||||
} else {
|
||||
ok(false, "receive unexpected message: " + event.data);
|
||||
}
|
||||
|
@ -24,11 +24,30 @@ function waitForEvent(aTarget, aEvent, aCallback) {
|
||||
});
|
||||
}
|
||||
|
||||
function doCheck(aDocument, aName, aHasBeenUserGestureActivated,
|
||||
aHasValidTransientUserGestureActivation,
|
||||
aLastUserGestureTimeStamp) {
|
||||
is(SpecialPowers.wrap(aDocument).hasBeenUserGestureActivated,
|
||||
aHasBeenUserGestureActivated,
|
||||
`check has-been-user-activated on the ${aName}`);
|
||||
is(SpecialPowers.wrap(aDocument).hasValidTransientUserGestureActivation,
|
||||
aHasValidTransientUserGestureActivation,
|
||||
`check has-valid-transient-user-activation on the ${aName}`);
|
||||
is(SpecialPowers.wrap(aDocument).lastUserGestureTimeStamp,
|
||||
aLastUserGestureTimeStamp,
|
||||
`check last-user-gesture-timestamp on the ${aName}`);
|
||||
}
|
||||
|
||||
add_task(async function checkInitialStatus() {
|
||||
doCheck(document, "top-level document", false, false, 0);
|
||||
ok(!SpecialPowers.wrap(document).consumeTransientUserGestureActivation(),
|
||||
"consume transient-user-activation on top-level document");
|
||||
|
||||
doCheck(frames[0].document, "first iframe", false, false, 0);
|
||||
ok(!SpecialPowers.wrap(frames[0].document).consumeTransientUserGestureActivation(),
|
||||
"consume transient-user-activation on first iframe");
|
||||
|
||||
doCheck(frames[1].document, "second iframe", false, false, 0);
|
||||
ok(!SpecialPowers.wrap(frames[1].document).consumeTransientUserGestureActivation(),
|
||||
"consume transient-user-activation on second iframe");
|
||||
});
|
||||
@ -36,6 +55,8 @@ add_task(async function checkInitialStatus() {
|
||||
add_task(async function consumeTransientUserActivation() {
|
||||
// Trigger user activation on the first iframe.
|
||||
SpecialPowers.wrap(frames[0].document).notifyUserGestureActivation();
|
||||
let lastTimeStampTop = SpecialPowers.wrap(document).lastUserGestureTimeStamp;
|
||||
let lastTimeStampFirst = SpecialPowers.wrap(frames[0].document).lastUserGestureTimeStamp;
|
||||
|
||||
// Try to consume transient user activation.
|
||||
ok(!SpecialPowers.wrap(frames[1].document).consumeTransientUserGestureActivation(),
|
||||
@ -46,21 +67,11 @@ add_task(async function consumeTransientUserActivation() {
|
||||
ok(!SpecialPowers.wrap(document).consumeTransientUserGestureActivation(),
|
||||
"consume transient-user-activation on top-level document");
|
||||
|
||||
// Check has-valid-transient-user-activation
|
||||
ok(!SpecialPowers.wrap(document).hasValidTransientUserGestureActivation,
|
||||
"check has-valid-transient-user-activation on the top-level document");
|
||||
ok(!SpecialPowers.wrap(frames[0].document).hasValidTransientUserGestureActivation,
|
||||
"check has-valid-transient-user-activation on the first iframe");
|
||||
ok(!SpecialPowers.wrap(frames[1].document).hasValidTransientUserGestureActivation,
|
||||
"check has-valid-transient-user-activation on the second iframe");
|
||||
|
||||
// Should not affect has-been-user-activated
|
||||
ok(SpecialPowers.wrap(document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the top-level document");
|
||||
ok(SpecialPowers.wrap(frames[0].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the first iframe");
|
||||
ok(!SpecialPowers.wrap(frames[1].document).hasBeenUserGestureActivated,
|
||||
"check has-been-user-activated on the second iframe");
|
||||
// Check has-valid-transient-user-activation and should not affect
|
||||
// has-been-user-activated
|
||||
doCheck(document, "top-level document", true, false, lastTimeStampTop);
|
||||
doCheck(frames[0].document, "first iframe", true, false, lastTimeStampFirst);
|
||||
doCheck(frames[1].document, "second iframe", false, false, 0);
|
||||
});
|
||||
|
||||
add_task(async function consumeTransientUserActivationTimeout() {
|
||||
|
@ -585,6 +585,8 @@ partial interface Document {
|
||||
[ChromeOnly]
|
||||
readonly attribute boolean hasValidTransientUserGestureActivation;
|
||||
[ChromeOnly]
|
||||
readonly attribute DOMHighResTimeStamp lastUserGestureTimeStamp;
|
||||
[ChromeOnly]
|
||||
boolean consumeTransientUserGestureActivation();
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user