mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Merge autoland to central, a=merge
MozReview-Commit-ID: 1k7iJedKGDR
This commit is contained in:
commit
b66d50d0ca
@ -18,7 +18,7 @@ def test(mod, path, entity = None):
|
||||
if mod not in ("browser", "extensions/spellcheck"):
|
||||
# we only have exceptions for browser and extensions/spellcheck
|
||||
return "error"
|
||||
if not entity:
|
||||
if entity is None:
|
||||
# the only files to ignore are spell checkers
|
||||
if mod == "extensions/spellcheck":
|
||||
return "ignore"
|
||||
|
169
browser/locales/l10n.toml
Normal file
169
browser/locales/l10n.toml
Normal file
@ -0,0 +1,169 @@
|
||||
# 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/.
|
||||
|
||||
basepath = "../.."
|
||||
|
||||
locales = [
|
||||
"ach",
|
||||
"af",
|
||||
"an",
|
||||
"ar",
|
||||
"as",
|
||||
"ast",
|
||||
"az",
|
||||
"be",
|
||||
"bg",
|
||||
"bn-BD",
|
||||
"bn-IN",
|
||||
"br",
|
||||
"bs",
|
||||
"ca",
|
||||
"cak",
|
||||
"cs",
|
||||
"cy",
|
||||
"da",
|
||||
"de",
|
||||
"dsb",
|
||||
"el",
|
||||
"en-GB",
|
||||
"en-ZA",
|
||||
"eo",
|
||||
"es-AR",
|
||||
"es-CL",
|
||||
"es-ES",
|
||||
"es-MX",
|
||||
"et",
|
||||
"eu",
|
||||
"fa",
|
||||
"ff",
|
||||
"fi",
|
||||
"fr",
|
||||
"fy-NL",
|
||||
"ga-IE",
|
||||
"gd",
|
||||
"gl",
|
||||
"gn",
|
||||
"gu-IN",
|
||||
"he",
|
||||
"hi-IN",
|
||||
"hr",
|
||||
"hsb",
|
||||
"hu",
|
||||
"hy-AM",
|
||||
"id",
|
||||
"is",
|
||||
"it",
|
||||
"ja",
|
||||
"ja-JP-mac",
|
||||
"ka",
|
||||
"kab",
|
||||
"kk",
|
||||
"km",
|
||||
"kn",
|
||||
"ko",
|
||||
"lij",
|
||||
"lo",
|
||||
"lt",
|
||||
"ltg",
|
||||
"lv",
|
||||
"mai",
|
||||
"mk",
|
||||
"ml",
|
||||
"mr",
|
||||
"ms",
|
||||
"my",
|
||||
"nb-NO",
|
||||
"ne-NP",
|
||||
"nl",
|
||||
"nn-NO",
|
||||
"or",
|
||||
"pa-IN",
|
||||
"pl",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
"rm",
|
||||
"ro",
|
||||
"ru",
|
||||
"si",
|
||||
"sk",
|
||||
"sl",
|
||||
"son",
|
||||
"sq",
|
||||
"sr",
|
||||
"sv-SE",
|
||||
"ta",
|
||||
"te",
|
||||
"th",
|
||||
"tl",
|
||||
"tr",
|
||||
"uk",
|
||||
"ur",
|
||||
"uz",
|
||||
"vi",
|
||||
"xh",
|
||||
"zh-CN",
|
||||
"zh-TW",
|
||||
]
|
||||
|
||||
[env]
|
||||
l = "{l10n_base}/{locale}/"
|
||||
|
||||
|
||||
[[paths]]
|
||||
reference = "browser/locales/en-US/**"
|
||||
l10n = "{l}browser/**"
|
||||
|
||||
[[paths]]
|
||||
reference = "browser/branding/official/locales/en-US/**"
|
||||
l10n = "{l}browser/branding/official/**"
|
||||
|
||||
[[paths]]
|
||||
reference = "browser/extensions/onboarding/locales/en-US/**"
|
||||
l10n = "{l}browser/extensions/onboarding/**"
|
||||
|
||||
[[paths]]
|
||||
reference = "browser/extensions/webcompat-reporter/locales/en-US/**"
|
||||
l10n = "{l}browser/extensions/webcompat-reporter/**"
|
||||
|
||||
[[paths]]
|
||||
reference = "services/sync/locales/en-US/**"
|
||||
l10n = "{l}services/sync/**"
|
||||
|
||||
|
||||
[[includes]]
|
||||
path = "toolkit/locales/l10n.toml"
|
||||
|
||||
[[includes]]
|
||||
path = "devtools/client/locales/l10n.toml"
|
||||
|
||||
# Filters
|
||||
# The filters below are evaluated one after the other, in the given order.
|
||||
# Enter a combination of path as in the localization, key, and an action,
|
||||
# to change the default behavior of compare-locales and l10n merge.
|
||||
#
|
||||
# For browser/locales/en-US/chrome/browser/foo.properties,
|
||||
# path would be {l}browser/chrome/browser/foo.properties
|
||||
# key: the key/id of the entity
|
||||
# If key isn't specified, the complete file can be missing.
|
||||
[[filters]]
|
||||
path = "{l}browser/defines.inc"
|
||||
key = "MOZ_LANGPACK_CONTRIBUTORS"
|
||||
action = "ignore"
|
||||
|
||||
[[filters]]
|
||||
path = [
|
||||
"{l}browser/defines.inc",
|
||||
"{l}browser/firefox-l10n.js",
|
||||
]
|
||||
action = "ignore"
|
||||
|
||||
[[filters]]
|
||||
path = "{l}browser/chrome/browser-region/region.properties"
|
||||
key = [
|
||||
"re:^browser\\.search\\.order\\.[1-9]$",
|
||||
"re:^browser\\.contentHandlers\\.types\\.[0-5]\\..*$",
|
||||
"re:^gecko\\.handlerService\\.schemes\\..*$",
|
||||
"re:^gecko\\.handlerService\\.defaultHandlersVersion$"
|
||||
]
|
||||
action = "ignore"
|
12
devtools/client/locales/l10n.toml
Normal file
12
devtools/client/locales/l10n.toml
Normal file
@ -0,0 +1,12 @@
|
||||
# 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/.
|
||||
|
||||
basepath = "../../.."
|
||||
|
||||
[env]
|
||||
l = "{l10n_base}/{locale}/"
|
||||
|
||||
[[paths]]
|
||||
reference = "devtools/client/locales/en-US/**"
|
||||
l10n = "{l}devtools/client/**"
|
12
devtools/shared/locales/l10n.toml
Normal file
12
devtools/shared/locales/l10n.toml
Normal file
@ -0,0 +1,12 @@
|
||||
# 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/.
|
||||
|
||||
basepath = "../../.."
|
||||
|
||||
[env]
|
||||
l = "{l10n_base}/{locale}/"
|
||||
|
||||
[[paths]]
|
||||
reference = "devtools/shared/locales/en-US/**"
|
||||
l10n = "{l}devtools/shared/**"
|
@ -33,6 +33,7 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "BatteryManager.h"
|
||||
#include "mozilla/dom/CredentialsContainer.h"
|
||||
#include "mozilla/dom/GamepadServiceTest.h"
|
||||
#include "mozilla/dom/PowerManager.h"
|
||||
#include "mozilla/dom/WakeLock.h"
|
||||
@ -48,7 +49,6 @@
|
||||
#include "mozilla/dom/VRDisplay.h"
|
||||
#include "mozilla/dom/VRDisplayEvent.h"
|
||||
#include "mozilla/dom/VRServiceTest.h"
|
||||
#include "mozilla/dom/WebAuthentication.h"
|
||||
#include "mozilla/dom/workers/RuntimeService.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
@ -205,7 +205,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAuthentication)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCredentials)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
|
||||
@ -2001,13 +2001,13 @@ Navigator::GetPresentation(ErrorResult& aRv)
|
||||
return mPresentation;
|
||||
}
|
||||
|
||||
WebAuthentication*
|
||||
Navigator::Authentication()
|
||||
CredentialsContainer*
|
||||
Navigator::Credentials()
|
||||
{
|
||||
if (!mAuthentication) {
|
||||
mAuthentication = new WebAuthentication(GetWindow());
|
||||
if (!mCredentials) {
|
||||
mCredentials = new CredentialsContainer(GetWindow());
|
||||
}
|
||||
return mAuthentication;
|
||||
return mCredentials;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -42,7 +42,7 @@ class ServiceWorkerContainer;
|
||||
class DOMRequest;
|
||||
struct FlyWebPublishOptions;
|
||||
struct FlyWebFilter;
|
||||
class WebAuthentication;
|
||||
class CredentialsContainer;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@ -225,7 +225,7 @@ public:
|
||||
|
||||
already_AddRefed<ServiceWorkerContainer> ServiceWorker();
|
||||
|
||||
mozilla::dom::WebAuthentication* Authentication();
|
||||
mozilla::dom::CredentialsContainer* Credentials();
|
||||
|
||||
void GetLanguages(nsTArray<nsString>& aLanguages);
|
||||
|
||||
@ -294,7 +294,7 @@ private:
|
||||
RefPtr<Promise> mBatteryPromise;
|
||||
RefPtr<PowerManager> mPowerManager;
|
||||
RefPtr<network::Connection> mConnection;
|
||||
RefPtr<WebAuthentication> mAuthentication;
|
||||
RefPtr<CredentialsContainer> mCredentials;
|
||||
RefPtr<MediaDevices> mMediaDevices;
|
||||
RefPtr<time::TimeManager> mTimeManager;
|
||||
RefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
|
||||
|
50
dom/credentialmanagement/Credential.cpp
Normal file
50
dom/credentialmanagement/Credential.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/Credential.h"
|
||||
#include "mozilla/dom/CredentialManagementBinding.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Credential, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Credential)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Credential)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Credential)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
Credential::Credential(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
{}
|
||||
|
||||
Credential::~Credential()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
Credential::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CredentialBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
Credential::GetId(nsAString& aId) const
|
||||
{
|
||||
aId.Assign(mId);
|
||||
}
|
||||
|
||||
void
|
||||
Credential::GetType(nsAString& aType) const
|
||||
{
|
||||
aType.Assign(mType);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
56
dom/credentialmanagement/Credential.h
Normal file
56
dom/credentialmanagement/Credential.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_Credential_h
|
||||
#define mozilla_dom_Credential_h
|
||||
|
||||
#include "mozilla/dom/CredentialManagementBinding.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Credential : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Credential)
|
||||
|
||||
public:
|
||||
explicit Credential(nsPIDOMWindowInner* aParent);
|
||||
|
||||
protected:
|
||||
virtual ~Credential();
|
||||
|
||||
public:
|
||||
nsISupports*
|
||||
GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void
|
||||
GetId(nsAString& aId) const;
|
||||
|
||||
void
|
||||
GetType(nsAString& aType) const;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
nsAutoString mId;
|
||||
nsAutoString mType;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Credential_h
|
54
dom/credentialmanagement/CredentialsContainer.cpp
Normal file
54
dom/credentialmanagement/CredentialsContainer.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/CredentialsContainer.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/WebAuthnManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CredentialsContainer, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CredentialsContainer)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CredentialsContainer)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CredentialsContainer)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
CredentialsContainer::CredentialsContainer(nsPIDOMWindowInner* aParent) :
|
||||
mParent(aParent)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
}
|
||||
|
||||
CredentialsContainer::~CredentialsContainer()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
CredentialsContainer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CredentialsContainerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CredentialsContainer::Get(const CredentialRequestOptions& aOptions)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
|
||||
MOZ_ASSERT(mgr);
|
||||
return mgr->GetAssertion(mParent, aOptions.mPublicKey);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CredentialsContainer::Create(const CredentialCreationOptions& aOptions)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
|
||||
MOZ_ASSERT(mgr);
|
||||
return mgr->MakeCredential(mParent, aOptions.mPublicKey);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
47
dom/credentialmanagement/CredentialsContainer.h
Normal file
47
dom/credentialmanagement/CredentialsContainer.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_CredentialsContainer_h
|
||||
#define mozilla_dom_CredentialsContainer_h
|
||||
|
||||
#include "mozilla/dom/CredentialManagementBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class CredentialsContainer final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CredentialsContainer)
|
||||
|
||||
explicit CredentialsContainer(nsPIDOMWindowInner* aParent);
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Get(const CredentialRequestOptions& aOptions);
|
||||
already_AddRefed<Promise>
|
||||
Create(const CredentialCreationOptions& aOptions);
|
||||
|
||||
private:
|
||||
~CredentialsContainer();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_CredentialsContainer_h
|
22
dom/credentialmanagement/moz.build
Normal file
22
dom/credentialmanagement/moz.build
Normal file
@ -0,0 +1,22 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Credential.h',
|
||||
'CredentialsContainer.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Credential.cpp',
|
||||
'CredentialsContainer.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
@ -50,6 +50,7 @@ DIRS += [
|
||||
'cache',
|
||||
'canvas',
|
||||
'commandhandler',
|
||||
'credentialmanagement',
|
||||
'crypto',
|
||||
'encoding',
|
||||
'events',
|
||||
|
66
dom/webauthn/AuthenticatorAssertionResponse.cpp
Normal file
66
dom/webauthn/AuthenticatorAssertionResponse.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/WebAuthenticationBinding.h"
|
||||
#include "mozilla/dom/AuthenticatorAssertionResponse.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(AuthenticatorAssertionResponse, AuthenticatorResponse)
|
||||
NS_IMPL_RELEASE_INHERITED(AuthenticatorAssertionResponse, AuthenticatorResponse)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AuthenticatorAssertionResponse)
|
||||
NS_INTERFACE_MAP_END_INHERITING(AuthenticatorResponse)
|
||||
|
||||
AuthenticatorAssertionResponse::AuthenticatorAssertionResponse(nsPIDOMWindowInner* aParent)
|
||||
: AuthenticatorResponse(aParent)
|
||||
{}
|
||||
|
||||
AuthenticatorAssertionResponse::~AuthenticatorAssertionResponse()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
AuthenticatorAssertionResponse::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return AuthenticatorAssertionResponseBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
AuthenticatorAssertionResponse::GetAuthenticatorData(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mAuthenticatorData.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
nsresult
|
||||
AuthenticatorAssertionResponse::SetAuthenticatorData(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (NS_WARN_IF(!mAuthenticatorData.Assign(aBuffer))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AuthenticatorAssertionResponse::GetSignature(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mSignature.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
nsresult
|
||||
AuthenticatorAssertionResponse::SetSignature(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (NS_WARN_IF(!mSignature.Assign(aBuffer))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
56
dom/webauthn/AuthenticatorAssertionResponse.h
Normal file
56
dom/webauthn/AuthenticatorAssertionResponse.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_AuthenticatorAssertionResponse_h
|
||||
#define mozilla_dom_AuthenticatorAssertionResponse_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/AuthenticatorResponse.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/CryptoBuffer.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AuthenticatorAssertionResponse final : public AuthenticatorResponse
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
explicit AuthenticatorAssertionResponse(nsPIDOMWindowInner* aParent);
|
||||
|
||||
protected:
|
||||
~AuthenticatorAssertionResponse() override;
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void
|
||||
GetAuthenticatorData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
nsresult
|
||||
SetAuthenticatorData(CryptoBuffer& aBuffer);
|
||||
|
||||
void
|
||||
GetSignature(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
nsresult
|
||||
SetSignature(CryptoBuffer& aBuffer);
|
||||
|
||||
private:
|
||||
CryptoBuffer mAuthenticatorData;
|
||||
CryptoBuffer mSignature;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_AuthenticatorAssertionResponse_h
|
50
dom/webauthn/AuthenticatorAttestationResponse.cpp
Normal file
50
dom/webauthn/AuthenticatorAttestationResponse.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/WebAuthenticationBinding.h"
|
||||
#include "mozilla/dom/AuthenticatorAttestationResponse.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(AuthenticatorAttestationResponse, AuthenticatorResponse)
|
||||
NS_IMPL_RELEASE_INHERITED(AuthenticatorAttestationResponse, AuthenticatorResponse)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AuthenticatorAttestationResponse)
|
||||
NS_INTERFACE_MAP_END_INHERITING(AuthenticatorResponse)
|
||||
|
||||
AuthenticatorAttestationResponse::AuthenticatorAttestationResponse(nsPIDOMWindowInner* aParent)
|
||||
: AuthenticatorResponse(aParent)
|
||||
{}
|
||||
|
||||
AuthenticatorAttestationResponse::~AuthenticatorAttestationResponse()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
AuthenticatorAttestationResponse::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return AuthenticatorAttestationResponseBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
AuthenticatorAttestationResponse::GetAttestationObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mAttestationObject.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
nsresult
|
||||
AuthenticatorAttestationResponse::SetAttestationObject(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (NS_WARN_IF(!mAttestationObject.Assign(aBuffer))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
49
dom/webauthn/AuthenticatorAttestationResponse.h
Normal file
49
dom/webauthn/AuthenticatorAttestationResponse.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_AuthenticatorAttestationResponse_h
|
||||
#define mozilla_dom_AuthenticatorAttestationResponse_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/AuthenticatorResponse.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/CryptoBuffer.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AuthenticatorAttestationResponse final : public AuthenticatorResponse
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
explicit AuthenticatorAttestationResponse(nsPIDOMWindowInner* aParent);
|
||||
|
||||
protected:
|
||||
~AuthenticatorAttestationResponse() override;
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void
|
||||
GetAttestationObject(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
nsresult
|
||||
SetAttestationObject(CryptoBuffer& aBuffer);
|
||||
|
||||
private:
|
||||
CryptoBuffer mAttestationObject;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_AuthenticatorAttestationResponse_h
|
53
dom/webauthn/AuthenticatorResponse.cpp
Normal file
53
dom/webauthn/AuthenticatorResponse.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/WebAuthenticationBinding.h"
|
||||
#include "mozilla/dom/AuthenticatorResponse.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Only needed for refcounted objects.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AuthenticatorResponse, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AuthenticatorResponse)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AuthenticatorResponse)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AuthenticatorResponse)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
AuthenticatorResponse::AuthenticatorResponse(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
{}
|
||||
|
||||
AuthenticatorResponse::~AuthenticatorResponse()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
AuthenticatorResponse::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return AuthenticatorResponseBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
AuthenticatorResponse::GetClientDataJSON(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mClientDataJSON.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
nsresult
|
||||
AuthenticatorResponse::SetClientDataJSON(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (NS_WARN_IF(!mClientDataJSON.Assign(aBuffer))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -4,33 +4,31 @@
|
||||
* 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_ScopedCredential_h
|
||||
#define mozilla_dom_ScopedCredential_h
|
||||
#ifndef mozilla_dom_AuthenticatorResponse_h
|
||||
#define mozilla_dom_AuthenticatorResponse_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/CryptoBuffer.h"
|
||||
#include "mozilla/dom/WebAuthenticationBinding.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ScopedCredential final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
class AuthenticatorResponse : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScopedCredential)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AuthenticatorResponse)
|
||||
|
||||
public:
|
||||
explicit ScopedCredential(nsPIDOMWindowInner* aParent);
|
||||
explicit AuthenticatorResponse(nsPIDOMWindowInner* aParent);
|
||||
|
||||
protected:
|
||||
~ScopedCredential();
|
||||
virtual ~AuthenticatorResponse();
|
||||
|
||||
public:
|
||||
nsISupports*
|
||||
@ -42,25 +40,21 @@ public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
ScopedCredentialType
|
||||
Type() const;
|
||||
void
|
||||
GetFormat(nsString& aRetVal) const;
|
||||
|
||||
void
|
||||
GetId(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
GetClientDataJSON(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
nsresult
|
||||
SetType(ScopedCredentialType aType);
|
||||
|
||||
nsresult
|
||||
SetId(CryptoBuffer& aBuffer);
|
||||
SetClientDataJSON(CryptoBuffer& aBuffer);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
CryptoBuffer mIdBuffer;
|
||||
ScopedCredentialType mType;
|
||||
CryptoBuffer mClientDataJSON;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ScopedCredential_h
|
||||
#endif // mozilla_dom_AuthenticatorResponse_h
|
@ -20,11 +20,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct WebAuthnScopedCredentialDescriptor {
|
||||
// Converted from mozilla::dom::ScopedCredentialType enum
|
||||
uint32_t type;
|
||||
uint8_t[] id;
|
||||
// Converted from mozilla::dom::WebAuthnTransport enum
|
||||
uint32_t[] transports;
|
||||
};
|
||||
|
||||
struct WebAuthnExtension {
|
||||
|
69
dom/webauthn/PublicKeyCredential.cpp
Normal file
69
dom/webauthn/PublicKeyCredential.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/PublicKeyCredential.h"
|
||||
#include "mozilla/dom/WebAuthenticationBinding.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(PublicKeyCredential, Credential, mResponse)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(PublicKeyCredential, Credential)
|
||||
NS_IMPL_RELEASE_INHERITED(PublicKeyCredential, Credential)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PublicKeyCredential)
|
||||
NS_INTERFACE_MAP_END_INHERITING(Credential)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PublicKeyCredential, Credential)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
PublicKeyCredential::PublicKeyCredential(nsPIDOMWindowInner* aParent)
|
||||
: Credential(aParent)
|
||||
{}
|
||||
|
||||
PublicKeyCredential::~PublicKeyCredential()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
PublicKeyCredential::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PublicKeyCredentialBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
PublicKeyCredential::GetRawId(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mRawId.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
already_AddRefed<AuthenticatorResponse>
|
||||
PublicKeyCredential::Response() const
|
||||
{
|
||||
RefPtr<AuthenticatorResponse> temp(mResponse);
|
||||
return temp.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
PublicKeyCredential::SetRawId(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (NS_WARN_IF(!mRawId.Assign(aBuffer))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PublicKeyCredential::SetResponse(RefPtr<AuthenticatorResponse> aResponse)
|
||||
{
|
||||
mResponse = aResponse;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
59
dom/webauthn/PublicKeyCredential.h
Normal file
59
dom/webauthn/PublicKeyCredential.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_PublicKeyCredential_h
|
||||
#define mozilla_dom_PublicKeyCredential_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/Credential.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PublicKeyCredential final : public Credential
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PublicKeyCredential,
|
||||
Credential)
|
||||
|
||||
explicit PublicKeyCredential(nsPIDOMWindowInner* aParent);
|
||||
|
||||
protected:
|
||||
~PublicKeyCredential() override;
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void
|
||||
GetRawId(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
already_AddRefed<AuthenticatorResponse>
|
||||
Response() const;
|
||||
|
||||
nsresult
|
||||
SetRawId(CryptoBuffer& aBuffer);
|
||||
|
||||
void
|
||||
SetResponse(RefPtr<AuthenticatorResponse>);
|
||||
|
||||
private:
|
||||
CryptoBuffer mRawId;
|
||||
RefPtr<AuthenticatorResponse> mResponse;
|
||||
// Extensions are not supported yet.
|
||||
// <some type> mClientExtensionResults;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PublicKeyCredential_h
|
@ -1,66 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/ScopedCredential.h"
|
||||
#include "mozilla/dom/WebAuthenticationBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Only needed for refcounted objects.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ScopedCredential, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ScopedCredential)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ScopedCredential)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScopedCredential)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
ScopedCredential::ScopedCredential(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
, mType(ScopedCredentialType::ScopedCred)
|
||||
{}
|
||||
|
||||
ScopedCredential::~ScopedCredential()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
ScopedCredential::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return ScopedCredentialBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
ScopedCredentialType
|
||||
ScopedCredential::Type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
void
|
||||
ScopedCredential::GetId(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mIdBuffer.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
nsresult
|
||||
ScopedCredential::SetType(ScopedCredentialType aType)
|
||||
{
|
||||
mType = aType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ScopedCredential::SetId(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (!mIdBuffer.Assign(aBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,63 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/ScopedCredentialInfo.h"
|
||||
#include "mozilla/dom/WebAuthenticationBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Only needed for refcounted objects.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ScopedCredentialInfo, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ScopedCredentialInfo)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ScopedCredentialInfo)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScopedCredentialInfo)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
ScopedCredentialInfo::ScopedCredentialInfo(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
{}
|
||||
|
||||
ScopedCredentialInfo::~ScopedCredentialInfo()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
ScopedCredentialInfo::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return ScopedCredentialInfoBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<ScopedCredential>
|
||||
ScopedCredentialInfo::Credential() const
|
||||
{
|
||||
RefPtr<ScopedCredential> temp(mCredential);
|
||||
return temp.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<WebAuthnAttestation>
|
||||
ScopedCredentialInfo::Attestation() const
|
||||
{
|
||||
RefPtr<WebAuthnAttestation> temp(mAttestation);
|
||||
return temp.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ScopedCredentialInfo::SetCredential(RefPtr<ScopedCredential> aCredential)
|
||||
{
|
||||
mCredential = aCredential;
|
||||
}
|
||||
|
||||
void
|
||||
ScopedCredentialInfo::SetAttestation(RefPtr<WebAuthnAttestation> aAttestation)
|
||||
{
|
||||
mAttestation = aAttestation;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,74 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_ScopedCredentialInfo_h
|
||||
#define mozilla_dom_ScopedCredentialInfo_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class CryptoKey;
|
||||
class ScopedCredential;
|
||||
class WebAuthnAttestation;
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ScopedCredentialInfo final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScopedCredentialInfo)
|
||||
|
||||
public:
|
||||
explicit ScopedCredentialInfo(nsPIDOMWindowInner* aParent);
|
||||
|
||||
protected:
|
||||
~ScopedCredentialInfo();
|
||||
|
||||
public:
|
||||
nsISupports*
|
||||
GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
already_AddRefed<ScopedCredential>
|
||||
Credential() const;
|
||||
|
||||
already_AddRefed<WebAuthnAttestation>
|
||||
Attestation() const;
|
||||
|
||||
void
|
||||
SetCredential(RefPtr<ScopedCredential>);
|
||||
|
||||
void
|
||||
SetAttestation(RefPtr<WebAuthnAttestation>);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
RefPtr<WebAuthnAttestation> mAttestation;
|
||||
RefPtr<ScopedCredential> mCredential;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ScopedCredentialInfo_h
|
@ -1,58 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/WebAuthentication.h"
|
||||
#include "mozilla/dom/WebAuthnManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Only needed for refcounted objects.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthentication, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthentication)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthentication)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthentication)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
WebAuthentication::WebAuthentication(nsPIDOMWindowInner* aParent) :
|
||||
mParent(aParent)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
}
|
||||
|
||||
WebAuthentication::~WebAuthentication()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
WebAuthentication::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return WebAuthenticationBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WebAuthentication::MakeCredential(JSContext* aCx, const Account& aAccount,
|
||||
const Sequence<ScopedCredentialParameters>& aCryptoParameters,
|
||||
const ArrayBufferViewOrArrayBuffer& aChallenge,
|
||||
const ScopedCredentialOptions& aOptions)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
|
||||
MOZ_ASSERT(mgr);
|
||||
return mgr->MakeCredential(mParent, aCx, aAccount, aCryptoParameters, aChallenge, aOptions);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WebAuthentication::GetAssertion(const ArrayBufferViewOrArrayBuffer& aChallenge,
|
||||
const AssertionOptions& aOptions)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
|
||||
MOZ_ASSERT(mgr);
|
||||
return mgr->GetAssertion(mParent, aChallenge, aOptions);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,66 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_WebAuthentication_h
|
||||
#define mozilla_dom_WebAuthentication_h
|
||||
|
||||
#include "mozilla/dom/WebAuthenticationBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct Account;
|
||||
class ArrayBufferViewOrArrayBuffer;
|
||||
struct AssertionOptions;
|
||||
struct ScopedCredentialOptions;
|
||||
struct ScopedCredentialParameters;
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WebAuthentication final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthentication)
|
||||
|
||||
explicit WebAuthentication(nsPIDOMWindowInner* aParent);
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MakeCredential(JSContext* aCx, const Account& accountInformation,
|
||||
const Sequence<ScopedCredentialParameters>& cryptoParameters,
|
||||
const ArrayBufferViewOrArrayBuffer& attestationChallenge,
|
||||
const ScopedCredentialOptions& options);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetAssertion(const ArrayBufferViewOrArrayBuffer& assertionChallenge,
|
||||
const AssertionOptions& options);
|
||||
private:
|
||||
~WebAuthentication();
|
||||
|
||||
already_AddRefed<Promise> CreatePromise();
|
||||
nsresult GetOrigin(/*out*/ nsAString& aOrigin);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_WebAuthentication_h
|
@ -1,98 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/WebAuthenticationBinding.h"
|
||||
#include "mozilla/dom/WebAuthnAssertion.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Only needed for refcounted objects.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthnAssertion, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthnAssertion)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthnAssertion)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthnAssertion)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
WebAuthnAssertion::WebAuthnAssertion(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
{}
|
||||
|
||||
WebAuthnAssertion::~WebAuthnAssertion()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
WebAuthnAssertion::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return WebAuthnAssertionBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<ScopedCredential>
|
||||
WebAuthnAssertion::Credential() const
|
||||
{
|
||||
RefPtr<ScopedCredential> temp(mCredential);
|
||||
return temp.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnAssertion::GetClientData(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mClientData.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnAssertion::GetAuthenticatorData(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mAuthenticatorData.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnAssertion::GetSignature(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mSignature.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebAuthnAssertion::SetCredential(RefPtr<ScopedCredential> aCredential)
|
||||
{
|
||||
mCredential = aCredential;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebAuthnAssertion::SetClientData(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (!mClientData.Assign(aBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebAuthnAssertion::SetAuthenticatorData(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (!mAuthenticatorData.Assign(aBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebAuthnAssertion::SetSignature(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (!mSignature.Assign(aBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,86 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_WebAuthnAssertion_h
|
||||
#define mozilla_dom_WebAuthnAssertion_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ScopedCredential;
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WebAuthnAssertion final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthnAssertion)
|
||||
|
||||
public:
|
||||
explicit WebAuthnAssertion(nsPIDOMWindowInner* aParent);
|
||||
|
||||
protected:
|
||||
~WebAuthnAssertion();
|
||||
|
||||
public:
|
||||
nsPIDOMWindowInner*
|
||||
GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
already_AddRefed<ScopedCredential>
|
||||
Credential() const;
|
||||
|
||||
void
|
||||
GetClientData(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
void
|
||||
GetAuthenticatorData(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
void
|
||||
GetSignature(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
nsresult
|
||||
SetCredential(RefPtr<ScopedCredential> aCredential);
|
||||
|
||||
nsresult
|
||||
SetClientData(CryptoBuffer& aBuffer);
|
||||
|
||||
nsresult
|
||||
SetAuthenticatorData(CryptoBuffer& aBuffer);
|
||||
|
||||
nsresult
|
||||
SetSignature(CryptoBuffer& aBuffer);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
RefPtr<ScopedCredential> mCredential;
|
||||
CryptoBuffer mAuthenticatorData;
|
||||
CryptoBuffer mClientData;
|
||||
CryptoBuffer mSignature;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_WebAuthnAssertion_h
|
@ -1,98 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/dom/WebAuthenticationBinding.h"
|
||||
#include "mozilla/dom/WebAuthnAttestation.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Only needed for refcounted objects.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthnAttestation, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthnAttestation)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthnAttestation)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthnAttestation)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
WebAuthnAttestation::WebAuthnAttestation(nsPIDOMWindowInner* aParent)
|
||||
: mParent(aParent)
|
||||
{}
|
||||
|
||||
WebAuthnAttestation::~WebAuthnAttestation()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
WebAuthnAttestation::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return WebAuthnAttestationBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnAttestation::GetFormat(nsString& aRetVal) const
|
||||
{
|
||||
aRetVal = mFormat;
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnAttestation::GetClientData(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mClientData.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnAttestation::GetAuthenticatorData(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetVal) const
|
||||
{
|
||||
aRetVal.set(mAuthenticatorData.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
void
|
||||
WebAuthnAttestation::GetAttestation(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aRetVal) const
|
||||
{
|
||||
aRetVal.setObject(*mAttestation.ToUint8Array(aCx));
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebAuthnAttestation::SetFormat(nsString aFormat)
|
||||
{
|
||||
mFormat = aFormat;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebAuthnAttestation::SetClientData(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (!mClientData.Assign(aBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebAuthnAttestation::SetAuthenticatorData(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (!mAuthenticatorData.Assign(aBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebAuthnAttestation::SetAttestation(CryptoBuffer& aBuffer)
|
||||
{
|
||||
if (!mAttestation.Assign(aBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,79 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_WebAuthnAttestation_h
|
||||
#define mozilla_dom_WebAuthnAttestation_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/CryptoBuffer.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WebAuthnAttestation final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthnAttestation)
|
||||
|
||||
public:
|
||||
explicit WebAuthnAttestation(nsPIDOMWindowInner* aParent);
|
||||
|
||||
protected:
|
||||
~WebAuthnAttestation();
|
||||
|
||||
public:
|
||||
nsISupports*
|
||||
GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void
|
||||
GetFormat(nsString& aRetVal) const;
|
||||
|
||||
void
|
||||
GetClientData(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
void
|
||||
GetAuthenticatorData(JSContext* caCxx, JS::MutableHandle<JSObject*> aRetVal) const;
|
||||
|
||||
void
|
||||
GetAttestation(JSContext* aCx, JS::MutableHandle<JS::Value> aRetVal) const;
|
||||
|
||||
nsresult
|
||||
SetFormat(nsString aFormat);
|
||||
|
||||
nsresult
|
||||
SetClientData(CryptoBuffer& aBuffer);
|
||||
|
||||
nsresult
|
||||
SetAuthenticatorData(CryptoBuffer& aBuffer);
|
||||
|
||||
nsresult
|
||||
SetAttestation(CryptoBuffer& aBuffer);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
nsString mFormat;
|
||||
CryptoBuffer mClientData;
|
||||
CryptoBuffer mAuthenticatorData;
|
||||
CryptoBuffer mAttestation;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_WebAuthnAttestation_h
|
@ -8,6 +8,7 @@
|
||||
#include "nsNetCID.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/AuthenticatorAttestationResponse.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/WebAuthnManager.h"
|
||||
#include "mozilla/dom/WebAuthnUtil.h"
|
||||
@ -39,7 +40,7 @@ NS_IMPL_ISUPPORTS(WebAuthnManager, nsIIPCBackgroundChildCreateCallback);
|
||||
|
||||
template<class OOS>
|
||||
static nsresult
|
||||
GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
|
||||
GetAlgorithmName(const OOS& aAlgorithm,
|
||||
/* out */ nsString& aName)
|
||||
{
|
||||
MOZ_ASSERT(aAlgorithm.IsString()); // TODO: remove assertion when we coerce.
|
||||
@ -99,10 +100,10 @@ AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
WebAuthnClientData clientDataObject;
|
||||
clientDataObject.mOrigin.Assign(aOrigin);
|
||||
clientDataObject.mHashAlg.SetAsString().Assign(NS_LITERAL_STRING("S256"));
|
||||
CollectedClientData clientDataObject;
|
||||
clientDataObject.mChallenge.Assign(challengeBase64);
|
||||
clientDataObject.mOrigin.Assign(aOrigin);
|
||||
clientDataObject.mHashAlg.Assign(NS_LITERAL_STRING("S256"));
|
||||
|
||||
nsAutoString temp;
|
||||
if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) {
|
||||
@ -214,11 +215,8 @@ WebAuthnManager::Get()
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
const Account& aAccountInformation,
|
||||
const Sequence<ScopedCredentialParameters>& aCryptoParameters,
|
||||
const ArrayBufferViewOrArrayBuffer& aChallenge,
|
||||
const ScopedCredentialOptions& aOptions)
|
||||
WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent,
|
||||
const MakeCredentialOptions& aOptions)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
|
||||
@ -244,15 +242,15 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
// closest value lying within that range.
|
||||
|
||||
double adjustedTimeout = 30.0;
|
||||
if (aOptions.mTimeoutSeconds.WasPassed()) {
|
||||
adjustedTimeout = aOptions.mTimeoutSeconds.Value();
|
||||
if (aOptions.mTimeout.WasPassed()) {
|
||||
adjustedTimeout = aOptions.mTimeout.Value();
|
||||
adjustedTimeout = std::max(15.0, adjustedTimeout);
|
||||
adjustedTimeout = std::min(120.0, adjustedTimeout);
|
||||
}
|
||||
|
||||
nsCString rpId;
|
||||
if (!aOptions.mRpId.WasPassed()) {
|
||||
// If rpId is not specified, then set rpId to callerOrigin, and rpIdHash to
|
||||
if (!aOptions.mRp.mId.WasPassed()) {
|
||||
// If rp.id is not specified, then set rpId to callerOrigin, and rpIdHash to
|
||||
// the SHA-256 hash of rpId.
|
||||
rpId.Assign(NS_ConvertUTF16toUTF8(origin));
|
||||
} else {
|
||||
@ -264,7 +262,7 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
// Otherwise, reject promise with a DOMException whose name is
|
||||
// "SecurityError", and terminate this algorithm.
|
||||
|
||||
if (NS_FAILED(RelaxSameOrigin(aParent, aOptions.mRpId.Value(), rpId))) {
|
||||
if (NS_FAILED(RelaxSameOrigin(aParent, aOptions.mRp.mId.Value(), rpId))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
@ -292,15 +290,15 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
|
||||
// Process each element of cryptoParameters using the following steps, to
|
||||
// produce a new sequence normalizedParameters.
|
||||
nsTArray<ScopedCredentialParameters> normalizedParams;
|
||||
for (size_t a = 0; a < aCryptoParameters.Length(); ++a) {
|
||||
nsTArray<PublicKeyCredentialParameters> normalizedParams;
|
||||
for (size_t a = 0; a < aOptions.mParameters.Length(); ++a) {
|
||||
// Let current be the currently selected element of
|
||||
// cryptoParameters.
|
||||
|
||||
// If current.type does not contain a ScopedCredentialType
|
||||
// If current.type does not contain a PublicKeyCredentialType
|
||||
// supported by this implementation, then stop processing current and move
|
||||
// on to the next element in cryptoParameters.
|
||||
if (aCryptoParameters[a].mType != ScopedCredentialType::ScopedCred) {
|
||||
if (aOptions.mParameters[a].mType != PublicKeyCredentialType::Public_key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -311,16 +309,16 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
// element in cryptoParameters.
|
||||
|
||||
nsString algName;
|
||||
if (NS_FAILED(GetAlgorithmName(aCx, aCryptoParameters[a].mAlgorithm,
|
||||
if (NS_FAILED(GetAlgorithmName(aOptions.mParameters[a].mAlgorithm,
|
||||
algName))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add a new object of type ScopedCredentialParameters to
|
||||
// Add a new object of type PublicKeyCredentialParameters to
|
||||
// normalizedParameters, with type set to current.type and algorithm set to
|
||||
// normalizedAlgorithm.
|
||||
ScopedCredentialParameters normalizedObj;
|
||||
normalizedObj.mType = aCryptoParameters[a].mType;
|
||||
PublicKeyCredentialParameters normalizedObj;
|
||||
normalizedObj.mType = aOptions.mParameters[a].mType;
|
||||
normalizedObj.mAlgorithm.SetAsString().Assign(algName);
|
||||
|
||||
if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){
|
||||
@ -332,7 +330,7 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
// If normalizedAlgorithm is empty and cryptoParameters was not empty, cancel
|
||||
// the timer started in step 2, reject promise with a DOMException whose name
|
||||
// is "NotSupportedError", and terminate this algorithm.
|
||||
if (normalizedParams.IsEmpty() && !aCryptoParameters.IsEmpty()) {
|
||||
if (normalizedParams.IsEmpty() && !aOptions.mParameters.IsEmpty()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
@ -340,16 +338,17 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
// TODO: The following check should not be here. This is checking for
|
||||
// parameters specific to the soft key, and should be put in the soft key
|
||||
// manager in the parent process. Still need to serialize
|
||||
// ScopedCredentialParameters first.
|
||||
// PublicKeyCredentialParameters first.
|
||||
|
||||
// Check if at least one of the specified combinations of ScopedCredentialType
|
||||
// and cryptographic parameters is supported. If not, return an error code
|
||||
// equivalent to NotSupportedError and terminate the operation.
|
||||
// Check if at least one of the specified combinations of
|
||||
// PublicKeyCredentialParameters and cryptographic parameters is supported. If
|
||||
// not, return an error code equivalent to NotSupportedError and terminate the
|
||||
// operation.
|
||||
|
||||
bool isValidCombination = false;
|
||||
|
||||
for (size_t a = 0; a < normalizedParams.Length(); ++a) {
|
||||
if (normalizedParams[a].mType == ScopedCredentialType::ScopedCred &&
|
||||
if (normalizedParams[a].mType == PublicKeyCredentialType::Public_key &&
|
||||
normalizedParams[a].mAlgorithm.IsString() &&
|
||||
normalizedParams[a].mAlgorithm.GetAsString().EqualsLiteral(
|
||||
WEBCRYPTO_NAMED_CURVE_P256)) {
|
||||
@ -378,7 +377,7 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
// and compute the clientDataJSON and clientDataHash.
|
||||
|
||||
CryptoBuffer challenge;
|
||||
if (!challenge.Assign(aChallenge)) {
|
||||
if (!challenge.Assign(aOptions.mChallenge)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
@ -406,15 +405,9 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
if (aOptions.mExcludeList.WasPassed()) {
|
||||
for (const auto& s: aOptions.mExcludeList.Value()) {
|
||||
WebAuthnScopedCredentialDescriptor c;
|
||||
c.type() = static_cast<uint32_t>(s.mType);
|
||||
CryptoBuffer cb;
|
||||
cb.Assign(s.mId);
|
||||
c.id() = cb;
|
||||
if (s.mTransports.WasPassed()) {
|
||||
for (const auto& t: s.mTransports.Value()) {
|
||||
c.transports().AppendElement(static_cast<uint32_t>(t));
|
||||
}
|
||||
}
|
||||
excludeList.AppendElement(c);
|
||||
}
|
||||
}
|
||||
@ -463,8 +456,7 @@ WebAuthnManager::StartSign() {
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
|
||||
const ArrayBufferViewOrArrayBuffer& aChallenge,
|
||||
const AssertionOptions& aOptions)
|
||||
const PublicKeyCredentialRequestOptions& aOptions)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
|
||||
@ -489,11 +481,11 @@ WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
|
||||
// reasonable range as defined by the platform and if not, correct it to the
|
||||
// closest value lying within that range.
|
||||
|
||||
double adjustedTimeout = 30.0;
|
||||
if (aOptions.mTimeoutSeconds.WasPassed()) {
|
||||
adjustedTimeout = aOptions.mTimeoutSeconds.Value();
|
||||
adjustedTimeout = std::max(15.0, adjustedTimeout);
|
||||
adjustedTimeout = std::min(120.0, adjustedTimeout);
|
||||
uint32_t adjustedTimeout = 30000;
|
||||
if (aOptions.mTimeout.WasPassed()) {
|
||||
adjustedTimeout = aOptions.mTimeout.Value();
|
||||
adjustedTimeout = std::max(15000u, adjustedTimeout);
|
||||
adjustedTimeout = std::min(120000u, adjustedTimeout);
|
||||
}
|
||||
|
||||
nsCString rpId;
|
||||
@ -541,7 +533,7 @@ WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
|
||||
// representing this request. Choose a hash algorithm for hashAlg and compute
|
||||
// the clientDataJSON and clientDataHash.
|
||||
CryptoBuffer challenge;
|
||||
if (!challenge.Assign(aChallenge)) {
|
||||
if (!challenge.Assign(aOptions.mChallenge)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
@ -567,23 +559,17 @@ WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
|
||||
|
||||
// Note: we only support U2F-style authentication for now, so we effectively
|
||||
// require an AllowList.
|
||||
if (!aOptions.mAllowList.WasPassed()) {
|
||||
if (aOptions.mAllowList.Length() < 1) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsTArray<WebAuthnScopedCredentialDescriptor> allowList;
|
||||
for (const auto& s: aOptions.mAllowList.Value()) {
|
||||
for (const auto& s: aOptions.mAllowList) {
|
||||
WebAuthnScopedCredentialDescriptor c;
|
||||
c.type() = static_cast<uint32_t>(s.mType);
|
||||
CryptoBuffer cb;
|
||||
cb.Assign(s.mId);
|
||||
c.id() = cb;
|
||||
if (s.mTransports.WasPassed()) {
|
||||
for (const auto& t: s.mTransports.Value()) {
|
||||
c.transports().AppendElement(static_cast<uint32_t>(t));
|
||||
}
|
||||
}
|
||||
allowList.AppendElement(c);
|
||||
}
|
||||
|
||||
@ -597,7 +583,7 @@ WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
|
||||
|
||||
WebAuthnTransactionInfo info(rpIdHash,
|
||||
clientDataHash,
|
||||
10000,
|
||||
adjustedTimeout,
|
||||
allowList,
|
||||
extensions);
|
||||
RefPtr<MozPromise<nsresult, nsresult, false>> p = GetOrCreateBackgroundActor();
|
||||
@ -674,25 +660,19 @@ WebAuthnManager::FinishMakeCredential(nsTArray<uint8_t>& aRegBuffer,
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new ScopedCredentialInfo object named value and populate its
|
||||
// fields with the values returned from the authenticator as well as the
|
||||
// clientDataJSON computed earlier.
|
||||
// Create a new PublicKeyCredential object and populate its fields with the
|
||||
// values returned from the authenticator as well as the clientDataJSON
|
||||
// computed earlier.
|
||||
RefPtr<AuthenticatorAttestationResponse> attestation =
|
||||
new AuthenticatorAttestationResponse(mCurrentParent);
|
||||
attestation->SetClientDataJSON(clientDataBuf);
|
||||
attestation->SetAttestationObject(regData);
|
||||
|
||||
RefPtr<ScopedCredential> credential = new ScopedCredential(mCurrentParent);
|
||||
credential->SetType(ScopedCredentialType::ScopedCred);
|
||||
credential->SetId(keyHandleBuf);
|
||||
RefPtr<PublicKeyCredential> credential = new PublicKeyCredential(mCurrentParent);
|
||||
credential->SetRawId(keyHandleBuf);
|
||||
credential->SetResponse(attestation);
|
||||
|
||||
RefPtr<WebAuthnAttestation> attestation = new WebAuthnAttestation(mCurrentParent);
|
||||
attestation->SetFormat(NS_LITERAL_STRING("u2f"));
|
||||
attestation->SetClientData(clientDataBuf);
|
||||
attestation->SetAuthenticatorData(authenticatorDataBuf);
|
||||
attestation->SetAttestation(regData);
|
||||
|
||||
RefPtr<ScopedCredentialInfo> info = new ScopedCredentialInfo(mCurrentParent);
|
||||
info->SetCredential(credential);
|
||||
info->SetAttestation(attestation);
|
||||
|
||||
mTransactionPromise->MaybeResolve(info);
|
||||
mTransactionPromise->MaybeResolve(credential);
|
||||
MaybeClearTransaction();
|
||||
}
|
||||
|
||||
@ -737,21 +717,21 @@ WebAuthnManager::FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
|
||||
|
||||
// If any authenticator returns success:
|
||||
|
||||
// Create a new WebAuthnAssertion object named value and populate its fields
|
||||
// Create a new PublicKeyCredential object named value and populate its fields
|
||||
// with the values returned from the authenticator as well as the
|
||||
// clientDataJSON computed earlier.
|
||||
|
||||
RefPtr<ScopedCredential> credential = new ScopedCredential(mCurrentParent);
|
||||
credential->SetType(ScopedCredentialType::ScopedCred);
|
||||
credential->SetId(credentialBuf);
|
||||
|
||||
RefPtr<WebAuthnAssertion> assertion = new WebAuthnAssertion(mCurrentParent);
|
||||
assertion->SetCredential(credential);
|
||||
assertion->SetClientData(clientDataBuf);
|
||||
RefPtr<AuthenticatorAssertionResponse> assertion =
|
||||
new AuthenticatorAssertionResponse(mCurrentParent);
|
||||
assertion->SetClientDataJSON(clientDataBuf);
|
||||
assertion->SetAuthenticatorData(authenticatorDataBuf);
|
||||
assertion->SetSignature(signatureData);
|
||||
|
||||
mTransactionPromise->MaybeResolve(assertion);
|
||||
RefPtr<PublicKeyCredential> credential =
|
||||
new PublicKeyCredential(mCurrentParent);
|
||||
credential->SetRawId(credentialBuf);
|
||||
credential->SetResponse(assertion);
|
||||
|
||||
mTransactionPromise->MaybeResolve(credential);
|
||||
MaybeClearTransaction();
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,9 @@
|
||||
#ifndef mozilla_dom_WebAuthnManager_h
|
||||
#define mozilla_dom_WebAuthnManager_h
|
||||
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/dom/PWebAuthnTransaction.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
|
||||
/*
|
||||
* Content process manager for the WebAuthn protocol. Created on calls to the
|
||||
@ -73,16 +73,12 @@ public:
|
||||
Cancel(const nsresult& aError);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MakeCredential(nsPIDOMWindowInner* aParent, JSContext* aCx,
|
||||
const Account& aAccountInformation,
|
||||
const Sequence<ScopedCredentialParameters>& aCryptoParameters,
|
||||
const ArrayBufferViewOrArrayBuffer& aAttestationChallenge,
|
||||
const ScopedCredentialOptions& aOptions);
|
||||
MakeCredential(nsPIDOMWindowInner* aParent,
|
||||
const MakeCredentialOptions& aOptions);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetAssertion(nsPIDOMWindowInner* aParent,
|
||||
const ArrayBufferViewOrArrayBuffer& aAssertionChallenge,
|
||||
const AssertionOptions& aOptions);
|
||||
const PublicKeyCredentialRequestOptions& aOptions);
|
||||
|
||||
void StartRegister();
|
||||
void StartSign();
|
||||
|
@ -12,15 +12,14 @@ IPDL_SOURCES += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'AuthenticatorAssertionResponse.h',
|
||||
'AuthenticatorAttestationResponse.h',
|
||||
'AuthenticatorResponse.h',
|
||||
'NSSU2FTokenRemote.h',
|
||||
'ScopedCredential.h',
|
||||
'ScopedCredentialInfo.h',
|
||||
'PublicKeyCredential.h',
|
||||
'U2FSoftTokenManager.h',
|
||||
'U2FTokenManager.h',
|
||||
'U2FTokenTransport.h',
|
||||
'WebAuthentication.h',
|
||||
'WebAuthnAssertion.h',
|
||||
'WebAuthnAttestation.h',
|
||||
'WebAuthnManager.h',
|
||||
'WebAuthnRequest.h',
|
||||
'WebAuthnTransactionChild.h',
|
||||
@ -29,14 +28,13 @@ EXPORTS.mozilla.dom += [
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AuthenticatorAssertionResponse.cpp',
|
||||
'AuthenticatorAttestationResponse.cpp',
|
||||
'AuthenticatorResponse.cpp',
|
||||
'NSSU2FTokenRemote.cpp',
|
||||
'ScopedCredential.cpp',
|
||||
'ScopedCredentialInfo.cpp',
|
||||
'PublicKeyCredential.cpp',
|
||||
'U2FSoftTokenManager.cpp',
|
||||
'U2FTokenManager.cpp',
|
||||
'WebAuthentication.cpp',
|
||||
'WebAuthnAssertion.cpp',
|
||||
'WebAuthnAttestation.cpp',
|
||||
'WebAuthnManager.cpp',
|
||||
'WebAuthnTransactionChild.cpp',
|
||||
'WebAuthnTransactionParent.cpp',
|
||||
|
@ -16,91 +16,112 @@
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
// Execute the full-scope test
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// Execute the full-scope test
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function arrivingHereIsBad(aResult) {
|
||||
ok(false, "Bad result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
function arrivingHereIsBad(aResult) {
|
||||
ok(false, "Bad result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function expectNotAllowedError(aResult) {
|
||||
ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
function expectNotAllowedError(aResult) {
|
||||
ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function expectTypeError(aResult) {
|
||||
ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
function expectTypeError(aResult) {
|
||||
ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
|
||||
["security.webauth.webauthn_enable_softtoken", true],
|
||||
["security.webauth.webauthn_enable_usbtoken", false]]},
|
||||
runTests);
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
|
||||
["security.webauth.webauthn_enable_softtoken", true],
|
||||
["security.webauth.webauthn_enable_usbtoken", false]]},
|
||||
runTests);
|
||||
|
||||
function runTests() {
|
||||
isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
|
||||
isnot(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint must exist");
|
||||
isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
|
||||
function runTests() {
|
||||
is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
|
||||
isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
|
||||
isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
|
||||
isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
|
||||
|
||||
let authn = navigator.authentication;
|
||||
let credm = navigator.credentials;
|
||||
|
||||
let gAssertionChallenge = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(gAssertionChallenge);
|
||||
let gAssertionChallenge = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(gAssertionChallenge);
|
||||
|
||||
let invalidCred = { type: "Magic", id: base64ToBytes("AAA=") };
|
||||
let unknownCred = { type: "ScopedCred", id: base64ToBytes("AAA=") };
|
||||
let invalidCred = {type: "Magic", id: base64ToBytes("AAA=")};
|
||||
let unknownCred = {type: "public-key", id: base64ToBytes("AAA=")};
|
||||
|
||||
var testFuncs = [
|
||||
function () {
|
||||
// Test basic good call, but without giving a credential so expect failures
|
||||
// this is OK by the standard, but not supported by U2F-backed authenticators
|
||||
// like the soft token in use here.
|
||||
return authn.getAssertion(gAssertionChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotAllowedError);
|
||||
},
|
||||
function () {
|
||||
// Test with an unexpected option
|
||||
return authn.getAssertion(gAssertionChallenge, { unknownValue: "hi" })
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotAllowedError);
|
||||
},
|
||||
function () {
|
||||
// Test with an invalid credential
|
||||
return authn.getAssertion(gAssertionChallenge, { allowList: [invalidCred] })
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
function () {
|
||||
// Test with an unknown credential
|
||||
return authn.getAssertion(gAssertionChallenge, { allowList: [unknownCred] })
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotAllowedError);
|
||||
},
|
||||
function () {
|
||||
// Test with an unexpected option and an invalid credential
|
||||
return authn.getAssertion(gAssertionChallenge, { unknownValue: "hi" })
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotAllowedError);
|
||||
}
|
||||
];
|
||||
var testFuncs = [
|
||||
function () {
|
||||
// Test basic good call, but without giving a credential so expect failures
|
||||
// this is OK by the standard, but not supported by U2F-backed authenticators
|
||||
// like the soft token in use here.
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: gAssertionChallenge
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotAllowedError);
|
||||
},
|
||||
function () {
|
||||
// Test with an unexpected option
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: gAssertionChallenge,
|
||||
unknownValue: "hi"
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotAllowedError);
|
||||
},
|
||||
function () {
|
||||
// Test with an invalid credential
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: gAssertionChallenge,
|
||||
allowList: [invalidCred]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
function () {
|
||||
// Test with an unknown credential
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: gAssertionChallenge,
|
||||
allowList: [unknownCred]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotAllowedError);
|
||||
},
|
||||
function () {
|
||||
// Test with an unexpected option and an invalid credential
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: gAssertionChallenge,
|
||||
unknownValue: "hi",
|
||||
allowList: [invalidCred]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
}
|
||||
];
|
||||
|
||||
var i = 0;
|
||||
var runNextTest = () => {
|
||||
if (i == testFuncs.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
testFuncs[i]().then(() => { runNextTest(); });
|
||||
i = i + 1;
|
||||
};
|
||||
runNextTest();
|
||||
var i = 0;
|
||||
var runNextTest = () => {
|
||||
if (i == testFuncs.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
testFuncs[i]().then(() => { runNextTest(); });
|
||||
i = i + 1;
|
||||
};
|
||||
runNextTest();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -25,11 +25,12 @@ SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
|
||||
["security.webauth.webauthn_enable_softtoken", true],
|
||||
["security.webauth.webauthn_enable_usbtoken", false]]},
|
||||
function() {
|
||||
isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
|
||||
isnot(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint must exist");
|
||||
isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
|
||||
is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
|
||||
isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
|
||||
isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
|
||||
isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
|
||||
|
||||
let authn = navigator.authentication;
|
||||
let credm = navigator.credentials;
|
||||
|
||||
let gCredentialChallenge = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(gCredentialChallenge);
|
||||
@ -39,28 +40,23 @@ function() {
|
||||
testMakeCredential();
|
||||
|
||||
function checkCredentialValid(aCredInfo) {
|
||||
/* ScopedCredentialInfo
|
||||
- Credential
|
||||
-- ID: Key Handle buffer pulled from U2F Register() Response
|
||||
-- Type: "ScopedCred"
|
||||
- WebAuthnAttestation
|
||||
-- Format: "u2f"
|
||||
-- ClientData: serialized JSON
|
||||
-- AuthenticatorData: RP ID Hash || U2F Sign() Response
|
||||
-- Attestation: U2F Register() Response */
|
||||
/* PublicKeyCredential : Credential
|
||||
- rawId: Key Handle buffer pulled from U2F Register() Response
|
||||
- response : AuthenticatorAttestationResponse : AuthenticatorResponse
|
||||
- attestationObject: RP ID Hash || U2F Sign() Response
|
||||
- clientDataJSON: serialized JSON
|
||||
- clientExtensionResults: (not yet supported)
|
||||
*/
|
||||
|
||||
is(aCredInfo.credential.type, "ScopedCred", "Type is correct");
|
||||
ok(aCredInfo.credential.id.length > 0, "Key ID exists");
|
||||
ok(aCredInfo.rawId.length > 0, "Key ID exists");
|
||||
|
||||
is(aCredInfo.attestation.format, "u2f", "Format is correct");
|
||||
is(aCredInfo.attestation.attestation[0], 0x05, "Reserved byte is correct");
|
||||
ok(aCredInfo.attestation.authenticatorData.length > 0, "Authenticator data exists");
|
||||
let clientData = JSON.parse(buffer2string(aCredInfo.attestation.clientData));
|
||||
is(aCredInfo.response.attestationObject[0], 0x05, "Reserved byte is correct");
|
||||
let clientData = JSON.parse(buffer2string(aCredInfo.response.clientDataJSON));
|
||||
is(clientData.challenge, bytesToBase64UrlSafe(gCredentialChallenge), "Challenge is correct");
|
||||
is(clientData.origin, window.location.origin, "Origin is correct");
|
||||
is(clientData.hashAlg, "S256", "Hash algorithm is correct");
|
||||
|
||||
return decodeU2FRegistration(aCredInfo.attestation.attestation)
|
||||
return decodeU2FRegistration(aCredInfo.response.attestationObject)
|
||||
.then(function(u2fObj) {
|
||||
aCredInfo.u2fReg = u2fObj;
|
||||
return aCredInfo;
|
||||
@ -68,37 +64,36 @@ function() {
|
||||
}
|
||||
|
||||
function checkAssertionAndSigValid(aPublicKey, aAssertion) {
|
||||
/* WebAuthnAssertion
|
||||
- Credential
|
||||
-- ID: ID of Credential from AllowList that succeeded
|
||||
-- Type: "ScopedCred"
|
||||
- ClientData: serialized JSON
|
||||
- AuthenticatorData: RP ID Hash || U2F Sign() Response
|
||||
- Signature: U2F Sign() Response */
|
||||
/* PublicKeyCredential : Credential
|
||||
- rawId: ID of Credential from AllowList that succeeded
|
||||
- response : AuthenticatorAssertionResponse : AuthenticatorResponse
|
||||
- clientDataJSON: serialized JSON
|
||||
- authenticatorData: RP ID Hash || U2F Sign() Response
|
||||
- signature: U2F Sign() Response
|
||||
*/
|
||||
|
||||
is(aAssertion.credential.type, "ScopedCred", "Type is correct");
|
||||
ok(aAssertion.credential.id.length > 0, "Key ID exists");
|
||||
ok(aAssertion.rawId.length > 0, "Key ID exists");
|
||||
|
||||
ok(aAssertion.authenticatorData.length > 0, "Authenticator data exists");
|
||||
let clientData = JSON.parse(buffer2string(aAssertion.clientData));
|
||||
ok(aAssertion.response.authenticatorData.length > 0, "Authenticator data exists");
|
||||
let clientData = JSON.parse(buffer2string(aAssertion.response.clientDataJSON));
|
||||
is(clientData.challenge, bytesToBase64UrlSafe(gAssertionChallenge), "Challenge is correct");
|
||||
is(clientData.origin, window.location.origin, "Origin is correct");
|
||||
is(clientData.hashAlg, "S256", "Hash algorithm is correct");
|
||||
|
||||
// Parse the signature data
|
||||
if (aAssertion.signature[0] != 0x01) {
|
||||
if (aAssertion.response.signature[0] != 0x01) {
|
||||
throw "User presence byte not set";
|
||||
}
|
||||
let presenceAndCounter = aAssertion.signature.slice(0,5);
|
||||
let signatureValue = aAssertion.signature.slice(5);
|
||||
let presenceAndCounter = aAssertion.response.signature.slice(0,5);
|
||||
let signatureValue = aAssertion.response.signature.slice(5);
|
||||
|
||||
let rpIdHash = aAssertion.authenticatorData.slice(0,32);
|
||||
let rpIdHash = aAssertion.response.authenticatorData.slice(0,32);
|
||||
|
||||
// Assemble the signed data and verify the signature
|
||||
return deriveAppAndChallengeParam(clientData.origin, aAssertion.clientData)
|
||||
return deriveAppAndChallengeParam(clientData.origin, aAssertion.response.clientDataJSON)
|
||||
.then(function(aParams) {
|
||||
console.log(aParams.appParam, rpIdHash, presenceAndCounter, aParams.challengeParam);
|
||||
console.log("ClientData buffer: ", hexEncode(aAssertion.clientData));
|
||||
console.log("ClientData buffer: ", hexEncode(aAssertion.response.clientDataJSON));
|
||||
console.log("ClientDataHash: ", hexEncode(aParams.challengeParam));
|
||||
return assembleSignedData(aParams.appParam, presenceAndCounter, aParams.challengeParam);
|
||||
})
|
||||
@ -109,10 +104,16 @@ function() {
|
||||
}
|
||||
|
||||
function testMakeCredential() {
|
||||
let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
|
||||
let param = {type: "ScopedCred", algorithm: "p-256"};
|
||||
|
||||
authn.makeCredential(acct, [param], gCredentialChallenge)
|
||||
let rp = {id: document.origin, name: "none", icon: "none"};
|
||||
let user = {id: "none", name: "none", icon: "none", displayName: "none"};
|
||||
let param = {type: "public-key", algorithm: "P-256"};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp,
|
||||
user: user,
|
||||
challenge: gCredentialChallenge,
|
||||
parameters: [param]
|
||||
};
|
||||
credm.create({publicKey: makeCredentialOptions})
|
||||
.then(checkCredentialValid)
|
||||
.then(testMakeDuplicate)
|
||||
.catch(function(aReason) {
|
||||
@ -122,33 +123,43 @@ function() {
|
||||
}
|
||||
|
||||
function testMakeDuplicate(aCredInfo) {
|
||||
let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
|
||||
let param = {type: "ScopedCred", algorithm: "p-256"};
|
||||
let options = {rpId: document.origin,
|
||||
excludeList: [aCredInfo.credential]};
|
||||
|
||||
authn.makeCredential(acct, [param], gCredentialChallenge, options)
|
||||
let rp = {id: document.origin, name: "none", icon: "none"};
|
||||
let user = {id: "none", name: "none", icon: "none", displayName: "none"};
|
||||
let param = {type: "public-key", algorithm: "P-256"};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp,
|
||||
user: user,
|
||||
challenge: gCredentialChallenge,
|
||||
parameters: [param],
|
||||
excludeList: [{type: "public-key", id: Uint8Array.from(aCredInfo.rawId),
|
||||
transports: ["usb"]}]
|
||||
};
|
||||
credm.create({publicKey: makeCredentialOptions})
|
||||
.then(function() {
|
||||
// We should have errored here!
|
||||
ok(false, "The excludeList didn't stop a duplicate being created!");
|
||||
SimpleTest.finish();
|
||||
})
|
||||
.catch(function(aReason) {
|
||||
ok(aReason.toString().startsWith("NotAllowedError"), "Expect NotAllowedError, got" + aReason);
|
||||
ok(aReason.toString().startsWith("NotAllowedError"), "Expect NotAllowedError, got " + aReason);
|
||||
testAssertion(aCredInfo);
|
||||
});
|
||||
}
|
||||
|
||||
function testAssertion(aCredInfo) {
|
||||
let newCredential = {
|
||||
type: aCredInfo.credential.type,
|
||||
id: Uint8Array.from(aCredInfo.credential.id),
|
||||
transports: [ "usb" ],
|
||||
type: "public-key",
|
||||
id: Uint8Array.from(aCredInfo.rawId),
|
||||
transports: ["usb"],
|
||||
}
|
||||
|
||||
let assertOptions = {rpId: document.origin, timeoutSeconds: 5,
|
||||
allowList: [ newCredential ]};
|
||||
authn.getAssertion(gAssertionChallenge, assertOptions)
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: gAssertionChallenge,
|
||||
timeout: 5000, // the minimum timeout is actually 15 seconds
|
||||
rpId: document.origin,
|
||||
allowList: [newCredential]
|
||||
};
|
||||
credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(function(aAssertion) {
|
||||
/* Pass along the pubKey. */
|
||||
return checkAssertionAndSigValid(aCredInfo.u2fReg.publicKey, aAssertion);
|
||||
|
@ -16,179 +16,231 @@
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
// Execute the full-scope test
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// Execute the full-scope test
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function arrivingHereIsGood(aResult) {
|
||||
ok(true, "Good result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
function arrivingHereIsGood(aResult) {
|
||||
ok(true, "Good result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function arrivingHereIsBad(aResult) {
|
||||
ok(false, "Bad result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
function arrivingHereIsBad(aResult) {
|
||||
ok(false, "Bad result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function expectNotAllowedError(aResult) {
|
||||
ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
function expectNotAllowedError(aResult) {
|
||||
ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function expectTypeError(aResult) {
|
||||
ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
function expectTypeError(aResult) {
|
||||
ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function expectNotSupportedError(aResult) {
|
||||
ok(aResult.toString().startsWith("NotSupportedError"), "Expecting a NotSupportedError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
function expectNotSupportedError(aResult) {
|
||||
ok(aResult.toString().startsWith("NotSupportedError"), "Expecting a NotSupportedError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
|
||||
["security.webauth.webauthn_enable_softtoken", true],
|
||||
["security.webauth.webauthn_enable_usbtoken", false]]}, runTests);
|
||||
function runTests() {
|
||||
isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
|
||||
isnot(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint must exist");
|
||||
isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
|
||||
["security.webauth.webauthn_enable_softtoken", true],
|
||||
["security.webauth.webauthn_enable_usbtoken", false]]}, runTests);
|
||||
function runTests() {
|
||||
is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
|
||||
isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
|
||||
isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
|
||||
isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
|
||||
|
||||
let authn = navigator.authentication;
|
||||
let credm = navigator.credentials;
|
||||
|
||||
let gCredentialChallenge = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(gCredentialChallenge);
|
||||
let gCredentialChallenge = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(gCredentialChallenge);
|
||||
|
||||
let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
|
||||
let param = {type: "ScopedCred", algorithm: "p-256"};
|
||||
let unsupportedParam = {type: "ScopedCred", algorithm: "3DES"};
|
||||
let badParam = {type: "SimplePassword", algorithm: "MaxLength=2"};
|
||||
let rp = {id: "none", name: "none", icon: "none"};
|
||||
let user = {id: "none", name: "none", icon: "none", displayName: "none"};
|
||||
let param = {type: "public-key", algorithm: "p-256"};
|
||||
let unsupportedParam = {type: "public-key", algorithm: "3DES"};
|
||||
let badParam = {type: "SimplePassword", algorithm: "MaxLength=2"};
|
||||
|
||||
var testFuncs = [
|
||||
// Test basic good call
|
||||
function() {
|
||||
return authn.makeCredential(acct, [param], gCredentialChallenge)
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
var testFuncs = [
|
||||
// Test basic good call
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
|
||||
// Test empty account
|
||||
function() {
|
||||
return authn.makeCredential({}, [param], gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test empty account
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
challenge: gCredentialChallenge, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
// Test without a parameter
|
||||
function() {
|
||||
return authn.makeCredential(acct, [], gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotSupportedError);
|
||||
},
|
||||
// Test without a parameter
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge, parameters: []
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotSupportedError);
|
||||
},
|
||||
|
||||
// Test without a parameter array at all
|
||||
function() {
|
||||
return authn.makeCredential(acct, null, gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test without a parameter array at all
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
// Test with an unsupported parameter
|
||||
function() {
|
||||
return authn.makeCredential(acct, [unsupportedParam], gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotSupportedError);
|
||||
},
|
||||
// Test with an unsupported parameter
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge, parameters: [unsupportedParam]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectNotSupportedError);
|
||||
},
|
||||
|
||||
// Test with an unsupported parameter and a good one
|
||||
function() {
|
||||
return authn.makeCredential(acct, [unsupportedParam, param], gCredentialChallenge)
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
// Test with an unsupported parameter and a good one
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge,
|
||||
parameters: [param, unsupportedParam]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
|
||||
// Test with a bad parameter
|
||||
function() {
|
||||
return authn.makeCredential(acct, [badParam], gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test with a bad parameter
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge, parameters: [badParam]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
// Test with an unsupported parameter, and a bad one
|
||||
function() {
|
||||
return authn.makeCredential(acct, [unsupportedParam, badParam],
|
||||
gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test with an unsupported parameter, and a bad one
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge,
|
||||
parameters: [unsupportedParam, badParam]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
// Test with an unsupported parameter, a bad one, and a good one. This
|
||||
// should still fail, as anything with a badParam should fail.
|
||||
function() {
|
||||
return authn.makeCredential(acct, [unsupportedParam, badParam, param],
|
||||
gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test with an unsupported parameter, a bad one, and a good one. This
|
||||
// should still fail, as anything with a badParam should fail.
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge,
|
||||
parameters: [param, unsupportedParam, badParam]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
// Test without a challenge
|
||||
function() {
|
||||
return authn.makeCredential(acct, [param], null)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test without a challenge
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
// Test with an invalid challenge
|
||||
function() {
|
||||
return authn.makeCredential(acct, [param], "begone, thou ill-fitting moist glove!")
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test with an invalid challenge
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: "begone, thou ill-fitting moist glove!",
|
||||
parameters: [unsupportedParam]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
// Test with duplicate parameters
|
||||
function() {
|
||||
return authn.makeCredential(acct, [param, param, param], gCredentialChallenge)
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
// Test with duplicate parameters
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: gCredentialChallenge,
|
||||
parameters: [param, param, param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
|
||||
// Test an incomplete account
|
||||
function() {
|
||||
return authn.makeCredential({id: "none"
|
||||
}, [param], gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test with missing rp
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
user: user, challenge: gCredentialChallenge, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
function() {
|
||||
return authn.makeCredential({name: "none", imageURL: "http://example.com/404"},
|
||||
[param], gCredentialChallenge)
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
// Test with missing user
|
||||
function() {
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, challenge: gCredentialChallenge, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectTypeError);
|
||||
},
|
||||
|
||||
// Test a complete account
|
||||
function() {
|
||||
return authn.makeCredential({rpDisplayName: "Foxxy", displayName: "Foxxy V",
|
||||
id: "foxes_are_the_best@example.com",
|
||||
name: "Fox F. Foxington",
|
||||
imageURL: "https://example.com/fox.svg"},
|
||||
[param], gCredentialChallenge)
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
}];
|
||||
// Test a complete account
|
||||
function() {
|
||||
let completeRP = {id: "Foxxy RP", name: "Foxxy Name",
|
||||
icon: "https://example.com/fox.svg"};
|
||||
let completeUser = {id: "foxes_are_the_best@example.com",
|
||||
name: "Fox F. Foxington",
|
||||
icon: "https://example.com/fox.svg",
|
||||
displayName: "Foxxy V"};
|
||||
let makeCredentialOptions = {
|
||||
rp: completeRP, user: completeUser, challenge: gCredentialChallenge,
|
||||
parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
}];
|
||||
|
||||
var i = 0;
|
||||
var runNextTest = () => {
|
||||
if (i == testFuncs.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
testFuncs[i]().then(() => { runNextTest(); });
|
||||
i = i + 1;
|
||||
};
|
||||
runNextTest();
|
||||
};
|
||||
var i = 0;
|
||||
var runNextTest = () => {
|
||||
if (i == testFuncs.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
testFuncs[i]().then(() => { runNextTest(); });
|
||||
i = i + 1;
|
||||
};
|
||||
runNextTest();
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -26,11 +26,12 @@ SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
|
||||
["security.webauth.webauthn_enable_softtoken", false],
|
||||
["security.webauth.webauthn_enable_usbtoken", false]]},
|
||||
function() {
|
||||
isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
|
||||
isnot(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint must exist");
|
||||
isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
|
||||
is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
|
||||
isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
|
||||
isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
|
||||
isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
|
||||
|
||||
let authn = navigator.authentication;
|
||||
let credm = navigator.credentials;
|
||||
|
||||
let credentialChallenge = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(credentialChallenge);
|
||||
@ -42,9 +43,13 @@ function() {
|
||||
testMakeCredential();
|
||||
|
||||
function testMakeCredential() {
|
||||
let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
|
||||
let param = {type: "ScopedCred", algorithm: "p-256"};
|
||||
authn.makeCredential(acct, [param], credentialChallenge)
|
||||
let rp = {id: "none", name: "none", icon: "none"};
|
||||
let user = {id: "none", name: "none", icon: "none", displayName: "none"};
|
||||
let param = {type: "public-key", algorithm: "p-256"};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: credentialChallenge, parameters: [param]
|
||||
};
|
||||
credm.create({publicKey: makeCredentialOptions})
|
||||
.then(function(aResult) {
|
||||
ok(false, "Should have failed.");
|
||||
testAssertion();
|
||||
@ -57,13 +62,17 @@ function() {
|
||||
|
||||
function testAssertion() {
|
||||
let newCredential = {
|
||||
type: "ScopedCred",
|
||||
type: "public-key",
|
||||
id: credentialId,
|
||||
transports: [ "usb" ],
|
||||
transports: ["usb"],
|
||||
}
|
||||
let assertOptions = {rpId: document.origin, timeoutSeconds: 5,
|
||||
allowList: [ newCredential ]};
|
||||
authn.getAssertion(assertionChallenge, assertOptions)
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: assertionChallenge,
|
||||
timeout: 5000, // the minimum timeout is actually 15 seconds
|
||||
rpId: document.origin,
|
||||
allowList: [newCredential]
|
||||
};
|
||||
credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(function(aResult) {
|
||||
ok(false, "Should have failed.");
|
||||
SimpleTest.finish();
|
||||
|
@ -16,169 +16,221 @@
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
// Execute the full-scope test
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// Execute the full-scope test
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var gTrackedCredential = {};
|
||||
var gTrackedCredential = {};
|
||||
|
||||
function arrivingHereIsGood(aResult) {
|
||||
ok(true, "Good result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
function arrivingHereIsGood(aResult) {
|
||||
ok(true, "Good result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function arrivingHereIsBad(aResult) {
|
||||
// TODO: Change to `ok` when Bug 1329764 lands
|
||||
todo(false, "Bad result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
function arrivingHereIsBad(aResult) {
|
||||
// TODO: Change to `ok` when Bug 1329764 lands
|
||||
todo(false, "Bad result! Received a: " + aResult);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function expectSecurityError(aResult) {
|
||||
// TODO: Change to `ok` when Bug 1329764 lands
|
||||
todo(aResult.toString().startsWith("SecurityError"), "Expecting a SecurityError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
function expectSecurityError(aResult) {
|
||||
// TODO: Change to `ok` when Bug 1329764 lands
|
||||
todo(aResult.toString().startsWith("SecurityError"), "Expecting a SecurityError");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function keepThisScopedCredential(aScopedCredInfo) {
|
||||
gTrackedCredential = {
|
||||
type: aScopedCredInfo.credential.type,
|
||||
id: Uint8Array.from(aScopedCredInfo.credential.id),
|
||||
transports: [ "usb" ],
|
||||
}
|
||||
return Promise.resolve(aScopedCredInfo);
|
||||
}
|
||||
function keepThisPublicKeyCredential(aPublicKeyCredential) {
|
||||
gTrackedCredential = {
|
||||
type: "public-key",
|
||||
id: Uint8Array.from(aPublicKeyCredential.rawId),
|
||||
transports: [ "usb" ],
|
||||
}
|
||||
return Promise.resolve(aPublicKeyCredential);
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
|
||||
isnot(navigator.authentication.makeCredential, undefined,
|
||||
"WebAuthn makeCredential API endpoint must exist");
|
||||
isnot(navigator.authentication.getAssertion, undefined,
|
||||
"WebAuthn getAssertion API endpoint must exist");
|
||||
function runTests() {
|
||||
is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
|
||||
isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
|
||||
isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
|
||||
isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
|
||||
|
||||
let authn = navigator.authentication;
|
||||
let credm = navigator.credentials;
|
||||
|
||||
let chall = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(chall);
|
||||
let chall = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(chall);
|
||||
|
||||
let acct = {rpDisplayName: "none", displayName: "none", id: "none"};
|
||||
let param = {type: "ScopedCred", algorithm: "p-256"};
|
||||
let user = {id: "none", name: "none", icon: "none", displayName: "none"};
|
||||
let param = {type: "public-key", algorithm: "p-256"};
|
||||
|
||||
var testFuncs = [
|
||||
function() {
|
||||
// Test basic good call
|
||||
return authn.makeCredential(acct, [param], chall, {rpId: document.origin})
|
||||
.then(keepThisScopedCredential)
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
var testFuncs = [
|
||||
function() {
|
||||
// Test basic good call
|
||||
let rp = {id: document.origin};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: chall, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(keepThisPublicKeyCredential)
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test rpId being unset
|
||||
return authn.makeCredential(acct, [param], chall, {})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
function() {
|
||||
// Test this origin with optional fields
|
||||
return authn.makeCredential(acct, [param], chall,
|
||||
{rpId: "user:pass@" + document.origin + ":8888"})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function() {
|
||||
// Test blank rpId
|
||||
return authn.makeCredential(acct, [param], chall, {rpId: ""})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function() {
|
||||
// Test subdomain of this origin
|
||||
return authn.makeCredential(acct, [param], chall,
|
||||
{rpId: "subdomain." + document.origin})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function() {
|
||||
// Test another origin
|
||||
return authn.makeCredential(acct, [param], chall, {rpId: "example.com"})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// est a different domain within the same TLD
|
||||
return authn.makeCredential(acct, [param], chall, {rpId: "alt.test"})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test basic good call
|
||||
return authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
|
||||
rpId: document.origin})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
function () {
|
||||
// Test rpId being unset
|
||||
return authn.getAssertion(chall, {allowList: [ gTrackedCredential ]})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
function () {
|
||||
// Test this origin with optional fields
|
||||
return authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
|
||||
rpId: "user:pass@" + document.origin + ":8888"})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test blank rpId
|
||||
return authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
|
||||
rpId: ""})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test subdomain of this origin
|
||||
return authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
|
||||
rpId: "subdomain." + document.origin})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test another origin
|
||||
return authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
|
||||
rpId: "example.com"})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test a different domain within the same TLD
|
||||
return authn.getAssertion(chall, {allowList: [ gTrackedCredential ],
|
||||
rpId: "alt.test"})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError)
|
||||
},
|
||||
function () {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
];
|
||||
var i = 0;
|
||||
var runNextTest = () => {
|
||||
if (i == testFuncs.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
console.log(i);
|
||||
testFuncs[i]().then(() => { runNextTest(); });
|
||||
i = i + 1;
|
||||
};
|
||||
runNextTest();
|
||||
};
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
|
||||
["security.webauth.webauthn_enable_softtoken", true],
|
||||
["security.webauth.webauthn_enable_usbtoken", false]]},
|
||||
runTests);
|
||||
function() {
|
||||
// Test rp.id being unset
|
||||
let makeCredentialOptions = {
|
||||
rp: {}, user: user, challenge: chall, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
function() {
|
||||
// Test this origin with optional fields
|
||||
let rp = {id: "user:pass@" + document.origin + ":8888"};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: chall, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function() {
|
||||
// Test blank rp.id
|
||||
let rp = {id: ""};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: chall, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function() {
|
||||
// Test subdomain of this origin
|
||||
let rp = {id: "subdomain." + document.origin};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: chall, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function() {
|
||||
// Test another origin
|
||||
let rp = {id: "example.com"};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: chall, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test a different domain within the same TLD
|
||||
let rp = {id: "alt.test"};
|
||||
let makeCredentialOptions = {
|
||||
rp: rp, user: user, challenge: chall, parameters: [param]
|
||||
};
|
||||
return credm.create({publicKey: makeCredentialOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test basic good call
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: chall,
|
||||
rpId: document.origin,
|
||||
allowList: [gTrackedCredential]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
function () {
|
||||
// Test rpId being unset
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: chall,
|
||||
allowList: [gTrackedCredential]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsGood)
|
||||
.catch(arrivingHereIsBad);
|
||||
},
|
||||
function () {
|
||||
// Test this origin with optional fields
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: chall,
|
||||
rpId: "user:pass@" + document.origin + ":8888",
|
||||
allowList: [gTrackedCredential]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test blank rpId
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: chall,
|
||||
rpId: "",
|
||||
allowList: [gTrackedCredential]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test subdomain of this origin
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: chall,
|
||||
rpId: "subdomain." + document.origin,
|
||||
allowList: [gTrackedCredential]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test another origin
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: chall,
|
||||
rpId: "example.com",
|
||||
allowList: [gTrackedCredential]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError);
|
||||
},
|
||||
function () {
|
||||
// Test a different domain within the same TLD
|
||||
let publicKeyCredentialRequestOptions = {
|
||||
challenge: chall,
|
||||
rpId: "alt.test",
|
||||
allowList: [gTrackedCredential]
|
||||
};
|
||||
return credm.get({publicKey: publicKeyCredentialRequestOptions})
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(expectSecurityError)
|
||||
},
|
||||
function () {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
];
|
||||
var i = 0;
|
||||
var runNextTest = () => {
|
||||
if (i == testFuncs.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
console.log(i);
|
||||
testFuncs[i]().then(() => { runNextTest(); });
|
||||
i = i + 1;
|
||||
};
|
||||
runNextTest();
|
||||
};
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
|
||||
["security.webauth.webauthn_enable_softtoken", true],
|
||||
["security.webauth.webauthn_enable_usbtoken", false]]},
|
||||
runTests);
|
||||
|
||||
</script>
|
||||
|
||||
|
28
dom/webidl/CredentialManagement.webidl
Normal file
28
dom/webidl/CredentialManagement.webidl
Normal file
@ -0,0 +1,28 @@
|
||||
/* -*- 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://www.w3.org/TR/credential-management-1/
|
||||
*/
|
||||
|
||||
[Exposed=Window, SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface Credential {
|
||||
readonly attribute USVString id;
|
||||
readonly attribute DOMString type;
|
||||
};
|
||||
|
||||
[Exposed=Window, SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface CredentialsContainer {
|
||||
Promise<Credential?> get(optional CredentialRequestOptions options);
|
||||
Promise<Credential?> create(optional CredentialCreationOptions options);
|
||||
};
|
||||
|
||||
dictionary CredentialRequestOptions {
|
||||
PublicKeyCredentialRequestOptions publicKey;
|
||||
};
|
||||
|
||||
dictionary CredentialCreationOptions {
|
||||
MakeCredentialOptions publicKey;
|
||||
};
|
@ -371,5 +371,5 @@ interface NavigatorConcurrentHardware {
|
||||
|
||||
partial interface Navigator {
|
||||
[Pref="security.webauth.webauthn", SameObject]
|
||||
readonly attribute WebAuthentication authentication;
|
||||
readonly attribute CredentialsContainer credentials;
|
||||
};
|
||||
|
@ -10,104 +10,102 @@
|
||||
/***** Interfaces to Data *****/
|
||||
|
||||
[SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface ScopedCredentialInfo {
|
||||
readonly attribute ScopedCredential credential;
|
||||
readonly attribute WebAuthnAttestation attestation;
|
||||
interface PublicKeyCredential : Credential {
|
||||
readonly attribute ArrayBuffer rawId;
|
||||
readonly attribute AuthenticatorResponse response;
|
||||
// Extensions are not supported yet.
|
||||
// readonly attribute AuthenticationExtensions clientExtensionResults;
|
||||
};
|
||||
|
||||
dictionary Account {
|
||||
required DOMString rpDisplayName;
|
||||
required DOMString displayName;
|
||||
required DOMString id;
|
||||
DOMString name;
|
||||
DOMString imageURL;
|
||||
[SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface AuthenticatorResponse {
|
||||
readonly attribute ArrayBuffer clientDataJSON;
|
||||
};
|
||||
|
||||
[SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface AuthenticatorAttestationResponse : AuthenticatorResponse {
|
||||
readonly attribute ArrayBuffer attestationObject;
|
||||
};
|
||||
|
||||
dictionary PublicKeyCredentialParameters {
|
||||
required PublicKeyCredentialType type;
|
||||
required WebAuthnAlgorithmID algorithm; // NOTE: changed from AllgorithmIdentifier because typedef (object or DOMString) not serializable
|
||||
};
|
||||
|
||||
dictionary PublicKeyCredentialUserEntity : PublicKeyCredentialEntity {
|
||||
DOMString displayName;
|
||||
};
|
||||
|
||||
dictionary MakeCredentialOptions {
|
||||
required PublicKeyCredentialEntity rp;
|
||||
required PublicKeyCredentialUserEntity user;
|
||||
|
||||
required BufferSource challenge;
|
||||
required sequence<PublicKeyCredentialParameters> parameters;
|
||||
|
||||
unsigned long timeout;
|
||||
sequence<PublicKeyCredentialDescriptor> excludeList;
|
||||
AuthenticatorSelectionCriteria authenticatorSelection;
|
||||
// Extensions are not supported yet.
|
||||
// AuthenticationExtensions extensions;
|
||||
};
|
||||
|
||||
dictionary PublicKeyCredentialEntity {
|
||||
DOMString id;
|
||||
DOMString name;
|
||||
USVString icon;
|
||||
};
|
||||
|
||||
dictionary AuthenticatorSelectionCriteria {
|
||||
Attachment attachment;
|
||||
boolean requireResidentKey = false;
|
||||
};
|
||||
|
||||
enum Attachment {
|
||||
"platform",
|
||||
"cross-platform"
|
||||
};
|
||||
|
||||
dictionary PublicKeyCredentialRequestOptions {
|
||||
required BufferSource challenge;
|
||||
unsigned long timeout;
|
||||
USVString rpId;
|
||||
sequence<PublicKeyCredentialDescriptor> allowList = [];
|
||||
// Extensions are not supported yet.
|
||||
// AuthenticationExtensions extensions;
|
||||
};
|
||||
|
||||
dictionary CollectedClientData {
|
||||
required DOMString challenge;
|
||||
required DOMString origin;
|
||||
required DOMString hashAlg;
|
||||
DOMString tokenBinding;
|
||||
// Extensions are not supported yet.
|
||||
// AuthenticationExtensions clientExtensions;
|
||||
// AuthenticationExtensions authenticatorExtensions;
|
||||
};
|
||||
|
||||
enum PublicKeyCredentialType {
|
||||
"public-key"
|
||||
};
|
||||
|
||||
dictionary PublicKeyCredentialDescriptor {
|
||||
required PublicKeyCredentialType type;
|
||||
required BufferSource id;
|
||||
sequence<WebAuthnTransport> transports;
|
||||
};
|
||||
|
||||
typedef (boolean or DOMString) WebAuthnAlgorithmID; // Fix when upstream there's a definition of how to serialize AlgorithmIdentifier
|
||||
|
||||
dictionary ScopedCredentialParameters {
|
||||
required ScopedCredentialType type;
|
||||
required WebAuthnAlgorithmID algorithm; // NOTE: changed from AllgorithmIdentifier because typedef (object or DOMString) not serializable
|
||||
};
|
||||
|
||||
dictionary ScopedCredentialOptions {
|
||||
unsigned long timeoutSeconds;
|
||||
USVString rpId;
|
||||
sequence<ScopedCredentialDescriptor> excludeList;
|
||||
WebAuthnExtensions extensions;
|
||||
};
|
||||
|
||||
[SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface WebAuthnAssertion {
|
||||
readonly attribute ScopedCredential credential;
|
||||
readonly attribute ArrayBuffer clientData;
|
||||
interface AuthenticatorAssertionResponse : AuthenticatorResponse {
|
||||
readonly attribute ArrayBuffer authenticatorData;
|
||||
readonly attribute ArrayBuffer signature;
|
||||
};
|
||||
|
||||
dictionary AssertionOptions {
|
||||
unsigned long timeoutSeconds;
|
||||
USVString rpId;
|
||||
sequence<ScopedCredentialDescriptor> allowList;
|
||||
WebAuthnExtensions extensions;
|
||||
};
|
||||
|
||||
dictionary WebAuthnExtensions {
|
||||
};
|
||||
|
||||
[SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface WebAuthnAttestation {
|
||||
readonly attribute USVString format;
|
||||
readonly attribute ArrayBuffer clientData;
|
||||
readonly attribute ArrayBuffer authenticatorData;
|
||||
readonly attribute any attestation;
|
||||
};
|
||||
|
||||
// Renamed from "ClientData" to avoid a collision with U2F
|
||||
dictionary WebAuthnClientData {
|
||||
required DOMString challenge;
|
||||
required DOMString origin;
|
||||
required WebAuthnAlgorithmID hashAlg; // NOTE: changed from AllgorithmIdentifier because typedef (object or DOMString) not serializable
|
||||
DOMString tokenBinding;
|
||||
WebAuthnExtensions extensions;
|
||||
};
|
||||
|
||||
enum ScopedCredentialType {
|
||||
"ScopedCred"
|
||||
};
|
||||
|
||||
[SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface ScopedCredential {
|
||||
readonly attribute ScopedCredentialType type;
|
||||
readonly attribute ArrayBuffer id;
|
||||
};
|
||||
|
||||
dictionary ScopedCredentialDescriptor {
|
||||
required ScopedCredentialType type;
|
||||
required BufferSource id;
|
||||
sequence <WebAuthnTransport> transports;
|
||||
};
|
||||
|
||||
// Renamed from "Transport" to avoid a collision with U2F
|
||||
enum WebAuthnTransport {
|
||||
"usb",
|
||||
"nfc",
|
||||
"ble"
|
||||
};
|
||||
|
||||
/***** The Main API *****/
|
||||
|
||||
[SecureContext, Pref="security.webauth.webauthn"]
|
||||
interface WebAuthentication {
|
||||
Promise<ScopedCredentialInfo> makeCredential (
|
||||
Account accountInformation,
|
||||
sequence<ScopedCredentialParameters> cryptoParameters,
|
||||
BufferSource attestationChallenge,
|
||||
optional ScopedCredentialOptions options
|
||||
);
|
||||
|
||||
Promise<WebAuthnAssertion> getAssertion (
|
||||
BufferSource assertionChallenge,
|
||||
optional AssertionOptions options
|
||||
);
|
||||
};
|
||||
|
@ -444,6 +444,7 @@ WEBIDL_FILES = [
|
||||
'ConvolverNode.webidl',
|
||||
'Coordinates.webidl',
|
||||
'CreateOfferRequest.webidl',
|
||||
'CredentialManagement.webidl',
|
||||
'Crypto.webidl',
|
||||
'CSPDictionaries.webidl',
|
||||
'CSPReport.webidl',
|
||||
|
@ -11,3 +11,14 @@ skip-if(!Android) fails-if(styloVsGecko) == time-simple-unthemed.html time-simpl
|
||||
# type change
|
||||
skip-if(Android) fails-if(styloVsGecko) == to-time-from-other-type-unthemed.html time-simple-unthemed.html
|
||||
skip-if(Android) == from-time-to-other-type-unthemed.html from-time-to-other-type-unthemed-ref.html
|
||||
|
||||
# content should not overflow on small width/height
|
||||
skip-if(Android) == time-small-width.html time-small-width-ref.html
|
||||
skip-if(Android) == time-small-height.html time-small-height-ref.html
|
||||
skip-if(Android) == time-small-width-height.html time-small-width-height-ref.html
|
||||
|
||||
# content (text) should be left aligned
|
||||
skip-if(Android) == time-content-left-aligned.html time-content-left-aligned-ref.html
|
||||
|
||||
# reset button should be right aligned
|
||||
skip-if(Android) fails-if(styloVsGecko) == time-reset-button-right-aligned.html time-reset-button-right-aligned-ref.html
|
||||
|
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" style="width: 200px;">
|
||||
<!-- div to cover the right area -->
|
||||
<div style="display:block; position:absolute; background-color:black;
|
||||
top:0px; left:40px; width:200px; height:100px;"></div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" style="width: 50px;">
|
||||
<!-- div to cover the right area -->
|
||||
<div style="display:block; position:absolute; background-color:black;
|
||||
top:0px; left:40px; width:200px; height:100px;"></div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" value="10:00" style="float: right; color: white;">
|
||||
<!-- div to cover the left area -->
|
||||
<div style="display:block; position:absolute; background-color:black;
|
||||
top:0px; right:30px; width:500px; height:100px;"></div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" value="10:00" style="width: 150px; float: right;
|
||||
color: white;">
|
||||
<!-- div to cover the left area -->
|
||||
<div style="display:block; position:absolute; background-color:black;
|
||||
top:0px; right:30px; width:500px; height:100px;"></div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
input {
|
||||
width: 200px;
|
||||
height: 5px;
|
||||
outline: 1px dotted black;
|
||||
/* Disable baseline alignment, so that our y-position isn't influenced by the
|
||||
* choice of font inside of input: */
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<input>
|
||||
</body>
|
||||
</html>
|
19
layout/reftests/forms/input/datetime/time-small-height.html
Normal file
19
layout/reftests/forms/input/datetime/time-small-height.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
input {
|
||||
width: 200px;
|
||||
height: 5px;
|
||||
outline: 1px dotted black;
|
||||
color: white;
|
||||
/* Disable baseline alignment, so that our y-position isn't influenced by the
|
||||
* choice of font inside of input: */
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<input type="time">
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
input {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
outline: 1px dotted black;
|
||||
/* Disable baseline alignment, so that our y-position isn't influenced by the
|
||||
* choice of font inside of input: */
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<input>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
input {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
outline: 1px dotted black;
|
||||
color: white;
|
||||
/* Disable baseline alignment, so that our y-position isn't influenced by the
|
||||
* choice of font inside of input: */
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<input type="time">
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
input {
|
||||
width: 10px;
|
||||
height: 1.5em;
|
||||
outline: 1px dotted black;
|
||||
background: white;
|
||||
/* Disable baseline alignment, so that our y-position isn't influenced by the
|
||||
* choice of font inside of input: */
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<input>
|
||||
</body>
|
||||
</html>
|
20
layout/reftests/forms/input/datetime/time-small-width.html
Normal file
20
layout/reftests/forms/input/datetime/time-small-width.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
input {
|
||||
width: 10px;
|
||||
height: 1.5em;
|
||||
outline: 1px dotted black;
|
||||
color: white;
|
||||
background: white;
|
||||
/* Disable baseline alignment, so that our y-position isn't influenced by the
|
||||
* choice of font inside of input: */
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<input type="time">
|
||||
</body>
|
||||
</html>
|
@ -1187,6 +1187,11 @@ input[type="number"] > div > div > div:hover {
|
||||
background-color: lightblue;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="time"] {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
:-moz-autofill, :-moz-autofill-preview {
|
||||
filter: grayscale(21%) brightness(88%) contrast(161%) invert(10%) sepia(40%) saturate(206%);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ def test(mod, path, entity = None):
|
||||
# we only have exceptions for mobile*
|
||||
return "error"
|
||||
if mod == "mobile/android":
|
||||
if not entity:
|
||||
if entity is None:
|
||||
if (re.match(r"mobile-l10n.js", path) or
|
||||
re.match(r"defines.inc", path)):
|
||||
return "ignore"
|
||||
|
@ -37,7 +37,7 @@
|
||||
% resource search-plugins chrome://browser/locale/searchplugins/
|
||||
|
||||
# overrides for toolkit l10n, also for en-US
|
||||
# keep this file list in sync with filter.py
|
||||
# keep this file list in sync with l10n.toml and filter.py
|
||||
relativesrcdir toolkit/locales:
|
||||
locale/@AB_CD@/browser/overrides/about.dtd (%chrome/global/about.dtd)
|
||||
locale/@AB_CD@/browser/overrides/aboutAbout.dtd (%chrome/global/aboutAbout.dtd)
|
||||
|
246
mobile/android/locales/l10n.toml
Normal file
246
mobile/android/locales/l10n.toml
Normal file
@ -0,0 +1,246 @@
|
||||
# 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/.
|
||||
|
||||
basepath = "../../.."
|
||||
|
||||
locales = [
|
||||
"an",
|
||||
"ar",
|
||||
"as",
|
||||
"ast",
|
||||
"az",
|
||||
"be",
|
||||
"bg",
|
||||
"bn-BD",
|
||||
"bn-IN",
|
||||
"br",
|
||||
"ca",
|
||||
"cak",
|
||||
"cs",
|
||||
"cy",
|
||||
"da",
|
||||
"de",
|
||||
"dsb",
|
||||
"el",
|
||||
"en-GB",
|
||||
"en-ZA",
|
||||
"eo",
|
||||
"es-AR",
|
||||
"es-CL",
|
||||
"es-ES",
|
||||
"es-MX",
|
||||
"et",
|
||||
"eu",
|
||||
"fa",
|
||||
"ff",
|
||||
"fi",
|
||||
"fr",
|
||||
"fy-NL",
|
||||
"ga-IE",
|
||||
"gd",
|
||||
"gl",
|
||||
"gn",
|
||||
"gu-IN",
|
||||
"he",
|
||||
"hi-IN",
|
||||
"hr",
|
||||
"hsb",
|
||||
"hu",
|
||||
"hy-AM",
|
||||
"id",
|
||||
"is",
|
||||
"it",
|
||||
"ja",
|
||||
"ka",
|
||||
"kab",
|
||||
"kk",
|
||||
"kn",
|
||||
"ko",
|
||||
"lo",
|
||||
"lt",
|
||||
"lv",
|
||||
"mai",
|
||||
"ml",
|
||||
"mr",
|
||||
"ms",
|
||||
"my",
|
||||
"nb-NO",
|
||||
"ne-NP",
|
||||
"nl",
|
||||
"nn-NO",
|
||||
"or",
|
||||
"pa-IN",
|
||||
"pl",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
"rm",
|
||||
"ro",
|
||||
"ru",
|
||||
"sk",
|
||||
"sl",
|
||||
"son",
|
||||
"sq",
|
||||
"sr",
|
||||
"sv-SE",
|
||||
"ta",
|
||||
"te",
|
||||
"th",
|
||||
"tr",
|
||||
"trs",
|
||||
"uk",
|
||||
"ur",
|
||||
"uz",
|
||||
"wo",
|
||||
"xh",
|
||||
"zam",
|
||||
"zh-CN",
|
||||
"zh-TW",
|
||||
]
|
||||
|
||||
[build]
|
||||
exclude-multi-locale = [
|
||||
"be",
|
||||
"bn-BD",
|
||||
"ne-NP",
|
||||
"trs",
|
||||
"wo",
|
||||
"zam",
|
||||
]
|
||||
|
||||
[env]
|
||||
l = "{l10n_base}/{locale}/"
|
||||
|
||||
|
||||
[[paths]]
|
||||
reference = "mobile/locales/en-US/**"
|
||||
l10n = "{l}mobile/**"
|
||||
|
||||
[[paths]]
|
||||
reference = "mobile/android/locales/en-US/**"
|
||||
l10n = "{l}mobile/android/**"
|
||||
|
||||
[[paths]]
|
||||
reference = "mobile/android/base/locales/en-US/**"
|
||||
l10n = "{l}mobile/android/base/**"
|
||||
test = [
|
||||
"android-dtd",
|
||||
]
|
||||
|
||||
# hand-picked paths from toolkit, keep in sync with jar.mn
|
||||
[[paths]]
|
||||
reference = "dom/locales/en-US/chrome/global.dtd"
|
||||
l10n = "{l}dom/chrome/global.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "dom/locales/en-US/chrome/accessibility/AccessFu.properties"
|
||||
l10n = "{l}dom/chrome/accessibility/AccessFu.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "dom/locales/en-US/chrome/dom/dom.properties"
|
||||
l10n = "{l}dom/chrome/dom/dom.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "dom/locales/en-US/chrome/plugins.properties"
|
||||
l10n = "{l}dom/chrome/plugins.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/about.dtd"
|
||||
l10n = "{l}toolkit/chrome/global/about.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/aboutAbout.dtd"
|
||||
l10n = "{l}toolkit/chrome/global/aboutAbout.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/aboutReader.properties"
|
||||
l10n = "{l}toolkit/chrome/global/aboutReader.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/aboutRights.dtd"
|
||||
l10n = "{l}toolkit/chrome/global/aboutRights.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/charsetMenu.properties"
|
||||
l10n = "{l}toolkit/chrome/global/charsetMenu.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/commonDialogs.properties"
|
||||
l10n = "{l}toolkit/chrome/global/commonDialogs.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/intl.properties"
|
||||
l10n = "{l}toolkit/chrome/global/intl.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/intl.css"
|
||||
l10n = "{l}toolkit/chrome/global/intl.css"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties"
|
||||
l10n = "{l}toolkit/chrome/passwordmgr/passwordmgr.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/search/search.properties"
|
||||
l10n = "{l}toolkit/chrome/search/search.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/pluginproblem/pluginproblem.dtd"
|
||||
l10n = "{l}toolkit/chrome/pluginproblem/pluginproblem.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/aboutSupport.dtd"
|
||||
l10n = "{l}toolkit/chrome/global/aboutSupport.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/aboutSupport.properties"
|
||||
l10n = "{l}toolkit/chrome/global/aboutSupport.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/crashreporter/crashes.dtd"
|
||||
l10n = "{l}toolkit/crashreporter/crashes.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/crashreporter/crashes.properties"
|
||||
l10n = "{l}toolkit/crashreporter/crashes.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/mozilla.dtd"
|
||||
l10n = "{l}toolkit/chrome/global/mozilla.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/aboutTelemetry.dtd"
|
||||
l10n = "{l}toolkit/chrome/global/aboutTelemetry.dtd"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/aboutTelemetry.properties"
|
||||
l10n = "{l}toolkit/chrome/global/aboutTelemetry.properties"
|
||||
|
||||
[[paths]]
|
||||
reference = "toolkit/locales/en-US/chrome/global/aboutWebrtc.properties"
|
||||
l10n = "{l}toolkit/chrome/global/aboutWebrtc.properties"
|
||||
|
||||
|
||||
[[filters]]
|
||||
path = [
|
||||
"{l}mobile/android/mobile-l10n.js",
|
||||
"{l}mobile/android/defines.inc",
|
||||
]
|
||||
action = "ignore"
|
||||
|
||||
[[filters]]
|
||||
path = "{l}mobile/android/defines.inc"
|
||||
key = "MOZ_LANGPACK_CONTRIBUTORS"
|
||||
action = "ignore"
|
||||
|
||||
[[filters]]
|
||||
path = "{l}mobile/chrome/region.properties"
|
||||
key = [
|
||||
"re:^browser\\.search\\.order\\.[1-9]$",
|
||||
"re:^browser\\.search\\.[a-zA-Z]+\\.US",
|
||||
"re:^browser\\.contentHandlers\\.types\\.[0-5]\\..*$",
|
||||
"re:^gecko\\.handlerService\\.schemes\\..+$",
|
||||
"re:^gecko\\.handlerService\\.defaultHandlersVersion$",
|
||||
"re:^browser\\.suggestedsites\\..+$",
|
||||
]
|
||||
action = "ignore"
|
@ -6,6 +6,6 @@ package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
public class FxAccountGetStartedActivityWeb extends FxAccountWebFlowActivity {
|
||||
public FxAccountGetStartedActivityWeb() {
|
||||
super(CANNOT_RESUME_WHEN_ACCOUNTS_EXIST, "signup");
|
||||
super(CANNOT_RESUME_WHEN_ACCOUNTS_EXIST, "signin");
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,10 @@ with Files('GNUmakefile'):
|
||||
with Files('*gradle*'):
|
||||
BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
|
||||
|
||||
with Files('**/l10n.toml'):
|
||||
BUG_COMPONENT = ('Core', 'Localization')
|
||||
FINAL = True
|
||||
|
||||
with Files('README.txt'):
|
||||
BUG_COMPONENT = ('Core', 'General')
|
||||
|
||||
@ -69,6 +73,7 @@ if not CONFIG['JS_STANDALONE']:
|
||||
# These python manifests are included here so they get picked up without an objdir
|
||||
PYTHON_UNITTEST_MANIFESTS += [
|
||||
'testing/marionette/harness/marionette_harness/tests/harness_unit/python.ini',
|
||||
'testing/mochitest/tests/python/python.ini',
|
||||
]
|
||||
|
||||
CONFIGURE_SUBST_FILES += [
|
||||
|
@ -6,8 +6,8 @@ from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import mozpack.path as mozpath
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from concurrent.futures import (
|
||||
ThreadPoolExecutor,
|
||||
@ -19,6 +19,7 @@ import mozinfo
|
||||
from manifestparser import TestManifest
|
||||
from manifestparser import filters as mpf
|
||||
|
||||
import mozpack.path as mozpath
|
||||
from mozbuild.base import (
|
||||
MachCommandBase,
|
||||
)
|
||||
@ -69,13 +70,21 @@ class MachCommands(MachCommandBase):
|
||||
metavar='TEST',
|
||||
help=('Tests to run. Each test can be a single file or a directory. '
|
||||
'Default test resolution relies on PYTHON_UNITTEST_MANIFESTS.'))
|
||||
def python_test(self,
|
||||
tests=[],
|
||||
test_objects=None,
|
||||
subsuite=None,
|
||||
verbose=False,
|
||||
stop=False,
|
||||
jobs=1):
|
||||
def python_test(self, *args, **kwargs):
|
||||
try:
|
||||
tempdir = os.environ[b'PYTHON_TEST_TMP'] = str(tempfile.mkdtemp(suffix='-python-test'))
|
||||
return self.run_python_tests(*args, **kwargs)
|
||||
finally:
|
||||
import mozfile
|
||||
mozfile.remove(tempdir)
|
||||
|
||||
def run_python_tests(self,
|
||||
tests=[],
|
||||
test_objects=None,
|
||||
subsuite=None,
|
||||
verbose=False,
|
||||
stop=False,
|
||||
jobs=1):
|
||||
self._activate_virtualenv()
|
||||
|
||||
def find_tests_by_path():
|
||||
@ -129,26 +138,38 @@ class MachCommands(MachCommandBase):
|
||||
filters.append(mpf.subsuite(subsuite))
|
||||
|
||||
tests = mp.active_tests(filters=filters, disabled=False, **mozinfo.info)
|
||||
parallel = []
|
||||
sequential = []
|
||||
for test in tests:
|
||||
if test.get('sequential'):
|
||||
sequential.append(test)
|
||||
else:
|
||||
parallel.append(test)
|
||||
|
||||
self.jobs = jobs
|
||||
self.terminate = False
|
||||
self.verbose = verbose
|
||||
|
||||
return_code = 0
|
||||
|
||||
def on_test_finished(result):
|
||||
output, ret, test_path = result
|
||||
|
||||
for line in output:
|
||||
self.log(logging.INFO, 'python-test', {'line': line.rstrip()}, '{line}')
|
||||
|
||||
if ret and not return_code:
|
||||
self.log(logging.ERROR, 'python-test', {'test_path': test_path, 'ret': ret},
|
||||
'Setting retcode to {ret} from {test_path}')
|
||||
return return_code or ret
|
||||
|
||||
with ThreadPoolExecutor(max_workers=self.jobs) as executor:
|
||||
futures = [executor.submit(self._run_python_test, test['path'])
|
||||
for test in tests]
|
||||
for test in parallel]
|
||||
|
||||
try:
|
||||
for future in as_completed(futures):
|
||||
output, ret, test_path = future.result()
|
||||
|
||||
for line in output:
|
||||
self.log(logging.INFO, 'python-test', {'line': line.rstrip()}, '{line}')
|
||||
|
||||
if ret and not return_code:
|
||||
self.log(logging.ERROR, 'python-test', {'test_path': test_path, 'ret': ret}, 'Setting retcode to {ret} from {test_path}')
|
||||
return_code = return_code or ret
|
||||
return_code = on_test_finished(future.result())
|
||||
except KeyboardInterrupt:
|
||||
# Hack to force stop currently running threads.
|
||||
# https://gist.github.com/clchiou/f2608cbe54403edb0b13
|
||||
@ -156,7 +177,11 @@ class MachCommands(MachCommandBase):
|
||||
thread._threads_queues.clear()
|
||||
raise
|
||||
|
||||
self.log(logging.INFO, 'python-test', {'return_code': return_code}, 'Return code from mach python-test: {return_code}')
|
||||
for test in sequential:
|
||||
return_code = on_test_finished(self._run_python_test(test['path']))
|
||||
|
||||
self.log(logging.INFO, 'python-test', {'return_code': return_code},
|
||||
'Return code from mach python-test: {return_code}')
|
||||
return return_code
|
||||
|
||||
def _run_python_test(self, test_path):
|
||||
|
@ -346,7 +346,6 @@ class MacArtifactJob(ArtifactJob):
|
||||
# These get copied into dist/bin without the path, so "root/a/b/c" -> "dist/bin/c".
|
||||
paths_no_keep_path = ('Contents/MacOS', [
|
||||
'crashreporter.app/Contents/MacOS/crashreporter',
|
||||
'crashreporter.app/Contents/MacOS/minidump-analyzer',
|
||||
'firefox',
|
||||
'firefox-bin',
|
||||
'libfreebl3.dylib',
|
||||
@ -370,15 +369,20 @@ class MacArtifactJob(ArtifactJob):
|
||||
])
|
||||
|
||||
# These get copied into dist/bin with the path, so "root/a/b/c" -> "dist/bin/a/b/c".
|
||||
paths_keep_path = ('Contents/Resources', [
|
||||
'browser/components/libbrowsercomps.dylib',
|
||||
'dependentlibs.list',
|
||||
# 'firefox',
|
||||
'gmp-clearkey/0.1/libclearkey.dylib',
|
||||
# 'gmp-fake/1.0/libfake.dylib',
|
||||
# 'gmp-fakeopenh264/1.0/libfakeopenh264.dylib',
|
||||
'**/interfaces.xpt',
|
||||
])
|
||||
paths_keep_path = [
|
||||
('Contents/MacOS', [
|
||||
'crashreporter.app/Contents/MacOS/minidump-analyzer',
|
||||
]),
|
||||
('Contents/Resources', [
|
||||
'browser/components/libbrowsercomps.dylib',
|
||||
'dependentlibs.list',
|
||||
# 'firefox',
|
||||
'gmp-clearkey/0.1/libclearkey.dylib',
|
||||
# 'gmp-fake/1.0/libfake.dylib',
|
||||
# 'gmp-fakeopenh264/1.0/libfakeopenh264.dylib',
|
||||
'**/interfaces.xpt',
|
||||
]),
|
||||
]
|
||||
|
||||
with JarWriter(file=processed_filename, optimize=False, compress_level=5) as writer:
|
||||
root, paths = paths_no_keep_path
|
||||
@ -391,15 +395,15 @@ class MacArtifactJob(ArtifactJob):
|
||||
destpath = mozpath.join('bin', os.path.basename(p))
|
||||
writer.add(destpath.encode('utf-8'), f, mode=f.mode)
|
||||
|
||||
root, paths = paths_keep_path
|
||||
finder = UnpackFinder(mozpath.join(source, root))
|
||||
for path in paths:
|
||||
for p, f in finder.find(path):
|
||||
self.log(logging.INFO, 'artifact',
|
||||
{'path': p},
|
||||
'Adding {path} to processed archive')
|
||||
destpath = mozpath.join('bin', p)
|
||||
writer.add(destpath.encode('utf-8'), f.open(), mode=f.mode)
|
||||
for root, paths in paths_keep_path:
|
||||
finder = UnpackFinder(mozpath.join(source, root))
|
||||
for path in paths:
|
||||
for p, f in finder.find(path):
|
||||
self.log(logging.INFO, 'artifact',
|
||||
{'path': p},
|
||||
'Adding {path} to processed archive')
|
||||
destpath = mozpath.join('bin', p)
|
||||
writer.add(destpath.encode('utf-8'), f.open(), mode=f.mode)
|
||||
|
||||
finally:
|
||||
os.chdir(oldcwd)
|
||||
|
@ -680,6 +680,25 @@ impl Debug for ${style_struct.gecko_struct_name} {
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name=None)">
|
||||
<%
|
||||
if gecko_ffi_name is None:
|
||||
gecko_ffi_name = "m" + to_camel_case(ident)
|
||||
%>
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
||||
self.gecko.${gecko_ffi_name} = From::from(v)
|
||||
}
|
||||
|
||||
<% impl_simple_copy(ident, gecko_ffi_name) %>
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
|
||||
From::from(self.gecko.${gecko_ffi_name})
|
||||
}
|
||||
</%def>
|
||||
|
||||
<%def name="raw_impl_trait(style_struct, skip_longhands='', skip_additionals='')">
|
||||
<%
|
||||
longhands = [x for x in style_struct.longhands
|
||||
@ -998,17 +1017,19 @@ fn static_assert() {
|
||||
% endfor
|
||||
}
|
||||
|
||||
<%
|
||||
border_image_repeat_keywords = ["Stretch", "Repeat", "Round", "Space"]
|
||||
%>
|
||||
|
||||
pub fn set_border_image_repeat(&mut self, v: longhands::border_image_repeat::computed_value::T) {
|
||||
use properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
|
||||
use gecko_bindings::structs::{NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT};
|
||||
use gecko_bindings::structs::{NS_STYLE_BORDER_IMAGE_REPEAT_ROUND, NS_STYLE_BORDER_IMAGE_REPEAT_SPACE};
|
||||
use gecko_bindings::structs;
|
||||
|
||||
% for i, side in enumerate(["H", "V"]):
|
||||
let k = match v.${i} {
|
||||
RepeatKeyword::Stretch => NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
|
||||
RepeatKeyword::Repeat => NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT,
|
||||
RepeatKeyword::Round => NS_STYLE_BORDER_IMAGE_REPEAT_ROUND,
|
||||
RepeatKeyword::Space => NS_STYLE_BORDER_IMAGE_REPEAT_SPACE,
|
||||
% for keyword in border_image_repeat_keywords:
|
||||
RepeatKeyword::${keyword} => structs::NS_STYLE_BORDER_IMAGE_REPEAT_${keyword.upper()},
|
||||
% endfor
|
||||
};
|
||||
|
||||
self.gecko.mBorderImageRepeat${side} = k as u8;
|
||||
@ -1020,6 +1041,21 @@ fn static_assert() {
|
||||
self.gecko.mBorderImageRepeatV = other.gecko.mBorderImageRepeatV;
|
||||
}
|
||||
|
||||
pub fn clone_border_image_repeat(&self) -> longhands::border_image_repeat::computed_value::T {
|
||||
use properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
|
||||
use gecko_bindings::structs;
|
||||
|
||||
% for side in ["H", "V"]:
|
||||
let servo_${side.lower()} = match self.gecko.mBorderImageRepeat${side} as u32 {
|
||||
% for keyword in border_image_repeat_keywords:
|
||||
structs::NS_STYLE_BORDER_IMAGE_REPEAT_${keyword.upper()} => RepeatKeyword::${keyword},
|
||||
% endfor
|
||||
x => panic!("Found unexpected value in mBorderImageRepeat${side}: {:?}", x),
|
||||
};
|
||||
% endfor
|
||||
longhands::border_image_repeat::computed_value::T(servo_h, servo_v)
|
||||
}
|
||||
|
||||
pub fn set_border_image_width(&mut self, v: longhands::border_image_width::computed_value::T) {
|
||||
use values::generics::border::BorderImageSideWidth;
|
||||
|
||||
@ -1134,53 +1170,11 @@ fn static_assert() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_align_content(&mut self, v: longhands::align_content::computed_value::T) {
|
||||
self.gecko.mAlignContent = v.bits()
|
||||
}
|
||||
|
||||
${impl_simple_copy('align_content', 'mAlignContent')}
|
||||
|
||||
pub fn set_justify_content(&mut self, v: longhands::justify_content::computed_value::T) {
|
||||
self.gecko.mJustifyContent = v.bits()
|
||||
}
|
||||
|
||||
${impl_simple_copy('justify_content', 'mJustifyContent')}
|
||||
|
||||
pub fn set_align_self(&mut self, v: longhands::align_self::computed_value::T) {
|
||||
self.gecko.mAlignSelf = v.0.bits()
|
||||
}
|
||||
|
||||
${impl_simple_copy('align_self', 'mAlignSelf')}
|
||||
|
||||
pub fn set_justify_self(&mut self, v: longhands::justify_self::computed_value::T) {
|
||||
self.gecko.mJustifySelf = v.0.bits()
|
||||
}
|
||||
|
||||
${impl_simple_copy('justify_self', 'mJustifySelf')}
|
||||
|
||||
pub fn set_align_items(&mut self, v: longhands::align_items::computed_value::T) {
|
||||
self.gecko.mAlignItems = v.0.bits()
|
||||
}
|
||||
|
||||
${impl_simple_copy('align_items', 'mAlignItems')}
|
||||
|
||||
pub fn clone_align_items(&self) -> longhands::align_items::computed_value::T {
|
||||
use values::specified::align::{AlignFlags, AlignItems};
|
||||
AlignItems(AlignFlags::from_bits(self.gecko.mAlignItems)
|
||||
.expect("mAlignItems contains valid flags"))
|
||||
}
|
||||
|
||||
pub fn set_justify_items(&mut self, v: longhands::justify_items::computed_value::T) {
|
||||
self.gecko.mJustifyItems = v.0.bits()
|
||||
}
|
||||
|
||||
${impl_simple_copy('justify_items', 'mJustifyItems')}
|
||||
|
||||
pub fn clone_justify_items(&self) -> longhands::justify_items::computed_value::T {
|
||||
use values::specified::align::{AlignFlags, JustifyItems};
|
||||
JustifyItems(AlignFlags::from_bits(self.gecko.mJustifyItems)
|
||||
.expect("mJustifyItems contains valid flags"))
|
||||
}
|
||||
% for kind in ["align", "justify"]:
|
||||
${impl_simple_type_with_conversion(kind + "_content")}
|
||||
${impl_simple_type_with_conversion(kind + "_self")}
|
||||
${impl_simple_type_with_conversion(kind + "_items")}
|
||||
% endfor
|
||||
|
||||
pub fn set_order(&mut self, v: longhands::order::computed_value::T) {
|
||||
self.gecko.mOrder = v;
|
||||
@ -1372,27 +1366,7 @@ fn static_assert() {
|
||||
}
|
||||
% endfor
|
||||
|
||||
pub fn set_grid_auto_flow(&mut self, v: longhands::grid_auto_flow::computed_value::T) {
|
||||
use gecko_bindings::structs::NS_STYLE_GRID_AUTO_FLOW_ROW;
|
||||
use gecko_bindings::structs::NS_STYLE_GRID_AUTO_FLOW_COLUMN;
|
||||
use gecko_bindings::structs::NS_STYLE_GRID_AUTO_FLOW_DENSE;
|
||||
use properties::longhands::grid_auto_flow::computed_value::AutoFlow::{Row, Column};
|
||||
|
||||
self.gecko.mGridAutoFlow = 0;
|
||||
|
||||
let value = match v.autoflow {
|
||||
Row => NS_STYLE_GRID_AUTO_FLOW_ROW,
|
||||
Column => NS_STYLE_GRID_AUTO_FLOW_COLUMN,
|
||||
};
|
||||
|
||||
self.gecko.mGridAutoFlow |= value as u8;
|
||||
|
||||
if v.dense {
|
||||
self.gecko.mGridAutoFlow |= NS_STYLE_GRID_AUTO_FLOW_DENSE as u8;
|
||||
}
|
||||
}
|
||||
|
||||
${impl_simple_copy('grid_auto_flow', 'mGridAutoFlow')}
|
||||
${impl_simple_type_with_conversion("grid_auto_flow")}
|
||||
|
||||
pub fn set_grid_template_areas(&mut self, v: longhands::grid_template_areas::computed_value::T) {
|
||||
use gecko_bindings::bindings::Gecko_NewGridTemplateAreasValue;
|
||||
@ -1814,21 +1788,7 @@ fn static_assert() {
|
||||
unsafe { transmute(self.gecko.mFont.weight) }
|
||||
}
|
||||
|
||||
pub fn set_font_synthesis(&mut self, v: longhands::font_synthesis::computed_value::T) {
|
||||
use gecko_bindings::structs::{NS_FONT_SYNTHESIS_WEIGHT, NS_FONT_SYNTHESIS_STYLE};
|
||||
|
||||
self.gecko.mFont.synthesis = 0;
|
||||
if v.weight {
|
||||
self.gecko.mFont.synthesis |= NS_FONT_SYNTHESIS_WEIGHT as u8;
|
||||
}
|
||||
if v.style {
|
||||
self.gecko.mFont.synthesis |= NS_FONT_SYNTHESIS_STYLE as u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_font_synthesis_from(&mut self, other: &Self) {
|
||||
self.gecko.mFont.synthesis = other.gecko.mFont.synthesis;
|
||||
}
|
||||
${impl_simple_type_with_conversion("font_synthesis", "mFont.synthesis")}
|
||||
|
||||
pub fn set_font_size_adjust(&mut self, v: longhands::font_size_adjust::computed_value::T) {
|
||||
use properties::longhands::font_size_adjust::computed_value::T;
|
||||
@ -1863,10 +1823,7 @@ fn static_assert() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_font_language_override(&mut self, v: longhands::font_language_override::computed_value::T) {
|
||||
self.gecko.mFont.languageOverride = v.0;
|
||||
}
|
||||
${impl_simple_copy('font_language_override', 'mFont.languageOverride')}
|
||||
<% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %>
|
||||
|
||||
pub fn set_font_variant_alternates(&mut self, v: longhands::font_variant_alternates::computed_value::T) {
|
||||
self.gecko.mFont.variantAlternates = v.to_gecko_keyword()
|
||||
@ -1879,23 +1836,9 @@ fn static_assert() {
|
||||
// self.gecko.mFont.alternateValues = other.gecko.mFont.alternateValues;
|
||||
}
|
||||
|
||||
pub fn set_font_variant_ligatures(&mut self, v: longhands::font_variant_ligatures::computed_value::T) {
|
||||
self.gecko.mFont.variantLigatures = v.to_gecko_keyword()
|
||||
}
|
||||
|
||||
${impl_simple_copy('font_variant_ligatures', 'mFont.variantLigatures')}
|
||||
|
||||
pub fn set_font_variant_east_asian(&mut self, v: longhands::font_variant_east_asian::computed_value::T) {
|
||||
self.gecko.mFont.variantEastAsian = v.to_gecko_keyword()
|
||||
}
|
||||
|
||||
${impl_simple_copy('font_variant_east_asian', 'mFont.variantEastAsian')}
|
||||
|
||||
pub fn set_font_variant_numeric(&mut self, v: longhands::font_variant_numeric::computed_value::T) {
|
||||
self.gecko.mFont.variantNumeric = v.to_gecko_keyword()
|
||||
}
|
||||
|
||||
${impl_simple_copy('font_variant_numeric', 'mFont.variantNumeric')}
|
||||
${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")}
|
||||
${impl_simple_type_with_conversion("font_variant_east_asian", "mFont.variantEastAsian")}
|
||||
${impl_simple_type_with_conversion("font_variant_numeric", "mFont.variantNumeric")}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set__moz_min_font_size_ratio(&mut self, v: longhands::_moz_min_font_size_ratio::computed_value::T) {
|
||||
@ -2654,7 +2597,7 @@ fn static_assert() {
|
||||
|
||||
<% scroll_snap_type_keyword = Keyword("scroll-snap-type", "none mandatory proximity") %>
|
||||
|
||||
${impl_keyword('scroll_snap_type_y', 'mScrollSnapTypeY', scroll_snap_type_keyword, need_clone=False)}
|
||||
${impl_keyword('scroll_snap_type_y', 'mScrollSnapTypeY', scroll_snap_type_keyword, need_clone=True)}
|
||||
|
||||
pub fn set_perspective_origin(&mut self, v: longhands::perspective_origin::computed_value::T) {
|
||||
self.gecko.mPerspectiveOrigin[0].set(v.horizontal);
|
||||
@ -2855,11 +2798,7 @@ fn static_assert() {
|
||||
|
||||
${impl_simple_copy("contain", "mContain")}
|
||||
|
||||
pub fn set_touch_action(&mut self, v: longhands::touch_action::computed_value::T) {
|
||||
self.gecko.mTouchAction = v.bits();
|
||||
}
|
||||
|
||||
${impl_simple_copy("touch_action", "mTouchAction")}
|
||||
${impl_simple_type_with_conversion("touch_action")}
|
||||
</%self:impl_trait>
|
||||
|
||||
<%def name="simple_image_array_property(name, shorthand, field_name)">
|
||||
@ -3637,7 +3576,7 @@ fn static_assert() {
|
||||
|
||||
<%self:impl_trait style_struct_name="InheritedText"
|
||||
skip_longhands="text-align text-emphasis-style text-shadow line-height letter-spacing word-spacing
|
||||
-webkit-text-stroke-width text-emphasis-position -moz-tab-size -moz-text-size-adjust">
|
||||
-webkit-text-stroke-width text-emphasis-position -moz-tab-size">
|
||||
|
||||
<% text_align_keyword = Keyword("text-align",
|
||||
"start end left right center justify -moz-center -moz-left -moz-right char",
|
||||
@ -3744,25 +3683,7 @@ fn static_assert() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_text_emphasis_position(&mut self, v: longhands::text_emphasis_position::computed_value::T) {
|
||||
use properties::longhands::text_emphasis_position::*;
|
||||
|
||||
let mut result = match v.0 {
|
||||
HorizontalWritingModeValue::Over => structs::NS_STYLE_TEXT_EMPHASIS_POSITION_OVER as u8,
|
||||
HorizontalWritingModeValue::Under => structs::NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER as u8,
|
||||
};
|
||||
match v.1 {
|
||||
VerticalWritingModeValue::Right => {
|
||||
result |= structs::NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT as u8;
|
||||
}
|
||||
VerticalWritingModeValue::Left => {
|
||||
result |= structs::NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT as u8;
|
||||
}
|
||||
}
|
||||
self.gecko.mTextEmphasisPosition = result;
|
||||
}
|
||||
|
||||
<%call expr="impl_simple_copy('text_emphasis_position', 'mTextEmphasisPosition')"></%call>
|
||||
${impl_simple_type_with_conversion("text_emphasis_position")}
|
||||
|
||||
pub fn set_text_emphasis_style(&mut self, v: longhands::text_emphasis_style::computed_value::T) {
|
||||
use properties::longhands::text_emphasis_style::computed_value::T;
|
||||
@ -3804,7 +3725,7 @@ fn static_assert() {
|
||||
self.gecko.mTextEmphasisStyle = other.gecko.mTextEmphasisStyle;
|
||||
}
|
||||
|
||||
<%call expr="impl_app_units('_webkit_text_stroke_width', 'mWebkitTextStrokeWidth', need_clone=False)"></%call>
|
||||
<%call expr="impl_app_units('_webkit_text_stroke_width', 'mWebkitTextStrokeWidth', need_clone=True)"></%call>
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set__moz_tab_size(&mut self, v: longhands::_moz_tab_size::computed_value::T) {
|
||||
@ -3832,39 +3753,13 @@ fn static_assert() {
|
||||
}
|
||||
|
||||
<%call expr="impl_coord_copy('_moz_tab_size', 'mTabSize')"></%call>
|
||||
|
||||
<% text_size_adjust_keyword = Keyword("text-size-adjust", "auto none") %>
|
||||
|
||||
${impl_keyword('_moz_text_size_adjust', 'mTextSizeAdjust', text_size_adjust_keyword, need_clone=False)}
|
||||
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="Text"
|
||||
skip_longhands="text-decoration-line text-overflow initial-letter"
|
||||
skip_additionals="*">
|
||||
|
||||
pub fn set_text_decoration_line(&mut self, v: longhands::text_decoration_line::computed_value::T) {
|
||||
let mut bits: u8 = 0;
|
||||
if v.contains(longhands::text_decoration_line::UNDERLINE) {
|
||||
bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8;
|
||||
}
|
||||
if v.contains(longhands::text_decoration_line::OVERLINE) {
|
||||
bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8;
|
||||
}
|
||||
if v.contains(longhands::text_decoration_line::LINE_THROUGH) {
|
||||
bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8;
|
||||
}
|
||||
if v.contains(longhands::text_decoration_line::BLINK) {
|
||||
bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_BLINK as u8;
|
||||
}
|
||||
if v.contains(longhands::text_decoration_line::COLOR_OVERRIDE) {
|
||||
bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL as u8;
|
||||
}
|
||||
self.gecko.mTextDecorationLine = bits;
|
||||
}
|
||||
|
||||
${impl_simple_copy('text_decoration_line', 'mTextDecorationLine')}
|
||||
|
||||
${impl_simple_type_with_conversion("text_decoration_line")}
|
||||
|
||||
fn clear_overflow_sides_if_string(&mut self) {
|
||||
use gecko_bindings::structs::nsStyleTextOverflowSide;
|
||||
@ -3938,6 +3833,18 @@ fn static_assert() {
|
||||
self.gecko.mInitialLetterSink = other.gecko.mInitialLetterSink;
|
||||
}
|
||||
|
||||
pub fn clone_initial_letter(&self) -> longhands::initial_letter::computed_value::T {
|
||||
use values::generics::text::InitialLetter;
|
||||
|
||||
if self.gecko.mInitialLetterSize == 0. && self.gecko.mInitialLetterSink == 0 {
|
||||
InitialLetter::Normal
|
||||
} else if self.gecko.mInitialLetterSize.floor() as i32 == self.gecko.mInitialLetterSink {
|
||||
InitialLetter::Specified(self.gecko.mInitialLetterSize, None)
|
||||
} else {
|
||||
InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_underline(&self) -> bool {
|
||||
(self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0
|
||||
@ -4491,12 +4398,7 @@ clip-path
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="UI" skip_longhands="-moz-force-broken-image-icon">
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set__moz_force_broken_image_icon(&mut self, v: longhands::_moz_force_broken_image_icon::computed_value::T) {
|
||||
self.gecko.mForceBrokenImageIcon = v.0 as u8;
|
||||
}
|
||||
|
||||
${impl_simple_copy("_moz_force_broken_image_icon", "mForceBrokenImageIcon")}
|
||||
${impl_simple_type_with_conversion("_moz_force_broken_image_icon", "mForceBrokenImageIcon")}
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="XUL"
|
||||
@ -4507,6 +4409,11 @@ clip-path
|
||||
}
|
||||
|
||||
${impl_simple_copy("_moz_box_ordinal_group", "mBoxOrdinal")}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn clone__moz_box_ordinal_group(&self) -> i32{
|
||||
self.gecko.mBoxOrdinal as i32
|
||||
}
|
||||
</%self:impl_trait>
|
||||
|
||||
<%def name="define_ffi_struct_accessor(style_struct)">
|
||||
|
@ -212,7 +212,7 @@ ${helpers.predefined_type("border-image-outset", "LengthOrNumberRect",
|
||||
animation_value_type="none",
|
||||
boxed=True)}
|
||||
|
||||
<%helpers:longhand name="border-image-repeat" animation_value_type="none"
|
||||
<%helpers:longhand name="border-image-repeat" animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
|
@ -1612,7 +1612,7 @@ ${helpers.single_keyword("scroll-snap-type-x",
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-x)",
|
||||
animation_value_type="discrete")}
|
||||
|
||||
<%helpers:longhand products="gecko" name="scroll-snap-type-y" animation_value_type="none"
|
||||
<%helpers:longhand products="gecko" name="scroll-snap-type-y" animation_value_type="discrete"
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-x)">
|
||||
pub use super::scroll_snap_type_x::SpecifiedValue;
|
||||
pub use super::scroll_snap_type_x::computed_value;
|
||||
@ -1710,7 +1710,7 @@ ${helpers.predefined_type("transform-origin",
|
||||
// FIXME: `size` and `content` values are not implemented and `strict` is implemented
|
||||
// like `content`(layout style paint) in gecko. We should implement `size` and `content`,
|
||||
// also update the glue once they are implemented in gecko.
|
||||
<%helpers:longhand name="contain" animation_value_type="none" products="gecko" need_clone="True"
|
||||
<%helpers:longhand name="contain" animation_value_type="discrete" products="gecko" need_clone="True"
|
||||
flags="FIXPOS_CB"
|
||||
spec="https://drafts.csswg.org/css-contain/#contain-property">
|
||||
use std::fmt;
|
||||
@ -1925,7 +1925,7 @@ ${helpers.predefined_type("shape-outside", "basic_shape::FloatAreaShape",
|
||||
|
||||
<%helpers:longhand name="touch-action"
|
||||
products="gecko"
|
||||
animation_value_type="none"
|
||||
animation_value_type="discrete"
|
||||
disable_when_testing="True"
|
||||
spec="https://compat.spec.whatwg.org/#touch-action">
|
||||
use gecko_bindings::structs;
|
||||
@ -1999,4 +1999,7 @@ ${helpers.predefined_type("shape-outside", "basic_shape::FloatAreaShape",
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl_bitflags_conversions!(SpecifiedValue);
|
||||
</%helpers:longhand>
|
||||
|
@ -16,6 +16,23 @@
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
macro_rules! impl_gecko_keyword_from_trait {
|
||||
($name: ident, $utype: ty) => {
|
||||
impl From<$utype> for $name {
|
||||
fn from(bits: $utype) -> $name {
|
||||
$name::from_gecko_keyword(bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name> for $utype {
|
||||
fn from(v: $name) -> $utype {
|
||||
v.to_gecko_keyword()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Define ToComputedValue, ToCss, and other boilerplate for a specified value
|
||||
// which is of the form `enum SpecifiedValue {Value(..), System(SystemFont)}`
|
||||
<%def name="simple_system_boilerplate(name)">
|
||||
@ -1144,7 +1161,7 @@ ${helpers.single_keyword_system("font-variant-caps",
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
<%helpers:longhand products="gecko" name="font-synthesis" animation_value_type="none"
|
||||
<%helpers:longhand products="gecko" name="font-synthesis" animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis">
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
@ -1204,6 +1221,34 @@ ${helpers.single_keyword_system("font-variant-caps",
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<u8> for SpecifiedValue {
|
||||
fn from(bits: u8) -> SpecifiedValue {
|
||||
use gecko_bindings::structs;
|
||||
|
||||
SpecifiedValue {
|
||||
weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0,
|
||||
style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<SpecifiedValue> for u8 {
|
||||
fn from(v: SpecifiedValue) -> u8 {
|
||||
use gecko_bindings::structs;
|
||||
|
||||
let mut bits: u8 = 0;
|
||||
if v.weight {
|
||||
bits |= structs::NS_FONT_SYNTHESIS_WEIGHT as u8;
|
||||
}
|
||||
if v.style {
|
||||
bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
|
||||
}
|
||||
bits
|
||||
}
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
${helpers.single_keyword_system("font-stretch",
|
||||
@ -1362,7 +1407,7 @@ macro_rules! exclusive_value {
|
||||
}
|
||||
}
|
||||
|
||||
<%helpers:longhand name="font-variant-east-asian" products="gecko" animation_value_type="none"
|
||||
<%helpers:longhand name="font-variant-east-asian" products="gecko" animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian">
|
||||
use properties::longhands::system_font::SystemFont;
|
||||
use std::fmt;
|
||||
@ -1500,9 +1545,12 @@ macro_rules! exclusive_value {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl_gecko_keyword_from_trait!(VariantEastAsian, u16);
|
||||
</%helpers:longhand>
|
||||
|
||||
<%helpers:longhand name="font-variant-ligatures" products="gecko" animation_value_type="none"
|
||||
<%helpers:longhand name="font-variant-ligatures" products="gecko" animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures">
|
||||
use properties::longhands::system_font::SystemFont;
|
||||
use std::fmt;
|
||||
@ -1650,9 +1698,12 @@ macro_rules! exclusive_value {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl_gecko_keyword_from_trait!(VariantLigatures, u16);
|
||||
</%helpers:longhand>
|
||||
|
||||
<%helpers:longhand name="font-variant-numeric" products="gecko" animation_value_type="none"
|
||||
<%helpers:longhand name="font-variant-numeric" products="gecko" animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric">
|
||||
use properties::longhands::system_font::SystemFont;
|
||||
use std::fmt;
|
||||
@ -1793,6 +1844,9 @@ macro_rules! exclusive_value {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl_gecko_keyword_from_trait!(VariantNumeric, u8);
|
||||
</%helpers:longhand>
|
||||
|
||||
${helpers.single_keyword_system("font-variant-position",
|
||||
@ -1875,7 +1929,7 @@ https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control-
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
<%helpers:longhand name="font-language-override" products="gecko" animation_value_type="none"
|
||||
<%helpers:longhand name="font-language-override" products="gecko" animation_value_type="discrete"
|
||||
extra_prefixes="moz" boxed="True"
|
||||
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override">
|
||||
use properties::longhands::system_font::SystemFont;
|
||||
@ -2009,6 +2063,20 @@ https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control-
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<u32> for computed_value::T {
|
||||
fn from(bits: u32) -> computed_value::T {
|
||||
computed_value::T(bits)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<computed_value::T> for u32 {
|
||||
fn from(v: computed_value::T) -> u32 {
|
||||
v.0
|
||||
}
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
<%helpers:longhand name="-x-lang" products="gecko" animation_value_type="none" internal="True"
|
||||
|
@ -18,18 +18,20 @@ ${helpers.predefined_type("line-height",
|
||||
${helpers.single_keyword("text-transform",
|
||||
"none capitalize uppercase lowercase",
|
||||
extra_gecko_values="full-width",
|
||||
animation_value_type="none",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-text-transform")}
|
||||
|
||||
${helpers.single_keyword("hyphens", "manual none auto",
|
||||
gecko_enum_prefix="StyleHyphens",
|
||||
products="gecko", animation_value_type="none", extra_prefixes="moz",
|
||||
gecko_inexhaustive=True,
|
||||
products="gecko", animation_value_type="discrete", extra_prefixes="moz",
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-hyphens")}
|
||||
|
||||
// TODO: Support <percentage>
|
||||
${helpers.single_keyword("-moz-text-size-adjust", "auto none",
|
||||
gecko_constant_prefix="NS_STYLE_TEXT_SIZE_ADJUST",
|
||||
products="gecko", animation_value_type="none",
|
||||
gecko_ffi_name="mTextSizeAdjust",
|
||||
products="gecko", animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
|
||||
alias="-webkit-text-size-adjust")}
|
||||
|
||||
@ -45,7 +47,7 @@ ${helpers.predefined_type("text-indent",
|
||||
${helpers.single_keyword("overflow-wrap",
|
||||
"normal break-word",
|
||||
gecko_constant_prefix="NS_STYLE_OVERFLOWWRAP",
|
||||
animation_value_type="none",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
|
||||
alias="word-wrap")}
|
||||
|
||||
@ -53,7 +55,7 @@ ${helpers.single_keyword("overflow-wrap",
|
||||
${helpers.single_keyword("word-break",
|
||||
"normal break-all keep-all",
|
||||
gecko_constant_prefix="NS_STYLE_WORDBREAK",
|
||||
animation_value_type="none",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-word-break")}
|
||||
|
||||
// TODO(pcwalton): Support `text-justify: distribute`.
|
||||
@ -62,7 +64,8 @@ ${helpers.single_keyword("word-break",
|
||||
extra_gecko_values="inter-character"
|
||||
extra_specified="${'distribute' if product == 'gecko' else ''}"
|
||||
gecko_enum_prefix="StyleTextJustify"
|
||||
animation_value_type="none"
|
||||
gecko_inexhaustive="True"
|
||||
animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-text-justify">
|
||||
no_viewport_percentage!(SpecifiedValue);
|
||||
|
||||
@ -101,11 +104,11 @@ ${helpers.single_keyword("text-align-last",
|
||||
"auto start end left right center justify",
|
||||
products="gecko",
|
||||
gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
|
||||
animation_value_type="none",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-text-align-last")}
|
||||
|
||||
// TODO make this a shorthand and implement text-align-last/text-align-all
|
||||
<%helpers:longhand name="text-align" animation_value_type="none" need_clone="True"
|
||||
<%helpers:longhand name="text-align" animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-text-align">
|
||||
no_viewport_percentage!(SpecifiedValue);
|
||||
pub mod computed_value {
|
||||
@ -360,7 +363,8 @@ ${helpers.predefined_type("word-spacing",
|
||||
extra_gecko_values="-moz-pre-space"
|
||||
gecko_enum_prefix="StyleWhiteSpace"
|
||||
needs_conversion="True"
|
||||
animation_value_type="none"
|
||||
gecko_inexhaustive="True"
|
||||
animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-text/#propdef-white-space">
|
||||
use values::computed::ComputedValueAsSpecified;
|
||||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||||
@ -605,7 +609,7 @@ ${helpers.predefined_type("word-spacing",
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
<%helpers:longhand name="text-emphasis-position" animation_value_type="none" products="gecko"
|
||||
<%helpers:longhand name="text-emphasis-position" animation_value_type="discrete" products="gecko"
|
||||
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
|
||||
use values::computed::ComputedValueAsSpecified;
|
||||
use style_traits::ToCss;
|
||||
@ -663,6 +667,32 @@ ${helpers.predefined_type("word-spacing",
|
||||
SpecifiedValue(horiz, vert)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for SpecifiedValue {
|
||||
fn from(bits: u8) -> SpecifiedValue {
|
||||
SpecifiedValue::from_gecko_keyword(bits as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpecifiedValue> for u8 {
|
||||
fn from(v: SpecifiedValue) -> u8 {
|
||||
use gecko_bindings::structs;
|
||||
|
||||
let mut result = match v.0 {
|
||||
HorizontalWritingModeValue::Over => structs::NS_STYLE_TEXT_EMPHASIS_POSITION_OVER,
|
||||
HorizontalWritingModeValue::Under => structs::NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER,
|
||||
};
|
||||
match v.1 {
|
||||
VerticalWritingModeValue::Right => {
|
||||
result |= structs::NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT;
|
||||
}
|
||||
VerticalWritingModeValue::Left => {
|
||||
result |= structs::NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT;
|
||||
}
|
||||
};
|
||||
result as u8
|
||||
}
|
||||
}
|
||||
% endif
|
||||
</%helpers:longhand>
|
||||
|
||||
|
@ -16,7 +16,7 @@ ${helpers.predefined_type("outline-color", "Color", "computed_value::T::currentc
|
||||
ignored_when_colors_disabled=True,
|
||||
spec="https://drafts.csswg.org/css-ui/#propdef-outline-color")}
|
||||
|
||||
<%helpers:longhand name="outline-style" need_clone="True" animation_value_type="none"
|
||||
<%helpers:longhand name="outline-style" animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
|
||||
use values::specified::BorderStyle;
|
||||
|
||||
|
@ -24,6 +24,24 @@
|
||||
animation_value_type="ComputedValue", logical=True)}
|
||||
% endfor
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
macro_rules! impl_align_conversions {
|
||||
($name: path) => {
|
||||
impl From<u8> for $name {
|
||||
fn from(bits: u8) -> $name {
|
||||
$name(::values::specified::align::AlignFlags::from_bits(bits)
|
||||
.expect("bits contain valid flag"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name> for u8 {
|
||||
fn from(v: $name) -> u8 {
|
||||
v.0.bits()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
${helpers.predefined_type("z-index", "IntegerOrAuto",
|
||||
"Either::Second(Auto)",
|
||||
spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
|
||||
@ -48,14 +66,14 @@ ${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
|
||||
${helpers.single_keyword("justify-content", "flex-start stretch flex-end center space-between space-around",
|
||||
extra_prefixes="webkit",
|
||||
spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
|
||||
animation_value_type="none")}
|
||||
animation_value_type="discrete")}
|
||||
% else:
|
||||
${helpers.predefined_type(name="justify-content",
|
||||
type="AlignJustifyContent",
|
||||
initial_value="specified::AlignJustifyContent::normal()",
|
||||
spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
|
||||
extra_prefixes="webkit",
|
||||
animation_value_type="none")}
|
||||
animation_value_type="discrete")}
|
||||
% endif
|
||||
|
||||
% if product == "servo":
|
||||
@ -63,7 +81,7 @@ ${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
|
||||
${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around",
|
||||
extra_prefixes="webkit",
|
||||
spec="https://drafts.csswg.org/css-align/#propdef-align-content",
|
||||
animation_value_type="none")}
|
||||
animation_value_type="discrete")}
|
||||
|
||||
${helpers.single_keyword("align-items",
|
||||
"stretch flex-start flex-end center baseline",
|
||||
@ -76,7 +94,7 @@ ${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
|
||||
initial_value="specified::AlignJustifyContent::normal()",
|
||||
spec="https://drafts.csswg.org/css-align/#propdef-align-content",
|
||||
extra_prefixes="webkit",
|
||||
animation_value_type="none")}
|
||||
animation_value_type="discrete")}
|
||||
|
||||
${helpers.predefined_type(name="align-items",
|
||||
type="AlignItems",
|
||||
@ -85,11 +103,17 @@ ${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
|
||||
extra_prefixes="webkit",
|
||||
animation_value_type="discrete")}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl_align_conversions!(::values::specified::align::AlignItems);
|
||||
|
||||
${helpers.predefined_type(name="justify-items",
|
||||
type="JustifyItems",
|
||||
initial_value="specified::JustifyItems::auto()",
|
||||
spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
|
||||
animation_value_type="none")}
|
||||
animation_value_type="discrete")}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl_align_conversions!(::values::specified::align::JustifyItems);
|
||||
% endif
|
||||
|
||||
// Flex item properties
|
||||
@ -109,23 +133,25 @@ ${helpers.predefined_type("flex-shrink", "Number",
|
||||
% if product == "servo":
|
||||
// FIXME: Update Servo to support the same syntax as Gecko.
|
||||
${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline",
|
||||
need_clone=True,
|
||||
extra_prefixes="webkit",
|
||||
spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
|
||||
animation_value_type="none")}
|
||||
animation_value_type="discrete")}
|
||||
% else:
|
||||
${helpers.predefined_type(name="align-self",
|
||||
type="AlignJustifySelf",
|
||||
initial_value="specified::AlignJustifySelf::auto()",
|
||||
spec="https://drafts.csswg.org/css-align/#align-self-property",
|
||||
extra_prefixes="webkit",
|
||||
animation_value_type="none")}
|
||||
animation_value_type="discrete")}
|
||||
|
||||
${helpers.predefined_type(name="justify-self",
|
||||
type="AlignJustifySelf",
|
||||
initial_value="specified::AlignJustifySelf::auto()",
|
||||
spec="https://drafts.csswg.org/css-align/#justify-self-property",
|
||||
animation_value_type="none")}
|
||||
animation_value_type="discrete")}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl_align_conversions!(::values::specified::align::AlignJustifySelf);
|
||||
% endif
|
||||
|
||||
// https://drafts.csswg.org/css-flexbox/#propdef-order
|
||||
@ -266,7 +292,7 @@ ${helpers.predefined_type("object-position",
|
||||
<%helpers:longhand name="grid-auto-flow"
|
||||
spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow"
|
||||
products="gecko"
|
||||
animation_value_type="none">
|
||||
animation_value_type="discrete">
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use values::computed::ComputedValueAsSpecified;
|
||||
@ -346,6 +372,43 @@ ${helpers.predefined_type("object-position",
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<u8> for SpecifiedValue {
|
||||
fn from(bits: u8) -> SpecifiedValue {
|
||||
use gecko_bindings::structs;
|
||||
use self::computed_value::AutoFlow;
|
||||
|
||||
SpecifiedValue {
|
||||
autoflow:
|
||||
if bits & structs::NS_STYLE_GRID_AUTO_FLOW_ROW as u8 != 0 {
|
||||
AutoFlow::Row
|
||||
} else {
|
||||
AutoFlow::Column
|
||||
},
|
||||
dense:
|
||||
bits & structs::NS_STYLE_GRID_AUTO_FLOW_DENSE as u8 != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<SpecifiedValue> for u8 {
|
||||
fn from(v: SpecifiedValue) -> u8 {
|
||||
use gecko_bindings::structs;
|
||||
use self::computed_value::AutoFlow;
|
||||
|
||||
let mut result: u8 = match v.autoflow {
|
||||
AutoFlow::Row => structs::NS_STYLE_GRID_AUTO_FLOW_ROW as u8,
|
||||
AutoFlow::Column => structs::NS_STYLE_GRID_AUTO_FLOW_COLUMN as u8,
|
||||
};
|
||||
|
||||
if v.dense {
|
||||
result |= structs::NS_STYLE_GRID_AUTO_FLOW_DENSE as u8;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
<%helpers:longhand name="grid-template-areas"
|
||||
|
@ -143,10 +143,9 @@ ${helpers.single_keyword("unicode-bidi",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi")}
|
||||
|
||||
// FIXME: This prop should be animatable.
|
||||
<%helpers:longhand name="text-decoration-line"
|
||||
custom_cascade="${product == 'servo'}"
|
||||
animation_value_type="none"
|
||||
animation_value_type="discrete"
|
||||
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
@ -159,8 +158,8 @@ ${helpers.single_keyword("unicode-bidi",
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub flags SpecifiedValue: u8 {
|
||||
const NONE = 0,
|
||||
const OVERLINE = 0x01,
|
||||
const UNDERLINE = 0x02,
|
||||
const UNDERLINE = 0x01,
|
||||
const OVERLINE = 0x02,
|
||||
const LINE_THROUGH = 0x04,
|
||||
const BLINK = 0x08,
|
||||
% if product == "gecko":
|
||||
@ -256,6 +255,9 @@ ${helpers.single_keyword("unicode-bidi",
|
||||
longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context);
|
||||
}
|
||||
% endif
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl_bitflags_conversions!(SpecifiedValue);
|
||||
</%helpers:longhand>
|
||||
|
||||
${helpers.single_keyword("text-decoration-style",
|
||||
@ -278,6 +280,6 @@ ${helpers.predefined_type(
|
||||
"InitialLetter",
|
||||
"computed::InitialLetter::normal()",
|
||||
initial_specified_value="specified::InitialLetter::normal()",
|
||||
animation_value_type="none",
|
||||
animation_value_type="discrete",
|
||||
products="gecko",
|
||||
spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials")}
|
||||
|
@ -42,7 +42,7 @@ ${helpers.single_keyword("-moz-window-shadow", "none default menu tooltip sheet"
|
||||
|
||||
<%helpers:longhand name="-moz-force-broken-image-icon"
|
||||
products="gecko"
|
||||
animation_value_type="none"
|
||||
animation_value_type="discrete"
|
||||
spec="None (Nonstandard Firefox-only property)">
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
@ -83,4 +83,19 @@ ${helpers.single_keyword("-moz-window-shadow", "none default menu tooltip sheet"
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for SpecifiedValue {
|
||||
fn from(bits: u8) -> SpecifiedValue {
|
||||
SpecifiedValue(bits == 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpecifiedValue> for u8 {
|
||||
fn from(v: SpecifiedValue) -> u8 {
|
||||
match v.0 {
|
||||
true => 1u8,
|
||||
false => 0u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
@ -59,5 +59,5 @@ ${helpers.predefined_type("-moz-box-ordinal-group", "Integer", "0",
|
||||
products="gecko",
|
||||
alias="-webkit-box-ordinal-group",
|
||||
gecko_ffi_name="mBoxOrdinal",
|
||||
animation_value_type="none",
|
||||
animation_value_type="discrete",
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)")}
|
||||
|
@ -53,6 +53,23 @@ macro_rules! property_name {
|
||||
($s: tt) => { atom!($s) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
macro_rules! impl_bitflags_conversions {
|
||||
($name: ident) => {
|
||||
impl From<u8> for $name {
|
||||
fn from(bits: u8) -> $name {
|
||||
$name::from_bits(bits).expect("bits contain valid flag")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name> for u8 {
|
||||
fn from(v: $name) -> u8 {
|
||||
v.bits()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
<%!
|
||||
from data import Method, Keyword, to_rust_ident, to_camel_case, SYSTEM_FONT_LONGHANDS
|
||||
import os.path
|
||||
|
@ -137,10 +137,6 @@ impl AlignJustifyContent {
|
||||
AlignJustifyContent(flags.bits() as u16 | ((fallback.bits() as u16) << ALIGN_ALL_SHIFT))
|
||||
}
|
||||
|
||||
/// The combined 16-bit flags, for copying into a Gecko style struct.
|
||||
#[inline]
|
||||
pub fn bits(self) -> u16 { self.0 }
|
||||
|
||||
/// The primary alignment
|
||||
#[inline]
|
||||
pub fn primary(self) -> AlignFlags {
|
||||
@ -325,6 +321,20 @@ impl Parse for JustifyItems {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<u16> for AlignJustifyContent {
|
||||
fn from(bits: u16) -> AlignJustifyContent {
|
||||
AlignJustifyContent(bits)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<AlignJustifyContent> for u16 {
|
||||
fn from(v: AlignJustifyContent) -> u16 {
|
||||
v.0
|
||||
}
|
||||
}
|
||||
|
||||
// auto | normal | stretch | <baseline-position>
|
||||
fn parse_auto_normal_stretch_baseline(input: &mut Parser) -> Result<AlignFlags, ()> {
|
||||
if let Ok(baseline) = input.try(parse_baseline) {
|
||||
|
@ -50,6 +50,42 @@ marionette-harness:
|
||||
- 'testing/mozbase/packages.txt'
|
||||
- 'python/mach_commands.py'
|
||||
|
||||
mochitest-harness:
|
||||
description: testing/mochitest unittests
|
||||
platform: linux64/opt
|
||||
require-build: true
|
||||
treeherder:
|
||||
symbol: py(mch)
|
||||
kind: test
|
||||
tier: 2
|
||||
worker-type:
|
||||
by-platform:
|
||||
linux64.*: aws-provisioner-v1/gecko-t-linux-xlarge
|
||||
worker:
|
||||
by-platform:
|
||||
linux64.*:
|
||||
docker-image: {in-tree: "desktop1604-test"}
|
||||
max-run-time: 3600
|
||||
run:
|
||||
using: run-task
|
||||
command: >
|
||||
source /home/worker/scripts/xvfb.sh &&
|
||||
start_xvfb '1600x1200x24' 0 &&
|
||||
cd /home/worker/checkouts/gecko &&
|
||||
./mach python-test --subsuite mochitest
|
||||
run-on-projects:
|
||||
- integration
|
||||
- release
|
||||
when:
|
||||
files-changed:
|
||||
- 'config/mozunit.py'
|
||||
- 'python/mach_commands.py'
|
||||
- 'testing/mochitest/**'
|
||||
- 'testing/mozharness/mozharness/base/log.py'
|
||||
- 'testing/mozharness/mozharness/mozilla/structuredlog.py'
|
||||
- 'testing/mozharness/mozharness/mozilla/testing/errors.py'
|
||||
- 'testing/profiles/prefs_general.js'
|
||||
|
||||
mozbase:
|
||||
description: testing/mozbase unit tests
|
||||
platform:
|
||||
|
@ -9,10 +9,6 @@ consistency.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
from taskgraph.util.attributes import keymatch
|
||||
|
||||
|
||||
ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}'
|
||||
SECRET_SCOPE = 'secrets:get:project/releng/gecko/{}/level-{}/{}'
|
||||
|
||||
|
||||
@ -61,30 +57,6 @@ def docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc):
|
||||
})
|
||||
|
||||
|
||||
def add_build_dependency(config, job, taskdesc):
|
||||
"""Add build dependency to the task description and installer_url to env."""
|
||||
key = job['platform']
|
||||
build_labels = config.config.get('dependent-build-platforms', {})
|
||||
matches = keymatch(build_labels, key)
|
||||
if not matches:
|
||||
raise Exception("No build platform found for '{}'. "
|
||||
"Define 'dependent-build-platforms' in the kind config.".format(key))
|
||||
|
||||
if len(matches) > 1:
|
||||
raise Exception("More than one build platform found for '{}'.".format(key))
|
||||
|
||||
label = matches[0]['label']
|
||||
target = matches[0]['target-name']
|
||||
deps = taskdesc.setdefault('dependencies', {})
|
||||
deps.update({'build': label})
|
||||
|
||||
build_artifact = 'public/build/{}'.format(target)
|
||||
installer_url = ARTIFACT_URL.format('<build>', build_artifact)
|
||||
|
||||
env = taskdesc['worker'].setdefault('env', {})
|
||||
env.update({'GECKO_INSTALLER_URL': {'task-reference': installer_url}})
|
||||
|
||||
|
||||
def support_vcs_checkout(config, job, taskdesc):
|
||||
"""Update a job/task with parameters to enable a VCS checkout.
|
||||
|
||||
|
@ -20,12 +20,6 @@ mach_schema = Schema({
|
||||
|
||||
# The mach command (omitting `./mach`) to run
|
||||
Required('mach'): basestring,
|
||||
|
||||
# Whether the job requires a build artifact or not. If True, the task
|
||||
# will depend on a build task and run-task will download and set up the
|
||||
# installer. Build labels are determined by the `dependent-build-platforms`
|
||||
# config in kind.yml.
|
||||
Required('requires-build', default=False): bool,
|
||||
})
|
||||
|
||||
|
||||
|
@ -439,28 +439,28 @@ def mozharness_test_buildbot_bridge(config, job, taskdesc):
|
||||
if test.get('suite', '') == 'talos':
|
||||
# on linux64-<variant>/<build>, we add the variant to the buildername
|
||||
m = re.match(r'\w+-([^/]+)/.*', test['test-platform'])
|
||||
variant = ''
|
||||
if m and m.group(1):
|
||||
variant = m.group(1) + ' '
|
||||
variant = m.group(1) if m and m.group(1) else ''
|
||||
|
||||
# On beta and release, we run nightly builds on-push; the talos
|
||||
# builders need to run against non-nightly buildernames
|
||||
if variant == 'nightly ':
|
||||
if variant == 'nightly':
|
||||
variant = ''
|
||||
|
||||
# this variant name has branch after the variant type in BBB bug 1338871
|
||||
if variant in ('stylo', 'stylo-sequential'):
|
||||
buildername = '{} {}{} talos {}'.format(
|
||||
BUILDER_NAME_PREFIX[platform],
|
||||
variant,
|
||||
branch,
|
||||
test_name
|
||||
)
|
||||
name = '{prefix} {variant} {branch} talos {test_name}'
|
||||
elif variant:
|
||||
name = '{prefix} {branch} {variant} talos {test_name}'
|
||||
else:
|
||||
buildername = '{} {} {}talos {}'.format(
|
||||
BUILDER_NAME_PREFIX[platform],
|
||||
branch,
|
||||
variant,
|
||||
test_name
|
||||
)
|
||||
name = '{prefix} {branch} talos {test_name}'
|
||||
|
||||
buildername = name.format(
|
||||
prefix=BUILDER_NAME_PREFIX[platform],
|
||||
variant=variant,
|
||||
branch=branch,
|
||||
test_name=test_name
|
||||
)
|
||||
|
||||
if buildername.startswith('Ubuntu'):
|
||||
buildername = buildername.replace('VM', 'HW')
|
||||
else:
|
||||
|
@ -9,10 +9,7 @@ from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
from taskgraph.transforms.job import run_job_using
|
||||
from taskgraph.util.schema import Schema
|
||||
from taskgraph.transforms.job.common import (
|
||||
add_build_dependency,
|
||||
support_vcs_checkout,
|
||||
)
|
||||
from taskgraph.transforms.job.common import support_vcs_checkout
|
||||
from voluptuous import Required, Any
|
||||
|
||||
run_task_schema = Schema({
|
||||
@ -29,12 +26,6 @@ run_task_schema = Schema({
|
||||
# checkout arguments. If a list, it will be passed directly; otherwise
|
||||
# it will be included in a single argument to `bash -cx`.
|
||||
Required('command'): Any([basestring], basestring),
|
||||
|
||||
# Whether the job requires a build artifact or not. If True, the task
|
||||
# will depend on a build task and run-task will download and set up the
|
||||
# installer. Build labels are determined by the `dependent-build-platforms`
|
||||
# config in kind.yml.
|
||||
Required('requires-build', default=False): bool,
|
||||
})
|
||||
|
||||
|
||||
@ -43,9 +34,6 @@ def common_setup(config, job, taskdesc):
|
||||
if run['checkout']:
|
||||
support_vcs_checkout(config, job, taskdesc)
|
||||
|
||||
if run['requires-build']:
|
||||
add_build_dependency(config, job, taskdesc)
|
||||
|
||||
|
||||
@run_job_using("docker-worker", "run-task", schema=run_task_schema)
|
||||
def docker_worker_run_task(config, job, taskdesc):
|
||||
|
@ -13,6 +13,7 @@ import copy
|
||||
|
||||
from taskgraph.transforms.base import TransformSequence
|
||||
from taskgraph.transforms.job import job_description_schema
|
||||
from taskgraph.util.attributes import keymatch
|
||||
from taskgraph.util.schema import (
|
||||
validate_schema,
|
||||
resolve_keyed_by,
|
||||
@ -24,6 +25,8 @@ from voluptuous import (
|
||||
Schema,
|
||||
)
|
||||
|
||||
ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}'
|
||||
|
||||
job_description_schema = {str(k): v for k, v in job_description_schema.schema.iteritems()}
|
||||
|
||||
source_test_description_schema = Schema({
|
||||
@ -36,6 +39,12 @@ source_test_description_schema = Schema({
|
||||
# the job will be "split" into multiple tasks, one with each platform.
|
||||
Required('platform'): Any(basestring, [basestring]),
|
||||
|
||||
# Whether the job requires a build artifact or not. If True, the task will
|
||||
# depend on a build task and the installer url will be saved to the
|
||||
# GECKO_INSTALLER_URL environment variable. Build labels are determined by the
|
||||
# `dependent-build-platforms` config in kind.yml.
|
||||
Required('require-build', default=False): bool,
|
||||
|
||||
# These fields can be keyed by "platform", and are otherwise identical to
|
||||
# job descriptions.
|
||||
Required('worker-type'): Any(
|
||||
@ -83,6 +92,32 @@ def expand_platforms(config, jobs):
|
||||
yield pjob
|
||||
|
||||
|
||||
def add_build_dependency(config, job):
|
||||
"""
|
||||
Add build dependency to the job and installer_url to env.
|
||||
"""
|
||||
key = job['platform']
|
||||
build_labels = config.config.get('dependent-build-platforms', {})
|
||||
matches = keymatch(build_labels, key)
|
||||
if not matches:
|
||||
raise Exception("No build platform found for '{}'. "
|
||||
"Define 'dependent-build-platforms' in the kind config.".format(key))
|
||||
|
||||
if len(matches) > 1:
|
||||
raise Exception("More than one build platform found for '{}'.".format(key))
|
||||
|
||||
label = matches[0]['label']
|
||||
target = matches[0]['target-name']
|
||||
deps = job.setdefault('dependencies', {})
|
||||
deps.update({'build': label})
|
||||
|
||||
build_artifact = 'public/build/{}'.format(target)
|
||||
installer_url = ARTIFACT_URL.format('<build>', build_artifact)
|
||||
|
||||
env = job['worker'].setdefault('env', {})
|
||||
env.update({'GECKO_INSTALLER_URL': {'task-reference': installer_url}})
|
||||
|
||||
|
||||
@transforms.add
|
||||
def handle_platform(config, jobs):
|
||||
"""
|
||||
@ -103,5 +138,8 @@ def handle_platform(config, jobs):
|
||||
if 'treeherder' in job:
|
||||
job['treeherder']['platform'] = platform
|
||||
|
||||
if job.pop('require-build'):
|
||||
add_build_dependency(config, job)
|
||||
|
||||
del job['platform']
|
||||
yield job
|
||||
|
@ -1,4 +1,4 @@
|
||||
# geckodriver [![Build Status](https://travis-ci.org/mozilla/geckodriver.svg?branch=master)](https://travis-ci.org/mozilla/geckodriver)
|
||||
# geckodriver [![Build Status](https://travis-ci.org/mozilla/geckodriver.svg?branch=release)](https://travis-ci.org/mozilla/geckodriver)
|
||||
|
||||
Proxy for using W3C WebDriver-compatible clients
|
||||
to interact with Gecko-based browsers.
|
||||
|
@ -269,23 +269,6 @@ class CommonTestCase(unittest.TestCase):
|
||||
pass
|
||||
self.marionette = None
|
||||
|
||||
def setup_SpecialPowers_observer(self):
|
||||
self.marionette.set_context("chrome")
|
||||
self.marionette.execute_script("""
|
||||
let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
|
||||
Components.utils.import("resource://gre/modules/Preferences.jsm");
|
||||
Preferences.set(SECURITY_PREF, true);
|
||||
|
||||
if (!testUtils.hasOwnProperty("specialPowersObserver")) {
|
||||
let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Components.interfaces.mozIJSSubScriptLoader);
|
||||
loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.jsm",
|
||||
testUtils);
|
||||
testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver();
|
||||
testUtils.specialPowersObserver.init();
|
||||
}
|
||||
""")
|
||||
|
||||
|
||||
class MarionetteTestCase(CommonTestCase):
|
||||
|
||||
|
@ -765,24 +765,19 @@ class MochitestArguments(ArgumentContainer):
|
||||
"data." % options.jscov_dir_prefix)
|
||||
|
||||
if options.testingModulesDir is None:
|
||||
# Try to guess the testing modules directory.
|
||||
possible = [os.path.join(here, os.path.pardir, 'modules')]
|
||||
if build_obj:
|
||||
options.testingModulesDir = os.path.join(
|
||||
build_obj.topobjdir, '_tests', 'modules')
|
||||
else:
|
||||
# Try to guess the testing modules directory.
|
||||
# This somewhat grotesque hack allows the buildbot machines to find the
|
||||
# modules directory without having to configure the buildbot hosts. This
|
||||
# code should never be executed in local runs because the build system
|
||||
# should always set the flag that populates this variable. If buildbot ever
|
||||
# passes this argument, this code can be deleted.
|
||||
possible = os.path.join(here, os.path.pardir, 'modules')
|
||||
possible.insert(0, os.path.join(build_obj.topobjdir, '_tests', 'modules'))
|
||||
|
||||
if os.path.isdir(possible):
|
||||
options.testingModulesDir = possible
|
||||
for p in possible:
|
||||
if os.path.isdir(p):
|
||||
options.testingModulesDir = p
|
||||
break
|
||||
|
||||
if build_obj:
|
||||
plugins_dir = os.path.join(build_obj.distdir, 'plugins')
|
||||
if plugins_dir not in options.extraProfileFiles:
|
||||
if os.path.isdir(plugins_dir) and plugins_dir not in options.extraProfileFiles:
|
||||
options.extraProfileFiles.append(plugins_dir)
|
||||
|
||||
# Even if buildbot is updated, we still want this, as the path we pass in
|
||||
|
@ -799,7 +799,6 @@ class MochitestDesktop(object):
|
||||
TEST_PATH = "tests"
|
||||
NESTED_OOP_TEST_PATH = "nested_oop"
|
||||
CHROME_PATH = "redirect.html"
|
||||
log = None
|
||||
|
||||
certdbNew = False
|
||||
sslTunnel = None
|
||||
@ -829,16 +828,10 @@ class MochitestDesktop(object):
|
||||
self.start_script_kwargs = {}
|
||||
self.urlOpts = []
|
||||
|
||||
if self.log is None:
|
||||
commandline.log_formatters["tbpl"] = (
|
||||
MochitestFormatter,
|
||||
"Mochitest specific tbpl formatter")
|
||||
self.log = commandline.setup_logging("mochitest",
|
||||
logger_options,
|
||||
{
|
||||
"tbpl": sys.stdout
|
||||
})
|
||||
MochitestDesktop.log = self.log
|
||||
commandline.log_formatters["tbpl"] = (
|
||||
MochitestFormatter,
|
||||
"Mochitest specific tbpl formatter")
|
||||
self.log = commandline.setup_logging("mochitest", logger_options, {"tbpl": sys.stdout})
|
||||
|
||||
# Jetpack flavors still don't use the structured logger. We need to process their output
|
||||
# slightly differently.
|
||||
|
178
testing/mochitest/tests/python/conftest.py
Normal file
178
testing/mochitest/tests/python/conftest.py
Normal file
@ -0,0 +1,178 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
from cStringIO import StringIO
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
import mozfile
|
||||
import mozinstall
|
||||
from manifestparser import TestManifest
|
||||
from mozbuild.base import MozbuildObject
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
build = MozbuildObject.from_environment(cwd=here)
|
||||
|
||||
HARNESS_ROOT_NOT_FOUND = """
|
||||
Could not find test harness root. Either a build or the 'GECKO_INSTALLER_URL'
|
||||
environment variable is required.
|
||||
""".lstrip()
|
||||
|
||||
|
||||
def filter_action(action, lines):
|
||||
return filter(lambda x: x['action'] == action, lines)
|
||||
|
||||
|
||||
def _get_harness_root():
|
||||
# Check if there is a local build
|
||||
harness_root = os.path.join(build.topobjdir, '_tests', 'testing', 'mochitest')
|
||||
if os.path.isdir(harness_root):
|
||||
return harness_root
|
||||
|
||||
# Check if it was previously set up by another test
|
||||
harness_root = os.path.join(os.environ['PYTHON_TEST_TMP'], 'tests', 'mochitest')
|
||||
if os.path.isdir(harness_root):
|
||||
return harness_root
|
||||
|
||||
# Check if there is a test package to download
|
||||
if 'GECKO_INSTALLER_URL' in os.environ:
|
||||
base_url = os.environ['GECKO_INSTALLER_URL'].rsplit('/', 1)[0]
|
||||
test_packages = requests.get(base_url + '/target.test_packages.json').json()
|
||||
|
||||
dest = os.path.join(os.environ['PYTHON_TEST_TMP'], 'tests')
|
||||
for name in test_packages['mochitest']:
|
||||
url = base_url + '/' + name
|
||||
bundle = os.path.join(os.environ['PYTHON_TEST_TMP'], name)
|
||||
|
||||
r = requests.get(url, stream=True)
|
||||
with open(bundle, 'w+b') as fh:
|
||||
for chunk in r.iter_content(chunk_size=1024):
|
||||
fh.write(chunk)
|
||||
|
||||
mozfile.extract(bundle, dest)
|
||||
|
||||
return os.path.join(dest, 'mochitest')
|
||||
|
||||
# Couldn't find a harness root, let caller do error handling.
|
||||
return None
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def setup_harness_root():
|
||||
harness_root = _get_harness_root()
|
||||
if harness_root:
|
||||
sys.path.insert(0, harness_root)
|
||||
|
||||
# Link the test files to the test package so updates are automatically
|
||||
# picked up. Fallback to copy on Windows.
|
||||
test_root = os.path.join(harness_root, 'tests', 'selftests')
|
||||
if not os.path.exists(test_root):
|
||||
files = os.path.join(here, 'files')
|
||||
if hasattr(os, 'symlink'):
|
||||
os.symlink(files, test_root)
|
||||
else:
|
||||
shutil.copytree(files, test_root)
|
||||
|
||||
elif 'GECKO_INSTALLER_URL' in os.environ:
|
||||
# The mochitest tests will run regardless of whether a build exists or not.
|
||||
# In a local environment, they should simply be skipped if setup fails. But
|
||||
# in automation, we'll need to make sure an error is propagated up.
|
||||
pytest.fail(HARNESS_ROOT_NOT_FOUND)
|
||||
else:
|
||||
# Tests will be marked skipped by the calls to pytest.importorskip() below.
|
||||
# We are purposefully not failing here because running |mach python-test|
|
||||
# without a build is a perfectly valid use case.
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def binary():
|
||||
try:
|
||||
return build.get_binary_path()
|
||||
except:
|
||||
pass
|
||||
|
||||
app = 'firefox'
|
||||
bindir = os.path.join(os.environ['PYTHON_TEST_TMP'], app)
|
||||
if os.path.isdir(bindir):
|
||||
try:
|
||||
return mozinstall.get_binary(bindir, app_name=app)
|
||||
except:
|
||||
pass
|
||||
|
||||
if 'GECKO_INSTALLER_URL' in os.environ:
|
||||
bindir = mozinstall.install(
|
||||
os.environ['GECKO_INSTALLER_URL'], os.environ['PYTHON_TEST_TMP'])
|
||||
return mozinstall.get_binary(bindir, app_name='firefox')
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def parser(request):
|
||||
parser = pytest.importorskip('mochitest_options')
|
||||
|
||||
app = getattr(request.module, 'APP', 'generic')
|
||||
return parser.MochitestArgumentParser(app=app)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def runtests(setup_harness_root, binary, parser, request):
|
||||
"""Creates an easy to use entry point into the mochitest harness.
|
||||
|
||||
:returns: A function with the signature `*tests, **opts`. Each test is a file name
|
||||
(relative to the `files` dir). At least one is required. The opts are
|
||||
used to override the default mochitest options, they are optional.
|
||||
"""
|
||||
runtests = pytest.importorskip('runtests')
|
||||
|
||||
mochitest_root = runtests.SCRIPT_DIR
|
||||
test_root = os.path.join(mochitest_root, 'tests', 'selftests')
|
||||
|
||||
buf = StringIO()
|
||||
options = vars(parser.parse_args([]))
|
||||
options.update({
|
||||
'app': binary,
|
||||
'keep_open': False,
|
||||
'log_raw': [buf],
|
||||
})
|
||||
|
||||
if not os.path.isdir(runtests.build_obj.bindir):
|
||||
package_root = os.path.dirname(mochitest_root)
|
||||
options.update({
|
||||
'certPath': os.path.join(package_root, 'certs'),
|
||||
'utilityPath': os.path.join(package_root, 'bin'),
|
||||
})
|
||||
options['extraProfileFiles'].append(os.path.join(package_root, 'bin', 'plugins'))
|
||||
|
||||
options.update(getattr(request.module, 'OPTIONS', {}))
|
||||
|
||||
def normalize(test):
|
||||
return {
|
||||
'name': test,
|
||||
'relpath': test,
|
||||
'path': os.path.join(test_root, test),
|
||||
# add a dummy manifest file because mochitest expects it
|
||||
'manifest': os.path.join(test_root, 'mochitest.ini'),
|
||||
}
|
||||
|
||||
def inner(*tests, **opts):
|
||||
assert len(tests) > 0
|
||||
|
||||
manifest = TestManifest()
|
||||
manifest.tests.extend(map(normalize, tests))
|
||||
options['manifestFile'] = manifest
|
||||
options.update(opts)
|
||||
|
||||
result = runtests.run_test_harness(parser, Namespace(**options))
|
||||
out = json.loads('[' + ','.join(buf.getvalue().splitlines()) + ']')
|
||||
buf.close()
|
||||
return result, out
|
||||
return inner
|
24
testing/mochitest/tests/python/files/test_fail.html
Normal file
24
testing/mochitest/tests/python/files/test_fail.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1343659
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test Fail</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
ok(false, "Test is ok");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1343659">Mozilla Bug 1343659</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
24
testing/mochitest/tests/python/files/test_pass.html
Normal file
24
testing/mochitest/tests/python/files/test_pass.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1343659
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test Pass</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
ok(true, "Test is ok");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1343659">Mozilla Bug 1343659</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
5
testing/mochitest/tests/python/python.ini
Normal file
5
testing/mochitest/tests/python/python.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
subsuite = mochitest
|
||||
sequential = true
|
||||
|
||||
[test_basic_mochitest_plain.py]
|
73
testing/mochitest/tests/python/test_basic_mochitest_plain.py
Normal file
73
testing/mochitest/tests/python/test_basic_mochitest_plain.py
Normal file
@ -0,0 +1,73 @@
|
||||
# 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/.
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from conftest import build, filter_action
|
||||
|
||||
sys.path.insert(0, os.path.join(build.topsrcdir, 'testing', 'mozharness'))
|
||||
from mozharness.base.log import INFO, WARNING
|
||||
from mozharness.base.errors import BaseErrorList
|
||||
from mozharness.mozilla.buildbot import TBPL_SUCCESS, TBPL_WARNING
|
||||
from mozharness.mozilla.structuredlog import StructuredOutputParser
|
||||
from mozharness.mozilla.testing.errors import HarnessErrorList
|
||||
|
||||
|
||||
def get_mozharness_status(lines, status):
|
||||
parser = StructuredOutputParser(
|
||||
config={'log_level': INFO},
|
||||
error_list=BaseErrorList+HarnessErrorList,
|
||||
strict=False,
|
||||
suite_category='mochitest',
|
||||
)
|
||||
|
||||
for line in lines:
|
||||
parser.parse_single_line(json.dumps(line))
|
||||
return parser.evaluate_parser(status)
|
||||
|
||||
|
||||
def test_output_pass(runtests):
|
||||
status, lines = runtests('test_pass.html')
|
||||
assert status == 0
|
||||
|
||||
tbpl_status, log_level = get_mozharness_status(lines, status)
|
||||
assert tbpl_status == TBPL_SUCCESS
|
||||
assert log_level == WARNING
|
||||
|
||||
lines = filter_action('test_status', lines)
|
||||
assert len(lines) == 1
|
||||
assert lines[0]['status'] == 'PASS'
|
||||
|
||||
|
||||
def test_output_fail(runtests):
|
||||
from runtests import build_obj
|
||||
|
||||
status, lines = runtests('test_fail.html')
|
||||
assert status == 1
|
||||
|
||||
tbpl_status, log_level = get_mozharness_status(lines, status)
|
||||
assert tbpl_status == TBPL_WARNING
|
||||
assert log_level == WARNING
|
||||
|
||||
lines = filter_action('test_status', lines)
|
||||
|
||||
# If we are running with a build_obj, the failed status will be
|
||||
# logged a second time at the end of the run.
|
||||
if build_obj:
|
||||
assert len(lines) == 2
|
||||
else:
|
||||
assert len(lines) == 1
|
||||
assert lines[0]['status'] == 'FAIL'
|
||||
|
||||
if build_obj:
|
||||
assert set(lines[0].keys()) == set(lines[1].keys())
|
||||
assert set(lines[0].values()) == set(lines[1].values())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main(['--verbose', __file__]))
|
@ -8,9 +8,12 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
import requests
|
||||
|
||||
import mozfile
|
||||
import mozinfo
|
||||
|
||||
@ -96,11 +99,20 @@ def install(src, dest):
|
||||
:param dest: Path to install to (to ensure we do not overwrite any existent
|
||||
files the folder should not exist yet)
|
||||
"""
|
||||
src = os.path.realpath(src)
|
||||
dest = os.path.realpath(dest)
|
||||
|
||||
if not is_installer(src):
|
||||
raise InvalidSource(src + ' is not valid installer file.')
|
||||
msg = "{} is not a valid installer file".format(src)
|
||||
if '://' in src:
|
||||
try:
|
||||
return _install_url(src, dest)
|
||||
except:
|
||||
exc, val, tb = sys.exc_info()
|
||||
msg = "{} ({})".format(msg, val)
|
||||
raise InvalidSource, msg, tb
|
||||
raise InvalidSource(msg)
|
||||
|
||||
src = os.path.realpath(src)
|
||||
dest = os.path.realpath(dest)
|
||||
|
||||
did_we_create = False
|
||||
if not os.path.exists(dest):
|
||||
@ -237,6 +249,26 @@ def uninstall(install_folder):
|
||||
mozfile.remove(install_folder)
|
||||
|
||||
|
||||
def _install_url(url, dest):
|
||||
"""Saves a url to a temporary file, and passes that through to the
|
||||
install function.
|
||||
|
||||
:param url: Url to the install file
|
||||
:param dest: Path to install to (to ensure we do not overwrite any existent
|
||||
files the folder should not exist yet)
|
||||
"""
|
||||
r = requests.get(url, stream=True)
|
||||
name = tempfile.mkstemp()[1]
|
||||
try:
|
||||
with open(name, 'w+b') as fh:
|
||||
for chunk in r.iter_content(chunk_size=16*1024):
|
||||
fh.write(chunk)
|
||||
result = install(name, dest)
|
||||
finally:
|
||||
mozfile.remove(name)
|
||||
return result
|
||||
|
||||
|
||||
def _install_dmg(src, dest):
|
||||
"""Extract a dmg file into the destination folder and return the
|
||||
application folder.
|
||||
|
@ -11,10 +11,11 @@ try:
|
||||
except IOError:
|
||||
description = None
|
||||
|
||||
PACKAGE_VERSION = '1.12'
|
||||
PACKAGE_VERSION = '1.13'
|
||||
|
||||
deps = ['mozinfo >= 0.7',
|
||||
'mozfile >= 1.0',
|
||||
'requests',
|
||||
]
|
||||
|
||||
setup(name='mozInstall',
|
||||
|
@ -113,6 +113,10 @@ class TestMozInstall(unittest.TestCase):
|
||||
self.assertRaises(mozinstall.InvalidSource, mozinstall.install,
|
||||
self.bz2, 'firefox')
|
||||
|
||||
# Test an invalid url handler
|
||||
self.assertRaises(mozinstall.InvalidSource, mozinstall.install,
|
||||
'file://foo.bar', 'firefox')
|
||||
|
||||
@unittest.skipIf(mozinfo.isWin, "Bug 1157352 - We need a new firefox.exe "
|
||||
"for mozinstall 1.12 and higher.")
|
||||
def test_install(self):
|
||||
@ -167,5 +171,6 @@ class TestMozInstall(unittest.TestCase):
|
||||
mozinstall.uninstall(installdir)
|
||||
self.assertFalse(os.path.exists(installdir))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
mozunit.main()
|
||||
|
@ -214,6 +214,9 @@ def setup_logging(logger, args, defaults=None, formatter_defaults=None,
|
||||
|
||||
if not isinstance(logger, StructuredLogger):
|
||||
logger = StructuredLogger(logger)
|
||||
# The likely intent when using this function is to get a brand new
|
||||
# logger, so reset state in case it was previously initialized.
|
||||
logger.reset_state()
|
||||
|
||||
# Keep track of any options passed for formatters.
|
||||
formatter_options = {}
|
||||
|
@ -111,6 +111,9 @@ def log_actions():
|
||||
class LoggerState(object):
|
||||
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.handlers = []
|
||||
self.running_tests = set()
|
||||
self.suite_started = False
|
||||
@ -154,6 +157,13 @@ class StructuredLogger(object):
|
||||
"""Remove a handler from the current logger"""
|
||||
self._state.handlers.remove(handler)
|
||||
|
||||
def reset_state(self):
|
||||
"""Resets the logger to a brand new state. This means all handlers
|
||||
are removed, running tests are discarded and components are reset.
|
||||
"""
|
||||
self._state.reset()
|
||||
self._component_state = self._state.component_states[self.component] = ComponentState()
|
||||
|
||||
def send_message(self, topic, command, *args):
|
||||
"""Send a message to each handler configured for this logger. This
|
||||
part of the api is useful to those users requiring dynamic control
|
||||
|
@ -180,8 +180,8 @@ class LocalesMixin(ChunkingMixin):
|
||||
dirs['abs_locales_src_dir'] = os.path.join(dirs['abs_mozilla_dir'],
|
||||
c['locales_dir'])
|
||||
dirs['abs_compare_locales_dir'] = os.path.join(dirs['abs_mozilla_dir'],
|
||||
'python', 'compare-locales',
|
||||
'compare_locales')
|
||||
'third_party', 'python',
|
||||
'compare-locales', 'compare_locales')
|
||||
else:
|
||||
# Use old-compare-locales if no mozilla_dir set, needed
|
||||
# for clobberer, and existing mozharness tests.
|
||||
|
@ -904,6 +904,9 @@ class ProcessHandler(ProcessHandlerMixin):
|
||||
def __init__(self, cmd, logfile=None, storeOutput=True, **kwargs):
|
||||
kwargs.setdefault('processOutputLine', [])
|
||||
|
||||
if not isinstance(kwargs['processOutputLine'], (list, tuple)):
|
||||
kwargs['processOutputLine'] = [kwargs['processOutputLine']]
|
||||
|
||||
# Print to standard output only if no outputline provided
|
||||
if not kwargs['processOutputLine']:
|
||||
kwargs['processOutputLine'].append(print_output)
|
||||
|
@ -873,10 +873,6 @@ this.Extension = class extends ExtensionData {
|
||||
return Promise.reject({errors: this.errors});
|
||||
}
|
||||
|
||||
if (AppConstants.RELEASE_OR_BETA) {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
// Load Experiments APIs that this extension depends on.
|
||||
return Promise.all(
|
||||
Array.from(this.apiNames, api => ExtensionAPIs.load(api))
|
||||
|
@ -24,6 +24,7 @@ this.runtime = class extends ExtensionAPI {
|
||||
|
||||
sendMessage(...args) {
|
||||
let extensionId, message, options, responseCallback;
|
||||
|
||||
if (typeof args[args.length - 1] === "function") {
|
||||
responseCallback = args.pop();
|
||||
}
|
||||
@ -55,18 +56,18 @@ this.runtime = class extends ExtensionAPI {
|
||||
message = args[0];
|
||||
} else if (args.length === 2) {
|
||||
// With two optional arguments, this is the ambiguous case,
|
||||
// particularly sendMessage("string", {});
|
||||
// particularly sendMessage("string", {} or null)
|
||||
// Given that sending a message within the extension is generally
|
||||
// more common than sending the empty object to another extension,
|
||||
// we prefer that conclusion, as long as the second argument looks
|
||||
// like valid options.
|
||||
// like valid options object, or is null/undefined.
|
||||
let [validOpts] = checkOptions(args[1]);
|
||||
if (validOpts) {
|
||||
if (validOpts || args[1] == null) {
|
||||
[message, options] = args;
|
||||
} else {
|
||||
[extensionId, message] = args;
|
||||
}
|
||||
} else if (args.length === 3) {
|
||||
} else if (args.length === 3 || (args.length === 4 && args[3] == null)) {
|
||||
[extensionId, message, options] = args;
|
||||
} else if (args.length === 4 && !responseCallback) {
|
||||
return Promise.reject({message: "runtime.sendMessage's last argument is not a function"});
|
||||
|
@ -59,6 +59,10 @@ add_task(async function() {
|
||||
// Due to this insane design we parse its arguments manually. This
|
||||
// test is meant to cover all the combinations.
|
||||
|
||||
// A single null or undefined argument is allowed, and represents the message
|
||||
extension1.sendMessage(null);
|
||||
await checkLocalMessage(null);
|
||||
|
||||
// With one argument, it must be just the message
|
||||
extension1.sendMessage("message");
|
||||
await checkLocalMessage("message");
|
||||
@ -70,13 +74,25 @@ add_task(async function() {
|
||||
extension1.sendMessage(ID2, {msg: "message"});
|
||||
await checkRemoteMessage({msg: "message"});
|
||||
|
||||
// And this case should be (message, options)
|
||||
// And these should be (message, options)
|
||||
extension1.sendMessage("message", {});
|
||||
await checkLocalMessage("message");
|
||||
|
||||
// or (message, non-callback), pick your poison
|
||||
extension1.sendMessage("message", undefined);
|
||||
await checkLocalMessage("message");
|
||||
|
||||
// With three arguments, we send a cross-extension message
|
||||
extension1.sendMessage(ID2, "message", {});
|
||||
await checkRemoteMessage("message");
|
||||
|
||||
// Even when the last one is null or undefined
|
||||
extension1.sendMessage(ID2, "message", undefined);
|
||||
await checkRemoteMessage("message");
|
||||
|
||||
// The four params case is unambigous, so we allow null as a (non-) callback
|
||||
extension1.sendMessage(ID2, "message", {}, null);
|
||||
await checkRemoteMessage("message");
|
||||
|
||||
await Promise.all([extension1.unload(), extension2.unload()]);
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ add_task(async function test_sendMessage_error() {
|
||||
let testCases = [
|
||||
// [arguments, expected error string],
|
||||
[[], "runtime.sendMessage's message argument is missing"],
|
||||
[[null, null, null, null], "runtime.sendMessage's last argument is not a function"],
|
||||
[[null, null, null, 42], "runtime.sendMessage's last argument is not a function"],
|
||||
[[null, null, 1], "runtime.sendMessage's options argument is invalid"],
|
||||
[[1, null, null], "runtime.sendMessage's extensionId argument is invalid"],
|
||||
[[null, null, null, null, null], "runtime.sendMessage received too many arguments"],
|
||||
|
@ -8,10 +8,18 @@
|
||||
.datetime-input-box-wrapper {
|
||||
-moz-appearance: none;
|
||||
display: inline-flex;
|
||||
flex: 1;
|
||||
cursor: default;
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
font-family: monospace;
|
||||
min-width: 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.datetime-input-edit-wrapper {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.datetime-edit-field {
|
||||
@ -48,5 +56,5 @@
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
align-self: center;
|
||||
justify-content: flex-end;
|
||||
flex: none;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user