Bug 1611961 - Move UserActivationState from BrowsingContext to WindowContext; r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D83126
This commit is contained in:
Edgar Chen 2020-08-17 11:02:34 +00:00
parent 35bbacb047
commit b2ef530476
11 changed files with 164 additions and 129 deletions

View File

@ -1076,7 +1076,9 @@ bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) {
// If SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION flag is not on, we are not
// sandboxed from our top if we have user interaction.
if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION) &&
HasValidTransientUserGestureActivation() && aTarget == Top()) {
mCurrentWindowContext &&
mCurrentWindowContext->HasValidTransientUserGestureActivation() &&
aTarget == Top()) {
return false;
}
@ -1167,60 +1169,6 @@ JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx,
return val.toObjectOrNull();
}
void BrowsingContext::NotifyUserGestureActivation() {
// Return value of setting synced field should be checked. See bug 1656492.
Unused << SetUserActivationState(UserActivation::State::FullActivated);
}
void BrowsingContext::NotifyResetUserGestureActivation() {
// Return value of setting synced field should be checked. See bug 1656492.
Unused << SetUserActivationState(UserActivation::State::None);
}
bool BrowsingContext::HasBeenUserGestureActivated() {
return GetUserActivationState() != UserActivation::State::None;
}
bool BrowsingContext::HasValidTransientUserGestureActivation() {
MOZ_ASSERT(mIsInProcess);
if (GetUserActivationState() != UserActivation::State::FullActivated) {
MOZ_ASSERT(mUserGestureStart.IsNull(),
"mUserGestureStart should be null if the document hasn't ever "
"been activated by user gesture");
return false;
}
MOZ_ASSERT(!mUserGestureStart.IsNull(),
"mUserGestureStart shouldn't be null if the document has ever "
"been activated by user gesture");
TimeDuration timeout = TimeDuration::FromMilliseconds(
StaticPrefs::dom_user_activation_transient_timeout());
return timeout <= TimeDuration() ||
(TimeStamp::Now() - mUserGestureStart) <= timeout;
}
bool BrowsingContext::ConsumeTransientUserGestureActivation() {
MOZ_ASSERT(mIsInProcess);
if (!HasValidTransientUserGestureActivation()) {
return false;
}
BrowsingContext* top = Top();
top->PreOrderWalk([&](BrowsingContext* aContext) {
if (aContext->GetUserActivationState() ==
UserActivation::State::FullActivated) {
// Updating user activation state on a discarded context has no effect.
Unused << aContext->SetUserActivationState(
UserActivation::State::HasBeenActivated);
}
});
return true;
}
bool BrowsingContext::CanSetOriginAttributes() {
// A discarded BrowsingContext has already been destroyed, and cannot modify
// its OriginAttributes.
@ -2058,23 +2006,6 @@ void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) {
"browsing context");
}
void BrowsingContext::DidSet(FieldIndex<IDX_UserActivationState>) {
MOZ_ASSERT_IF(!mIsInProcess, mUserGestureStart.IsNull());
USER_ACTIVATION_LOG("Set user gesture activation %" PRIu8
" for %s browsing context 0x%08" PRIx64,
static_cast<uint8_t>(GetUserActivationState()),
XRE_IsParentProcess() ? "Parent" : "Child", Id());
if (mIsInProcess) {
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();
}
}
void BrowsingContext::DidSet(FieldIndex<IDX_IsActive>, bool aOldValue) {
if (!IsTop() || aOldValue == GetIsActive() ||
!StaticPrefs::dom_suspend_inactive_enabled()) {

View File

@ -495,29 +495,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
// This function would be called when its corresponding document is activated
// by user gesture, and we would set the flag in the top level browsing
// context.
void NotifyUserGestureActivation();
// This function would be called when we want to reset the user gesture
// activation flag of the top level browsing context.
void NotifyResetUserGestureActivation();
// Return true if its corresponding document has been activated by user
// gesture.
bool HasBeenUserGestureActivated();
// Return true if its corresponding document has transient user gesture
// activation and the transient user gesture activation haven't yet timed
// out.
bool HasValidTransientUserGestureActivation();
// Return true if the corresponding document has valid transient user gesture
// activation and the transient user gesture activation had been consumed
// successfully.
bool ConsumeTransientUserGestureActivation();
// Return the window proxy object that corresponds to this browsing context.
inline JSObject* GetWindowProxy() const { return mWindowProxy; }
inline JSObject* GetUnbarrieredWindowProxy() const {
@ -731,7 +708,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return true;
}
void DidSet(FieldIndex<IDX_UserActivationState>);
void DidSet(FieldIndex<IDX_IsActive>, bool aOldValue);
// Ensure that we only set the flag on the top level browsingContext.

View File

@ -426,14 +426,15 @@ void CanonicalBrowsingContext::CanonicalDiscard() {
}
void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() {
if (!GetCurrentWindowGlobal()) {
WindowContext* windowContext = GetCurrentWindowContext();
if (!windowContext) {
return;
}
// As this function would only be called when user click the play icon on the
// tab bar. That's clear user intent to play, so gesture activate the browsing
// tab bar. That's clear user intent to play, so gesture activate the window
// context so that the block-autoplay logic allows the media to autoplay.
NotifyUserGestureActivation();
windowContext->NotifyUserGestureActivation();
AUTOPLAY_LOG("NotifyStartDelayedAutoplayMedia for chrome bc 0x%08" PRIx64,
Id());
StartDelayedAutoplayMediaComponents();

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/SyncedContextInlines.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/ClearOnShutdown.h"
#include "nsGlobalWindowInner.h"
@ -27,6 +28,11 @@ template class syncedcontext::Transaction<WindowContext>;
static LazyLogModule gWindowContextLog("WindowContext");
extern mozilla::LazyLogModule gUserInteractionPRLog;
#define USER_ACTIVATION_LOG(msg, ...) \
MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
using WindowContextByIdMap = nsDataHashtable<nsUint64HashKey, WindowContext*>;
static StaticAutoPtr<WindowContextByIdMap> gWindowContexts;
@ -213,6 +219,23 @@ bool WindowContext::CanSet(
return CheckOnlyOwningProcessCanSet(aSource);
}
void WindowContext::DidSet(FieldIndex<IDX_UserActivationState>) {
MOZ_ASSERT_IF(!mInProcess, mUserGestureStart.IsNull());
USER_ACTIVATION_LOG("Set user gesture activation %" PRIu8
" for %s browsing context 0x%08" PRIx64,
static_cast<uint8_t>(GetUserActivationState()),
XRE_IsParentProcess() ? "Parent" : "Child", Id());
if (mInProcess) {
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();
}
}
void WindowContext::DidSet(FieldIndex<IDX_HasReportedShadowDOMUsage>,
bool aOldValue) {
if (!aOldValue && GetHasReportedShadowDOMUsage() && IsInProcess()) {
@ -304,6 +327,59 @@ void WindowContext::AddMixedContentSecurityState(uint32_t aStateFlags) {
}
}
void WindowContext::NotifyUserGestureActivation() {
Unused << SetUserActivationState(UserActivation::State::FullActivated);
}
void WindowContext::NotifyResetUserGestureActivation() {
Unused << SetUserActivationState(UserActivation::State::None);
}
bool WindowContext::HasBeenUserGestureActivated() {
return GetUserActivationState() != UserActivation::State::None;
}
bool WindowContext::HasValidTransientUserGestureActivation() {
MOZ_ASSERT(mInProcess);
if (GetUserActivationState() != UserActivation::State::FullActivated) {
MOZ_ASSERT(mUserGestureStart.IsNull(),
"mUserGestureStart should be null if the document hasn't ever "
"been activated by user gesture");
return false;
}
MOZ_ASSERT(!mUserGestureStart.IsNull(),
"mUserGestureStart shouldn't be null if the document has ever "
"been activated by user gesture");
TimeDuration timeout = TimeDuration::FromMilliseconds(
StaticPrefs::dom_user_activation_transient_timeout());
return timeout <= TimeDuration() ||
(TimeStamp::Now() - mUserGestureStart) <= timeout;
}
bool WindowContext::ConsumeTransientUserGestureActivation() {
MOZ_ASSERT(mInProcess);
MOZ_ASSERT(!IsCached());
if (!HasValidTransientUserGestureActivation()) {
return false;
}
BrowsingContext* top = mBrowsingContext->Top();
top->PreOrderWalk([&](BrowsingContext* aBrowsingContext) {
WindowContext* windowContext = aBrowsingContext->GetCurrentWindowContext();
if (windowContext && windowContext->GetUserActivationState() ==
UserActivation::State::FullActivated) {
Unused << windowContext->SetUserActivationState(
UserActivation::State::HasBeenActivated);
}
});
return true;
}
bool WindowContext::CanShowPopup() {
uint32_t permit = GetPopupPermission();
if (permit == nsIPermissionManager::ALLOW_ACTION) {

View File

@ -12,6 +12,7 @@
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/MaybeDiscarded.h"
#include "mozilla/dom/SyncedContext.h"
#include "mozilla/dom/UserActivation.h"
#include "nsILoadInfo.h"
#include "nsWrapperCache.h"
@ -54,6 +55,9 @@ class BrowsingContextGroup;
/* Whether the user has overriden the mixed content blocker to allow \
* mixed content loads to happen */ \
FIELD(AllowMixedContent, bool) \
/* Controls whether the WindowContext is currently considered to be \
* activated by a gesture */ \
FIELD(UserActivationState, UserActivation::State) \
FIELD(EmbedderPolicy, nsILoadInfo::CrossOriginEmbedderPolicy) \
/* True if this document tree contained an HTMLMediaElement that \
* played audibly. This should only be set on top level context. */ \
@ -131,6 +135,28 @@ class WindowContext : public nsISupports, public nsWrapperCache {
// top window context.
void AddMixedContentSecurityState(uint32_t aStateFlags);
// This function would be called when its corresponding window is activated
// by user gesture.
void NotifyUserGestureActivation();
// This function would be called when we want to reset the user gesture
// activation flag.
void NotifyResetUserGestureActivation();
// Return true if its corresponding window has been activated by user
// gesture.
bool HasBeenUserGestureActivated();
// Return true if its corresponding window has transient user gesture
// activation and the transient user gesture activation haven't yet timed
// out.
bool HasValidTransientUserGestureActivation();
// Return true if the corresponding window has valid transient user gesture
// activation and the transient user gesture activation had been consumed
// successfully.
bool ConsumeTransientUserGestureActivation();
bool CanShowPopup();
protected:
@ -199,6 +225,12 @@ class WindowContext : public nsISupports, public nsWrapperCache {
bool CanSet(FieldIndex<IDX_DelegatedExactHostMatchPermissions>,
const PermissionDelegateHandler::DelegatedPermissionList& aValue,
ContentParent* aSource);
bool CanSet(FieldIndex<IDX_UserActivationState>,
const UserActivation::State& aUserActivationState,
ContentParent* aSource) {
return true;
}
bool CanSet(FieldIndex<IDX_HasReportedShadowDOMUsage>, const bool& aValue,
ContentParent* aSource) {
return true;
@ -213,6 +245,7 @@ class WindowContext : public nsISupports, public nsWrapperCache {
void DidSet(FieldIndex<I>) {}
template <size_t I, typename T>
void DidSet(FieldIndex<I>, T&& aOldValue) {}
void DidSet(FieldIndex<IDX_UserActivationState>);
uint64_t mInnerWindowId;
uint64_t mOuterWindowId;
@ -226,6 +259,10 @@ class WindowContext : public nsISupports, public nsWrapperCache {
bool mIsDiscarded = false;
bool mInProcess = false;
// The start time of user gesture, this is only available if the window
// context is in process.
TimeStamp mUserGestureStart;
};
using WindowContextTransaction = WindowContext::BaseTransaction;

View File

@ -3940,6 +3940,7 @@ nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext();
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
loadState->SetReferrerInfo(aReferrerInfo);
loadState->SetOriginalURI(originalURI);
@ -3957,8 +3958,7 @@ nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
loadState->SetSourceBrowsingContext(aBrowsingContext);
loadState->SetBaseURI(baseURI);
loadState->SetHasValidUserGestureActivation(
aBrowsingContext &&
aBrowsingContext->HasValidTransientUserGestureActivation());
context && context->HasValidTransientUserGestureActivation());
return aDocShell->InternalLoad(loadState);
}
@ -9723,9 +9723,10 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
Maybe<mozilla::dom::ClientInfo>(),
Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
sandboxFlags);
RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
if (mLoadType != LOAD_ERROR_PAGE &&
mBrowsingContext->HasValidTransientUserGestureActivation()) {
if (mLoadType != LOAD_ERROR_PAGE && context &&
context->HasValidTransientUserGestureActivation()) {
aLoadState->SetHasValidUserGestureActivation(true);
}
@ -12175,6 +12176,7 @@ nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
nsCOMPtr<nsIReferrerInfo> referrerInfo =
isElementAnchorOrArea ? new ReferrerInfo(*aContent->AsElement())
: new ReferrerInfo(*referrerDoc);
RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
aLoadState->SetReferrerInfo(referrerInfo);
@ -12183,8 +12185,7 @@ nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
aLoadState->SetLoadType(loadType);
aLoadState->SetSourceBrowsingContext(mBrowsingContext);
aLoadState->SetHasValidUserGestureActivation(
mBrowsingContext &&
mBrowsingContext->HasValidTransientUserGestureActivation());
context && context->HasValidTransientUserGestureActivation());
nsresult rv = InternalLoad(aLoadState);

View File

@ -15376,8 +15376,13 @@ BrowsingContext* Document::GetBrowsingContext() const {
void Document::NotifyUserGestureActivation() {
if (RefPtr<BrowsingContext> bc = GetBrowsingContext()) {
bc->PreOrderWalk([&](BrowsingContext* aContext) {
nsIDocShell* docShell = aContext->GetDocShell();
bc->PreOrderWalk([&](BrowsingContext* aBC) {
WindowContext* windowContext = aBC->GetCurrentWindowContext();
if (!windowContext) {
return;
}
nsIDocShell* docShell = aBC->GetDocShell();
if (!docShell) {
return;
}
@ -15390,38 +15395,42 @@ void Document::NotifyUserGestureActivation() {
// XXXedgar we probably could just check `IsInProcess()` after fission
// enable.
if (NodePrincipal()->Equals(document->NodePrincipal())) {
aContext->NotifyUserGestureActivation();
windowContext->NotifyUserGestureActivation();
}
});
for (bc = bc->GetParent(); bc; bc = bc->GetParent()) {
bc->NotifyUserGestureActivation();
if (WindowContext* windowContext = bc->GetCurrentWindowContext()) {
windowContext->NotifyUserGestureActivation();
}
}
}
}
bool Document::HasBeenUserGestureActivated() {
RefPtr<BrowsingContext> bc = GetBrowsingContext();
return bc && bc->HasBeenUserGestureActivated();
RefPtr<WindowContext> wc = GetWindowContext();
return wc && wc->HasBeenUserGestureActivated();
}
void Document::ClearUserGestureActivation() {
if (RefPtr<BrowsingContext> bc = GetBrowsingContext()) {
bc = bc->Top();
bc->PreOrderWalk([&](BrowsingContext* aContext) {
aContext->NotifyResetUserGestureActivation();
bc->PreOrderWalk([&](BrowsingContext* aBC) {
if (WindowContext* windowContext = aBC->GetCurrentWindowContext()) {
windowContext->NotifyResetUserGestureActivation();
}
});
}
}
bool Document::HasValidTransientUserGestureActivation() {
RefPtr<BrowsingContext> bc = GetBrowsingContext();
return bc && bc->HasValidTransientUserGestureActivation();
RefPtr<WindowContext> wc = GetWindowContext();
return wc && wc->HasValidTransientUserGestureActivation();
}
bool Document::ConsumeTransientUserGestureActivation() {
RefPtr<BrowsingContext> bc = GetBrowsingContext();
return bc && bc->ConsumeTransientUserGestureActivation();
RefPtr<WindowContext> wc = GetWindowContext();
return wc && wc->ConsumeTransientUserGestureActivation();
}
void Document::SetDocTreeHadAudibleMedia() {

View File

@ -16,6 +16,7 @@
#include "nsGlobalWindow.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/WindowContext.h"
namespace mozilla {
namespace dom {
@ -133,10 +134,10 @@ void LocationBase::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
nsContentUtils::CallerInnerWindow();
if (sourceWindow) {
RefPtr<BrowsingContext> sourceBC = sourceWindow->GetBrowsingContext();
loadState->SetSourceBrowsingContext(sourceBC);
WindowContext* context = sourceWindow->GetWindowContext();
loadState->SetSourceBrowsingContext(sourceWindow->GetBrowsingContext());
loadState->SetHasValidUserGestureActivation(
sourceBC && sourceBC->HasValidTransientUserGestureActivation());
context && context->HasValidTransientUserGestureActivation());
}
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);

View File

@ -1653,6 +1653,9 @@ void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
if (!mWindowGlobalChild) {
mWindowGlobalChild = WindowGlobalChild::Create(this);
}
MOZ_ASSERT(!GetWindowContext()->HasBeenUserGestureActivated(),
"WindowContext should always not have user gesture activation at "
"this point.");
UpdatePermissions();
@ -1663,10 +1666,6 @@ void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
permDelegateHandler->PopulateAllDelegatedPermissions();
}
if (mWindowGlobalChild && GetBrowsingContext()) {
GetBrowsingContext()->NotifyResetUserGestureActivation();
}
#if defined(MOZ_WIDGET_ANDROID)
// When we insert the new document to the window in the top-level browsing
// context, we should reset the status of the request which is used for the

View File

@ -78,8 +78,9 @@ static bool IsWindowAllowedToPlay(nsPIDOMWindowInner* aWindow) {
return true;
}
RefPtr<BrowsingContext> topLevelBC = aWindow->GetBrowsingContext()->Top();
if (topLevelBC->HasBeenUserGestureActivated()) {
WindowContext* topContext =
aWindow->GetBrowsingContext()->GetTopWindowContext();
if (topContext && topContext->HasBeenUserGestureActivated()) {
AUTOPLAY_LOG(
"Allow autoplay as top-level context has been activated by user "
"gesture.");

View File

@ -1138,17 +1138,22 @@ nsresult nsWindowWatcher::OpenWindowInternal(
newBC->UseRemoteSubframes() ==
!!(chromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW));
nsCOMPtr<nsPIDOMWindowInner> pInnerWin =
parentWindow ? parentWindow->GetCurrentInnerWindow() : nullptr;
;
RefPtr<nsDocShellLoadState> loadState = aLoadState;
if (uriToLoad && loadState) {
// If a URI was passed to this function, open that, not what was passed in
// the original LoadState. See Bug 1515433.
loadState->SetURI(uriToLoad);
} else if (uriToLoad && aNavigate && !loadState) {
RefPtr<WindowContext> context =
pInnerWin ? pInnerWin->GetWindowContext() : nullptr;
loadState = new nsDocShellLoadState(uriToLoad);
loadState->SetSourceBrowsingContext(parentBC);
loadState->SetHasValidUserGestureActivation(
parentBC && parentBC->HasValidTransientUserGestureActivation());
context && context->HasValidTransientUserGestureActivation());
if (parentBC) {
loadState->SetTriggeringSandboxFlags(parentBC->GetSandboxFlags());
}
@ -1249,8 +1254,6 @@ nsresult nsWindowWatcher::OpenWindowInternal(
if (parentStorageManager && newStorageManager) {
RefPtr<Storage> storage;
nsCOMPtr<nsPIDOMWindowInner> pInnerWin =
parentWindow->GetCurrentInnerWindow();
parentStorageManager->GetStorage(
pInnerWin, subjectPrincipal, subjectPrincipal,
newBC->UsePrivateBrowsing(), getter_AddRefs(storage));