Merge autoland to central, a=merge

MozReview-Commit-ID: 1k7iJedKGDR
This commit is contained in:
Wes Kocher 2017-06-09 15:48:12 -07:00
commit b66d50d0ca
104 changed files with 2976 additions and 1717 deletions

View File

@ -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
View 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"

View 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/**"

View 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/**"

View File

@ -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

View File

@ -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;

View 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

View 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

View 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

View 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

View 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'

View File

@ -50,6 +50,7 @@ DIRS += [
'cache',
'canvas',
'commandhandler',
'credentialmanagement',
'crypto',
'encoding',
'events',

View 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

View 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

View 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

View 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

View 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

View File

@ -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

View File

@ -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 {

View 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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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();

View File

@ -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',

View File

@ -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>

View File

@ -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);

View File

@ -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>

View File

@ -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();

View File

@ -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>

View 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;
};

View File

@ -371,5 +371,5 @@ interface NavigatorConcurrentHardware {
partial interface Navigator {
[Pref="security.webauth.webauthn", SameObject]
readonly attribute WebAuthentication authentication;
readonly attribute CredentialsContainer credentials;
};

View File

@ -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
);
};

View File

@ -444,6 +444,7 @@ WEBIDL_FILES = [
'ConvolverNode.webidl',
'Coordinates.webidl',
'CreateOfferRequest.webidl',
'CredentialManagement.webidl',
'Crypto.webidl',
'CSPDictionaries.webidl',
'CSPReport.webidl',

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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%);
}

View File

@ -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"

View File

@ -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)

View 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"

View File

@ -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");
}
}

View File

@ -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 += [

View File

@ -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):

View File

@ -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)

View File

@ -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)">

View File

@ -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;

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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;

View File

@ -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"

View File

@ -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")}

View File

@ -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>

View File

@ -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)")}

View File

@ -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

View File

@ -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) {

View File

@ -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:

View File

@ -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.

View File

@ -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,
})

View File

@ -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:

View File

@ -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):

View File

@ -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

View File

@ -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.

View File

@ -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):

View File

@ -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

View File

@ -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.

View 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

View 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>

View 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>

View File

@ -0,0 +1,5 @@
[DEFAULT]
subsuite = mochitest
sequential = true
[test_basic_mochitest_plain.py]

View 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__]))

View 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.

View File

@ -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',

View File

@ -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()

View File

@ -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 = {}

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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))

View File

@ -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"});

View File

@ -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()]);
});

View File

@ -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"],

View File

@ -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