Backed out 5 changesets (bug 1945576, bug 1945573, bug 1945577) for causing junit failures @ PromptDelegateTest CLOSED TREE
Backed out changeset bad19f7c69d8 (bug 1945577) Backed out changeset f691730e3dac (bug 1945573) Backed out changeset 194f70082e01 (bug 1945573) Backed out changeset 8ce3493a6152 (bug 1945576) Backed out changeset b34ad4456d7c (bug 1945576)
This commit is contained in:
@@ -52,7 +52,6 @@
|
||||
#include "mozilla/dom/LockManager.h"
|
||||
#include "mozilla/dom/MIDIAccessManager.h"
|
||||
#include "mozilla/dom/MIDIOptionsBinding.h"
|
||||
#include "mozilla/dom/NavigatorLogin.h"
|
||||
#include "mozilla/dom/Permissions.h"
|
||||
#include "mozilla/dom/ServiceWorkerContainer.h"
|
||||
#include "mozilla/dom/StorageManager.h"
|
||||
@@ -165,7 +164,6 @@ 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(mLogin)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrivateAttribution)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserActivation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
|
||||
@@ -256,8 +254,6 @@ void Navigator::Invalidate() {
|
||||
mLocks = nullptr;
|
||||
}
|
||||
|
||||
mLogin = nullptr;
|
||||
|
||||
mPrivateAttribution = nullptr;
|
||||
|
||||
mUserActivation = nullptr;
|
||||
@@ -2274,13 +2270,6 @@ dom::LockManager* Navigator::Locks() {
|
||||
return mLocks;
|
||||
}
|
||||
|
||||
NavigatorLogin* Navigator::Login() {
|
||||
if (!mLogin) {
|
||||
mLogin = new NavigatorLogin(GetWindow()->AsGlobal());
|
||||
}
|
||||
return mLogin;
|
||||
}
|
||||
|
||||
dom::PrivateAttribution* Navigator::PrivateAttribution() {
|
||||
if (!mPrivateAttribution) {
|
||||
mPrivateAttribution = new dom::PrivateAttribution(GetWindow()->AsGlobal());
|
||||
|
||||
@@ -42,7 +42,6 @@ class ServiceWorkerContainer;
|
||||
class CredentialsContainer;
|
||||
class Clipboard;
|
||||
class LockManager;
|
||||
class NavigatorLogin;
|
||||
class PrivateAttribution;
|
||||
class HTMLMediaElement;
|
||||
class AudioContext;
|
||||
@@ -211,7 +210,6 @@ class Navigator final : public nsISupports, public nsWrapperCache {
|
||||
dom::Clipboard* Clipboard();
|
||||
webgpu::Instance* Gpu();
|
||||
dom::LockManager* Locks();
|
||||
NavigatorLogin* Login();
|
||||
dom::PrivateAttribution* PrivateAttribution();
|
||||
|
||||
static bool Webdriver();
|
||||
@@ -309,7 +307,6 @@ class Navigator final : public nsISupports, public nsWrapperCache {
|
||||
RefPtr<webgpu::Instance> mWebGpu;
|
||||
RefPtr<Promise> mSharePromise; // Web Share API related
|
||||
RefPtr<LockManager> mLocks;
|
||||
RefPtr<NavigatorLogin> mLogin;
|
||||
RefPtr<dom::PrivateAttribution> mPrivateAttribution;
|
||||
RefPtr<dom::UserActivation> mUserActivation;
|
||||
RefPtr<dom::WakeLockJS> mWakeLock;
|
||||
|
||||
@@ -98,9 +98,7 @@ static bool ConsumeUserActivation(nsPIDOMWindowInner* aParent) {
|
||||
return doc->ConsumeTransientUserGestureActivation();
|
||||
}
|
||||
|
||||
// static
|
||||
bool CredentialsContainer::IsSameOriginWithAncestors(
|
||||
nsPIDOMWindowInner* aParent) {
|
||||
static bool IsSameOriginWithAncestors(nsPIDOMWindowInner* aParent) {
|
||||
// This method returns true if aParent is either not in a frame / iframe, or
|
||||
// is in a frame or iframe and all ancestors for aParent are the same origin.
|
||||
// This is useful for Credential Management because we need to prohibit
|
||||
|
||||
@@ -38,8 +38,6 @@ class CredentialsContainer final : public nsISupports, public nsWrapperCache {
|
||||
|
||||
already_AddRefed<Promise> PreventSilentAccess(ErrorResult& aRv);
|
||||
|
||||
static bool IsSameOriginWithAncestors(nsPIDOMWindowInner* aParent);
|
||||
|
||||
private:
|
||||
~CredentialsContainer();
|
||||
|
||||
|
||||
@@ -1346,6 +1346,73 @@ RefPtr<IdentityCredential::GetTokenPromise> IdentityCredential::FetchToken(
|
||||
});
|
||||
}
|
||||
|
||||
// static
|
||||
RefPtr<IdentityCredential::GetMetadataPromise>
|
||||
IdentityCredential::FetchMetadata(nsIPrincipal* aPrincipal,
|
||||
const IdentityProviderConfig& aProvider,
|
||||
const IdentityProviderAPIConfig& aManifest) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
// Build the URL
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
nsCString baseURIString = aProvider.mConfigURL.Value();
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(baseURI), baseURIString);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return IdentityCredential::GetMetadataPromise::CreateAndReject(rv,
|
||||
__func__);
|
||||
}
|
||||
nsCOMPtr<nsIURI> idpURI;
|
||||
nsCString metadataSpec = aManifest.mClient_metadata_endpoint;
|
||||
rv = NS_NewURI(getter_AddRefs(idpURI), metadataSpec.get(), baseURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return IdentityCredential::GetMetadataPromise::CreateAndReject(rv,
|
||||
__func__);
|
||||
}
|
||||
nsCString configLocation;
|
||||
rv = idpURI->GetSpec(configLocation);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return IdentityCredential::GetMetadataPromise::CreateAndReject(rv,
|
||||
__func__);
|
||||
}
|
||||
|
||||
// Create the global
|
||||
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
||||
MOZ_ASSERT(xpc, "This should never be null!");
|
||||
nsCOMPtr<nsIGlobalObject> global;
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> sandbox(cx);
|
||||
rv = xpc->CreateSandbox(cx, aPrincipal, sandbox.address());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return IdentityCredential::GetMetadataPromise::CreateAndReject(rv,
|
||||
__func__);
|
||||
}
|
||||
MOZ_ASSERT(JS_IsGlobalObject(sandbox));
|
||||
global = xpc::NativeGlobal(sandbox);
|
||||
if (NS_WARN_IF(!global)) {
|
||||
return IdentityCredential::GetMetadataPromise::CreateAndReject(
|
||||
NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
|
||||
// Create a new request
|
||||
constexpr auto fragment = ""_ns;
|
||||
auto internalRequest =
|
||||
MakeSafeRefPtr<InternalRequest>(configLocation, fragment);
|
||||
internalRequest->SetRedirectMode(RequestRedirect::Error);
|
||||
internalRequest->SetCredentialsMode(RequestCredentials::Omit);
|
||||
internalRequest->SetReferrerPolicy(ReferrerPolicy::No_referrer);
|
||||
internalRequest->SetMode(RequestMode::Cors);
|
||||
internalRequest->SetCacheMode(RequestCache::No_cache);
|
||||
internalRequest->SetHeaders(new InternalHeaders(HeadersGuardEnum::Request));
|
||||
internalRequest->OverrideContentPolicyType(
|
||||
nsContentPolicyType::TYPE_WEB_IDENTITY);
|
||||
RefPtr<Request> request =
|
||||
new Request(global, std::move(internalRequest), nullptr);
|
||||
return IdentityNetworkHelpers::FetchJSONStructure<
|
||||
IdentityProviderClientMetadata>(request);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise> IdentityCredential::Disconnect(
|
||||
const GlobalObject& aGlobal,
|
||||
@@ -1741,11 +1808,107 @@ IdentityCredential::PromptUserWithPolicy(
|
||||
__func__);
|
||||
}
|
||||
|
||||
// Mark as logged in and return
|
||||
icStorageService->SetState(aPrincipal, idpPrincipal,
|
||||
NS_ConvertUTF16toUTF8(aAccount.mId), true, true);
|
||||
return IdentityCredential::GetAccountPromise::CreateAndResolve(
|
||||
std::make_tuple(aManifest, aAccount), __func__);
|
||||
// if registered, mark as logged in and return
|
||||
if (registered) {
|
||||
icStorageService->SetState(aPrincipal, idpPrincipal,
|
||||
NS_ConvertUTF16toUTF8(aAccount.mId), true, true);
|
||||
return IdentityCredential::GetAccountPromise::CreateAndResolve(
|
||||
std::make_tuple(aManifest, aAccount), __func__);
|
||||
}
|
||||
|
||||
// otherwise, fetch ->Then display ->Then return ->Catch reject
|
||||
RefPtr<BrowsingContext> browsingContext(aBrowsingContext);
|
||||
nsCOMPtr<nsIPrincipal> argumentPrincipal(aPrincipal);
|
||||
return FetchMetadata(aPrincipal, aProvider, aManifest)
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[aAccount, aManifest, aProvider, argumentPrincipal, browsingContext,
|
||||
icStorageService,
|
||||
idpPrincipal](const IdentityProviderClientMetadata& metadata)
|
||||
-> RefPtr<GenericPromise> {
|
||||
nsresult error;
|
||||
nsCOMPtr<nsIIdentityCredentialPromptService> icPromptService =
|
||||
mozilla::components::IdentityCredentialPromptService::Service(
|
||||
&error);
|
||||
if (NS_WARN_IF(!icPromptService)) {
|
||||
return GenericPromise::CreateAndReject(error, __func__);
|
||||
}
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wrapped =
|
||||
do_QueryInterface(icPromptService);
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(wrapped->GetJSObjectGlobal()))) {
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> providerJS(jsapi.cx());
|
||||
bool success = ToJSValue(jsapi.cx(), aProvider, &providerJS);
|
||||
if (NS_WARN_IF(!success)) {
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
JS::Rooted<JS::Value> metadataJS(jsapi.cx());
|
||||
success = ToJSValue(jsapi.cx(), metadata, &metadataJS);
|
||||
if (NS_WARN_IF(!success)) {
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
JS::Rooted<JS::Value> manifestJS(jsapi.cx());
|
||||
success = ToJSValue(jsapi.cx(), aManifest, &manifestJS);
|
||||
if (NS_WARN_IF(!success)) {
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
|
||||
RefPtr<Promise> showPromptPromise;
|
||||
icPromptService->ShowPolicyPrompt(
|
||||
browsingContext, providerJS, manifestJS, metadataJS,
|
||||
getter_AddRefs(showPromptPromise));
|
||||
|
||||
RefPtr<GenericPromise::Private> resultPromise =
|
||||
new GenericPromise::Private(__func__);
|
||||
showPromptPromise->AddCallbacksWithCycleCollectedArgs(
|
||||
[aAccount, argumentPrincipal, idpPrincipal, resultPromise,
|
||||
icStorageService](JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
ErrorResult&) {
|
||||
bool isBool = aValue.isBoolean();
|
||||
if (!isBool) {
|
||||
resultPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
icStorageService->SetState(
|
||||
argumentPrincipal, idpPrincipal,
|
||||
NS_ConvertUTF16toUTF8(aAccount.mId), true, true);
|
||||
resultPromise->Resolve(aValue.toBoolean(), __func__);
|
||||
},
|
||||
[resultPromise](JSContext*, JS::Handle<JS::Value> aValue,
|
||||
ErrorResult&) {
|
||||
resultPromise->Reject(
|
||||
Promise::TryExtractNSResultFromRejectionValue(aValue),
|
||||
__func__);
|
||||
});
|
||||
// Working around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85883
|
||||
showPromptPromise->AppendNativeHandler(
|
||||
new MozPromiseRejectOnDestruction{resultPromise, __func__});
|
||||
return resultPromise;
|
||||
},
|
||||
[](nsresult error) {
|
||||
return GenericPromise::CreateAndReject(error, __func__);
|
||||
})
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[aManifest, aAccount](bool success) {
|
||||
if (success) {
|
||||
return IdentityCredential::GetAccountPromise::CreateAndResolve(
|
||||
std::make_tuple(aManifest, aAccount), __func__);
|
||||
}
|
||||
return IdentityCredential::GetAccountPromise::CreateAndReject(
|
||||
NS_ERROR_FAILURE, __func__);
|
||||
},
|
||||
[](nsresult error) {
|
||||
return IdentityCredential::GetAccountPromise::CreateAndReject(
|
||||
error, __func__);
|
||||
});
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "mozilla/dom/IdentityCredential.h"
|
||||
#include "mozilla/dom/IdentityCredentialBinding.h"
|
||||
#include "mozilla/dom/CredentialManagementBinding.h"
|
||||
#include "mozilla/dom/LoginStatusBinding.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
@@ -82,10 +81,6 @@ struct ParamTraits<mozilla::dom::IdentityCredentialRequestOptions> {
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::LoginStatus>
|
||||
: public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::LoginStatus> {};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_dom_identitycredentialserializationhelpers_h__
|
||||
|
||||
@@ -38,6 +38,99 @@ class IdentityNetworkHelpers {
|
||||
static RefPtr<MozPromise<DisconnectedAccount, nsresult, true>>
|
||||
FetchDisconnectHelper(nsIURI* aAccountsEndpoint, const nsCString& aBody,
|
||||
nsIPrincipal* aTriggeringPrincipal);
|
||||
|
||||
// Helper to get a JSON structure via a Fetch.
|
||||
// The Request must already be built and T should be a webidl type with
|
||||
// annotation GenerateConversionToJS so it has an Init method.
|
||||
template <typename T, typename TPromise = MozPromise<T, nsresult, true>>
|
||||
static RefPtr<TPromise> FetchJSONStructure(Request* aRequest) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
// Create the returned Promise
|
||||
RefPtr<typename TPromise::Private> resultPromise =
|
||||
new typename TPromise::Private(__func__);
|
||||
|
||||
// Fetch the provided request
|
||||
RequestOrUTF8String fetchInput;
|
||||
fetchInput.SetAsRequest() = aRequest;
|
||||
RootedDictionary<RequestInit> requestInit(RootingCx());
|
||||
IgnoredErrorResult error;
|
||||
RefPtr<Promise> fetchPromise =
|
||||
FetchRequest(aRequest->GetParentObject(), fetchInput, requestInit,
|
||||
CallerType::System, error);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
resultPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return resultPromise;
|
||||
}
|
||||
|
||||
// Working around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85883
|
||||
RefPtr<PromiseNativeHandler> reject =
|
||||
new MozPromiseRejectOnDestruction{resultPromise, __func__};
|
||||
|
||||
// Handle the response
|
||||
fetchPromise->AddCallbacksWithCycleCollectedArgs(
|
||||
[resultPromise, reject](JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
ErrorResult&) {
|
||||
// Get the Response object from the argument to the callback
|
||||
if (NS_WARN_IF(!aValue.isObject())) {
|
||||
resultPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
|
||||
MOZ_ASSERT(obj);
|
||||
Response* response = nullptr;
|
||||
if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Response, &obj, response)))) {
|
||||
resultPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the request was a success
|
||||
if (!response->Ok()) {
|
||||
resultPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the body into JSON, which must be done async
|
||||
IgnoredErrorResult error;
|
||||
RefPtr<Promise> jsonPromise = response->ConsumeBody(
|
||||
aCx, BodyConsumer::ConsumeType::JSON, error);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
resultPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the parsed JSON from the Response body
|
||||
jsonPromise->AddCallbacksWithCycleCollectedArgs(
|
||||
[resultPromise](JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
ErrorResult&) {
|
||||
// Parse the JSON into the correct type, validating fields and
|
||||
// types
|
||||
T result;
|
||||
bool success = result.Init(aCx, aValue);
|
||||
if (!success) {
|
||||
resultPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
resultPromise->Resolve(result, __func__);
|
||||
},
|
||||
[resultPromise](JSContext*, JS::Handle<JS::Value> aValue,
|
||||
ErrorResult&) {
|
||||
resultPromise->Reject(
|
||||
Promise::TryExtractNSResultFromRejectionValue(aValue),
|
||||
__func__);
|
||||
});
|
||||
jsonPromise->AppendNativeHandler(reject);
|
||||
},
|
||||
[resultPromise](JSContext*, JS::Handle<JS::Value> aValue,
|
||||
ErrorResult&) {
|
||||
resultPromise->Reject(
|
||||
Promise::TryExtractNSResultFromRejectionValue(aValue), __func__);
|
||||
});
|
||||
fetchPromise->AppendNativeHandler(reject);
|
||||
|
||||
return resultPromise;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/dom/CredentialsContainer.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/NavigatorLogin.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/net/SFVService.h"
|
||||
#include "mozilla/dom/WindowGlobalChild.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static constexpr nsLiteralCString kLoginStatusPermission =
|
||||
"self-reported-logged-in"_ns;
|
||||
|
||||
uint32_t ConvertStatusToPermission(LoginStatus aStatus) {
|
||||
if (aStatus == LoginStatus::Logged_in) {
|
||||
return nsIPermissionManager::ALLOW_ACTION;
|
||||
} else if (aStatus == LoginStatus::Logged_out) {
|
||||
return nsIPermissionManager::DENY_ACTION;
|
||||
} else {
|
||||
// This should be unreachable, but let's return a real value
|
||||
return nsIPermissionManager::UNKNOWN_ACTION;
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<LoginStatus> PermissionToStatus(uint32_t aPermission) {
|
||||
if (aPermission == nsIPermissionManager::ALLOW_ACTION) {
|
||||
return Some(LoginStatus::Logged_in);
|
||||
} else if (aPermission == nsIPermissionManager::DENY_ACTION) {
|
||||
return Some(LoginStatus::Logged_out);
|
||||
} else {
|
||||
if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) {
|
||||
MOZ_ASSERT(false, "Unexpected permission action from login status");
|
||||
}
|
||||
return Nothing();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(NavigatorLogin, mOwner)
|
||||
|
||||
NavigatorLogin::~NavigatorLogin() = default;
|
||||
|
||||
JSObject* NavigatorLogin::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return NavigatorLogin_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NavigatorLogin::NavigatorLogin(nsIGlobalObject* aGlobal) : mOwner(aGlobal) {
|
||||
MOZ_ASSERT(mOwner);
|
||||
};
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> NavigatorLogin::SetStatus(
|
||||
LoginStatus aStatus, mozilla::ErrorResult& aRv) {
|
||||
RefPtr<Promise> promise = Promise::Create(mOwner, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner* window = mOwner->GetAsInnerWindow();
|
||||
if (!window) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"navigator.login.setStatus called on unavailable window"_ns);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
if (!CredentialsContainer::IsSameOriginWithAncestors(window)) {
|
||||
promise->MaybeRejectWithSecurityError(
|
||||
"navigator.login.setStatus must be called in a frame that is same-origin with its ancestors"_ns);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
WindowGlobalChild* wgc = window->GetWindowGlobalChild();
|
||||
if (!wgc) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"navigator.login.setStatus called while window already destroyed"_ns);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
wgc->SendSetLoginStatus(aStatus)->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise](
|
||||
const WindowGlobalChild::SetLoginStatusPromise::ResolveValueType&
|
||||
aResult) {
|
||||
if (NS_SUCCEEDED(aResult)) {
|
||||
promise->MaybeResolveWithUndefined();
|
||||
} else {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"navigator.login.setStatus had an unexpected internal error");
|
||||
}
|
||||
},
|
||||
[promise](const WindowGlobalChild::SetLoginStatusPromise::RejectValueType&
|
||||
aResult) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"navigator.login.setStatus had an unexpected internal error");
|
||||
});
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult NavigatorLogin::SetLoginStatus(nsIPrincipal* aPrincipal,
|
||||
LoginStatus aStatus) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
components::PermissionManager::Service();
|
||||
if (!permMgr) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return permMgr->AddFromPrincipal(aPrincipal, kLoginStatusPermission,
|
||||
ConvertStatusToPermission(aStatus),
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult NavigatorLogin::SetLoginStatus(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aStatus) {
|
||||
LoginStatus parsedStatus;
|
||||
nsresult rv = ParseLoginStatusHeader(aStatus, parsedStatus);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return SetLoginStatus(aPrincipal, parsedStatus);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult NavigatorLogin::ParseLoginStatusHeader(const nsACString& aStatus,
|
||||
LoginStatus& aResult) {
|
||||
nsCOMPtr<nsISFVService> sfv = net::GetSFVService();
|
||||
nsCOMPtr<nsISFVItem> item;
|
||||
nsresult rv = sfv->ParseItem(aStatus, getter_AddRefs(item));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISFVBareItem> value;
|
||||
rv = item->GetValue(getter_AddRefs(value));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISFVToken> token = do_QueryInterface(value);
|
||||
if (!token) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsAutoCString parsedStatus;
|
||||
rv = token->GetValue(parsedStatus);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (parsedStatus == "logged-in") {
|
||||
aResult = LoginStatus::Logged_in;
|
||||
return NS_OK;
|
||||
}
|
||||
if (parsedStatus == "logged-out") {
|
||||
aResult = LoginStatus::Logged_out;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult GetLoginStatus(nsIPrincipal* aPrincipal, Maybe<LoginStatus>& aStatus) {
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
components::PermissionManager::Service();
|
||||
if (!permMgr) {
|
||||
aStatus = Nothing();
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
uint32_t action;
|
||||
nsresult rv = permMgr->TestPermissionFromPrincipal(
|
||||
aPrincipal, kLoginStatusPermission, &action);
|
||||
if (NS_FAILED(rv)) {
|
||||
aStatus = Nothing();
|
||||
return rv;
|
||||
}
|
||||
aStatus = PermissionToStatus(action);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -1,49 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_NavigatorLogin_h
|
||||
#define mozilla_dom_NavigatorLogin_h
|
||||
|
||||
#include "ErrorList.h"
|
||||
#include "mozilla/dom/LoginStatusBinding.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class NavigatorLogin : public nsWrapperCache {
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(NavigatorLogin)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(NavigatorLogin)
|
||||
|
||||
explicit NavigatorLogin(nsIGlobalObject* aGlobal);
|
||||
nsIGlobalObject* GetParentObject() const { return mOwner; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> SetStatus(LoginStatus aStatus,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
static Maybe<LoginStatus> GetLoginStatus(nsIPrincipal* aPrincipal);
|
||||
static nsresult SetLoginStatus(nsIPrincipal* aPrincipal, LoginStatus aStatus);
|
||||
static nsresult SetLoginStatus(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aStatus);
|
||||
static nsresult ParseLoginStatusHeader(const nsACString& aStatus,
|
||||
LoginStatus& aResult);
|
||||
|
||||
protected:
|
||||
virtual ~NavigatorLogin();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // mozilla_dom_NavigatorLogin_h
|
||||
@@ -12,18 +12,13 @@ EXPORTS.mozilla.dom += [
|
||||
"IdentityCredential.h",
|
||||
"IdentityCredentialSerializationHelpers.h",
|
||||
"IdentityNetworkHelpers.h",
|
||||
"NavigatorLogin.h",
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
"IPCIdentityCredential.ipdlh",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"IdentityCredential.cpp",
|
||||
"IdentityNetworkHelpers.cpp",
|
||||
"NavigatorLogin.cpp",
|
||||
]
|
||||
UNIFIED_SOURCES += ["IdentityCredential.cpp", "IdentityNetworkHelpers.cpp"]
|
||||
|
||||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
||||
|
||||
@@ -13,17 +13,16 @@ support-files = [
|
||||
"server_disconnect.json^headers^",
|
||||
"server_idtoken.json",
|
||||
"server_idtoken.json^headers^",
|
||||
"server_loginStatus.sjs",
|
||||
"server_manifest_disconnect_failure.json",
|
||||
"server_manifest_disconnect_failure.json^headers^",
|
||||
"server_manifest.json",
|
||||
"server_manifest.json^headers^",
|
||||
"server_metadata.json",
|
||||
"server_metadata.json^headers^",
|
||||
]
|
||||
|
||||
["browser_close_prompt_on_timeout.js"]
|
||||
|
||||
["browser_disconnect.js"]
|
||||
|
||||
["browser_loginStatus.js"]
|
||||
|
||||
["browser_single_concurrent_identity_request.js"]
|
||||
|
||||
@@ -1,499 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"IdentityCredentialStorageService",
|
||||
"@mozilla.org/browser/identity-credential-storage-service;1",
|
||||
"nsIIdentityCredentialStorageService"
|
||||
);
|
||||
|
||||
const TEST_URL = "https://example.com/";
|
||||
const TEST_XORIGIN_URL = "https://example.net/";
|
||||
const TEST_LOGIN_STATUS_BASE =
|
||||
TEST_URL +
|
||||
"browser/dom/credentialmanagement/identity/tests/browser/server_loginStatus.sjs?status=";
|
||||
const TEST_LOGIN_STATUS_XORIGIN_BASE =
|
||||
TEST_XORIGIN_URL +
|
||||
"browser/dom/credentialmanagement/identity/tests/browser/server_loginStatus.sjs?status=";
|
||||
|
||||
/**
|
||||
* Perform a test with a function that should change a page's login status.
|
||||
*
|
||||
* This function opens a new foreground tab, then calls stepFn with two arguments:
|
||||
* the Browser of the new tab and the value that should be set as the login status.
|
||||
* This repeats for various values of the header, making sure the status is correct
|
||||
* after each instance.
|
||||
*
|
||||
* @param {Function(Browser, string) => Promise} stepFn - The function to update login status,
|
||||
* @param {string} - An optional description describing the test case, used in assertion descriptions.
|
||||
*/
|
||||
async function login_logout_sequence(stepFn, desc = "") {
|
||||
// Open a test page, get its principal
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
const principal = gBrowser.contentPrincipal;
|
||||
|
||||
// Make sure we don't have a starting permission
|
||||
let permission = Services.perms.testPermissionFromPrincipal(
|
||||
principal,
|
||||
"self-reported-logged-in"
|
||||
);
|
||||
Assert.equal(
|
||||
permission,
|
||||
Services.perms.UNKNOWN_ACTION,
|
||||
"Permission correctly not initialized in test of " + desc
|
||||
);
|
||||
|
||||
// Try using a bad value for the argument
|
||||
await stepFn(tab.linkedBrowser, "should-reject");
|
||||
permission = Services.perms.testPermissionFromPrincipal(
|
||||
principal,
|
||||
"self-reported-logged-in"
|
||||
);
|
||||
Assert.equal(
|
||||
permission,
|
||||
Services.perms.UNKNOWN_ACTION,
|
||||
"Permission not altered by bad enum value in test of " + desc
|
||||
);
|
||||
|
||||
// Try logging in
|
||||
await stepFn(tab.linkedBrowser, "logged-in");
|
||||
permission = Services.perms.testPermissionFromPrincipal(
|
||||
principal,
|
||||
"self-reported-logged-in"
|
||||
);
|
||||
Assert.equal(
|
||||
permission,
|
||||
Services.perms.ALLOW_ACTION,
|
||||
"Permission stored correcty for `logged-in` in test of " + desc
|
||||
);
|
||||
|
||||
// Try logging out
|
||||
await stepFn(tab.linkedBrowser, "logged-out");
|
||||
permission = Services.perms.testPermissionFromPrincipal(
|
||||
principal,
|
||||
"self-reported-logged-in"
|
||||
);
|
||||
Assert.equal(
|
||||
permission,
|
||||
Services.perms.DENY_ACTION,
|
||||
"Permission stored correcty for `logged-out` in test of " + desc
|
||||
);
|
||||
|
||||
// Try using a bad value for the argument, after it's already been set
|
||||
await stepFn(tab.linkedBrowser, "should-reject");
|
||||
permission = Services.perms.testPermissionFromPrincipal(
|
||||
principal,
|
||||
"self-reported-logged-in"
|
||||
);
|
||||
Assert.equal(
|
||||
permission,
|
||||
Services.perms.DENY_ACTION,
|
||||
"Permission not altered by bad enum value in test of " + desc
|
||||
);
|
||||
|
||||
// Try logging in again
|
||||
await stepFn(tab.linkedBrowser, "logged-in");
|
||||
permission = Services.perms.testPermissionFromPrincipal(
|
||||
principal,
|
||||
"self-reported-logged-in"
|
||||
);
|
||||
Assert.equal(
|
||||
permission,
|
||||
Services.perms.ALLOW_ACTION,
|
||||
"Permission stored correcty for `logged-in` in test of " + desc
|
||||
);
|
||||
|
||||
// Make sure we don't have any extra permissinons laying about
|
||||
let permissions = Services.perms.getAllByTypes(["self-reported-logged-in"]);
|
||||
Assert.equal(
|
||||
permissions.length,
|
||||
1,
|
||||
"One permission must be left after all modifications in test of " + desc
|
||||
);
|
||||
|
||||
// Clear the permission
|
||||
Services.perms.removeByType("self-reported-logged-in");
|
||||
|
||||
// Close tabs.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a test with a function that should NOT change a page's login status.
|
||||
*
|
||||
* This function opens a new foreground tab, then calls stepFn with two arguments:
|
||||
* the Browser of the new tab and the value that should be set as the login status.
|
||||
* Then it makes sure that no permission has been set for the login status.
|
||||
*
|
||||
* @param {Function(Browser, string) => Promise} stepFn - The function to update login status,
|
||||
* @param {string} - An optional description describing the test case, used in assertion descriptions.
|
||||
*/
|
||||
async function login_doesnt_work(stepFn, desc = "") {
|
||||
// Open a test page, get its principal
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
const principal = gBrowser.contentPrincipal;
|
||||
|
||||
// Make sure we don't have a starting permission
|
||||
let permission = Services.perms.testPermissionFromPrincipal(
|
||||
principal,
|
||||
"self-reported-logged-in"
|
||||
);
|
||||
Assert.equal(
|
||||
permission,
|
||||
Services.perms.UNKNOWN_ACTION,
|
||||
"Permission correctly not initialized in test of " + desc
|
||||
);
|
||||
|
||||
// Try logging in
|
||||
await stepFn(tab.linkedBrowser, "logged-in");
|
||||
permission = Services.perms.testPermissionFromPrincipal(
|
||||
principal,
|
||||
"self-reported-logged-in"
|
||||
);
|
||||
Assert.equal(
|
||||
permission,
|
||||
Services.perms.UNKNOWN_ACTION,
|
||||
"Permission not set for `logged-in` in test of " + desc
|
||||
);
|
||||
|
||||
// Make sure we don't have any extra permissinons laying about
|
||||
let permissions = Services.perms.getAllByTypes(["self-reported-logged-in"]);
|
||||
Assert.equal(permissions.length, 0, "No permission set in test of " + desc);
|
||||
|
||||
// Clear the permission
|
||||
Services.perms.removeByType("self-reported-logged-in");
|
||||
|
||||
// Close tabs.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
// Function that we can use to set the status and return with a string for the different possible outcomes
|
||||
async function setStatusInContent(status) {
|
||||
try {
|
||||
let result = await content.navigator.login.setStatus(status);
|
||||
if (result === undefined) {
|
||||
return "resolved with undefined";
|
||||
}
|
||||
return "resolved with defined";
|
||||
} catch (err) {
|
||||
if (err.name == "TypeError") {
|
||||
return "rejected with TypeError";
|
||||
}
|
||||
if (err.name == "SecurityError") {
|
||||
return "rejected with SecurityError";
|
||||
}
|
||||
return "rejected with other error";
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function test_logiStatus_js() {
|
||||
let setLoginStatusInJavascript = async function (browser, value) {
|
||||
let loginResult = await SpecialPowers.spawn(
|
||||
browser,
|
||||
[value],
|
||||
setStatusInContent
|
||||
);
|
||||
if (value == "logged-in" || value == "logged-out") {
|
||||
Assert.equal(
|
||||
"resolved with undefined",
|
||||
loginResult,
|
||||
"Successful call resolves with `undefined`"
|
||||
);
|
||||
} else {
|
||||
Assert.equal(
|
||||
loginResult,
|
||||
"rejected with TypeError",
|
||||
"Unsuccessful JS call rejects with TypeError"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
await login_logout_sequence(setLoginStatusInJavascript, "javascript API");
|
||||
});
|
||||
|
||||
add_task(async function test_loginStatus_js_frame() {
|
||||
let setLoginStatusInSubframeJavascript = async function (browser, value) {
|
||||
const iframeBC = await SpecialPowers.spawn(
|
||||
browser,
|
||||
[TEST_URL],
|
||||
async url => {
|
||||
const iframe = content.document.createElement("iframe");
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener("load", resolve, { once: true });
|
||||
iframe.src = url;
|
||||
content.document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
return iframe.browsingContext;
|
||||
}
|
||||
);
|
||||
let loginResult = await SpecialPowers.spawn(
|
||||
iframeBC,
|
||||
[value],
|
||||
setStatusInContent
|
||||
);
|
||||
if (value == "logged-in" || value == "logged-out") {
|
||||
Assert.equal(
|
||||
"resolved with undefined",
|
||||
loginResult,
|
||||
"Successful call resolves with `undefined`"
|
||||
);
|
||||
} else {
|
||||
Assert.equal(
|
||||
loginResult,
|
||||
"rejected with TypeError",
|
||||
"Unsuccessful JS call rejects with TypeError"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
await login_logout_sequence(
|
||||
setLoginStatusInSubframeJavascript,
|
||||
"javascript API"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_loginStatus_js_xorigin_frame() {
|
||||
let setLoginStatusInSubframeJavascript = async function (browser, value) {
|
||||
const iframeBC = await SpecialPowers.spawn(
|
||||
browser,
|
||||
[TEST_XORIGIN_URL],
|
||||
async url => {
|
||||
const iframe = content.document.createElement("iframe");
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener("load", resolve, { once: true });
|
||||
iframe.src = url;
|
||||
content.document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
return iframe.browsingContext;
|
||||
}
|
||||
);
|
||||
let loginResult = await SpecialPowers.spawn(
|
||||
iframeBC,
|
||||
[value],
|
||||
setStatusInContent
|
||||
);
|
||||
if (value == "logged-in" || value == "logged-out") {
|
||||
Assert.equal(
|
||||
loginResult,
|
||||
"rejected with SecurityError",
|
||||
"Cross origin JS call with correct enum rejects with SecurityError"
|
||||
);
|
||||
} else {
|
||||
Assert.equal(
|
||||
loginResult,
|
||||
"rejected with TypeError",
|
||||
"Unsuccessful JS call rejects with TypeError"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
await login_doesnt_work(setLoginStatusInSubframeJavascript, "javascript API");
|
||||
});
|
||||
|
||||
add_task(async function test_loginStatus_js_xorigin_ancestor_frame() {
|
||||
let setLoginStatusInSubframeJavascript = async function (browser, value) {
|
||||
const iframeBC = await SpecialPowers.spawn(
|
||||
browser,
|
||||
[TEST_XORIGIN_URL],
|
||||
async url => {
|
||||
const iframe = content.document.createElement("iframe");
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener("load", resolve, { once: true });
|
||||
iframe.src = url;
|
||||
content.document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
return iframe.browsingContext;
|
||||
}
|
||||
);
|
||||
const innerIframeBC = await SpecialPowers.spawn(
|
||||
iframeBC,
|
||||
[TEST_URL],
|
||||
async url => {
|
||||
const iframe = content.document.createElement("iframe");
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener("load", resolve, { once: true });
|
||||
iframe.src = url;
|
||||
content.document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
return iframe.browsingContext;
|
||||
}
|
||||
);
|
||||
let loginResult = await SpecialPowers.spawn(
|
||||
innerIframeBC,
|
||||
[value],
|
||||
setStatusInContent
|
||||
);
|
||||
if (value == "logged-in" || value == "logged-out") {
|
||||
Assert.equal(
|
||||
loginResult,
|
||||
"rejected with SecurityError",
|
||||
"Cross origin JS call with correct enum rejects with SecurityError"
|
||||
);
|
||||
} else {
|
||||
Assert.equal(
|
||||
loginResult,
|
||||
"rejected with TypeError",
|
||||
"Unsuccessful JS call rejects with TypeError"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
await login_doesnt_work(setLoginStatusInSubframeJavascript, "javascript API");
|
||||
});
|
||||
|
||||
add_task(async function test_login_logout_document_headers() {
|
||||
let setLoginStatusInDocumentHeader = async function (browser, value) {
|
||||
let loaded = BrowserTestUtils.browserLoaded(browser, false, TEST_URL);
|
||||
BrowserTestUtils.startLoadingURIString(
|
||||
browser,
|
||||
TEST_LOGIN_STATUS_BASE + value
|
||||
);
|
||||
await loaded;
|
||||
};
|
||||
|
||||
await login_logout_sequence(
|
||||
setLoginStatusInDocumentHeader,
|
||||
"document redirect"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_loginStatus_subresource_headers() {
|
||||
let setLoginStatusViaHeader = async function (browser, value) {
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[TEST_LOGIN_STATUS_BASE + value],
|
||||
async function (url) {
|
||||
await content.fetch(url);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
await login_logout_sequence(setLoginStatusViaHeader, "subresource header");
|
||||
});
|
||||
|
||||
add_task(async function test_loginStatus_xorigin_subresource_headers() {
|
||||
let setLoginStatusViaHeader = async function (browser, value) {
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[TEST_LOGIN_STATUS_XORIGIN_BASE + value],
|
||||
async function (url) {
|
||||
await content.fetch(url, { mode: "no-cors" });
|
||||
}
|
||||
);
|
||||
};
|
||||
await login_doesnt_work(
|
||||
setLoginStatusViaHeader,
|
||||
"xorigin subresource header"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(
|
||||
async function test_loginStatus_xorigin_subdocument_subresource_headers() {
|
||||
let setLoginStatusViaSubdocumentSubresource = async function (
|
||||
browser,
|
||||
value
|
||||
) {
|
||||
const iframeBC = await SpecialPowers.spawn(
|
||||
browser,
|
||||
[TEST_XORIGIN_URL],
|
||||
async url => {
|
||||
const iframe = content.document.createElement("iframe");
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener("load", resolve, { once: true });
|
||||
iframe.src = url;
|
||||
content.document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
return iframe.browsingContext;
|
||||
}
|
||||
);
|
||||
await SpecialPowers.spawn(
|
||||
iframeBC,
|
||||
[TEST_LOGIN_STATUS_BASE + value],
|
||||
async function (url) {
|
||||
await content.fetch(url, { mode: "no-cors" });
|
||||
}
|
||||
);
|
||||
};
|
||||
await login_doesnt_work(
|
||||
setLoginStatusViaSubdocumentSubresource,
|
||||
"xorigin subresource header in xorigin frame"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
async function test_loginStatus_xorigin_subdocument_xorigin_subresource_headers() {
|
||||
let setLoginStatusViaSubdocumentSubresource = async function (
|
||||
browser,
|
||||
value
|
||||
) {
|
||||
const iframeBC = await SpecialPowers.spawn(
|
||||
browser,
|
||||
[TEST_XORIGIN_URL],
|
||||
async url => {
|
||||
const iframe = content.document.createElement("iframe");
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener("load", resolve, { once: true });
|
||||
iframe.src = url;
|
||||
content.document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
return iframe.browsingContext;
|
||||
}
|
||||
);
|
||||
await SpecialPowers.spawn(
|
||||
iframeBC,
|
||||
[TEST_LOGIN_STATUS_XORIGIN_BASE + value],
|
||||
async function (url) {
|
||||
await content.fetch(url, { mode: "no-cors" });
|
||||
}
|
||||
);
|
||||
};
|
||||
await login_doesnt_work(
|
||||
setLoginStatusViaSubdocumentSubresource,
|
||||
"xorigin subresource header in xorigin frame"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(async function test_loginStatus_subdocument_subresource_headers() {
|
||||
let setLoginStatusViaSubdocumentSubresource = async function (
|
||||
browser,
|
||||
value
|
||||
) {
|
||||
const iframeBC = await SpecialPowers.spawn(
|
||||
browser,
|
||||
[TEST_URL],
|
||||
async url => {
|
||||
const iframe = content.document.createElement("iframe");
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener("load", resolve, { once: true });
|
||||
iframe.src = url;
|
||||
content.document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
return iframe.browsingContext;
|
||||
}
|
||||
);
|
||||
await SpecialPowers.spawn(
|
||||
iframeBC,
|
||||
[TEST_LOGIN_STATUS_BASE + value],
|
||||
async function (url) {
|
||||
await content.fetch(url, { mode: "no-cors" });
|
||||
}
|
||||
);
|
||||
};
|
||||
await login_logout_sequence(
|
||||
setLoginStatusViaSubdocumentSubresource,
|
||||
"subresource header in frame"
|
||||
);
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
function handleRequest(request, response) {
|
||||
let params = new URLSearchParams(request.queryString);
|
||||
if (params.has("status")) {
|
||||
response.setHeader("Set-Login", params.get("status"), false);
|
||||
}
|
||||
response.setHeader("Access-Control-Allow-Origin", "*", false);
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.setHeader("Location", "/", false);
|
||||
response.setStatusLine(request.httpVersion, "307", "Temporary Redirect");
|
||||
response.write("<!DOCTYPE html>");
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"accounts_endpoint": "https://example.net/browser/dom/credentialmanagement/identity/tests/browser/server_accounts.json",
|
||||
"client_metadata_endpoint": "https://example.net/browser/dom/credentialmanagement/identity/tests/browser/server_metadata.json",
|
||||
"id_assertion_endpoint": "https://example.net/browser/dom/credentialmanagement/identity/tests/browser/server_idtoken.json",
|
||||
"disconnect_endpoint": "https://example.net/browser/dom/credentialmanagement/identity/tests/browser/server_disconnect.json"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"accounts_endpoint": "https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_accounts.json",
|
||||
"client_metadata_endpoint": "https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_metadata.json",
|
||||
"id_assertion_endpoint": "https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_idtoken.json",
|
||||
"disconnect_endpoint": "https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_disconnect_intentionally_missing.json"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"privacy_policy_url": "https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/null.txt",
|
||||
"terms_of_service_url": "https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/null.txt"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Content-Type: application/json
|
||||
Access-Control-Allow-Origin: *
|
||||
@@ -22,6 +22,8 @@ support-files = [
|
||||
"/.well-known/web-identity^headers^",
|
||||
"server_manifest.sjs",
|
||||
"server_manifest_wrong_provider_in_manifest.sjs",
|
||||
"server_metadata.json",
|
||||
"server_metadata.json^headers^",
|
||||
"server_simple_accounts.sjs",
|
||||
"server_simple_idtoken.sjs",
|
||||
"server_no_accounts_accounts.sjs",
|
||||
|
||||
@@ -32,6 +32,8 @@ function handleRequest(request, response) {
|
||||
let content = {
|
||||
accounts_endpoint:
|
||||
"https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_TESTNAME_accounts.sjs",
|
||||
client_metadata_endpoint:
|
||||
"https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_metadata.json",
|
||||
id_assertion_endpoint:
|
||||
"https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_TESTNAME_idtoken.sjs",
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@ function handleRequest(request, response) {
|
||||
let content = {
|
||||
accounts_endpoint:
|
||||
"https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_simple_accounts.sjs",
|
||||
client_metadata_endpoint:
|
||||
"https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_simple_metadata.sjs",
|
||||
id_assertion_endpoint:
|
||||
"https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/server_simple_idtoken.sjs",
|
||||
};
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"privacy_policy_url": "https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/null.txt",
|
||||
"terms_of_service_url": "https://example.net/tests/dom/credentialmanagement/identity/tests/mochitest/null.txt"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Content-Type: application/json
|
||||
Access-Control-Allow-Origin: *
|
||||
@@ -40,7 +40,6 @@ using mozilla::dom::IdentityCredentialDisconnectOptions from "mozilla/dom/Identi
|
||||
using mozilla::dom::IdentityCredentialInit from "mozilla/dom/IdentityCredentialBinding.h";
|
||||
using mozilla::dom::IdentityCredentialRequestOptions from "mozilla/dom/IdentityCredentialBinding.h";
|
||||
using mozilla::dom::IdentityLoginTargetType from "mozilla/dom/IdentityCredentialBinding.h";
|
||||
using mozilla::dom::LoginStatus from "mozilla/dom/LoginStatusBinding.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -219,9 +218,6 @@ parent:
|
||||
async DisconnectIdentityCredential(IdentityCredentialDisconnectOptions aOptions) returns (nsresult rv);
|
||||
async PreventSilentAccess() returns (nsresult rv);
|
||||
|
||||
// Indicate that the page has reported itself as logged in
|
||||
async SetLoginStatus(LoginStatus foo) returns (nsresult rv);
|
||||
|
||||
async GetStorageAccessPermission(bool aIncludeIdentityCredential) returns(uint32_t permission_action);
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "mozilla/dom/BrowserParent.h"
|
||||
#include "mozilla/dom/IdentityCredential.h"
|
||||
#include "mozilla/dom/MediaController.h"
|
||||
#include "mozilla/dom/NavigatorLogin.h"
|
||||
#include "mozilla/dom/WebAuthnTransactionParent.h"
|
||||
#include "mozilla/dom/WindowGlobalChild.h"
|
||||
#include "mozilla/dom/ChromeUtils.h"
|
||||
@@ -1504,18 +1503,6 @@ IPCResult WindowGlobalParent::RecvPreventSilentAccess(
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult WindowGlobalParent::RecvSetLoginStatus(
|
||||
LoginStatus aStatus, const SetLoginStatusResolver& aResolver) {
|
||||
nsIPrincipal* principal = DocumentPrincipal();
|
||||
if (!principal) {
|
||||
aResolver(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
||||
return IPC_OK();
|
||||
}
|
||||
nsresult rv = NavigatorLogin::SetLoginStatus(principal, aStatus);
|
||||
aResolver(rv);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
IPCResult WindowGlobalParent::RecvGetStorageAccessPermission(
|
||||
bool aIncludeIdentityCredential,
|
||||
GetStorageAccessPermissionResolver&& aResolve) {
|
||||
|
||||
@@ -328,9 +328,6 @@ class WindowGlobalParent final : public WindowContext,
|
||||
mozilla::ipc::IPCResult RecvDisconnectIdentityCredential(
|
||||
const IdentityCredentialDisconnectOptions& aOptions,
|
||||
const DisconnectIdentityCredentialResolver& aResolver);
|
||||
mozilla::ipc::IPCResult RecvSetLoginStatus(
|
||||
LoginStatus aStatus, const SetLoginStatusResolver& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvPreventSilentAccess(
|
||||
const PreventSilentAccessResolver& aResolver);
|
||||
|
||||
|
||||
@@ -937,8 +937,6 @@ let interfaceNamesInGlobalScope = [
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "Navigator", insecureContext: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"NavigatorLogin",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "NetworkInformation", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "Node", insecureContext: true },
|
||||
|
||||
@@ -91,9 +91,7 @@ dictionary IdentityProviderBranding {
|
||||
[GenerateInit, GenerateConversionToJS]
|
||||
dictionary IdentityProviderAPIConfig {
|
||||
required UTF8String accounts_endpoint;
|
||||
// We do not want to gather consent for identity providers, so we
|
||||
// omit this requirement and its use: https://github.com/w3c-fedid/FedCM/issues/703
|
||||
// required UTF8String client_metadata_endpoint;
|
||||
required UTF8String client_metadata_endpoint;
|
||||
required UTF8String id_assertion_endpoint;
|
||||
UTF8String disconnect_endpoint;
|
||||
IdentityProviderBranding branding;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/* -*- 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://w3c-fedid.github.io/login-status/#login-status-javascript
|
||||
*/
|
||||
|
||||
enum LoginStatus {
|
||||
"logged-in",
|
||||
"logged-out",
|
||||
};
|
||||
|
||||
[Exposed=Window, SecureContext]
|
||||
interface NavigatorLogin {
|
||||
[NewObject]
|
||||
Promise<undefined> setStatus(LoginStatus status);
|
||||
};
|
||||
@@ -398,9 +398,3 @@ partial interface Navigator {
|
||||
[SameObject, Trial="PrivateAttributionV2"]
|
||||
readonly attribute PrivateAttribution privateAttribution;
|
||||
};
|
||||
|
||||
// https://w3c-fedid.github.io/login-status/#login-status-javascript
|
||||
[SecureContext]
|
||||
partial interface Navigator {
|
||||
[SameObject] readonly attribute NavigatorLogin login;
|
||||
};
|
||||
|
||||
@@ -725,7 +725,6 @@ WEBIDL_FILES = [
|
||||
"Location.webidl",
|
||||
"Lock.webidl",
|
||||
"LockManager.webidl",
|
||||
"LoginStatus.webidl",
|
||||
"MathMLElement.webidl",
|
||||
"MediaCapabilities.webidl",
|
||||
"MediaDebugInfo.webidl",
|
||||
|
||||
@@ -90,7 +90,6 @@ HTTP_ATOM(Server, "Server")
|
||||
HTTP_ATOM(Server_Timing, "Server-Timing")
|
||||
HTTP_ATOM(Service_Worker_Allowed, "Service-Worker-Allowed")
|
||||
HTTP_ATOM(Set_Cookie, "Set-Cookie")
|
||||
HTTP_ATOM(Set_Login, "Set-Login")
|
||||
HTTP_ATOM(Status_URI, "Status-URI")
|
||||
HTTP_ATOM(Strict_Transport_Security, "Strict-Transport-Security")
|
||||
HTTP_ATOM(TE, "TE")
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
#include "mozilla/dom/NavigatorLogin.h"
|
||||
#include "mozilla/glean/AntitrackingMetrics.h"
|
||||
#include "mozilla/glean/NetwerkMetrics.h"
|
||||
#include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
|
||||
@@ -5995,32 +5994,6 @@ nsresult nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType) {
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a Set-Login header, we should try to handle it here
|
||||
nsAutoCString setLogin;
|
||||
if (NS_SUCCEEDED(mResponseHead->GetHeader(nsHttp::Set_Login, setLogin))) {
|
||||
bool isDocument = mLoadInfo->GetExternalContentPolicyType() ==
|
||||
ExtContentPolicy::TYPE_DOCUMENT;
|
||||
if (isDocument) {
|
||||
auto ssm = nsContentUtils::GetSecurityManager();
|
||||
if (ssm) {
|
||||
nsCOMPtr<nsIPrincipal> documentPrincipal;
|
||||
nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
|
||||
this, getter_AddRefs(documentPrincipal));
|
||||
dom::NavigatorLogin::SetLoginStatus(documentPrincipal, setLogin);
|
||||
}
|
||||
} else {
|
||||
bool inThirdPartyContext = mLoadInfo->GetIsInThirdPartyContext();
|
||||
nsIPrincipal* loadingPrincipal = mLoadInfo->GetLoadingPrincipal();
|
||||
if (loadingPrincipal) {
|
||||
bool isSameOriginToLoadingPrincipal =
|
||||
loadingPrincipal->IsSameOrigin(mURI);
|
||||
if (!inThirdPartyContext && isSameOriginToLoadingPrincipal) {
|
||||
dom::NavigatorLogin::SetLoginStatus(loadingPrincipal, setLogin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mRedirectURI)) {
|
||||
LOG(("Invalid redirect URI after performaing query string stripping"));
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
@@ -311,6 +311,178 @@ export class IdentityCredentialPromptService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the user, using a PopupNotification, to approve or disapprove of the policies of the Identity Provider.
|
||||
* @param {BrowsingContext} browsingContext - The BrowsingContext of the document requesting an identity credential via navigator.credentials.get()
|
||||
* @param {IdentityProviderConfig} identityProvider - The Identity Provider that the user has selected to use
|
||||
* @param {IdentityProviderAPIConfig} identityManifest - The Identity Provider that the user has selected to use's manifest
|
||||
* @param {IdentityCredentialMetadata} identityCredentialMetadata - The metadata displayed to the user
|
||||
* @returns {Promise<bool>} A boolean representing the user's acceptance of the metadata.
|
||||
*/
|
||||
async showPolicyPrompt(
|
||||
browsingContext,
|
||||
identityProvider,
|
||||
identityManifest,
|
||||
identityCredentialMetadata
|
||||
) {
|
||||
// For testing only.
|
||||
if (lazy.SELECT_FIRST_IN_UI_LISTS) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
if (
|
||||
!identityCredentialMetadata ||
|
||||
!identityCredentialMetadata.privacy_policy_url ||
|
||||
!identityCredentialMetadata.terms_of_service_url
|
||||
) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
let iconResult = await this.loadIconFromManifest(
|
||||
identityManifest,
|
||||
BEST_HEADER_ICON_SIZE,
|
||||
"chrome://global/skin/icons/defaultFavicon.svg"
|
||||
);
|
||||
|
||||
const providerName = identityManifest?.branding?.name;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
let browser = browsingContext.top.embedderElement;
|
||||
if (!browser) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
let providerURL = new URL(identityProvider.configURL);
|
||||
let providerDisplayDomain = lazy.IDNService.convertToDisplayIDN(
|
||||
providerURL.host
|
||||
);
|
||||
let currentBaseDomain =
|
||||
browsingContext.currentWindowContext.documentPrincipal.baseDomain;
|
||||
|
||||
if (AppConstants.platform === "android") {
|
||||
lazy.GeckoViewIdentityCredential.onShowPolicyPrompt(
|
||||
browsingContext,
|
||||
identityCredentialMetadata.privacy_policy_url,
|
||||
identityCredentialMetadata.terms_of_service_url,
|
||||
providerDisplayDomain,
|
||||
currentBaseDomain,
|
||||
iconResult,
|
||||
resolve,
|
||||
reject
|
||||
);
|
||||
} else {
|
||||
// Localize the description
|
||||
// Bug 1797154 - Convert localization calls to use the async formatValues.
|
||||
let localization = new Localization(
|
||||
["browser/identityCredentialNotification.ftl"],
|
||||
true
|
||||
);
|
||||
let [accept, cancel] = localization.formatMessagesSync([
|
||||
{ id: "identity-credential-accept-button" },
|
||||
{ id: "identity-credential-cancel-button" },
|
||||
]);
|
||||
|
||||
let cancelLabel = cancel.attributes.find(x => x.name == "label").value;
|
||||
let cancelKey = cancel.attributes.find(
|
||||
x => x.name == "accesskey"
|
||||
).value;
|
||||
let acceptLabel = accept.attributes.find(x => x.name == "label").value;
|
||||
let acceptKey = accept.attributes.find(
|
||||
x => x.name == "accesskey"
|
||||
).value;
|
||||
|
||||
let title = localization.formatValueSync(
|
||||
"identity-credential-policy-title",
|
||||
{
|
||||
provider: providerName || providerDisplayDomain,
|
||||
}
|
||||
);
|
||||
|
||||
if (iconResult) {
|
||||
let headerIcon = browser.ownerDocument.getElementsByClassName(
|
||||
"identity-credential-header-icon"
|
||||
)[0];
|
||||
headerIcon.setAttribute("src", iconResult);
|
||||
}
|
||||
|
||||
const headerText = browser.ownerDocument.getElementById(
|
||||
"identity-credential-header-text"
|
||||
);
|
||||
headerText.textContent = title;
|
||||
|
||||
let privacyPolicyAnchor = browser.ownerDocument.getElementById(
|
||||
"identity-credential-privacy-policy"
|
||||
);
|
||||
privacyPolicyAnchor.href =
|
||||
identityCredentialMetadata.privacy_policy_url;
|
||||
let termsOfServiceAnchor = browser.ownerDocument.getElementById(
|
||||
"identity-credential-terms-of-service"
|
||||
);
|
||||
termsOfServiceAnchor.href =
|
||||
identityCredentialMetadata.terms_of_service_url;
|
||||
|
||||
// Populate the content of the policy panel
|
||||
let description = browser.ownerDocument.getElementById(
|
||||
"identity-credential-policy-explanation"
|
||||
);
|
||||
browser.ownerDocument.l10n.setAttributes(
|
||||
description,
|
||||
"identity-credential-policy-description",
|
||||
{
|
||||
host: currentBaseDomain,
|
||||
provider: providerDisplayDomain,
|
||||
}
|
||||
);
|
||||
|
||||
// Construct the necessary arguments for notification behavior
|
||||
let options = {
|
||||
hideClose: true,
|
||||
eventCallback: (topic, nextRemovalReason, isCancel) => {
|
||||
if (topic == "removed" && isCancel) {
|
||||
reject();
|
||||
}
|
||||
},
|
||||
};
|
||||
let mainAction = {
|
||||
label: acceptLabel,
|
||||
accessKey: acceptKey,
|
||||
callback(_event) {
|
||||
resolve(true);
|
||||
},
|
||||
};
|
||||
let secondaryActions = [
|
||||
{
|
||||
label: cancelLabel,
|
||||
accessKey: cancelKey,
|
||||
callback(_event) {
|
||||
resolve(false);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Show the popup
|
||||
let ownerDocument = browser.ownerDocument;
|
||||
ownerDocument.getElementById("identity-credential-provider").hidden =
|
||||
true;
|
||||
ownerDocument.getElementById("identity-credential-policy").hidden =
|
||||
false;
|
||||
ownerDocument.getElementById("identity-credential-account").hidden =
|
||||
true;
|
||||
ownerDocument.getElementById("identity-credential-header").hidden =
|
||||
false;
|
||||
browser.ownerGlobal.PopupNotifications.show(
|
||||
browser,
|
||||
"identity-credential",
|
||||
"",
|
||||
"identity-credential-notification-icon",
|
||||
mainAction,
|
||||
secondaryActions,
|
||||
options
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the user, using a PopupNotification, to select an account from a provided list.
|
||||
* @param {BrowsingContext} browsingContext - The BrowsingContext of the document requesting an identity credential via navigator.credentials.get()
|
||||
|
||||
@@ -11,6 +11,9 @@ interface nsIIdentityCredentialPromptService : nsISupports {
|
||||
// Display to the user an interface to choose from among the identity providers listed
|
||||
// Resolves with an index referring to one pair of the elements of the lists.
|
||||
Promise showProviderPrompt(in BrowsingContext browsingContext, in jsval identityProviders, in jsval identityManifests);
|
||||
// Display to the user an interface to approve (or disapprove) of the terms of service for
|
||||
// the identity provider when used on the current site.
|
||||
Promise showPolicyPrompt(in BrowsingContext browsingContext, in jsval identityProvider, in jsval identityManifest, in jsval identityClientMetadata);
|
||||
// Display to the user an interface to choose from among the accounts listed with the information of the provider.
|
||||
// Resolves with an index referring to one of the elements of the list.
|
||||
Promise showAccountListPrompt(in BrowsingContext browsingContext, in jsval accountList, in jsval identityProvider, in jsval identityManifest);
|
||||
|
||||
@@ -15,4 +15,6 @@ support-files = ["custom.svg"]
|
||||
|
||||
["browser_credential_chooser.js"]
|
||||
|
||||
["browser_policy_dialog.js"]
|
||||
|
||||
["browser_provider_dialog.js"]
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"IdentityCredentialPromptService",
|
||||
"@mozilla.org/browser/identitycredentialpromptservice;1",
|
||||
"nsIIdentityCredentialPromptService"
|
||||
);
|
||||
|
||||
const TEST_URL = "https://example.com/";
|
||||
|
||||
// Test that a policy dialog does not appear when no policies are given
|
||||
add_task(async function test_policy_dialog_empty() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
let prompt = IdentityCredentialPromptService.showPolicyPrompt(
|
||||
tab.linkedBrowser.browsingContext,
|
||||
{
|
||||
configURL: "https://idp.example/",
|
||||
clientId: "123",
|
||||
},
|
||||
{
|
||||
accounts_endpoint: "",
|
||||
client_metadata_endpoint: "",
|
||||
id_assertion_endpoint: "",
|
||||
},
|
||||
{} // No policies!
|
||||
);
|
||||
|
||||
// Make sure we resolve with true without interaction
|
||||
let value = await prompt;
|
||||
is(value, true, "Automatically accept the missing policies");
|
||||
|
||||
// Close tab
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// Make sure that a policy dialog shows up when we have policies to show.
|
||||
// Also test the accept path.
|
||||
add_task(async function test_policy_dialog() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
let popupShown = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popupshown"
|
||||
);
|
||||
|
||||
// Show a prompt- the operative argument is the last one
|
||||
let prompt = IdentityCredentialPromptService.showPolicyPrompt(
|
||||
tab.linkedBrowser.browsingContext,
|
||||
{
|
||||
configURL: "https://idp.example/",
|
||||
clientId: "123",
|
||||
},
|
||||
{
|
||||
accounts_endpoint: "",
|
||||
client_metadata_endpoint: "",
|
||||
id_assertion_endpoint: "",
|
||||
branding: {
|
||||
background_color: "0x6200ee",
|
||||
color: "0xffffff",
|
||||
icons: [
|
||||
{
|
||||
size: 256,
|
||||
url: "https://example.net/browser/toolkit/components/credentialmanagement/tests/browser/custom.svg",
|
||||
},
|
||||
],
|
||||
name: "demo ip",
|
||||
},
|
||||
},
|
||||
{
|
||||
privacy_policy_url: "https://idp.example/privacy-policy.html",
|
||||
terms_of_service_url: "https://idp.example/terms-of-service.html",
|
||||
}
|
||||
);
|
||||
|
||||
// Make sure the popup shows up
|
||||
await popupShown;
|
||||
|
||||
let popupHiding = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popuphiding"
|
||||
);
|
||||
|
||||
// Validate the contents of the popup
|
||||
let document = tab.linkedBrowser.browsingContext.topChromeWindow.document;
|
||||
|
||||
let description = document.getElementById(
|
||||
"identity-credential-policy-explanation"
|
||||
);
|
||||
|
||||
ok(
|
||||
description.textContent.includes("idp.example"),
|
||||
"IDP domain in the policy prompt text"
|
||||
);
|
||||
ok(
|
||||
description.textContent.includes("example.com"),
|
||||
"RP domain in the policy prompt text"
|
||||
);
|
||||
ok(
|
||||
description.textContent.includes("Privacy Policy"),
|
||||
"Link to the privacy policy in the policy prompt text"
|
||||
);
|
||||
ok(
|
||||
description.textContent.includes("Terms of Service"),
|
||||
"Link to the ToS in the policy prompt text"
|
||||
);
|
||||
|
||||
let title = document.getElementById("identity-credential-header-text");
|
||||
ok(
|
||||
title.textContent.includes("demo ip"),
|
||||
"IDP domain in the policy prompt header as business short name"
|
||||
);
|
||||
|
||||
const headerIcon = document.getElementsByClassName(
|
||||
"identity-credential-header-icon"
|
||||
)[0];
|
||||
|
||||
ok(BrowserTestUtils.isVisible(headerIcon), "Header Icon is showing");
|
||||
ok(
|
||||
headerIcon.src.startsWith(
|
||||
"data:image/svg+xml;base64,PCEtLSBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljCiAgIC0gTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpcwogICAtIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uIC0tPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDE2IDE2IiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9ImNvbnRleHQtZmlsbCIgZmlsbC1vcGFjaXR5PSJjb250ZXh0LWZpbGwtb3BhY2l0eSI+CiAgPHBhdGggZD0iTS42MjUgMTNhLjYyNS42MjUgMCAwIDEgMC0xLjI1bDMuMjUgMEE0Ljg4IDQuODggMCAwIDAgOC43NSA2Ljg3NWwwLS4yNWEuNjI1LjYyNSAwIDAgMSAxLjI1IDBsMCAuMjVBNi4xMzIgNi4xMzIgMCAwIDEgMy44NzUgMTNsLTMuMjUgMHoiLz"
|
||||
),
|
||||
"The header icon matches the icon resource from manifest"
|
||||
);
|
||||
|
||||
// Accept the policies
|
||||
document
|
||||
.getElementsByClassName("popup-notification-primary-button")[0]
|
||||
.click();
|
||||
|
||||
// Make sure the call to the propmt resolves with true
|
||||
let value = await prompt;
|
||||
is(value, true, "User clicking accept resolves with true");
|
||||
|
||||
// Wait for the prompt to go away
|
||||
await popupHiding;
|
||||
|
||||
// Close tab
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// Test that rejecting the policies works
|
||||
add_task(async function test_policy_reject() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
let popupShown = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popupshown"
|
||||
);
|
||||
|
||||
// Show the same prompt with policies
|
||||
let prompt = IdentityCredentialPromptService.showPolicyPrompt(
|
||||
tab.linkedBrowser.browsingContext,
|
||||
{
|
||||
configURL: "https://idp.example/",
|
||||
clientId: "123",
|
||||
},
|
||||
{
|
||||
accounts_endpoint: "",
|
||||
client_metadata_endpoint: "",
|
||||
id_assertion_endpoint: "",
|
||||
},
|
||||
{
|
||||
privacy_policy_url: "https://idp.example/privacy-policy.html",
|
||||
terms_of_service_url: "https://idp.example/terms-of-service.html",
|
||||
}
|
||||
);
|
||||
|
||||
// Wait for the prompt to show up
|
||||
await popupShown;
|
||||
|
||||
let popupHiding = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popuphiding"
|
||||
);
|
||||
|
||||
let document = tab.linkedBrowser.browsingContext.topChromeWindow.document;
|
||||
|
||||
let title = document.getElementById("identity-credential-header-text");
|
||||
ok(
|
||||
title.textContent.includes("idp.example"),
|
||||
"IDP domain in the policy prompt header as domain"
|
||||
);
|
||||
|
||||
// Click reject.
|
||||
document
|
||||
.getElementsByClassName("popup-notification-secondary-button")[0]
|
||||
.click();
|
||||
|
||||
// Make sure the prompt call accepts with an indication of the user's reject choice.
|
||||
let value = await prompt;
|
||||
is(value, false, "User clicking reject causes the promise to resolve(false)");
|
||||
|
||||
// Wait for the popup to go away.
|
||||
await popupHiding;
|
||||
|
||||
// Close tab.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
Reference in New Issue
Block a user