☠☠ backed out by ae6cc33d751b ☠ ☠ | |
author | Paul Zuehlcke <pbz@mozilla.com> |
Mon, 11 Oct 2021 14:54:49 +0000 | |
changeset 595371 | 2c86a1d595b83492f8eb19b2208e98d77dc51f10 |
parent 595370 | e15c1956a0ebadaf0e4e869eb8c7e24667d63359 |
child 595372 | 759d9b550cca9061128e98b24909c9547e02bc89 |
push id | 151230 |
push user | pzuhlcke@mozilla.com |
push date | Mon, 11 Oct 2021 14:57:15 +0000 |
treeherder | autoland@759d9b550cca [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | edgar, anti-tracking-reviewers, timhuang |
bugs | 1732919 |
milestone | 95.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -16574,54 +16574,66 @@ Document::GetContentBlockingEvents() { already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess( mozilla::ErrorResult& aRv) { nsIGlobalObject* global = GetScopeObject(); if (!global) { aRv.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } - // Propagate user input event handling to the resolve handler - RefPtr<Promise> promise = - Promise::Create(global, aRv, Promise::ePropagateUserInteraction); + RefPtr<Promise> promise = Promise::Create(global, aRv); if (aRv.Failed()) { return nullptr; } + // Window doesn't have user activation, reject. + if (!this->HasValidTransientUserGestureActivation()) { + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + nsLiteralCString("requestStorageAccess"), + this, nsContentUtils::eDOM_PROPERTIES, + "RequestStorageAccessUserGesture"); + promise->MaybeRejectWithUndefined(); + return promise.forget(); + } + nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow(); if (!inner) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } // Step 1. If the document already has been granted access, resolve. RefPtr<nsGlobalWindowOuter> outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow()); if (!outer) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } if (outer->IsStorageAccessPermissionGranted()) { promise->MaybeResolveWithUndefined(); return promise.forget(); } // Step 2. If the document has a null origin, reject. if (NodePrincipal()->GetIsNullPrincipal()) { nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, nsLiteralCString("requestStorageAccess"), this, nsContentUtils::eDOM_PROPERTIES, "RequestStorageAccessNullPrincipal"); + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } RefPtr<BrowsingContext> bc = GetBrowsingContext(); if (!bc) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } // Only enforce third-party checks when there is a reason to enforce them. if (!CookieJarSettings()->GetRejectThirdPartyContexts()) { // Step 3. If the document's frame is the main frame, resolve. if (IsTopLevelContentDocument()) { @@ -16634,22 +16646,24 @@ already_AddRefed<mozilla::dom::Promise> // In fission, if the sub frame's origin differs from the main frame's // origin, they will be in different processes. We use IsInProcess() // check here to deterimine whether they have the same origin. In // non-fission mode, it is always in-process so we need to compare their // principals. if (bc->Top()->IsInProcess()) { nsCOMPtr<nsPIDOMWindowOuter> topOuter = bc->Top()->GetDOMWindow(); if (!topOuter) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } nsCOMPtr<Document> topLevelDoc = topOuter->GetExtantDoc(); if (!topLevelDoc) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } if (topLevelDoc->NodePrincipal()->Equals(NodePrincipal())) { promise->MaybeResolveWithUndefined(); return promise.forget(); } @@ -16658,36 +16672,28 @@ already_AddRefed<mozilla::dom::Promise> // Step 5. If the sub frame is not sandboxed, skip to step 7. // Step 6. If the sub frame doesn't have the token // "allow-storage-access-by-user-activation", reject. if (StorageAccessSandboxed()) { nsContentUtils::ReportToConsole( nsIScriptError::errorFlag, nsLiteralCString("requestStorageAccess"), this, nsContentUtils::eDOM_PROPERTIES, "RequestStorageAccessSandboxed"); + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } // Step 7. If the sub frame's parent frame is not the top frame, reject. RefPtr<BrowsingContext> parentBC = bc->GetParent(); if (parentBC && !parentBC->IsTopContent()) { nsContentUtils::ReportToConsole( nsIScriptError::errorFlag, nsLiteralCString("requestStorageAccess"), this, nsContentUtils::eDOM_PROPERTIES, "RequestStorageAccessNested"); - promise->MaybeRejectWithUndefined(); - return promise.forget(); - } - - // Step 8. If the browser is not processing a user gesture, reject. - if (!UserActivation::IsHandlingUserInput()) { - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - nsLiteralCString("requestStorageAccess"), - this, nsContentUtils::eDOM_PROPERTIES, - "RequestStorageAccessUserGesture"); + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } // Step 9. Check any additional rules that the browser has. // Examples: skip-lists, on-device classification, // user settings, anti-clickjacking heuristics, or prompting the // user for explicit permission. Reject if some rule is not fulfilled. @@ -16695,16 +16701,17 @@ already_AddRefed<mozilla::dom::Promise> // Only do something special for third-party tracking content. uint32_t antiTrackingRejectedReason = 0; if (StorageDisabledByAntiTracking(this, nullptr, antiTrackingRejectedReason)) { // If storage is disabled because of a custom cookie permission for the // site, reject. if (antiTrackingRejectedReason == nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } // Note: If this has returned true, the top-level document is guaranteed // to not be on the Content Blocking allow list. MOZ_ASSERT(!CookieJarSettings()->GetIsOnContentBlockingAllowList()); @@ -16789,27 +16796,33 @@ already_AddRefed<mozilla::dom::Promise> sapr->RequestDelayedTask( inner->EventTargetFor(TaskCategory::Other), ContentPermissionRequestBase::DelayedTaskType::Request); }); return p; }; + + // Consume user activation before entering the async part of this method. + // This prevents usage of other transient activation-gated APIs. + this->ConsumeTransientUserGestureActivation(); + ContentBlocking::AllowAccessFor( NodePrincipal(), bc, ContentBlockingNotifier::eStorageAccessAPI, performFinalChecks) ->Then( GetCurrentSerialEventTarget(), __func__, - [outer, promise] { + [self, outer, promise] { // Step 10. Grant the document access to cookies and store // that fact for // the purposes of future calls to // hasStorageAccess() and requestStorageAccess(). outer->SetStorageAccessPermissionGranted(true); + self->NotifyUserGestureActivation(); promise->MaybeResolveWithUndefined(); }, [outer, promise] { outer->SetStorageAccessPermissionGranted(false); promise->MaybeRejectWithUndefined(); }); return promise.forget(); @@ -16824,41 +16837,53 @@ already_AddRefed<mozilla::dom::Promise> already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin( const nsAString& aThirdPartyOrigin, mozilla::ErrorResult& aRv) { nsIGlobalObject* global = GetScopeObject(); if (!global) { aRv.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } - RefPtr<Promise> promise = - Promise::Create(global, aRv, Promise::ePropagateUserInteraction); + RefPtr<Promise> promise = Promise::Create(global, aRv); if (aRv.Failed()) { return nullptr; } + // Window doesn't have user activation, reject. + if (!this->HasValidTransientUserGestureActivation()) { + nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, + nsLiteralCString("requestStorageAccess"), + this, nsContentUtils::eDOM_PROPERTIES, + "RequestStorageAccessUserGesture"); + promise->MaybeRejectWithUndefined(); + return promise.forget(); + } + nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow(); if (!inner) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } // We only allow request storage access for third-party origin from the // first-party context. if (AntiTrackingUtils::IsThirdPartyWindow(inner, nullptr)) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } // If the document has a null origin, reject. if (NodePrincipal()->GetIsNullPrincipal()) { nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, nsLiteralCString("requestStorageAccess"), this, nsContentUtils::eDOM_PROPERTIES, "RequestStorageAccessNullPrincipal"); + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } nsCOMPtr<nsIURI> thirdPartyURI; nsresult rv = NS_NewURI(getter_AddRefs(thirdPartyURI), aThirdPartyOrigin); if (NS_WARN_IF(NS_FAILED(rv))) { aRv.Throw(rv); @@ -16873,44 +16898,40 @@ already_AddRefed<mozilla::dom::Promise> nsContentUtils::IsInPrivateBrowsing(this), &isSameOrigin); if (isSameOrigin) { promise->MaybeResolveWithUndefined(); return promise.forget(); } } - // If the browser is not processing a user gesture, reject. - if (!UserActivation::IsHandlingUserInput()) { - nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, - nsLiteralCString("requestStorageAccess"), - this, nsContentUtils::eDOM_PROPERTIES, - "RequestStorageAccessUserGesture"); - promise->MaybeRejectWithUndefined(); - return promise.forget(); - } - // Check any additional rules that the browser has. if (CookieJarSettings()->GetRejectThirdPartyContexts()) { RefPtr<BrowsingContext> bc = GetBrowsingContext(); if (!bc) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal( thirdPartyURI, NodePrincipal()->OriginAttributesRef()); if (!principal) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } RefPtr<Document> self(this); + // Consume user activation before entering the async part of this method. + // This prevents usage of other transient activation-gated APIs. + this->ConsumeTransientUserGestureActivation(); + auto performFinalChecks = [inner, self, principal]() { RefPtr<ContentBlocking::StorageAccessFinalCheckPromise::Private> p = new ContentBlocking::StorageAccessFinalCheckPromise::Private( __func__); RefPtr<StorageAccessPermissionRequest> sapr = StorageAccessPermissionRequest::Create( inner, principal, // Allow @@ -17020,17 +17041,20 @@ already_AddRefed<mozilla::dom::Promise> return ContentBlocking::AllowAccessFor( principal, bc, ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI, performFinalChecks); }) ->Then( GetCurrentSerialEventTarget(), __func__, - [promise] { promise->MaybeResolveWithUndefined(); }, + [self, promise] { + self->NotifyUserGestureActivation(); + promise->MaybeResolveWithUndefined(); + }, [promise] { promise->MaybeRejectWithUndefined(); }); return promise.forget(); } promise->MaybeResolveWithUndefined(); return promise.forget(); }