Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2016-02-18 14:04:37 +01:00
Родитель fb6357bbdd 65589f686c
Коммит 5a7bf7c7b3
403 изменённых файлов: 8022 добавлений и 3541 удалений

Просмотреть файл

@ -5,7 +5,7 @@
MOZ_APP_BASENAME=B2G
MOZ_APP_VENDOR=Mozilla
MOZ_APP_VERSION=46.0a1
MOZ_APP_VERSION=$FIREFOX_VERSION
MOZ_APP_UA_NAME=Firefox
MOZ_UA_OS_AGNOSTIC=1

Просмотреть файл

@ -279,6 +279,7 @@ function prompt(aBrowser, aRequest) {
requestTypes: requestTypes} = aRequest;
let uri = Services.io.newURI(aRequest.documentURI, null, null);
let host = getHost(uri);
let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
let chromeDoc = aBrowser.ownerDocument;
let chromeWin = chromeDoc.defaultView;
let stringBundle = chromeWin.gNavigatorBundle;
@ -374,7 +375,16 @@ function prompt(aBrowser, aRequest) {
if (micPerm == perms.PROMPT_ACTION)
micPerm = perms.UNKNOWN_ACTION;
let camPermanentPerm = perms.testExactPermanentPermission(principal, "camera");
let camPerm = perms.testExactPermission(uri, "camera");
// Session approval given but never used to allocate a camera, remove
// and ask again
if (camPerm && !camPermanentPerm) {
perms.remove(uri, "camera");
camPerm = perms.UNKNOWN_ACTION;
}
if (camPerm == perms.PROMPT_ACTION)
camPerm = perms.UNKNOWN_ACTION;
@ -515,11 +525,14 @@ function prompt(aBrowser, aRequest) {
let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
let videoDeviceIndex = chromeDoc.getElementById(listId).value;
let allowCamera = videoDeviceIndex != "-1";
if (allowCamera)
if (allowCamera) {
allowedDevices.push(videoDeviceIndex);
if (aRemember) {
perms.add(uri, "camera",
allowCamera ? perms.ALLOW_ACTION : perms.DENY_ACTION);
// Session permission will be removed after use
// (it's really one-shot, not for the entire session)
perms.add(uri, "camera", perms.ALLOW_ACTION,
aRemember ? perms.EXPIRE_NEVER : perms.EXPIRE_SESSION);
} else if (aRemember) {
perms.add(uri, "camera", perms.DENY_ACTION);
}
}
if (audioDevices.length) {

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Element.h"
#include "mozilla/RefPtr.h"
#include "nsCSSPseudoElements.h"
#include "nsWrapperCache.h"

Просмотреть файл

@ -23,16 +23,17 @@ FormData::FormData(nsISupports* aOwner)
namespace {
already_AddRefed<Blob>
GetBlobForFormDataStorage(Blob& aBlob, const Optional<nsAString>& aFilename,
ErrorResult& aRv)
already_AddRefed<File>
GetOrCreateFileCalledBlob(Blob& aBlob, ErrorResult& aRv)
{
if (!aFilename.WasPassed()) {
RefPtr<Blob> blob = &aBlob;
return blob.forget();
// If this is file, we can just use it
RefPtr<File> file = aBlob.ToFile();
if (file) {
return file.forget();
}
RefPtr<File> file = aBlob.ToFile(aFilename.Value(), aRv);
// Forcing 'blob' as filename
file = aBlob.ToFile(NS_LITERAL_STRING("blob"), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -40,6 +41,23 @@ GetBlobForFormDataStorage(Blob& aBlob, const Optional<nsAString>& aFilename,
return file.forget();
}
already_AddRefed<File>
GetBlobForFormDataStorage(Blob& aBlob, const Optional<nsAString>& aFilename,
ErrorResult& aRv)
{
// Forcing a filename
if (aFilename.WasPassed()) {
RefPtr<File> file = aBlob.ToFile(aFilename.Value(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return file.forget();
}
return GetOrCreateFileCalledBlob(aBlob, aRv);
}
} // namespace
// -------------------------------------------------------------------------
@ -102,12 +120,12 @@ FormData::Append(const nsAString& aName, Blob& aBlob,
const Optional<nsAString>& aFilename,
ErrorResult& aRv)
{
RefPtr<Blob> blob = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
AddNameBlobPair(aName, blob);
AddNameBlobPair(aName, file);
}
void
@ -165,8 +183,14 @@ FormData::AddNameBlobPair(const nsAString& aName, Blob* aBlob)
{
MOZ_ASSERT(aBlob);
ErrorResult rv;
RefPtr<File> file = GetOrCreateFileCalledBlob(*aBlob, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
FormDataTuple* data = mFormData.AppendElement();
SetNameBlobPair(data, aName, aBlob);
SetNameFilePair(data, aName, file);
return NS_OK;
}
@ -199,12 +223,12 @@ FormData::Set(const nsAString& aName, Blob& aBlob,
{
FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
if (tuple) {
RefPtr<Blob> blob = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
SetNameBlobPair(tuple, aName, blob);
SetNameFilePair(tuple, aName, file);
} else {
Append(aName, aBlob, aFilename, aRv);
}
@ -253,15 +277,15 @@ FormData::SetNameValuePair(FormDataTuple* aData,
}
void
FormData::SetNameBlobPair(FormDataTuple* aData,
FormData::SetNameFilePair(FormDataTuple* aData,
const nsAString& aName,
Blob* aBlob)
File* aFile)
{
MOZ_ASSERT(aData);
MOZ_ASSERT(aBlob);
MOZ_ASSERT(aFile);
aData->name = aName;
aData->value.SetAsBlob() = aBlob;
aData->value.SetAsBlob() = aFile;
}
// -------------------------------------------------------------------------

Просмотреть файл

@ -47,9 +47,9 @@ private:
const nsAString& aName,
const nsAString& aValue);
void SetNameBlobPair(FormDataTuple* aData,
void SetNameFilePair(FormDataTuple* aData,
const nsAString& aName,
Blob* aBlob);
File* aFile);
public:
explicit FormData(nsISupports* aOwner = nullptr);

Просмотреть файл

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageEncoder.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
@ -491,6 +490,38 @@ ImageEncoder::GetImageEncoder(nsAString& aType)
return encoder.forget();
}
class EncoderThreadPoolTerminator final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_IMETHODIMP Observe(nsISupports *, const char *topic, const char16_t *) override
{
NS_ASSERTION(!strcmp(topic, "xpcom-shutdown-threads"),
"Unexpected topic");
if (ImageEncoder::sThreadPool) {
ImageEncoder::sThreadPool->Shutdown();
ImageEncoder::sThreadPool = nullptr;
}
return NS_OK;
}
private:
~EncoderThreadPoolTerminator() {}
};
NS_IMPL_ISUPPORTS(EncoderThreadPoolTerminator, nsIObserver)
static void
RegisterEncoderThreadPoolTerminatorObserver()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
NS_ASSERTION(os, "do_GetService failed");
os->AddObserver(new EncoderThreadPoolTerminator(),
"xpcom-shutdown-threads",
false);
}
/* static */
nsresult
ImageEncoder::EnsureThreadPool()
@ -498,12 +529,13 @@ ImageEncoder::EnsureThreadPool()
if (!sThreadPool) {
nsCOMPtr<nsIThreadPool> threadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
sThreadPool = threadPool;
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
ClearOnShutdown(&sThreadPool);
RegisterEncoderThreadPoolTerminatorObserver();
}));
} else {
ClearOnShutdown(&sThreadPool);
RegisterEncoderThreadPoolTerminatorObserver();
}
const uint32_t kThreadLimit = 2;

Просмотреть файл

@ -114,6 +114,7 @@ private:
static StaticRefPtr<nsIThreadPool> sThreadPool;
friend class EncodingRunnable;
friend class EncoderThreadPoolTerminator;
};
/**

Просмотреть файл

@ -507,8 +507,13 @@ AutoJSAPI::ReportException()
// In this case, we enter the privileged junk scope and don't dispatch any
// error events.
JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx()));
if (!errorGlobal)
errorGlobal = xpc::PrivilegedJunkScope();
if (!errorGlobal) {
if (mIsMainThread) {
errorGlobal = xpc::PrivilegedJunkScope();
} else {
errorGlobal = workers::GetCurrentThreadWorkerGlobal();
}
}
JSAutoCompartment ac(cx(), errorGlobal);
JS::Rooted<JS::Value> exn(cx());
js::ErrorReport jsReport(cx());

Просмотреть файл

@ -436,6 +436,7 @@ LOCAL_INCLUDES += [
'/dom/ipc',
'/dom/storage',
'/dom/svg',
'/dom/u2f',
'/dom/workers',
'/dom/xbl',
'/dom/xml',

Просмотреть файл

@ -226,6 +226,7 @@
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/ServiceWorkerRegistration.h"
#include "mozilla/dom/U2F.h"
#ifdef HAVE_SIDEBAR
#include "mozilla/dom/ExternalBinding.h"
#endif
@ -1885,6 +1886,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport)
@ -1959,6 +1961,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
@ -4348,6 +4351,23 @@ nsGlobalWindow::GetCrypto(ErrorResult& aError)
return mCrypto;
}
mozilla::dom::U2F*
nsGlobalWindow::GetU2f(ErrorResult& aError)
{
MOZ_RELEASE_ASSERT(IsInnerWindow());
if (!mU2F) {
RefPtr<U2F> u2f = new U2F();
u2f->Init(AsInner(), aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
mU2F = u2f;
}
return mU2F;
}
nsIControllers*
nsGlobalWindow::GetControllersOuter(ErrorResult& aError)
{

Просмотреть файл

@ -106,7 +106,6 @@ class Crypto;
class External;
class Function;
class Gamepad;
class VRDevice;
class MediaQueryList;
class MozSelfSupport;
class Navigator;
@ -117,6 +116,8 @@ struct RequestInit;
class RequestOrUSVString;
class Selection;
class SpeechSynthesis;
class U2F;
class VRDevice;
class WakeLock;
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
class WindowOrientationObserver;
@ -1071,6 +1072,7 @@ public:
void SizeToContentOuter(mozilla::ErrorResult& aError, bool aCallerIsChrome);
void SizeToContent(mozilla::ErrorResult& aError);
mozilla::dom::Crypto* GetCrypto(mozilla::ErrorResult& aError);
mozilla::dom::U2F* GetU2f(mozilla::ErrorResult& aError);
nsIControllers* GetControllersOuter(mozilla::ErrorResult& aError);
nsIControllers* GetControllers(mozilla::ErrorResult& aError);
nsresult GetControllers(nsIControllers** aControllers) override;
@ -1766,6 +1768,7 @@ protected:
nsString mDefaultStatus;
RefPtr<nsGlobalWindowObserver> mObserver; // Inner windows only.
RefPtr<mozilla::dom::Crypto> mCrypto;
RefPtr<mozilla::dom::U2F> mU2F;
RefPtr<mozilla::dom::cache::CacheStorage> mCacheStorage;
RefPtr<mozilla::dom::Console> mConsole;
// We need to store an nsISupports pointer to this object because the

Просмотреть файл

@ -2757,22 +2757,27 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
}
// When we are sync loading, we need to bypass the local cache when it would
// otherwise block us waiting for exclusive access to the cache. If we don't
// do this, then we could dead lock in some cases (see bug 309424).
//
// Also don't block on the cache entry on async if it is busy - favoring parallelism
// over cache hit rate for xhr. This does not disable the cache everywhere -
// only in cases where more than one channel for the same URI is accessed
// simultanously.
// Bypass the network cache in cases where it makes no sense:
// POST responses are always unique, and we provide no API that would
// allow our consumers to specify a "cache key" to access old POST
// responses, so they are not worth caching.
if (method.EqualsLiteral("POST")) {
AddLoadFlags(mChannel,
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE |
nsIRequest::INHIBIT_CACHING);
} else {
// When we are sync loading, we need to bypass the local cache when it would
// otherwise block us waiting for exclusive access to the cache. If we don't
// do this, then we could dead lock in some cases (see bug 309424).
//
// Also don't block on the cache entry on async if it is busy - favoring parallelism
// over cache hit rate for xhr. This does not disable the cache everywhere -
// only in cases where more than one channel for the same URI is accessed
// simultanously.
AddLoadFlags(mChannel,
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
// While it would be optimal to bypass the cache in case of POST requests
// since they are never cached, our ServiceWorker interception implementation
// on single-process systems is implemented via the HTTP cache, so DO NOT
// bypass the cache based on method!
AddLoadFlags(mChannel,
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
}
// Since we expect XML data, set the type hint accordingly
// if the channel doesn't know any content type.

Просмотреть файл

@ -36,7 +36,7 @@ function testFile(file, contents, test) {
[{ name: "hello", value: "world"},
{ name: "myfile",
value: contents,
fileName: file.name || "",
fileName: file.name || "blob",
contentType: file.type || "application/octet-stream" }]);
testHasRun();
}

Просмотреть файл

@ -3247,8 +3247,7 @@ UnprivilegedJunkScopeOrWorkerGlobal()
return xpc::UnprivilegedJunkScope();
}
return workers::GetCurrentThreadWorkerPrivate()->
GlobalScope()->GetGlobalJSObject();
return workers::GetCurrentThreadWorkerGlobal();
}
} // namespace binding_detail

Просмотреть файл

@ -1136,11 +1136,13 @@ class CGHeaders(CGWrapper):
if desc.interface.maplikeOrSetlikeOrIterable:
# We need ToJSValue.h for maplike/setlike type conversions
bindingHeaders.add("mozilla/dom/ToJSValue.h")
# Add headers for the key and value types of the maplike, since
# they'll be needed for convenience functions
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
desc, None))
if desc.interface.maplikeOrSetlikeOrIterable.valueType:
# Add headers for the key and value types of the
# maplike/setlike/iterable, since they'll be needed for
# convenience functions
if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
desc, None))
if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType,
desc, None))
@ -2281,34 +2283,49 @@ class MethodDefiner(PropertyDefiner):
"condition": MemberCondition()
})
# Generate the maplike/setlike iterator, if one wasn't already
# generated by a method. If we already have an @@iterator symbol, fail.
if descriptor.interface.maplikeOrSetlikeOrIterable:
if hasIterator(methods, self.regular):
raise TypeError("Cannot have maplike/setlike/iterable interface with "
"other members that generate @@iterator "
"on interface %s, such as indexed getters "
"or aliased functions." %
self.descriptor.interface.identifier.name)
for m in methods:
if (m.isMaplikeOrSetlikeOrIterableMethod() and
(((m.maplikeOrSetlikeOrIterable.isMaplike() or
(m.maplikeOrSetlikeOrIterable.isIterable() and
m.maplikeOrSetlikeOrIterable.hasValueType())) and
m.identifier.name == "entries") or
(((m.maplikeOrSetlikeOrIterable.isSetlike() or
(m.maplikeOrSetlikeOrIterable.isIterable() and
not m.maplikeOrSetlikeOrIterable.hasValueType()))) and
m.identifier.name == "values"))):
self.regular.append({
"name": "@@iterator",
"methodName": m.identifier.name,
"length": methodLength(m),
"flags": "0",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor),
})
break
# Generate the keys/values/entries aliases for value iterables.
maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
if (not static and
maplikeOrSetlikeOrIterable and
maplikeOrSetlikeOrIterable.isIterable() and
maplikeOrSetlikeOrIterable.isValueIterator()):
# Add our keys/values/entries/forEach
self.regular.append({
"name": "keys",
"methodInfo": False,
"selfHostedName": "ArrayKeys",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
self.regular.append({
"name": "values",
"methodInfo": False,
"selfHostedName": "ArrayValues",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
self.regular.append({
"name": "entries",
"methodInfo": False,
"selfHostedName": "ArrayEntries",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
self.regular.append({
"name": "forEach",
"methodInfo": False,
"selfHostedName": "ArrayForEach",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
if not static:
stringifier = descriptor.operations['Stringifier']
@ -13073,8 +13090,9 @@ class CGForwardDeclarations(CGWrapper):
# arguments to helper functions, and they'll need to be forward
# declared in the header.
if d.interface.maplikeOrSetlikeOrIterable:
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
config)
if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
config)
if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType,
config)
@ -15802,6 +15820,31 @@ class CGIterableMethodGenerator(CGGeneric):
using CGCallGenerator.
"""
def __init__(self, descriptor, iterable, methodName):
if methodName == "forEach":
CGGeneric.__init__(self, fill(
"""
if (!JS::IsCallable(arg0)) {
ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "Argument 1 of ${ifaceName}.forEach");
return false;
}
JS::AutoValueArray<3> callArgs(cx);
callArgs[2].setObject(*obj);
JS::Rooted<JS::Value> ignoredReturnVal(cx);
for (size_t i = 0; i < self->GetIterableLength(); ++i) {
if (!ToJSValue(cx, self->GetValueAtIndex(i), callArgs[0])) {
return false;
}
if (!ToJSValue(cx, self->GetKeyAtIndex(i), callArgs[1])) {
return false;
}
if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
&ignoredReturnVal)) {
return false;
}
}
""",
ifaceName=descriptor.interface.identifier.name))
return
CGGeneric.__init__(self, fill(
"""
typedef ${iterClass} itrType;

Просмотреть файл

@ -908,8 +908,5 @@ def getAllTypes(descriptors, dictionaries, callbacks):
def iteratorNativeType(descriptor):
assert descriptor.interface.isIterable()
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
if iterableDecl.valueType is None:
iterClass = "OneTypeIterableIterator"
else:
iterClass = "TwoTypeIterableIterator"
return "mozilla::dom::%s<%s>" % (iterClass, descriptor.nativeType)
assert iterableDecl.isPairIterator()
return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType

Просмотреть файл

@ -6,19 +6,17 @@
/**
* The IterableIterator class is used for WebIDL interfaces that have a
* iterable<> member defined. It handles the ES6 Iterator-like functions that
* are generated for the iterable interface.
* iterable<> member defined with two types (so a pair iterator). It handles
* the ES6 Iterator-like functions that are generated for the iterable
* interface.
*
* For iterable interfaces, the implementation class will need to
* implement these two functions:
* For iterable interfaces with a pair iterator, the implementation class will
* need to implement these two functions:
*
* - size_t GetIterableLength()
* - Returns the number of elements available to iterate over
* - [type] GetValueAtIndex(size_t index)
* - Returns the value at the requested index.
*
* If this is a two-type iterator, then the implementation class will also need to implement:
*
* - [type] GetKeyAtIndex(size_t index)
* - Returns the key at the requested index
*
@ -60,13 +58,77 @@ protected:
};
template <typename T>
class IterableIterator : public IterableIteratorBase
class IterableIterator final : public IterableIteratorBase
{
public:
explicit IterableIterator(T* aIterableObj)
typedef bool (*WrapFunc)(JSContext* aCx,
IterableIterator<T>* aObject,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
explicit IterableIterator(T* aIterableObj,
IterableIteratorType aIteratorType,
WrapFunc aWrapFunc)
: mIterableObj(aIterableObj)
, mIteratorType(aIteratorType)
, mWrapFunc(aWrapFunc)
, mIndex(0)
{
MOZ_ASSERT(mIterableObj);
MOZ_ASSERT(mWrapFunc);
}
void
Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
{
JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
if (mIndex >= this->mIterableObj->GetIterableLength()) {
DictReturn(aCx, aResult, true, value, aRv);
return;
}
switch (mIteratorType) {
case IterableIteratorType::Keys:
{
if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Values:
{
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Entries:
{
JS::Rooted<JS::Value> key(aCx);
if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
KeyAndValueReturn(aCx, key, value, aResult, aRv);
break;
}
default:
MOZ_CRASH("Invalid iterator type!");
}
++mIndex;
}
bool
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
{
return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
}
protected:
@ -128,161 +190,6 @@ protected:
// Binding Implementation object that we're iterating over.
RefPtr<T> mIterableObj;
};
template<typename T>
class OneTypeIterableIterator final : public IterableIterator<T>
{
public:
typedef typename IterableIterator<T>::IterableIteratorType IterableIteratorType;
using IterableIterator<T>::DictReturn;
using IterableIterator<T>::KeyAndValueReturn;
typedef bool (*WrapFunc)(JSContext* aCx,
OneTypeIterableIterator<T>* aObject,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
OneTypeIterableIterator(T* aIterableObj,
IterableIteratorType aIteratorType,
WrapFunc aWrapFunc)
: IterableIterator<T>(aIterableObj)
, mIteratorType(aIteratorType)
, mWrapFunc(aWrapFunc)
, mIndex(0)
{
MOZ_ASSERT(mWrapFunc);
}
void
Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
{
JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
if (mIndex >= this->mIterableObj->GetIterableLength()) {
DictReturn(aCx, aResult, true, value, aRv);
return;
}
switch (mIteratorType) {
case IterableIteratorType::Keys:
case IterableIteratorType::Values:
{
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Entries:
{
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
KeyAndValueReturn(aCx, value, value, aResult, aRv);
break;
}
default:
MOZ_CRASH("Invalid iterator type!");
}
++mIndex;
}
bool
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
{
return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
}
protected:
virtual ~OneTypeIterableIterator() {}
// Tells whether this is a key, value, or entries iterator.
IterableIteratorType mIteratorType;
// Function pointer to binding-type-specific Wrap() call for this iterator.
WrapFunc mWrapFunc;
// Current index of iteration.
uint32_t mIndex;
};
template<typename T>
class TwoTypeIterableIterator final : public IterableIterator<T>
{
public:
typedef typename IterableIterator<T>::IterableIteratorType IterableIteratorType;
using IterableIterator<T>::DictReturn;
using IterableIterator<T>::KeyAndValueReturn;
typedef bool (*WrapFunc)(JSContext* aCx,
TwoTypeIterableIterator<T>* aObject,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
TwoTypeIterableIterator(T* aIterableObj, IterableIteratorType aIteratorType,
WrapFunc aWrapFunc)
: IterableIterator<T>(aIterableObj)
, mIteratorType(aIteratorType)
, mWrapFunc(aWrapFunc)
, mIndex(0)
{
MOZ_ASSERT(mWrapFunc);
}
void
Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
{
JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
if (mIndex >= this->mIterableObj->GetIterableLength()) {
DictReturn(aCx, aResult, true, value, aRv);
return;
}
switch (mIteratorType) {
case IterableIteratorType::Keys:
{
if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Values:
{
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Entries:
{
JS::Rooted<JS::Value> key(aCx);
if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
KeyAndValueReturn(aCx, key, value, aResult, aRv);
break;
}
default:
MOZ_CRASH("Invalid iterator type!");
}
++mIndex;
}
bool
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
{
return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
}
protected:
virtual ~TwoTypeIterableIterator() {}
// Tells whether this is a key, value, or entries iterator.
IterableIteratorType mIteratorType;
// Function pointer to binding-type-specific Wrap() call for this iterator.

Просмотреть файл

@ -1098,7 +1098,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
def validate(self):
# We don't support consequential unforgeable interfaces. Need to check
# this here, becaue in finish() an interface might not know yet that
# this here, because in finish() an interface might not know yet that
# it's consequential.
if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
raise WebIDLError(
@ -1117,6 +1117,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
self.identifier.name,
locations)
indexedGetter = None
hasLengthAttribute = False
for member in self.members:
member.validate()
@ -1127,8 +1129,13 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
[self.location, member.location])
# Check that PutForwards refers to another attribute and that no
# cycles exist in forwarded assignments.
# cycles exist in forwarded assignments. Also check for a
# integer-typed "length" attribute.
if member.isAttr():
if (member.identifier.name == "length" and
member.type.isInteger()):
hasLengthAttribute = True
iface = self
attr = member
putForwards = attr.getExtendedAttribute("PutForwards")
@ -1166,8 +1173,11 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
putForwards = attr.getExtendedAttribute("PutForwards")
# Check that the name of an [Alias] doesn't conflict with an
# interface member.
# interface member and whether we support indexed properties.
if member.isMethod():
if member.isGetter() and member.isIndexed():
indexedGetter = member
for alias in member.aliases:
if self.isOnGlobalProtoChain():
raise WebIDLError("[Alias] must not be used on a "
@ -1228,6 +1238,35 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
"exposed conditionally",
[self.location])
# Value iterators are only allowed on interfaces with indexed getters,
# and pair iterators are only allowed on interfaces without indexed
# getters.
if self.isIterable():
iterableDecl = self.maplikeOrSetlikeOrIterable
if iterableDecl.isValueIterator():
if not indexedGetter:
raise WebIDLError("Interface with value iterator does not "
"support indexed properties",
[self.location])
if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
raise WebIDLError("Iterable type does not match indexed "
"getter type",
[iterableDecl.location,
indexedGetter.location])
if not hasLengthAttribute:
raise WebIDLError('Interface with value iterator does not '
'have an integer-typed "length" attribute',
[self.location])
else:
assert iterableDecl.isPairIterator()
if indexedGetter:
raise WebIDLError("Interface with pair iterator supports "
"indexed properties",
[self.location, iterableDecl.location,
indexedGetter.location])
def isInterface(self):
return True
@ -3426,7 +3465,10 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind):
IDLInterfaceMember.__init__(self, location, identifier, ifaceKind)
assert isinstance(keyType, IDLType)
if keyType is not None:
assert isinstance(keyType, IDLType)
else:
assert valueType is not None
assert ifaceType in ['maplike', 'setlike', 'iterable']
if valueType is not None:
assert isinstance(valueType, IDLType)
@ -3445,6 +3487,9 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
def isIterable(self):
return self.maplikeOrSetlikeOrIterableType == "iterable"
def hasKeyType(self):
return self.keyType is not None
def hasValueType(self):
return self.valueType is not None
@ -3469,7 +3514,8 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
[self.location, member.location])
def addMethod(self, name, members, allowExistingOperations, returnType, args=[],
chromeOnly=False, isPure=False, affectsNothing=False, newObject=False):
chromeOnly=False, isPure=False, affectsNothing=False, newObject=False,
isIteratorAlias=False):
"""
Create an IDLMethod based on the parameters passed in.
@ -3529,16 +3575,20 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
if newObject:
method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("NewObject",))])
if isIteratorAlias:
method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
members.append(method)
def resolve(self, parentScope):
self.keyType.resolveType(parentScope)
if self.keyType:
self.keyType.resolveType(parentScope)
if self.valueType:
self.valueType.resolveType(parentScope)
def finish(self, scope):
IDLInterfaceMember.finish(self, scope)
if not self.keyType.isComplete():
if self.keyType and not self.keyType.isComplete():
t = self.keyType.complete(scope)
assert not isinstance(t, IDLUnresolvedType)
@ -3560,9 +3610,23 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
IDLInterfaceMember.handleExtendedAttribute(self, attr)
def _getDependentObjects(self):
deps = set()
if self.keyType:
deps.add(self.keyType)
if self.valueType:
return set([self.keyType, self.valueType])
return set([self.keyType])
deps.add(self.valueType)
return deps
def getForEachArguments(self):
return [IDLArgument(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
"callback"),
BuiltinTypes[IDLBuiltinType.Types.object]),
IDLArgument(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
"thisArg"),
BuiltinTypes[IDLBuiltinType.Types.any],
optional=True)]
# Iterable adds ES6 iterator style functions and traits
# (keys/values/entries/@@iterator) to an interface.
@ -3583,9 +3647,15 @@ class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
we generate our functions as if they were part of the interface
specification during parsing.
"""
# We only need to add entries/keys/values here if we're a pair iterator.
# Value iterators just copy these from %ArrayPrototype% instead.
if not self.isPairIterator():
return
# object entries()
self.addMethod("entries", members, False, self.iteratorType,
affectsNothing=True, newObject=True)
affectsNothing=True, newObject=True,
isIteratorAlias=True)
# object keys()
self.addMethod("keys", members, False, self.iteratorType,
affectsNothing=True, newObject=True)
@ -3593,6 +3663,17 @@ class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
self.addMethod("values", members, False, self.iteratorType,
affectsNothing=True, newObject=True)
# void forEach(callback(valueType, keyType), optional any thisArg)
self.addMethod("forEach", members, False,
BuiltinTypes[IDLBuiltinType.Types.void],
self.getForEachArguments())
def isValueIterator(self):
return not self.isPairIterator()
def isPairIterator(self):
return self.hasKeyType()
# MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface.
class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
@ -3629,26 +3710,17 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
# object entries()
self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
affectsNothing=True)
affectsNothing=True, isIteratorAlias=self.isMaplike())
# object keys()
self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
affectsNothing=True)
# object values()
self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
affectsNothing=True)
affectsNothing=True, isIteratorAlias=self.isSetlike())
# void forEach(callback(valueType, keyType), thisVal)
foreachArguments = [IDLArgument(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
"callback"),
BuiltinTypes[IDLBuiltinType.Types.object]),
IDLArgument(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
"thisArg"),
BuiltinTypes[IDLBuiltinType.Types.any],
optional=True)]
self.addMethod("forEach", members, False, BuiltinTypes[IDLBuiltinType.Types.void],
foreachArguments)
self.getForEachArguments())
def getKeyArg():
return IDLArgument(self.location,
@ -5438,10 +5510,13 @@ class Parser(Tokenizer):
location = self.getLocation(p, 2)
identifier = IDLUnresolvedIdentifier(location, "__iterable",
allowDoubleUnderscore=True)
keyType = p[3]
valueType = None
if (len(p) > 6):
keyType = p[3]
valueType = p[5]
else:
keyType = None
valueType = p[3]
p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope())
def p_Setlike(self, p):
@ -6511,7 +6586,7 @@ class Parser(Tokenizer):
if isinstance(m, IDLIterable):
iterable = m
break
if iterable:
if iterable and iterable.isPairIterator():
def simpleExtendedAttr(str):
return IDLExtendedAttribute(iface.location, (str, ))
nextMethod = IDLMethod(

Просмотреть файл

@ -45,8 +45,8 @@ def WebIDLTest(parser, harness):
prefix + " - Interface failed but not as a WebIDLError exception: %s" % e)
iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
"values"]]
setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has", "forEach"]] +
"values", "forEach"]]
setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has"]] +
[("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
iterableMembers)
setROMembers.extend([("size", WebIDL.IDLAttribute)])
@ -62,7 +62,7 @@ def WebIDLTest(parser, harness):
"__clear",
"__delete"]] +
setRWMembers)
mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has", "forEach"]] +
mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has"]] +
[("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
iterableMembers)
mapROMembers.extend([("size", WebIDL.IDLAttribute)])
@ -78,6 +78,10 @@ def WebIDLTest(parser, harness):
# __iterable to it for the iterable<> case.
iterableMembers.append(("__iterable", WebIDL.IDLIterable))
valueIterableMembers = [("__iterable", WebIDL.IDLIterable)]
valueIterableMembers.append(("__indexedgetter", WebIDL.IDLMethod))
valueIterableMembers.append(("length", WebIDL.IDLAttribute))
disallowedIterableNames = ["keys", "entries", "values"]
disallowedMemberNames = ["forEach", "has", "size"] + disallowedIterableNames
mapDisallowedMemberNames = ["get"] + disallowedMemberNames
@ -93,10 +97,10 @@ def WebIDLTest(parser, harness):
"""
interface Foo1 {
iterable<long>;
readonly attribute unsigned long length;
getter long(unsigned long index);
};
""", iterableMembers,
# numProductions == 2 because of the generated iterator iface,
numProductions=2)
""", valueIterableMembers)
shouldPass("Iterable (key and value)",
"""

Просмотреть файл

@ -23,7 +23,7 @@ NS_INTERFACE_MAP_END
TestInterfaceIterableSingle::TestInterfaceIterableSingle(nsPIDOMWindowInner* aParent)
: mParent(aParent)
{
for(int i = 0; i < 3; ++i) {
for (int i = 0; i < 3; ++i) {
mValues.AppendElement(i);
}
}
@ -55,17 +55,22 @@ TestInterfaceIterableSingle::GetParentObject() const
return mParent;
}
size_t
TestInterfaceIterableSingle::GetIterableLength() const
uint32_t
TestInterfaceIterableSingle::Length() const
{
return mValues.Length();
}
uint32_t
TestInterfaceIterableSingle::GetValueAtIndex(uint32_t index) const
int32_t
TestInterfaceIterableSingle::IndexedGetter(uint32_t aIndex, bool& aFound) const
{
MOZ_ASSERT(index < mValues.Length());
return mValues.ElementAt(index);
if (aIndex >= mValues.Length()) {
aFound = false;
return 0;
}
aFound = true;
return mValues[aIndex];
}
} // namespace dom

Просмотреть файл

@ -36,12 +36,13 @@ public:
static already_AddRefed<TestInterfaceIterableSingle>
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
size_t GetIterableLength() const;
uint32_t GetValueAtIndex(uint32_t aIndex) const;
uint32_t Length() const;
int32_t IndexedGetter(uint32_t aIndex, bool& aFound) const;
private:
virtual ~TestInterfaceIterableSingle() {}
nsCOMPtr<nsPIDOMWindowInner> mParent;
nsTArray<uint32_t> mValues;
nsTArray<int32_t> mValues;
};
} // namespace dom

Просмотреть файл

@ -71,4 +71,5 @@ skip-if = debug == false
[test_jsimplemented_eventhandler.html]
skip-if = debug == false
[test_iterable.html]
skip-if = debug == false
skip-if = debug == false
[test_oom_reporting.html]

Просмотреть файл

@ -93,7 +93,8 @@
is(e[1], 1, "SimpleMap: iterable second array element should be value");
}
is(m[Symbol.iterator].length, 0, "SimpleMap: @@iterator symbol is correct length");
is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleMap: @@iterator symbol has correct name");
is(m[Symbol.iterator].name, "entries", "SimpleMap: @@iterator symbol has correct name");
is(m[Symbol.iterator], m.entries, 'SimpleMap: @@iterator is an alias for "entries"');
ok(iterable, "SimpleMap: @@iterator symbol resolved correctly");
for (var k of m.keys()) {
is(k, "test", "SimpleMap: first keys element should be 'test'");
@ -139,7 +140,8 @@
is(e, "test", "SimpleSet: iterable first array element should be key");
}
is(m[Symbol.iterator].length, 0, "SimpleSet: @@iterator symbol is correct length");
is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleSet: @@iterator symbol has correct name");
is(m[Symbol.iterator].name, "values", "SimpleSet: @@iterator symbol has correct name");
is(m[Symbol.iterator], m.values, 'SimpleSet: @@iterator is an alias for "values"');
ok(iterable, "SimpleSet: @@iterator symbol resolved correctly");
for (var k of m.keys()) {
is(k, "test", "SimpleSet: first keys element should be 'test'");

Просмотреть файл

@ -14,7 +14,8 @@
base_properties = [["entries", "function", 0],
["keys", "function", 0],
["values", "function", 0]]
["values", "function", 0],
["forEach", "function", 1]]
var testExistence = function testExistence(prefix, obj, properties) {
for (var [name, type, args] of properties) {
// Properties are somewhere up the proto chain, hasOwnProperty won't work
@ -50,6 +51,16 @@
info("IterableSingle: Testing simple iterable creation and functionality");
itr = new TestInterfaceIterableSingle();
testExistence("IterableSingle: ", itr, base_properties);
is(itr[Symbol.iterator], Array.prototype[Symbol.iterator],
"IterableSingle: Should be using %ArrayIterator% for @@iterator");
is(itr.keys, Array.prototype.keys,
"IterableSingle: Should be using %ArrayIterator% for 'keys'");
is(itr.entries, Array.prototype.entries,
"IterableSingle: Should be using %ArrayIterator% for 'entries'");
is(itr.values, itr[Symbol.iterator],
"IterableSingle: Should be using @@iterator for 'values'");
is(itr.forEach, Array.prototype.forEach,
"IterableSingle: Should be using %ArrayIterator% for 'forEach'");
var keys = [...itr.keys()];
var values = [...itr.values()];
var entries = [...itr.entries()];
@ -73,6 +84,23 @@
is(entry.value[1], entries[i][1],
"IterableSingle: Entry iterator value 1 should match destructuring " + i);
}
var callsToForEachCallback = 0;
var thisArg = {};
itr.forEach(function(value, index, obj) {
is(index, callsToForEachCallback,
`IterableSingle: Should have the right index at ${callsToForEachCallback} calls to forEach callback`);
is(value, values[index],
`IterableSingle: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
is(this, thisArg,
"IterableSingle: Should have the right this value for forEach callback");
is(obj, itr,
"IterableSingle: Should have the right third arg for forEach callback");
++callsToForEachCallback;
}, thisArg);
is(callsToForEachCallback, 3,
"IterableSingle: Should have right total number of calls to forEach callback");
var key = key_itr.next();
var value = value_itr.next();
var entry = entries_itr.next();
@ -83,13 +111,15 @@
is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined");
is(entry.done, true, "IterableSingle: Entry iterator done should be true");
is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
"[object TestInterfaceIterableSingleIteratorPrototype]",
"[object Array Iterator]",
"iterator prototype should have the right brand");
// Simple dual type iterable creation and functionality test
info("IterableDouble: Testing simple iterable creation and functionality");
itr = new TestInterfaceIterableDouble();
testExistence("IterableDouble: ", itr, base_properties);
is(itr.entries, itr[Symbol.iterator],
"IterableDouble: Should be using @@iterator for 'entries'");
var elements = [["a", "b"], ["c", "d"], ["e", "f"]]
var keys = [...itr.keys()];
var values = [...itr.values()];
@ -114,6 +144,23 @@
is(entry.value[1], entries[i][1],
"IterableDouble: Entry iterator value 1 should match destructuring " + i);
}
callsToForEachCallback = 0;
thisArg = {};
itr.forEach(function(value, key, obj) {
is(key, keys[callsToForEachCallback],
`IterableDouble: Should have the right key at ${callsToForEachCallback} calls to forEach callback`);
is(value, values[callsToForEachCallback],
`IterableDouble: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
is(this, thisArg,
"IterableDouble: Should have the right this value for forEach callback");
is(obj, itr,
"IterableSingle: Should have the right third arg for forEach callback");
++callsToForEachCallback;
}, thisArg);
is(callsToForEachCallback, 3,
"IterableDouble: Should have right total number of calls to forEach callback");
var key = key_itr.next();
var value = value_itr.next();
var entry = entries_itr.next()

Просмотреть файл

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=
-->
<head>
<meta charset="utf-8">
<title>Test for Bug </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">
/** Test for Bug **/
SimpleTest.waitForExplicitFinish();
window.length = 0xffffffff;
SimpleTest.expectUncaughtException();
setTimeout(Array.prototype.splice, 0, undefined);
addEventListener("error", function(e) {
is(e.type, "error", "Should have an error event");
is(e.message, "uncaught exception: out of memory",
"Should have the right error message");
// Make sure we finish async, in case the expectUncaughtException assertion
// about having seen the exception runs after our listener
SimpleTest.executeSoon(SimpleTest.finish);
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

Просмотреть файл

@ -27,6 +27,13 @@ CryptoBuffer::Assign(const uint8_t* aData, uint32_t aLength)
return ReplaceElementsAt(0, Length(), aData, aLength, fallible);
}
uint8_t*
CryptoBuffer::Assign(const nsACString& aString)
{
return Assign(reinterpret_cast<uint8_t const*>(aString.BeginReading()),
aString.Length());
}
uint8_t*
CryptoBuffer::Assign(const SECItem* aItem)
{

Просмотреть файл

@ -22,6 +22,7 @@ class CryptoBuffer : public FallibleTArray<uint8_t>
public:
uint8_t* Assign(const CryptoBuffer& aData);
uint8_t* Assign(const uint8_t* aData, uint32_t aLength);
uint8_t* Assign(const nsACString& aString);
uint8_t* Assign(const SECItem* aItem);
uint8_t* Assign(const ArrayBuffer& aData);
uint8_t* Assign(const ArrayBufferView& aData);

Просмотреть файл

@ -882,13 +882,19 @@ WindowsGamepadService::Cleanup()
mXInputPollTimer->Cancel();
}
mGamepads.Clear();
if (mObserver) {
mObserver->Stop();
mObserver = nullptr;
}
}
void
WindowsGamepadService::DevicesChanged(DeviceChangeType type)
{
if (type == DeviceChangeNotification) {
mObserver->SetDeviceChangeTimer();
if (mObserver) {
mObserver->SetDeviceChangeTimer();
}
} else if (type == DeviceChangeStable) {
ScanForDevices();
}

Просмотреть файл

@ -162,7 +162,7 @@ function testSend(doneCb) {
}
is(response[0].headers['Content-Disposition'],
'form-data; name="empty"; filename=""');
'form-data; name="empty"; filename="blob"');
is(response[1].headers['Content-Disposition'],
'form-data; name="explicit"; filename="explicit-file-name"');

Просмотреть файл

@ -590,7 +590,7 @@ var expectedAugment = [
//{ name: "aNameUndef", value: "undefined" },
];
function checkMPSubmission(sub, expected, test) {
function checkMPSubmission(sub, expected, test, isFormData = false) {
function getPropCount(o) {
var x, l = 0;
for (x in o) ++l;
@ -625,7 +625,7 @@ function checkMPSubmission(sub, expected, test) {
else {
is(sub[i].headers["Content-Disposition"],
"form-data; name=\"" + mpquote(expected[i].name) + "\"; filename=\"" +
mpquote(expected[i].fileName) + "\"",
mpquote(expected[i].fileName != "" || !isFormData ? expected[i].fileName : "blob") + "\"",
"Correct name in " + test);
is(sub[i].headers["Content-Type"],
expected[i].contentType,
@ -782,14 +782,14 @@ function runTest() {
xhr.open("POST", "form_submit_server.sjs");
xhr.send(new FormData(form));
yield undefined; // Wait for XHR load
checkMPSubmission(JSON.parse(xhr.responseText), expectedSub, "send form using XHR and FormData");
checkMPSubmission(JSON.parse(xhr.responseText), expectedSub, "send form using XHR and FormData", true);
// Send disabled form using XHR and FormData
setDisabled(document.querySelectorAll("input, select, textarea"), true);
xhr.open("POST", "form_submit_server.sjs");
xhr.send(new FormData(form));
yield undefined;
checkMPSubmission(JSON.parse(xhr.responseText), [], "send disabled form using XHR and FormData");
checkMPSubmission(JSON.parse(xhr.responseText), [], "send disabled form using XHR and FormData", true);
setDisabled(document.querySelectorAll("input, select, textarea"), false);
// Send FormData
@ -804,7 +804,7 @@ function runTest() {
xhr.open("POST", "form_submit_server.sjs");
xhr.send(fd);
yield undefined;
checkMPSubmission(JSON.parse(xhr.responseText), expectedAugment, "send FormData");
checkMPSubmission(JSON.parse(xhr.responseText), expectedAugment, "send FormData", true);
// Augment <form> using FormData
fd = new FormData(form);
@ -813,7 +813,7 @@ function runTest() {
xhr.send(fd);
yield undefined;
checkMPSubmission(JSON.parse(xhr.responseText),
expectedSub.concat(expectedAugment), "send augmented FormData");
expectedSub.concat(expectedAugment), "send augmented FormData", true);
SimpleTest.finish();
yield undefined;

Просмотреть файл

@ -44,6 +44,7 @@
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/TestShellChild.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/layers/APZChild.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/PCompositorChild.h"
@ -1260,6 +1261,19 @@ ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport,
return GMPServiceChild::Create(aTransport, aOtherProcess);
}
PAPZChild*
ContentChild::AllocPAPZChild(const TabId& aTabId)
{
return APZChild::Create(aTabId);
}
bool
ContentChild::DeallocPAPZChild(PAPZChild* aActor)
{
delete aActor;
return true;
}
PCompositorChild*
ContentChild::AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)

Просмотреть файл

@ -144,6 +144,11 @@ public:
AllocPGMPServiceChild(mozilla::ipc::Transport* transport,
base::ProcessId otherProcess) override;
PAPZChild*
AllocPAPZChild(const TabId& aTabId) override;
bool
DeallocPAPZChild(PAPZChild* aActor) override;
PCompositorChild*
AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess) override;

Просмотреть файл

@ -83,6 +83,7 @@
#include "mozilla/ipc/TestShellParent.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/layers/PAPZParent.h"
#include "mozilla/layers/CompositorParent.h"
#include "mozilla/layers/ImageBridgeParent.h"
#include "mozilla/layers/SharedBufferManagerParent.h"
@ -2028,11 +2029,61 @@ NestedBrowserLayerIds()
}
} // namespace
/* static */
bool
ContentParent::RecvAllocateLayerTreeId(uint64_t* aId)
ContentParent::AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId)
{
return AllocateLayerTreeId(aTabParent->Manager()->AsContentParent(),
aTabParent, aTabParent->GetTabId(), aId);
}
/* static */
bool
ContentParent::AllocateLayerTreeId(ContentParent* aContent,
TabParent* aTopLevel, const TabId& aTabId,
uint64_t* aId)
{
*aId = CompositorParent::AllocateLayerTreeId();
if (!gfxPlatform::AsyncPanZoomEnabled()) {
return true;
}
if (!aContent || !aTopLevel) {
return false;
}
return CompositorParent::UpdateRemoteContentController(*aId, aContent,
aTabId, aTopLevel);
}
bool
ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId,
const TabId& aTabId, uint64_t* aId)
{
// Protect against spoofing by a compromised child. aCpId must either
// correspond to the process that this ContentParent represents or be a
// child of it.
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
if (ChildID() != aCpId) {
ContentParentId parent;
if (!cpm->GetParentProcessId(aCpId, &parent) ||
ChildID() != parent) {
return false;
}
}
// GetTopLevelTabParentByProcessAndTabId will make sure that aTabId
// lives in the process for aCpId.
RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
RefPtr<TabParent> browserParent =
cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId);
MOZ_ASSERT(contentParent && browserParent);
if (!AllocateLayerTreeId(contentParent, browserParent, aTabId, aId)) {
return false;
}
auto iter = NestedBrowserLayerIds().find(this);
if (iter == NestedBrowserLayerIds().end()) {
std::set<uint64_t> ids;
@ -3398,6 +3449,21 @@ ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
return GMPServiceParent::Create(aTransport, aOtherProcess);
}
PAPZParent*
ContentParent::AllocPAPZParent(const TabId& aTabId)
{
// The PAPZParent should just be created in the main process and then an IPDL
// constructor message sent to hook it up.
MOZ_CRASH("This shouldn't be called");
return nullptr;
}
bool
ContentParent::DeallocPAPZParent(PAPZParent* aActor)
{
return true;
}
PCompositorParent*
ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
@ -5697,6 +5763,18 @@ ContentParent::RecvGetGraphicsDeviceInitData(DeviceInitData* aOut)
return true;
}
bool
ContentParent::RecvGraphicsError(const nsCString& aError)
{
gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder();
if (lf) {
std::stringstream message;
message << "CP+" << aError.get();
lf->UpdateStringsVector(message.str());
}
return true;
}
bool
ContentParent::RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed)
{

Просмотреть файл

@ -506,6 +506,8 @@ public:
InfallibleTArray<FrameScriptInfo>* aFrameScripts,
nsCString* aURLToLoad) override;
static bool AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId);
protected:
void OnChannelConnected(int32_t pid) override;
@ -650,10 +652,19 @@ private:
static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
static bool AllocateLayerTreeId(ContentParent* aContent,
TabParent* aTopLevel, const TabId& aTabId,
uint64_t* aId);
PGMPServiceParent*
AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess) override;
PAPZParent*
AllocPAPZParent(const TabId& aTabId) override;
bool
DeallocPAPZParent(PAPZParent* aActor) override;
PCompositorParent*
AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess) override;
@ -1004,7 +1015,9 @@ private:
virtual void ProcessingError(Result aCode, const char* aMsgName) override;
virtual bool RecvAllocateLayerTreeId(uint64_t* aId) override;
virtual bool RecvAllocateLayerTreeId(const ContentParentId& aCpId,
const TabId& aTabId,
uint64_t* aId) override;
virtual bool RecvDeallocateLayerTreeId(const uint64_t& aId) override;
@ -1012,6 +1025,8 @@ private:
int32_t* aStatus,
bool* aSuccess) override;
virtual bool RecvGraphicsError(const nsCString& aError) override;
virtual bool
RecvBeginDriverCrashGuard(const uint32_t& aGuardType,
bool* aOutCrashed) override;

Просмотреть файл

@ -56,13 +56,9 @@ using class mozilla::WidgetPluginEvent from "ipc/nsGUIEventIPC.h";
using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h";
using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
using mozilla::CSSPoint from "Units.h";
using mozilla::CSSToScreenScale from "Units.h";
using mozilla::CommandInt from "mozilla/EventForwards.h";
using mozilla::Modifiers from "mozilla/EventForwards.h";
using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
using mozilla::WritingMode from "mozilla/WritingModes.h";
using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
using nsIWidget::TouchPointerState from "nsIWidget.h";
using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
@ -417,43 +413,6 @@ parent:
nsString aName, nsString aFeatures)
returns (bool windowOpened);
/**
* Instructs the TabParent to forward a request to zoom to a rect given in
* CSS pixels. This rect is relative to the document.
*/
async ZoomToRect(uint32_t aPresShellId, ViewID aViewId, CSSRect aRect, uint32_t aFlags);
/**
* We know for sure that content has either preventDefaulted or not
* preventDefaulted. This applies to an entire batch of touch events. It is
* expected that, if there are any DOM touch listeners, touch events will be
* batched and only processed for panning and zooming if content does not
* preventDefault.
*/
async ContentReceivedInputBlock(ScrollableLayerGuid aGuid, uint64_t aInputBlockId, bool aPreventDefault);
/**
* Notifies the APZ code of the results of the gecko hit-test for a
* particular input block. Each target corresponds to one touch point in the
* touch event.
*/
async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
/**
* Notifies the APZ code of the allowed touch-behaviours for a particular
* input block. Each item in the aFlags array corresponds to one touch point
* in the touch event.
*/
async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aFlags);
/**
* Updates the zoom constraints for a scrollable frame in this tab.
* The zoom controller code lives on the parent side and so this allows it to
* have up-to-date zoom constraints.
*/
async UpdateZoomConstraints(uint32_t aPresShellId, ViewID aViewId,
MaybeZoomConstraints aConstraints);
/**
* Tells the containing widget whether the given input block results in a
* swipe. Should be called in response to a WidgetWheelEvent that has
@ -538,9 +497,6 @@ parent:
prio(high) sync DispatchMouseEvent(WidgetMouseEvent event);
prio(high) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
// Start an APZ drag on a scrollbar
async StartScrollbarDrag(AsyncDragMetrics aDragMetrics);
async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
nsCString visualData, uint32_t width, uint32_t height,
uint32_t stride, uint8_t format,
@ -576,20 +532,6 @@ child:
ScreenOrientationInternal orientation,
LayoutDeviceIntPoint chromeDisp) compressall;
async UpdateFrame(FrameMetrics frame);
// The following methods correspond to functions on the GeckoContentController
// interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
// in that file for these functions.
async RequestFlingSnap(ViewID aScrollID, CSSPoint aDestination);
async AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
async HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
async HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
async HandleLongTap(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
async NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
async NotifyFlushComplete();
/**
* Sending an activate message moves focus to the child.
*/
@ -661,7 +603,7 @@ child:
/**
* APZ notification for mouse scroll testing events.
*/
async MouseScrollTestEvent(ViewID aScrollId, nsString aEvent);
async MouseScrollTestEvent(uint64_t aLayersId, ViewID aScrollId, nsString aEvent);
async CompositionEvent(WidgetCompositionEvent event);

Просмотреть файл

@ -4,6 +4,7 @@
* 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 protocol PAPZ;
include protocol PBackground;
include protocol PBlob;
include protocol PBluetooth;
@ -465,6 +466,7 @@ prio(normal upto urgent) sync protocol PContent
parent opens PVRManager;
child opens PBackground;
manages PAPZ;
manages PBlob;
manages PBluetooth;
manages PBrowser;
@ -575,6 +577,8 @@ child:
async PTestShell();
async PAPZ(TabId tabId);
async RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions,
OverrideMapping[] overrides, nsCString locale, bool reset);
async RegisterChromeItem(ChromeRegistryItem item);
@ -993,7 +997,7 @@ parent:
async CopyFavicon(URIParams oldURI, URIParams newURI, Principal aLoadingPrincipal, bool isPrivate);
// Tell the compositor to allocate a layer tree id for nested remote mozbrowsers.
sync AllocateLayerTreeId()
sync AllocateLayerTreeId(ContentParentId cpId, TabId tabId)
returns (uint64_t id);
async DeallocateLayerTreeId(uint64_t id);
@ -1016,6 +1020,9 @@ parent:
sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, bool aSuccess);
// Graphics errors
async GraphicsError(nsCString aError);
// Driver crash guards. aGuardType must be a member of CrashGuardType.
sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected);
sync EndDriverCrashGuard(uint32_t aGuardType);

Просмотреть файл

@ -16,6 +16,7 @@
#include "ContentChild.h"
#include "TabParent.h"
#include "mozilla/Preferences.h"
#include "mozilla/BrowserElementParent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/dom/workers/ServiceWorkerManager.h"
@ -28,6 +29,7 @@
#include "ipc/Nuwa.h"
#endif
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/layers/APZChild.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/APZCTreeManager.h"
#include "mozilla/layers/APZEventState.h"
@ -37,6 +39,7 @@
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/ShadowLayers.h"
#include "mozilla/layout/RenderFrameChild.h"
#include "mozilla/layout/RenderFrameParent.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/Move.h"
@ -589,6 +592,7 @@ TabChild::TabChild(nsIContentChild* aManager,
, mIPCOpen(true)
, mParentIsActive(false)
, mDidSetRealShowInfo(false)
, mAPZChild(nullptr)
{
// In the general case having the TabParent tell us if APZ is enabled or not
// doesn't really work because the TabParent itself may not have a reference
@ -602,7 +606,7 @@ TabChild::TabChild(nsIContentChild* aManager,
const nsTArray<TouchBehaviorFlags>& aFlags)
{
if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
static_cast<TabChild*>(tabChild.get())->SendSetAllowedTouchBehavior(aInputBlockId, aFlags);
static_cast<TabChild*>(tabChild.get())->SetAllowedTouchBehavior(aInputBlockId, aFlags);
}
};
@ -710,6 +714,35 @@ TabChild::Observe(nsISupports *aSubject,
return NS_OK;
}
void
TabChild::ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId,
bool aPreventDefault) const
{
if (mAPZChild) {
mAPZChild->SendContentReceivedInputBlock(aGuid, aInputBlockId,
aPreventDefault);
}
}
void
TabChild::SetTargetAPZC(uint64_t aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets) const
{
if (mAPZChild) {
mAPZChild->SendSetTargetAPZC(aInputBlockId, aTargets);
}
}
void
TabChild::SetAllowedTouchBehavior(uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags>& aTargets) const
{
if (mAPZChild) {
mAPZChild->SendSetAllowedTouchBehavior(aInputBlockId, aTargets);
}
}
bool
TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId,
const ViewID& aViewId,
@ -722,9 +755,12 @@ TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId,
return true;
}
return SendUpdateZoomConstraints(aPresShellId,
aViewId,
aConstraints);
if (!mAPZChild) {
return false;
}
return mAPZChild->SendUpdateZoomConstraints(aPresShellId, aViewId,
aConstraints);
}
nsresult
@ -811,7 +847,7 @@ TabChild::Init()
bool aPreventDefault)
{
if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
static_cast<TabChild*>(tabChild.get())->SendContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
static_cast<TabChild*>(tabChild.get())->ContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
}
});
mAPZEventState = new APZEventState(mPuppetWidget, Move(callback));
@ -1633,7 +1669,7 @@ TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
}
bool
TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
TabChild::UpdateFrame(const FrameMetrics& aFrameMetrics)
{
return TabChildBase::UpdateFrameHandler(aFrameMetrics);
}
@ -1652,77 +1688,68 @@ TabChild::RecvSuppressDisplayport(const bool& aEnabled)
return true;
}
bool
TabChild::RecvRequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
const mozilla::CSSPoint& aDestination)
void
TabChild::HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid)
{
APZCCallbackHelper::RequestFlingSnap(aScrollId, aDestination);
return true;
TABC_LOG("Handling double tap at %s with %p %p\n",
Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
if (!mGlobal || !mTabChildGlobal) {
return;
}
// Note: there is nothing to do with the modifiers here, as we are not
// synthesizing any sort of mouse event.
CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
nsCOMPtr<nsIDocument> document = GetDocument();
CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
// The double-tap can be dispatched by any scroll frame (so |aGuid| could be
// the guid of any scroll frame), but the zoom-to-rect operation must be
// performed by the root content scroll frame, so query its identifiers
// for the SendZoomToRect() call rather than using the ones from |aGuid|.
uint32_t presShellId;
ViewID viewId;
if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
document->GetDocumentElement(), &presShellId, &viewId) &&
mAPZChild) {
mAPZChild->SendZoomToRect(presShellId, viewId, zoomToRect,
DEFAULT_BEHAVIOR);
}
}
bool
TabChild::RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
const uint32_t& aScrollGeneration)
{
APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
return true;
}
bool
TabChild::RecvHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
{
TABC_LOG("Handling double tap at %s with %p %p\n",
Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
if (!mGlobal || !mTabChildGlobal) {
return true;
}
// Note: there is nothing to do with the modifiers here, as we are not
// synthesizing any sort of mouse event.
CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
nsCOMPtr<nsIDocument> document = GetDocument();
CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
// The double-tap can be dispatched by any scroll frame (so |aGuid| could be
// the guid of any scroll frame), but the zoom-to-rect operation must be
// performed by the root content scroll frame, so query its identifiers
// for the SendZoomToRect() call rather than using the ones from |aGuid|.
uint32_t presShellId;
ViewID viewId;
if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
document->GetDocumentElement(), &presShellId, &viewId)) {
SendZoomToRect(presShellId, viewId, zoomToRect, DEFAULT_BEHAVIOR);
}
return true;
}
bool
TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
void
TabChild::HandleSingleTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid,
bool aCallTakeFocusForClickFromTap)
{
if (aCallTakeFocusForClickFromTap && mRemoteFrame) {
mRemoteFrame->SendTakeFocusForClickFromTap();
}
if (mGlobal && mTabChildGlobal) {
mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
}
return true;
}
bool
TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
void
TabChild::HandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
if (mGlobal && mTabChildGlobal) {
mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
aInputBlockId);
}
return true;
}
bool
TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
const APZStateChange& aChange,
const int& aArg)
TabChild::NotifyAPZStateChange(const ViewID& aViewId,
const layers::GeckoContentController::APZStateChange& aChange,
const int& aArg)
{
mAPZEventState->ProcessAPZStateChange(GetDocument(), aViewId, aChange, aArg);
if (aChange == APZStateChange::TransformEnd) {
if (aChange == layers::GeckoContentController::APZStateChange::TransformEnd) {
// This is used by tests to determine when the APZ is done doing whatever
// it's doing. XXX generify this as needed when writing additional tests.
DispatchMessageManagerMessage(
@ -1732,11 +1759,23 @@ TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
return true;
}
bool
TabChild::RecvNotifyFlushComplete()
void
TabChild::StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics)
{
APZCCallbackHelper::NotifyFlushComplete();
return true;
if (mAPZChild) {
mAPZChild->SendStartScrollbarDrag(aDragMetrics);
}
}
void
TabChild::ZoomToRect(const uint32_t& aPresShellId,
const FrameMetrics::ViewID& aViewId,
const CSSRect& aRect,
const uint32_t& aFlags)
{
if (mAPZChild) {
mAPZChild->SendZoomToRect(aPresShellId, aViewId, aRect, aFlags);
}
}
bool
@ -1854,8 +1893,21 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
}
bool
TabChild::RecvMouseScrollTestEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
TabChild::RecvMouseScrollTestEvent(const uint64_t& aLayersId,
const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
{
if (aLayersId != mLayersId) {
RefPtr<TabParent> browser = TabParent::GetTabParentFromLayersId(aLayersId);
if (!browser) {
return false;
}
NS_DispatchToMainThread(NS_NewRunnableFunction(
[aLayersId, browser, aScrollId, aEvent] () -> void {
Unused << browser->SendMouseScrollTestEvent(aLayersId, aScrollId, aEvent);
}));
return true;
}
APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
return true;
}
@ -2622,7 +2674,10 @@ TabChild::MakeHidden()
void
TabChild::UpdateHitRegion(const nsRegion& aRegion)
{
mRemoteFrame->SendUpdateHitRegion(aRegion);
mRemoteFrame->SendUpdateHitRegion(aRegion);
if (mAPZChild) {
mAPZChild->SendUpdateHitRegion(aRegion);
}
}
NS_IMETHODIMP

Просмотреть файл

@ -37,6 +37,7 @@
#include "mozilla/dom/ipc/IdType.h"
#include "AudioChannelService.h"
#include "PuppetWidget.h"
#include "mozilla/layers/GeckoContentController.h"
class nsICachedFileDescriptorListener;
class nsIDOMWindowUtils;
@ -47,7 +48,9 @@ class RenderFrameChild;
} // namespace layout
namespace layers {
class APZChild;
class APZEventState;
class AsyncDragMetrics;
class ImageCompositeNotification;
} // namespace layers
@ -332,39 +335,6 @@ public:
const ScreenOrientationInternal& aOrientation,
const LayoutDeviceIntPoint& aChromeDisp) override;
virtual bool
RecvUpdateFrame(const layers::FrameMetrics& aFrameMetrics) override;
virtual bool
RecvRequestFlingSnap(const ViewID& aScrollId,
const CSSPoint& aDestination) override;
virtual bool
RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
const uint32_t& aScrollGeneration) override;
virtual bool
RecvHandleDoubleTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const mozilla::layers::ScrollableLayerGuid& aGuid) override;
virtual bool
RecvHandleSingleTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const mozilla::layers::ScrollableLayerGuid& aGuid) override;
virtual bool
RecvHandleLongTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const mozilla::layers::ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
const APZStateChange& aChange,
const int& aArg) override;
virtual bool RecvNotifyFlushComplete() override;
virtual bool RecvActivate() override;
virtual bool RecvDeactivate() override;
@ -418,7 +388,8 @@ public:
const int32_t& aModifiers,
const bool& aPreventDefault) override;
virtual bool RecvMouseScrollTestEvent(const FrameMetrics::ViewID& aScrollId,
virtual bool RecvMouseScrollTestEvent(const uint64_t& aLayersId,
const FrameMetrics::ViewID& aScrollId,
const nsString& aEvent) override;
virtual bool RecvNativeSynthesisResponse(const uint64_t& aObserverId,
@ -622,6 +593,40 @@ public:
PRenderFrameChild* aRenderFrame,
const ShowInfo& aShowInfo);
void ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId,
bool aPreventDefault) const;
void SetTargetAPZC(uint64_t aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets) const;
void HandleDoubleTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const mozilla::layers::ScrollableLayerGuid& aGuid);
void HandleSingleTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const mozilla::layers::ScrollableLayerGuid& aGuid,
bool aCallTakeFocusForClickFromTap);
void HandleLongTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const mozilla::layers::ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId);
void SetAllowedTouchBehavior(uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags>& aFlags) const;
bool UpdateFrame(const FrameMetrics& aFrameMetrics);
bool NotifyAPZStateChange(const ViewID& aViewId,
const layers::GeckoContentController::APZStateChange& aChange,
const int& aArg);
void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics);
void ZoomToRect(const uint32_t& aPresShellId,
const FrameMetrics::ViewID& aViewId,
const CSSRect& aRect,
const uint32_t& aFlags);
void SetAPZChild(layers::APZChild* aAPZChild)
{
mAPZChild = aAPZChild;
}
protected:
virtual ~TabChild();
@ -736,6 +741,10 @@ private:
AutoTArray<bool, NUMBER_OF_AUDIO_CHANNELS> mAudioChannelsActive;
// APZChild clears this pointer from its destructor, so it shouldn't be a
// dangling pointer.
layers::APZChild* mAPZChild;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};

Просмотреть файл

@ -995,14 +995,6 @@ TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size)
}
}
void
TabParent::UpdateFrame(const FrameMetrics& aFrameMetrics)
{
if (!mIsDestroyed) {
Unused << SendUpdateFrame(aFrameMetrics);
}
}
void
TabParent::UIResolutionChanged()
{
@ -1042,76 +1034,6 @@ TabParent::HandleAccessKey(nsTArray<uint32_t>& aCharCodes,
}
}
void
TabParent::RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
const mozilla::CSSPoint& aDestination)
{
if (!mIsDestroyed) {
Unused << SendRequestFlingSnap(aScrollId, aDestination);
}
}
void
TabParent::AcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration)
{
if (!mIsDestroyed) {
Unused << SendAcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
}
}
void TabParent::HandleDoubleTap(const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid &aGuid)
{
if (!mIsDestroyed) {
Unused << SendHandleDoubleTap(aPoint, aModifiers, aGuid);
}
}
void TabParent::HandleSingleTap(const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid &aGuid)
{
if (!mIsDestroyed) {
Unused << SendHandleSingleTap(aPoint, aModifiers, aGuid);
}
}
void TabParent::HandleLongTap(const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid &aGuid,
uint64_t aInputBlockId)
{
if (!mIsDestroyed) {
Unused << SendHandleLongTap(aPoint, aModifiers, aGuid, aInputBlockId);
}
}
void TabParent::NotifyAPZStateChange(ViewID aViewId,
APZStateChange aChange,
int aArg)
{
if (!mIsDestroyed) {
Unused << SendNotifyAPZStateChange(aViewId, aChange, aArg);
}
}
void
TabParent::NotifyMouseScrollTestEvent(const ViewID& aScrollId, const nsString& aEvent)
{
if (!mIsDestroyed) {
Unused << SendMouseScrollTestEvent(aScrollId, aEvent);
}
}
void
TabParent::NotifyFlushComplete()
{
if (!mIsDestroyed) {
Unused << SendNotifyFlushComplete();
}
}
void
TabParent::Activate()
{
@ -1400,33 +1322,6 @@ CSSPoint TabParent::AdjustTapToChildWidget(const CSSPoint& aPoint)
return aPoint + (LayoutDevicePoint(GetChildProcessOffset()) * GetLayoutDeviceToCSSScale());
}
bool TabParent::SendHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
{
if (mIsDestroyed) {
return false;
}
return PBrowserParent::SendHandleSingleTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid);
}
bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
{
if (mIsDestroyed) {
return false;
}
return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid, aInputBlockId);
}
bool TabParent::SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
{
if (mIsDestroyed) {
return false;
}
return PBrowserParent::SendHandleDoubleTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid);
}
bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
{
if (mIsDestroyed) {
@ -2796,29 +2691,6 @@ TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
return true;
}
bool
TabParent::RecvZoomToRect(const uint32_t& aPresShellId,
const ViewID& aViewId,
const CSSRect& aRect,
const uint32_t& aFlags)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->ZoomToRect(aPresShellId, aViewId, aRect, aFlags);
}
return true;
}
bool
TabParent::RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
const ViewID& aViewId,
const MaybeZoomConstraints& aConstraints)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->UpdateZoomConstraints(aPresShellId, aViewId, aConstraints);
}
return true;
}
bool
TabParent::RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
const bool& aStartSwipe)
@ -2829,46 +2701,6 @@ TabParent::RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
return true;
}
bool
TabParent::RecvContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const bool& aPreventDefault)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->ContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
}
return true;
}
bool
TabParent::RecvSetTargetAPZC(const uint64_t& aInputBlockId,
nsTArray<ScrollableLayerGuid>&& aTargets)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->SetTargetAPZC(aInputBlockId, aTargets);
}
return true;
}
bool
TabParent::RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMetrics)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->StartScrollbarDrag(aDragMetrics);
}
return true;
}
bool
TabParent::RecvSetAllowedTouchBehavior(const uint64_t& aInputBlockId,
nsTArray<TouchBehaviorFlags>&& aFlags)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->SetAllowedTouchBehavior(aInputBlockId, aFlags);
}
return true;
}
already_AddRefed<nsILoadContext>
TabParent::GetLoadContext()
{

Просмотреть файл

@ -50,8 +50,6 @@ class CpowHolder;
} // namespace jsipc
namespace layers {
class AsyncDragMetrics;
struct FrameMetrics;
struct TextureFactoryIdentifier;
} // namespace layers
@ -90,7 +88,6 @@ class TabParent final : public PBrowserParent
, public nsIWebBrowserPersistable
{
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics;
virtual ~TabParent();
@ -297,31 +294,9 @@ public:
virtual bool RecvDispatchFocusToTopLevelWindow() override;
virtual bool RecvZoomToRect(const uint32_t& aPresShellId,
const ViewID& aViewId,
const CSSRect& aRect,
const uint32_t& aFlags) override;
virtual bool
RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
const ViewID& aViewId,
const MaybeZoomConstraints& aConstraints) override;
virtual bool RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
const bool& aStartSwipe) override;
virtual bool
RecvContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const bool& aPreventDefault) override;
virtual bool RecvSetTargetAPZC(const uint64_t& aInputBlockId,
nsTArray<ScrollableLayerGuid>&& aTargets) override;
virtual bool
RecvSetAllowedTouchBehavior(const uint64_t& aInputBlockId,
nsTArray<TouchBehaviorFlags>&& aTargets) override;
virtual bool
RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) override;
@ -331,9 +306,6 @@ public:
virtual bool
RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;
virtual bool
RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override;
virtual PColorPickerParent*
AllocPColorPickerParent(const nsString& aTitle,
const nsString& aInitialColor) override;
@ -365,8 +337,6 @@ public:
void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
void UIResolutionChanged();
void ThemeChanged();
@ -375,34 +345,6 @@ public:
const bool& aIsTrusted,
const int32_t& aModifierMask);
void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId,
const mozilla::CSSPoint& aDestination);
void AcknowledgeScrollUpdate(const ViewID& aScrollId,
const uint32_t& aScrollGeneration);
void HandleDoubleTap(const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid);
void HandleSingleTap(const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid);
void HandleLongTap(const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId);
void NotifyAPZStateChange(ViewID aViewId,
APZStateChange aChange,
int aArg);
void NotifyMouseScrollTestEvent(const ViewID& aScrollId,
const nsString& aEvent);
void NotifyFlushComplete();
void Activate();
void Deactivate();
@ -482,19 +424,6 @@ public:
bool SendRealTouchEvent(WidgetTouchEvent& event);
bool SendHandleSingleTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid);
bool SendHandleLongTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId);
bool SendHandleDoubleTap(const CSSPoint& aPoint,
const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid);
virtual PDocumentRendererParent*
AllocPDocumentRendererParent(const nsRect& documentRect,
const gfx::Matrix& transform,

Просмотреть файл

@ -52,7 +52,6 @@ GraphDriver::GraphDriver(MediaStreamGraphImpl* aGraphImpl)
mIterationEnd(0),
mGraphImpl(aGraphImpl),
mWaitState(WAITSTATE_RUNNING),
mAudioInput(nullptr),
mCurrentTimeStamp(TimeStamp::Now()),
mPreviousDriver(nullptr),
mNextDriver(nullptr)
@ -546,6 +545,7 @@ StreamAndPromiseForOperation::StreamAndPromiseForOperation(MediaStream* aStream,
AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl)
: GraphDriver(aGraphImpl)
, mSampleRate(0)
, mInputChannels(1)
, mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS)
, mStarted(false)
, mAudioInput(nullptr)
@ -604,7 +604,7 @@ AudioCallbackDriver::Init()
}
input = output;
input.channels = 1; // change to support optional stereo capture
input.channels = mInputChannels; // change to support optional stereo capture
cubeb_stream* stream;
// XXX Only pass input input if we have an input listener. Always
@ -922,14 +922,14 @@ AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
// removed/added to this list and TSAN issues, but input and output will
// use separate callback methods.
mGraphImpl->NotifyOutputData(aOutputBuffer, static_cast<size_t>(aFrames),
ChannelCount);
mSampleRate, ChannelCount);
// Process mic data if any/needed -- after inserting far-end data for AEC!
if (aInputBuffer) {
if (mAudioInput) { // for this specific input-only or full-duplex stream
mAudioInput->NotifyInputData(mGraphImpl, aInputBuffer,
static_cast<size_t>(aFrames),
ChannelCount);
mSampleRate, mInputChannels);
}
}

Просмотреть файл

@ -197,15 +197,6 @@ public:
virtual bool OnThread() = 0;
// These are invoked on the MSG thread (or MainThread in shutdown)
virtual void SetInputListener(AudioDataListener *aListener) {
mAudioInput = aListener;
}
// XXX do we need the param? probably no
virtual void RemoveInputListener(AudioDataListener *aListener) {
mAudioInput = nullptr;
}
protected:
GraphTime StateComputedTime() const;
@ -236,9 +227,6 @@ protected:
// This must be access with the monitor.
WaitState mWaitState;
// Callback for mic data, if any
AudioDataListener *mAudioInput;
// This is used on the main thread (during initialization), and the graph
// thread. No monitor needed because we know the graph thread does not run
// during the initialization.
@ -421,6 +409,17 @@ public:
uint32_t aFrames,
uint32_t aSampleRate) override;
// These are invoked on the MSG thread (we don't call this if not LIFECYCLE_RUNNING)
virtual void SetInputListener(AudioDataListener *aListener) {
MOZ_ASSERT(OnThread());
mAudioInput = aListener;
}
// XXX do we need the param? probably no
virtual void RemoveInputListener(AudioDataListener *aListener) {
MOZ_ASSERT(OnThread());
mAudioInput = nullptr;
}
AudioCallbackDriver* AsAudioCallbackDriver() override {
return this;
}
@ -486,6 +485,9 @@ private:
/* The sample rate for the aforementionned cubeb stream. This is set on
* initialization and can be read safely afterwards. */
uint32_t mSampleRate;
/* The number of input channels from cubeb. Should be set before opening cubeb
* and then be static. */
uint32_t mInputChannels;
/* Approximation of the time between two callbacks. This is used to schedule
* video frames. This is in milliseconds. Only even used (after
* inizatialization) on the audio callback thread. */

Просмотреть файл

@ -634,13 +634,15 @@ AudioDevice::GetSource()
}
nsresult VideoDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs) {
return GetSource()->Allocate(aConstraints, aPrefs, mID);
const MediaEnginePrefs &aPrefs,
const nsACString& aOrigin) {
return GetSource()->Allocate(aConstraints, aPrefs, mID, aOrigin);
}
nsresult AudioDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs) {
return GetSource()->Allocate(aConstraints, aPrefs, mID);
const MediaEnginePrefs &aPrefs,
const nsACString& aOrigin) {
return GetSource()->Allocate(aConstraints, aPrefs, mID, aOrigin);
}
nsresult VideoDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
@ -1203,7 +1205,8 @@ public:
nsresult rv;
if (mAudioDevice) {
rv = mAudioDevice->Allocate(GetInvariant(mConstraints.mAudio), mPrefs);
rv = mAudioDevice->Allocate(GetInvariant(mConstraints.mAudio),
mPrefs, mOrigin);
if (NS_FAILED(rv)) {
LOG(("Failed to allocate audiosource %d",rv));
Fail(NS_LITERAL_STRING("SourceUnavailableError"),
@ -1212,7 +1215,8 @@ public:
}
}
if (mVideoDevice) {
rv = mVideoDevice->Allocate(GetInvariant(mConstraints.mVideo), mPrefs);
rv = mVideoDevice->Allocate(GetInvariant(mConstraints.mVideo),
mPrefs, mOrigin);
if (NS_FAILED(rv)) {
LOG(("Failed to allocate videosource %d\n",rv));
if (mAudioDevice) {

Просмотреть файл

@ -94,7 +94,8 @@ public:
NS_IMETHOD GetType(nsAString& aType);
Source* GetSource();
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs);
const MediaEnginePrefs &aPrefs,
const nsACString& aOrigin);
nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs);
};
@ -108,7 +109,8 @@ public:
NS_IMETHOD GetType(nsAString& aType);
Source* GetSource();
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs);
const MediaEnginePrefs &aPrefs,
const nsACString& aOrigin);
nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs);
};

Просмотреть файл

@ -938,23 +938,37 @@ void
MediaStreamGraphImpl::OpenAudioInputImpl(CubebUtils::AudioDeviceID aID,
AudioDataListener *aListener)
{
// Bug 1238038 Need support for multiple mics at once
MOZ_ASSERT(!mInputWanted);
if (mInputWanted) {
// Bug 1238038 Need support for multiple mics at once
if (mInputDeviceUsers.Count() > 0 &&
!mInputDeviceUsers.Get(aListener, nullptr)) {
NS_ASSERTION(false, "Input from multiple mics not yet supported; bug 1238038");
// Need to support separate input-only AudioCallback drivers; they'll
// call us back on "other" threads. We will need to echo-cancel them, though.
return;
}
mInputWanted = true;
// Add to count of users for this ID.
// XXX Since we can't rely on IDs staying valid (ugh), use the listener as
// a stand-in for the ID. Fix as part of support for multiple-captures
// (Bug 1238038)
uint32_t count = 0;
mInputDeviceUsers.Get(aListener, &count); // ok if this fails
count++;
mInputDeviceUsers.Put(aListener, count); // creates a new entry in the hash if needed
// aID is a cubeb_devid, and we assume that opaque ptr is valid until
// we close cubeb.
mInputDeviceID = aID;
mAudioInputs.AppendElement(aListener); // always monitor speaker data
if (count == 1) { // first open for this listener
mAudioInputs.AppendElement(aListener); // always monitor speaker data
}
// Switch Drivers since we're adding input (to input-only or full-duplex)
MonitorAutoLock mon(mMonitor);
if (mLifecycleState == LIFECYCLE_RUNNING) {
AudioCallbackDriver* driver = new AudioCallbackDriver(this);
driver->SetInputListener(aListener);
CurrentDriver()->SwitchAtNextIteration(driver);
}
}
@ -985,6 +999,7 @@ MediaStreamGraphImpl::OpenAudioInput(CubebUtils::AudioDeviceID aID,
CubebUtils::AudioDeviceID mID;
RefPtr<AudioDataListener> mListener;
};
// XXX Check not destroyed!
this->AppendMessage(MakeUnique<Message>(this, aID, aListener));
return NS_OK;
}
@ -992,9 +1007,20 @@ MediaStreamGraphImpl::OpenAudioInput(CubebUtils::AudioDeviceID aID,
void
MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
{
uint32_t count;
DebugOnly<bool> result = mInputDeviceUsers.Get(aListener, &count);
MOZ_ASSERT(result);
if (--count > 0) {
mInputDeviceUsers.Put(aListener, count);
return; // still in use
}
mInputDeviceUsers.Remove(aListener);
mInputDeviceID = nullptr;
mInputWanted = false;
CurrentDriver()->RemoveInputListener(aListener);
AudioCallbackDriver *driver = CurrentDriver()->AsAudioCallbackDriver();
if (driver) {
driver->RemoveInputListener(aListener);
}
mAudioInputs.RemoveElement(aListener);
// Switch Drivers since we're adding or removing an input (to nothing/system or output only)
@ -1059,10 +1085,10 @@ MediaStreamGraphImpl::CloseAudioInput(AudioDataListener *aListener)
// All AudioInput listeners get the same speaker data (at least for now).
void
MediaStreamGraph::NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels)
TrackRate aRate, uint32_t aChannels)
{
for (auto& listener : mAudioInputs) {
listener->NotifyOutputData(this, aBuffer, aFrames, aChannels);
listener->NotifyOutputData(this, aBuffer, aFrames, aRate, aChannels);
}
}
@ -2295,9 +2321,32 @@ MediaStream::AddMainThreadListener(MainThreadMediaStreamListener* aListener)
NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())));
}
nsresult
SourceMediaStream::OpenAudioInput(CubebUtils::AudioDeviceID aID,
AudioDataListener *aListener)
{
if (GraphImpl()) {
mInputListener = aListener;
return GraphImpl()->OpenAudioInput(aID, aListener);
}
return NS_ERROR_FAILURE;
}
void
SourceMediaStream::CloseAudioInput()
{
// Destroy() may have run already and cleared this
if (GraphImpl() && mInputListener) {
GraphImpl()->CloseAudioInput(mInputListener);
}
mInputListener = nullptr;
}
void
SourceMediaStream::DestroyImpl()
{
CloseAudioInput();
// Hold mMutex while mGraph is reset so that other threads holding mMutex
// can null-check know that the graph will not destroyed.
MutexAutoLock lock(mMutex);

Просмотреть файл

@ -197,14 +197,14 @@ public:
*/
virtual void NotifyOutputData(MediaStreamGraph* aGraph,
AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) = 0;
TrackRate aRate, uint32_t aChannels) = 0;
/**
* Input data from a microphone (or other audio source. This is not
* guaranteed to be in any particular size chunks.
*/
virtual void NotifyInputData(MediaStreamGraph* aGraph,
const AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) = 0;
TrackRate aRate, uint32_t aChannels) = 0;
};
class AudioDataListener : public AudioDataListenerInterface {
@ -734,6 +734,16 @@ public:
SourceMediaStream* AsSourceStream() override { return this; }
// Media graph thread only
// Users of audio inputs go through the stream so it can track when the
// last stream referencing an input goes away, so it can close the cubeb
// input. Also note: callable on any thread (though it bounces through
// MainThread to set the command if needed).
nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID,
AudioDataListener *aListener);
// Note: also implied when Destroy() happens
void CloseAudioInput();
void DestroyImpl() override;
// Call these on any thread.
@ -920,6 +930,12 @@ protected:
void NotifyDirectConsumers(TrackData *aTrack,
MediaSegment *aSegment);
// Only accessed on the MSG thread. Used so to ask the MSGImpl to usecount
// users of a specific input.
// XXX Should really be a CubebUtils::AudioDeviceID, but they aren't
// copyable (opaque pointers)
RefPtr<AudioDataListener> mInputListener;
// This must be acquired *before* MediaStreamGraphImpl's lock, if they are
// held together.
Mutex mMutex;
@ -1298,7 +1314,7 @@ public:
* to notify any listeners (for echo cancellation).
*/
void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels);
TrackRate aRate, uint32_t aChannels);
protected:
explicit MediaStreamGraph(TrackRate aSampleRate)

Просмотреть файл

@ -8,6 +8,8 @@
#include "MediaStreamGraph.h"
#include "nsDataHashtable.h"
#include "mozilla/Monitor.h"
#include "mozilla/TimeStamp.h"
#include "nsIMemoryReporter.h"
@ -629,6 +631,9 @@ public:
CubebUtils::AudioDeviceID mInputDeviceID;
bool mOutputWanted;
CubebUtils::AudioDeviceID mOutputDeviceID;
// Maps AudioDataListeners to a usecount of streams using the listener
// so we can know when it's no longer in use.
nsDataHashtable<nsPtrHashKey<AudioDataListener>, uint32_t> mInputDeviceUsers;
// True if the graph needs another iteration after the current iteration.
Atomic<bool> mNeedAnotherIteration;

Просмотреть файл

@ -169,7 +169,7 @@ void
TrackBuffersManager::ResetParserState()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running, abort must have been called");
MOZ_DIAGNOSTIC_ASSERT(!mAppendRunning, "Append is running, abort must have been called");
MSE_DEBUG("");
// 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed.
@ -187,7 +187,7 @@ RefPtr<TrackBuffersManager::RangeRemovalPromise>
TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running");
MOZ_DIAGNOSTIC_ASSERT(!mAppendRunning, "Append is running");
MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds());
mEnded = false;
@ -301,7 +301,7 @@ void
TrackBuffersManager::CompleteResetParserState()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_RELEASE_ASSERT(!mSegmentParserLoopRunning);
MOZ_DIAGNOSTIC_ASSERT(!mSegmentParserLoopRunning);
MSE_DEBUG("");
for (auto& track : GetTracksList()) {
@ -539,7 +539,7 @@ RefPtr<TrackBuffersManager::AppendPromise>
TrackBuffersManager::InitSegmentParserLoop()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_RELEASE_ASSERT(mAppendPromise.IsEmpty());
MOZ_DIAGNOSTIC_ASSERT(mAppendPromise.IsEmpty());
MSE_DEBUG("");
RefPtr<AppendPromise> p = mAppendPromise.Ensure(__func__);

Просмотреть файл

@ -157,7 +157,7 @@ public:
NS_IMETHOD Run() {
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
if (mBackend == LayersBackend::LAYERS_D3D11 &&
Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", false) &&
Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", true) &&
IsWin8OrLater()) {
mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(mFailureReason);
} else {

Просмотреть файл

@ -351,13 +351,15 @@ int
CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int unique_idUTF8Length,
int& capture_id)
int& capture_id,
const nsACString& aOrigin)
{
LOG((__PRETTY_FUNCTION__));
nsCString unique_id(unique_idUTF8);
nsCString origin(aOrigin);
nsCOMPtr<nsIRunnable> runnable =
media::NewRunnableFrom([this, aCapEngine, unique_id]() -> nsresult {
if (this->SendAllocateCaptureDevice(aCapEngine, unique_id)) {
media::NewRunnableFrom([this, aCapEngine, unique_id, origin]() -> nsresult {
if (this->SendAllocateCaptureDevice(aCapEngine, unique_id, origin)) {
return NS_OK;
}
return NS_ERROR_FAILURE;

Просмотреть файл

@ -181,7 +181,8 @@ public:
int AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int unique_idUTF8Length,
int& capture_id);
int& capture_id,
const nsACString& aOrigin);
int GetCaptureCapability(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int capability_number,

Просмотреть файл

@ -11,10 +11,14 @@
#include "mozilla/Assertions.h"
#include "mozilla/unused.h"
#include "mozilla/Services.h"
#include "mozilla/Logging.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/Preferences.h"
#include "nsIPermissionManager.h"
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#include "nsNetUtil.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
@ -641,39 +645,127 @@ CamerasParent::RecvGetCaptureDevice(const int& aCapEngine,
return true;
}
static nsresult
GetPrincipalFromOrigin(const nsACString& aOrigin, nsIPrincipal** aPrincipal)
{
nsAutoCString originNoSuffix;
mozilla::PrincipalOriginAttributes attrs;
if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
principal.forget(aPrincipal);
return NS_OK;
}
// Find out whether the given origin has permission to use the
// camera. If the permission is not persistent, we'll make it
// a one-shot by removing the (session) permission.
static bool
HasCameraPermission(const nsCString& aOrigin)
{
// Name used with nsIPermissionManager
static const char* cameraPermission = "camera";
bool allowed = false;
bool permanent = false;
nsresult rv;
nsCOMPtr<nsIPermissionManager> mgr =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIIOService> ioServ(do_GetIOService());
nsCOMPtr<nsIURI> uri;
rv = ioServ->NewURI(aOrigin, nullptr, nullptr, getter_AddRefs(uri));
if (NS_SUCCEEDED(rv)) {
// Permanent permissions are only retrievable via principal, not uri
nsCOMPtr<nsIPrincipal> principal;
rv = GetPrincipalFromOrigin(aOrigin, getter_AddRefs(principal));
if (NS_SUCCEEDED(rv)) {
uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
rv = mgr->TestExactPermissionFromPrincipal(principal,
cameraPermission,
&video);
if (NS_SUCCEEDED(rv)) {
allowed = (video == nsIPermissionManager::ALLOW_ACTION);
// Was allowed, now see if this is a persistent permission
// or a session one.
if (allowed) {
rv = mgr->TestExactPermanentPermission(principal,
cameraPermission,
&video);
if (NS_SUCCEEDED(rv)) {
permanent = (video == nsIPermissionManager::ALLOW_ACTION);
}
}
}
// Session permissions are removed after one use.
if (allowed && !permanent) {
mgr->RemoveFromPrincipal(principal, cameraPermission);
}
}
}
}
return allowed;
}
bool
CamerasParent::RecvAllocateCaptureDevice(const int& aCapEngine,
const nsCString& unique_id)
const nsCString& unique_id,
const nsCString& aOrigin)
{
LOG((__PRETTY_FUNCTION__));
LOG(("%s: Verifying permissions for %s", __PRETTY_FUNCTION__, aOrigin.get()));
RefPtr<CamerasParent> self(this);
RefPtr<nsRunnable> webrtc_runnable =
media::NewRunnableFrom([self, aCapEngine, unique_id]() -> nsresult {
int numdev = -1;
int error = -1;
if (self->EnsureInitialized(aCapEngine)) {
error = self->mEngines[aCapEngine].mPtrViECapture->AllocateCaptureDevice(
unique_id.get(), MediaEngineSource::kMaxUniqueIdLength, numdev);
RefPtr<nsRunnable> mainthread_runnable =
media::NewRunnableFrom([self, aCapEngine, unique_id, aOrigin]() -> nsresult {
// Verify whether the claimed origin has received permission
// to use the camera, either persistently or this session (one shot).
bool allowed = HasCameraPermission(aOrigin);
if (!allowed) {
// Developer preference for turning off permission check.
if (Preferences::GetBool("media.navigator.permission.disabled", false)
|| Preferences::GetBool("media.navigator.permission.fake")) {
allowed = true;
LOG(("No permission but checks are disabled or fake sources active"));
} else {
LOG(("No camera permission for this origin"));
}
}
RefPtr<nsIRunnable> ipc_runnable =
media::NewRunnableFrom([self, numdev, error]() -> nsresult {
if (self->IsShuttingDown()) {
return NS_ERROR_FAILURE;
}
if (error) {
Unused << self->SendReplyFailure();
return NS_ERROR_FAILURE;
} else {
LOG(("Allocated device nr %d", numdev));
Unused << self->SendReplyAllocateCaptureDevice(numdev);
return NS_OK;
}
// After retrieving the permission (or not) on the main thread,
// bounce to the WebRTC thread to allocate the device (or not),
// then bounce back to the IPC thread for the reply to content.
RefPtr<nsRunnable> webrtc_runnable =
media::NewRunnableFrom([self, allowed, aCapEngine, unique_id]() -> nsresult {
int numdev = -1;
int error = -1;
if (allowed && self->EnsureInitialized(aCapEngine)) {
error = self->mEngines[aCapEngine].mPtrViECapture->AllocateCaptureDevice(
unique_id.get(), MediaEngineSource::kMaxUniqueIdLength, numdev);
}
RefPtr<nsIRunnable> ipc_runnable =
media::NewRunnableFrom([self, numdev, error]() -> nsresult {
if (self->IsShuttingDown()) {
return NS_ERROR_FAILURE;
}
if (error) {
Unused << self->SendReplyFailure();
return NS_ERROR_FAILURE;
} else {
LOG(("Allocated device nr %d", numdev));
Unused << self->SendReplyAllocateCaptureDevice(numdev);
return NS_OK;
}
});
self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
return NS_OK;
});
self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
self->DispatchToVideoCaptureThread(webrtc_runnable);
return NS_OK;
});
DispatchToVideoCaptureThread(webrtc_runnable);
NS_DispatchToMainThread(mainthread_runnable);
return true;
}

Просмотреть файл

@ -86,7 +86,7 @@ public:
static already_AddRefed<CamerasParent> Create();
// Messages received form the child. These run on the IPC/PBackground thread.
virtual bool RecvAllocateCaptureDevice(const int&, const nsCString&) override;
virtual bool RecvAllocateCaptureDevice(const int&, const nsCString&, const nsCString&) override;
virtual bool RecvReleaseCaptureDevice(const int&, const int &) override;
virtual bool RecvNumberOfCaptureDevices(const int&) override;
virtual bool RecvNumberOfCapabilities(const int&, const nsCString&) override;

Просмотреть файл

@ -45,7 +45,7 @@ parent:
async GetCaptureCapability(int engine, nsCString unique_idUTF8, int capability_number);
async GetCaptureDevice(int engine, int num);
async AllocateCaptureDevice(int engine, nsCString unique_idUTF8);
async AllocateCaptureDevice(int engine, nsCString unique_idUTF8, nsCString origin);
async ReleaseCaptureDevice(int engine, int numdev);
async StartCapture(int engine, int numdev, CaptureCapability capability);
async StopCapture(int engine, int numdev);

Просмотреть файл

@ -67,6 +67,7 @@ for (var i=0; i<gSmallTests.length; ++i) {
}
// ensure metadata is loaded for default preload is none on b2g
v.preload = "metadata";
v.autoplay = "true";
v._complete = false;
v.addEventListener("error", loadError, false);
v.addEventListener("loadedmetadata", loadedMetadata, false);

Просмотреть файл

@ -171,7 +171,8 @@ public:
/* This call reserves but does not start the device. */
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId) = 0;
const nsString& aDeviceId,
const nsACString& aOrigin) = 0;
virtual uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,

Просмотреть файл

@ -87,7 +87,8 @@ MediaEngineDefaultVideoSource::GetBestFitnessDistance(
nsresult
MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
const nsString& aDeviceId,
const nsACString& aOrigin)
{
if (mState != kReleased) {
return NS_ERROR_FAILURE;
@ -396,7 +397,8 @@ MediaEngineDefaultAudioSource::GetBestFitnessDistance(
nsresult
MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
const nsString& aDeviceId,
const nsACString& aOrigin)
{
if (mState != kReleased) {
return NS_ERROR_FAILURE;

Просмотреть файл

@ -45,7 +45,8 @@ public:
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId) override;
const nsString& aDeviceId,
const nsACString& aOrigin) override;
nsresult Deallocate() override;
nsresult Start(SourceMediaStream*, TrackID) override;
nsresult Stop(SourceMediaStream*, TrackID) override;
@ -114,7 +115,8 @@ public:
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId) override;
const nsString& aDeviceId,
const nsACString& aOrigin) override;
nsresult Deallocate() override;
nsresult Start(SourceMediaStream*, TrackID) override;
nsresult Stop(SourceMediaStream*, TrackID) override;
@ -138,11 +140,11 @@ public:
void NotifyOutputData(MediaStreamGraph* aGraph,
AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) override
TrackRate aRate, uint32_t aChannels) override
{}
void NotifyInputData(MediaStreamGraph* aGraph,
const AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) override
TrackRate aRate, uint32_t aChannels) override
{}
bool IsFake() override {
return true;

Просмотреть файл

@ -100,7 +100,8 @@ MediaEngineRemoteVideoSource::Shutdown()
nsresult
MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId)
const nsString& aDeviceId,
const nsACString& aOrigin)
{
LOG((__PRETTY_FUNCTION__));
AssertIsOnOwningThread();
@ -120,11 +121,12 @@ MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstr
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::AllocateCaptureDevice,
mCapEngine, GetUUID().get(), kMaxUniqueIdLength, mCaptureIndex)) {
mCapEngine, GetUUID().get(), kMaxUniqueIdLength, mCaptureIndex, aOrigin)) {
return NS_ERROR_FAILURE;
}
mState = kAllocated;
LOG(("Video device %d allocated", mCaptureIndex));
LOG(("Video device %d allocated for %s", mCaptureIndex,
PromiseFlatCString(aOrigin).get()));
} else if (MOZ_LOG_TEST(GetMediaManagerLog(), mozilla::LogLevel::Debug)) {
MonitorAutoLock lock(mMonitor);
if (mSources.IsEmpty()) {

Просмотреть файл

@ -73,7 +73,8 @@ public:
nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId) override;
const nsString& aDeviceId,
const nsACString& aOrigin) override;
nsresult Deallocate() override;;
nsresult Start(SourceMediaStream*, TrackID) override;
nsresult Stop(SourceMediaStream*, TrackID) override;

Просмотреть файл

@ -128,7 +128,8 @@ MediaEngineTabVideoSource::GetUUID(nsACString_internal& aUuid)
nsresult
MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId)
const nsString& aDeviceId,
const nsACString& aOrigin)
{
// windowId is not a proper constraint, so just read it.
// It has no well-defined behavior in advanced, so ignore it there.

Просмотреть файл

@ -23,7 +23,8 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
void GetUUID(nsACString_internal&) override;
nsresult Allocate(const dom::MediaTrackConstraints &,
const mozilla::MediaEnginePrefs&,
const nsString& aDeviceId) override;
const nsString& aDeviceId,
const nsACString& aOrigin) override;
nsresult Deallocate() override;
nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) override;
void SetDirectListeners(bool aHasDirectListeners) override {};

Просмотреть файл

@ -14,6 +14,7 @@
#include "mozilla/dom/File.h"
#include "mozilla/Mutex.h"
#include "mozilla/Monitor.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
#include "DOMMediaStream.h"
@ -72,7 +73,8 @@ public:
void GetUUID(nsACString& aUUID) override;
nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId) override
const nsString& aDeviceId,
const nsACString& aOrigin) override
{
// Nothing to do here, everything is managed in MediaManager.cpp
return NS_OK;
@ -95,11 +97,11 @@ public:
{}
void NotifyOutputData(MediaStreamGraph* aGraph,
AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) override
TrackRate aRate, uint32_t aChannels) override
{}
void NotifyInputData(MediaStreamGraph* aGraph,
const AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) override
TrackRate aRate, uint32_t aChannels) override
{}
void NotifyPull(MediaStreamGraph* aGraph, SourceMediaStream* aSource,
TrackID aID, StreamTime aDesiredTime) override
@ -138,8 +140,8 @@ public:
virtual int GetRecordingDeviceName(int aIndex, char aStrNameUTF8[128],
char aStrGuidUTF8[128]) = 0;
virtual int GetRecordingDeviceStatus(bool& aIsAvailable) = 0;
virtual void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener) = 0;
virtual void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener) = 0;
virtual void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) = 0;
virtual void StopRecording(SourceMediaStream *aStream) = 0;
virtual int SetRecordingDevice(int aIndex) = 0;
protected:
@ -153,7 +155,7 @@ class AudioInputCubeb final : public AudioInput
{
public:
explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine, int aIndex = 0) :
AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUse(false)
AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUseCount(0)
{
if (!mDeviceIndexes) {
mDeviceIndexes = new nsTArray<int>;
@ -214,25 +216,29 @@ public:
return 0;
}
void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener)
{
MOZ_ASSERT(mDevices);
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoERender;
ptrVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
if (ptrVoERender) {
ptrVoERender->SetExternalRecordingStatus(true);
if (mInUseCount == 0) {
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoERender;
ptrVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
if (ptrVoERender) {
ptrVoERender->SetExternalRecordingStatus(true);
}
mAnyInUse = true;
}
aGraph->OpenAudioInput(mDevices->device[mSelectedDevice]->devid, aListener);
mInUse = true;
mAnyInUse = true;
mInUseCount++;
// Always tell the stream we're using it for input
aStream->OpenAudioInput(mDevices->device[mSelectedDevice]->devid, aListener);
}
void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener)
void StopRecording(SourceMediaStream *aStream)
{
aGraph->CloseAudioInput(aListener);
mInUse = false;
mAnyInUse = false;
aStream->CloseAudioInput();
if (--mInUseCount == 0) {
mAnyInUse = false;
}
}
int SetRecordingDevice(int aIndex)
@ -247,7 +253,7 @@ public:
protected:
~AudioInputCubeb() {
MOZ_RELEASE_ASSERT(!mInUse);
MOZ_RELEASE_ASSERT(mInUseCount == 0);
}
private:
@ -270,10 +276,14 @@ private:
// Calculate translation from existing mDevices to new devices. Note we
// never end up with less devices than before, since people have
// stashed indexes.
// For some reason the "fake" device for automation is marked as DISABLED,
// so white-list it.
for (uint32_t i = 0; i < devices->count; i++) {
if (devices->device[i]->type == CUBEB_DEVICE_TYPE_INPUT && // paranoia
(devices->device[i]->state == CUBEB_DEVICE_STATE_ENABLED ||
devices->device[i]->state == CUBEB_DEVICE_STATE_UNPLUGGED))
devices->device[i]->state == CUBEB_DEVICE_STATE_UNPLUGGED ||
(devices->device[i]->state == CUBEB_DEVICE_STATE_DISABLED &&
strcmp(devices->device[i]->friendly_name, "Sine source at 440 Hz") == 0)))
{
auto j = mDeviceNames->IndexOf(devices->device[i]->device_id);
if (j != nsTArray<nsCString>::NoIndex) {
@ -300,7 +310,7 @@ private:
// for this - and be careful of threading access. The mappings need to
// updated on each re-enumeration.
int mSelectedDevice;
bool mInUse; // for assertions about listener lifetime
uint32_t mInUseCount;
// pointers to avoid static constructors
static nsTArray<int>* mDeviceIndexes;
@ -347,8 +357,8 @@ public:
return 0;
}
void StartRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener) {}
void StopRecording(MediaStreamGraph *aGraph, AudioDataListener *aListener) {}
void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) {}
void StopRecording(SourceMediaStream *aStream) {}
int SetRecordingDevice(int aIndex)
{
@ -379,15 +389,15 @@ public:
// AudioDataListenerInterface methods
virtual void NotifyOutputData(MediaStreamGraph* aGraph,
AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) override
TrackRate aRate, uint32_t aChannels) override
{
mAudioSource->NotifyOutputData(aGraph, aBuffer, aFrames, aChannels);
mAudioSource->NotifyOutputData(aGraph, aBuffer, aFrames, aRate, aChannels);
}
virtual void NotifyInputData(MediaStreamGraph* aGraph,
const AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) override
TrackRate aRate, uint32_t aChannels) override
{
mAudioSource->NotifyInputData(aGraph, aBuffer, aFrames, aChannels);
mAudioSource->NotifyInputData(aGraph, aBuffer, aFrames, aRate, aChannels);
}
private:
@ -421,7 +431,8 @@ public:
, mAGC(webrtc::kAgcDefault)
, mNoiseSuppress(webrtc::kNsDefault)
, mPlayoutDelay(0)
, mNullTransport(nullptr) {
, mNullTransport(nullptr)
, mInputBufferLen(0) {
MOZ_ASSERT(aVoiceEnginePtr);
MOZ_ASSERT(aAudioInput);
mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
@ -435,7 +446,8 @@ public:
nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId) override;
const nsString& aDeviceId,
const nsACString& aOrigin) override;
nsresult Deallocate() override;
nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
@ -452,10 +464,10 @@ public:
// AudioDataListenerInterface methods
void NotifyOutputData(MediaStreamGraph* aGraph,
AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) override;
TrackRate aRate, uint32_t aChannels) override;
void NotifyInputData(MediaStreamGraph* aGraph,
const AudioDataValue* aBuffer, size_t aFrames,
uint32_t aChannels) override;
TrackRate aRate, uint32_t aChannels) override;
bool IsFake() override {
return false;
@ -524,6 +536,10 @@ private:
int32_t mPlayoutDelay;
NullTransport *mNullTransport;
// For full_duplex packetizer output
size_t mInputBufferLen;
UniquePtr<int16_t[]> mInputBuffer;
};
class MediaEngineWebRTC : public MediaEngine

Просмотреть файл

@ -220,7 +220,8 @@ uint32_t MediaEngineWebRTCMicrophoneSource::GetBestFitnessDistance(
nsresult
MediaEngineWebRTCMicrophoneSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
const nsString& aDeviceId,
const nsACString& aOrigin)
{
AssertIsOnOwningThread();
if (mState == kReleased) {
@ -358,6 +359,8 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
if (mState == kStarted) {
MOZ_ASSERT(aID == mTrackID);
// Make sure we're associated with this stream
mAudioInput->StartRecording(aStream, mListener);
return NS_OK;
}
mState = kStarted;
@ -374,6 +377,10 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
if (mVoEBase->StartReceive(mChannel)) {
return NS_ERROR_FAILURE;
}
// Must be *before* StartSend() so it will notice we selected external input (full_duplex)
mAudioInput->StartRecording(aStream, mListener);
if (mVoEBase->StartSend(mChannel)) {
return NS_ERROR_FAILURE;
}
@ -381,8 +388,6 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
// Attach external media processor, so this::Process will be called.
mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this);
mAudioInput->StartRecording(aStream->Graph(), mListener);
return NS_OK;
}
@ -401,6 +406,7 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
aSource->EndTrack(aID);
if (!mSources.IsEmpty()) {
mAudioInput->StopRecording(aSource);
return NS_OK;
}
if (mState != kStarted) {
@ -413,7 +419,7 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
mState = kStopped;
}
mAudioInput->StopRecording(aSource->Graph(), mListener);
mAudioInput->StopRecording(aSource);
mVoERender->DeRegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel);
@ -440,6 +446,7 @@ void
MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph,
AudioDataValue* aBuffer,
size_t aFrames,
TrackRate aRate,
uint32_t aChannels)
{
}
@ -449,23 +456,29 @@ void
MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph,
const AudioDataValue* aBuffer,
size_t aFrames,
TrackRate aRate,
uint32_t aChannels)
{
// This will call Process() with data coming out of the AEC/NS/AGC/etc chain
if (!mPacketizer ||
mPacketizer->PacketSize() != mSampleFrequency/100 ||
mPacketizer->PacketSize() != aRate/100u ||
mPacketizer->Channels() != aChannels) {
// It's ok to drop the audio still in the packetizer here.
mPacketizer = new AudioPacketizer<AudioDataValue, int16_t>(mSampleFrequency/100, aChannels);
}
mPacketizer = new AudioPacketizer<AudioDataValue, int16_t>(aRate/100, aChannels);
}
mPacketizer->Input(aBuffer, static_cast<uint32_t>(aFrames));
while (mPacketizer->PacketsAvailable()) {
uint32_t samplesPerPacket = mPacketizer->PacketSize() *
mPacketizer->Channels();
int16_t* packet = mPacketizer->Output();
mVoERender->ExternalRecordingInsertData(packet, samplesPerPacket, mSampleFrequency, 0);
if (mInputBufferLen < samplesPerPacket) {
mInputBuffer = MakeUnique<int16_t[]>(samplesPerPacket);
}
int16_t *packet = mInputBuffer.get();
mPacketizer->Output(packet);
mVoERender->ExternalRecordingInsertData(packet, samplesPerPacket, aRate, 0);
}
}

Просмотреть файл

@ -114,6 +114,7 @@ DIRS += [
'manifest',
'vr',
'newapps',
'u2f',
]
if CONFIG['OS_ARCH'] == 'WINNT':

Просмотреть файл

@ -0,0 +1,6 @@
var Ci = Components.interfaces;
var Cc = Components.classes;
var am = Cc["@mozilla.org/network/http-auth-manager;1"].
getService(Ci.nsIHttpAuthManager);
am.setAuthIdentity("http", "mochi.test", 8888, "basic", "testrealm", "",
"mochi.test", "user1", "password1");

Просмотреть файл

@ -3,6 +3,7 @@ skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #b2g
support-files =
307-xo-redirect.sjs
crashing_subpage.html
file_authident.js
file_bug738396.html
file_bug771202.html
file_bug863792.html
@ -66,7 +67,6 @@ skip-if = !crashreporter || e10s
[test_enumerate.html]
[test_fullpage.html]
[test_getauthenticationinfo.html]
skip-if = e10s # Bug 1237834
[test_hanging.html]
skip-if = !crashreporter || e10s
[test_instance_re-parent.html]

Просмотреть файл

@ -61,11 +61,17 @@ function iframeLoad() {
SimpleTest.waitForExplicitFinish();
setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
// Authentication info is added twice here. In the non-e10s case, this does
// nothing. In the e10s case, we need to add auth info in both the child process,
// which the plugin checks for auth validity, and the parent process, which the
// http network objects use.
// TODO: Clean this up once HTTPAuthManager is made e10s compliant in bug 1249172
var iframe = document.getElementById("iframe");
var am = Cc["@mozilla.org/network/http-auth-manager;1"].
getService(Ci.nsIHttpAuthManager);
am.setAuthIdentity("http", "mochi.test", 8888, "basic", "testrealm", "",
"mochi.test", "user1", "password1");
SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("file_authident.js"));
iframe.onload = iframeLoad;
iframe.src = "http://mochi.test:8888/tests/toolkit/components/passwordmgr/" +
"test/authenticate.sjs?user=user1&pass=password1&realm=testrealm&plugin=1";

Просмотреть файл

@ -15,7 +15,7 @@
#include "nsWrapperCache.h"
#include "mozilla/ErrorResult.h"
class nsIDOMWindow;
class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {

Просмотреть файл

@ -98,6 +98,8 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' # b2g(Mouse selection not wor
[test_text_update.html]
[test_transform.xhtml]
[test_transformParsing.html]
[test_use_with_hsts.html]
support-files = use-with-hsts-helper.html use-with-hsts-helper.html^headers^
[test_valueAsString.xhtml]
[test_valueLeaks.xhtml]
[test_viewport.html]

Просмотреть файл

@ -0,0 +1,139 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1247733
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1247733</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247733">Mozilla Bug 1247733</a>
<p id="display">
<iframe id="myIframe"></iframe>
</p>
<div id="content" style="display: none">
</div>
<pre id="test"></pre>
<script type="application/javascript">
/** Test for Bug 1247733 **/
/**
* This test ensures that we render the SVG 'use' element correctly, in
* pages that have been upgraded from HTTP to HTTPS using strict transport
* security (HSTS)
*
* Specifically:
* (1) We load a file using HTTPS, in an iframe. The file gets sent
* with a Strict-Transport-Security flag.
* (2) We load the same file again, but now over HTTP (which should get
* upgraded to HTTPS, since we received the Strict-Transport-Security
* flag during the first load).
* (3) After each of the above loads, we take a snapshot of the iframe
* and ensure that it renders as fully lime (which the 'use' element
* is responsible for). If the 'use' element fails to render, the iframe
* will be fully red, and we'll fail an "assertSnapshots" check.
*/
SimpleTest.waitForExplicitFinish();
const iframe = document.getElementById("myIframe");
const iframeWin = iframe.contentWindow;
// URI for our testcase with 'use' element, via HTTP and HTTPS:
const insecureURI = "http://example.com/tests/dom/svg/test/use-with-hsts-helper.html";
const secureURI = "https://example.com/tests/dom/svg/test/use-with-hsts-helper.html";
// Snapshots that we'll populate below:
var blankSnapshot; // Snapshot of blank iframe.
var refSnapshot; // Snapshot of lime reference rendering in iframe.
var secureSnapshot; // Snapshot of testcase using HTTPS.
var upgradedSnapshot; // Snapshot of testcase using HTTP-upgraded-to-HTTPS.
// Bookkeeping to be sure receiveMessage is called as many times as we expect:
var numPostMessageCalls = 0;
const expectedNumPostMessageCalls = 2; // (We load the helper file twice.)
// Helper function, called via postMessage, to check iframe's actual location:
function receiveMessage(event) {
is(event.data, secureURI, "iframe should end up viewing secure URI");
numPostMessageCalls++;
}
// TEST CODE BEGINS HERE.
// Execution basically proceeds top-to-bottom through the functions
// from this point on, via a chain of iframe onload-callbacks.
function runTest() {
// Capture a snapshot with nothing in the iframe, so we can do a
// sanity-check not-equal comparison against our reference case, to be
// sure we're rendering anything at all:
blankSnapshot = snapshotWindow(iframeWin);
// Point iframe at a reference case:
iframe.onload = captureRefSnapshot;
iframe.src = "data:text/html,<body style='background:lime'>";
}
function captureRefSnapshot() {
// Capture the reference screenshot:
refSnapshot = snapshotWindow(iframeWin);
// Ensure reference-case looks different from blank snapshot:
assertSnapshots(refSnapshot, blankSnapshot,
false /*not equal*/, null /*no fuzz*/,
"refSnapshot", "blankSnapshot");
// OK, assuming we've got a valid refSnapshot, we can now proceed to
// capture test screenshots.
// Register a postMessage handler, so that iframe can report its location:
window.addEventListener("message", receiveMessage, false);
// Point iframe at secure (HTTPS) version of testcase, & wait for callback:
iframe.onload = captureSecureSnapshot;
iframe.src = secureURI;
}
function captureSecureSnapshot() {
// Capture snapshot of iframe showing always-HTTPS version of testcase:
secureSnapshot = snapshotWindow(iframeWin);
assertSnapshots(secureSnapshot, refSnapshot,
true /*equal*/, null /*no fuzz*/,
"secureSnapshot", "refSnapshot");
// Point iframe at insecure (HTTP) version of testcase (which should get
// automatically upgraded to secure (HTTPS) under the hood), & wait for
// callback:
iframe.onload = captureUpgradedSnapshot;
iframe.src = insecureURI;
}
function captureUpgradedSnapshot() {
// Double-check that iframe is really pointed at insecure URI, to be sure
// we're actually exercising HSTS. (Note that receiveMessage() will make
// sure it's been upgraded to a secure HTTPS URI under the hood.)
is(iframe.src, insecureURI,
"test should've attempted to load insecure HTTP URI, to exercise HSTS");
// Capture snapshot of iframe showing upgraded-to-HTTPS version of testcase:
upgradedSnapshot = snapshotWindow(iframeWin);
assertSnapshots(upgradedSnapshot, refSnapshot,
true /*equal*/, null /*no fuzz*/,
"upgradedSnapshot", "refSnapshot");
cleanupAndFinish();
}
function cleanupAndFinish() {
is(numPostMessageCalls, expectedNumPostMessageCalls,
"didn't receive as many messages from child iframe as expected");
SpecialPowers.cleanUpSTSData("http://example.com");
SimpleTest.finish();
}
runTest();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<script>
// Notify parent of our final URI:
window.parent.postMessage(window.location.href, "*");
</script>
<style>
html, body {
margin: 0;
height: 100%;
}
svg {
display: block;
height: 100%;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1">
<defs>
<rect id="limeRect" width="100%" height="100%" fill="lime"/>
</defs>
<rect width="100%" height="100%" fill="red"/>
<use xlink:href="#limeRect"/>
</svg>
</body>
</html>

Просмотреть файл

@ -0,0 +1,2 @@
Cache-Control: no-cache
Strict-Transport-Security: max-age=60

Просмотреть файл

@ -169,7 +169,7 @@ function testFormDataSend() {
}
is(response[1].headers['Content-Disposition'],
'form-data; name="empty"; filename=""');
'form-data; name="empty"; filename="blob"');
is(response[2].headers['Content-Disposition'],
'form-data; name="explicit"; filename="explicit-file-name"');

Просмотреть файл

@ -344,6 +344,7 @@ function testFormDataBodyCreation() {
ok(fd.has("more"), "more should exist.");
var b = fd.get("blob");
ok(b.name, "blob", "blob entry should be a Blob.");
ok(b instanceof Blob, "blob entry should be a Blob.");
return readAsText(b).then(function(output) {
@ -417,6 +418,7 @@ function testFormDataBodyExtraction() {
ok(fd.has("blob"), "Has entry 'blob'.");
var entries = fd.getAll("blob");
is(entries.length, 1, "getAll returns all items.");
is(entries[0].name, "blob", "Filename should be blob.");
ok(entries[0] instanceof Blob, "getAll returns blobs.");
});
@ -428,6 +430,7 @@ function testFormDataBodyExtraction() {
ok(fd.has("blob"), "Has entry 'blob'.");
var entries = fd.getAll("blob");
is(entries.length, 1, "getAll returns all items.");
is(entries[0].name, "blob", "Filename should be blob.");
ok(entries[0] instanceof Blob, "getAll returns blobs.");
ok(fd.has("key"), "Has entry 'key'.");

Просмотреть файл

@ -1377,6 +1377,8 @@ var interfaceNamesInGlobalScope =
{name: "TVSource", b2g: true, permission: ["tv"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "TVTuner", b2g: true, permission: ["tv"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "U2F", release: false},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "UDPMessageEvent", b2g: true, permission: ["udp-socket"]},
// IMPORTANT: Do not change this list without review from a DOM peer!

172
dom/u2f/NSSToken.cpp Normal file
Просмотреть файл

@ -0,0 +1,172 @@
/* -*- 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 "NSSToken.h"
#include "nsNSSComponent.h"
#include "pk11pub.h"
namespace mozilla {
namespace dom {
const nsString NSSToken::mVersion = NS_LITERAL_STRING("U2F_V2");
const uint32_t kParamLen = 32;
const uint32_t kPublicKeyLen = 65;
const uint32_t kSignedDataLen = (2 * kParamLen) + 1 + 4;
NSSToken::NSSToken()
: mInitialized(false)
, mMutex("NSSToken::mMutex")
{}
NSSToken::~NSSToken()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
destructorSafeDestroyNSSReference();
shutdown(calledFromObject);
}
void
NSSToken::virtualDestroyNSSReference()
{
destructorSafeDestroyNSSReference();
}
void
NSSToken::destructorSafeDestroyNSSReference()
{
mSlot = nullptr;
}
nsresult
NSSToken::Init()
{
MOZ_ASSERT(!mInitialized);
if (mInitialized) {
return NS_OK;
}
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
MutexAutoLock lock(mMutex);
if (!EnsureNSSInitializedChromeOrContent()) {
return NS_ERROR_FAILURE;
}
mSlot = PK11_GetInternalSlot();
if (!mSlot.get()) {
return NS_ERROR_FAILURE;
}
mInitialized = true;
return NS_OK;
}
bool
NSSToken::IsCompatibleVersion(const nsString& aVersionParam) const
{
MOZ_ASSERT(mInitialized);
return mVersion == aVersionParam;
}
/*
* IsRegistered determines if the provided key handle is usable by this token.
*/
bool
NSSToken::IsRegistered(const CryptoBuffer& aKeyHandle) const
{
MOZ_ASSERT(mInitialized);
return false;
}
/*
* A U2F Register operation causes a new key pair to be generated by the token.
* The token then returns the public key of the key pair, and a handle to the
* private key. The input parameters are used only for attestation, which this
* token does not provide. (We'll see how that works!)
*
* The format of the return registration data is as follows:
*
* Bytes Value
* 1 0x05
* 65 public key
* 1 key handle length
* * key handle
* * attestation certificate (omitted for now)
* * attestation signature (omitted for now)
*
*/
nsresult
NSSToken::Register(const CryptoBuffer& /* aChallengeParam */,
const CryptoBuffer& /* aApplicationParam */,
CryptoBuffer& aRegistrationData)
{
MOZ_ASSERT(mInitialized);
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
MutexAutoLock lock(mMutex);
if (!mInitialized) {
return NS_ERROR_NOT_INITIALIZED;
}
return NS_OK;
}
/*
* A U2F Sign operation creates a signature over the "param" arguments (plus
* some other stuff) using the private key indicated in the key handle argument.
*
* The format of the signed data is as follows:
*
* 32 Application parameter
* 1 User presence (0x01)
* 4 Counter
* 32 Challenge parameter
*
* The format of the signature data is as follows:
*
* 1 User presence
* 4 Counter
* * Signature
*
*/
nsresult
NSSToken::Sign(const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
const CryptoBuffer& aKeyHandle,
CryptoBuffer& aSignatureData)
{
MOZ_ASSERT(mInitialized);
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
MutexAutoLock lock(mMutex);
if (!mInitialized) {
return NS_ERROR_NOT_INITIALIZED;
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

57
dom/u2f/NSSToken.h Normal file
Просмотреть файл

@ -0,0 +1,57 @@
/* -*- 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_NSSToken_h
#define mozilla_dom_NSSToken_h
#include "mozilla/dom/CryptoBuffer.h"
#include "mozilla/Mutex.h"
#include "nsNSSShutDown.h"
#include "ScopedNSSTypes.h"
namespace mozilla {
namespace dom {
// NSSToken will support FIDO U2F operations using NSS for the crypto layer.
// This is a stub. It will be implemented in bug 1244960.
class NSSToken final : public nsNSSShutDownObject
{
public:
NSSToken();
~NSSToken();
nsresult Init();
bool IsCompatibleVersion(const nsString& aVersionParam) const;
bool IsRegistered(const CryptoBuffer& aKeyHandle) const;
nsresult Register(const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
CryptoBuffer& aRegistrationData);
nsresult Sign(const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
const CryptoBuffer& aKeyHandle,
CryptoBuffer& aSignatureData);
// For nsNSSShutDownObject
virtual void virtualDestroyNSSReference() override;
void destructorSafeDestroyNSSReference();
private:
bool mInitialized;
ScopedPK11SlotInfo mSlot;
mozilla::Mutex mMutex;
static const nsString mVersion;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_NSSToken_h

583
dom/u2f/U2F.cpp Normal file
Просмотреть файл

@ -0,0 +1,583 @@
/* -*- 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/CryptoBuffer.h"
#include "mozilla/dom/U2F.h"
#include "mozilla/dom/U2FBinding.h"
#include "mozilla/Preferences.h"
#include "nsContentUtils.h"
#include "nsIEffectiveTLDService.h"
#include "nsURLParsers.h"
#include "nsNetCID.h"
#include "pk11pub.h"
namespace mozilla {
namespace dom {
// These enumerations are defined in the FIDO U2F Javascript API under the
// interface "ErrorCode" as constant integers, and thus in the U2F.webidl file.
// Any changes to these must occur in both locations.
enum class ErrorCode {
OK = 0,
OTHER_ERROR = 1,
BAD_REQUEST = 2,
CONFIGURATION_UNSUPPORTED = 3,
DEVICE_INELIGIBLE = 4,
TIMEOUT = 5
};
#define PREF_U2F_SOFTTOKEN_ENABLED "security.webauth.u2f.softtoken"
#define PREF_U2F_USBTOKEN_ENABLED "security.webauth.u2f.usbtoken"
const nsString
U2F::FinishEnrollment = NS_LITERAL_STRING("navigator.id.finishEnrollment");
const nsString
U2F::GetAssertion = NS_LITERAL_STRING("navigator.id.getAssertion");
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(U2F)
NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
U2F::U2F()
{}
U2F::~U2F()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
shutdown(calledFromObject);
}
/* virtual */ JSObject*
U2F::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return U2FBinding::Wrap(aCx, this, aGivenProto);
}
void
U2F::Init(nsPIDOMWindowInner* aParent, ErrorResult& aRv)
{
MOZ_ASSERT(!mParent);
mParent = do_QueryInterface(aParent);
MOZ_ASSERT(mParent);
nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
MOZ_ASSERT(doc);
nsIPrincipal* principal = doc->NodePrincipal();
aRv = nsContentUtils::GetUTFOrigin(principal, mOrigin);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
if (NS_WARN_IF(mOrigin.IsEmpty())) {
return;
}
if (!EnsureNSSInitializedChromeOrContent()) {
return;
}
aRv = mSoftToken.Init();
if (NS_WARN_IF(aRv.Failed())) {
return;
}
aRv = mUSBToken.Init();
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
nsresult
U2F::AssembleClientData(const nsAString& aTyp,
const nsAString& aChallenge,
CryptoBuffer& aClientData) const
{
ClientData clientDataObject;
clientDataObject.mTyp.Construct(aTyp); // "Typ" from the U2F specification
clientDataObject.mChallenge.Construct(aChallenge);
clientDataObject.mOrigin.Construct(mOrigin);
nsAutoString json;
if (NS_WARN_IF(!clientDataObject.ToJSON(json))) {
return NS_ERROR_FAILURE;
}
if (NS_WARN_IF(!aClientData.Assign(NS_ConvertUTF16toUTF8(json)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
U2F::ValidAppID(/* in/out */ nsString& aAppId) const
{
nsCOMPtr<nsIURLParser> urlParser =
do_GetService(NS_STDURLPARSER_CONTRACTID);
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
MOZ_ASSERT(urlParser);
MOZ_ASSERT(tldService);
uint32_t facetSchemePos;
int32_t facetSchemeLen;
uint32_t facetAuthPos;
int32_t facetAuthLen;
// Facet is the specification's way of referring to the web origin.
nsAutoCString facetUrl = NS_ConvertUTF16toUTF8(mOrigin);
nsresult rv = urlParser->ParseURL(facetUrl.get(), mOrigin.Length(),
&facetSchemePos, &facetSchemeLen,
&facetAuthPos, &facetAuthLen,
nullptr, nullptr); // ignore path
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
nsAutoCString facetScheme(Substring(facetUrl, facetSchemePos, facetSchemeLen));
nsAutoCString facetAuth(Substring(facetUrl, facetAuthPos, facetAuthLen));
uint32_t appIdSchemePos;
int32_t appIdSchemeLen;
uint32_t appIdAuthPos;
int32_t appIdAuthLen;
nsAutoCString appIdUrl = NS_ConvertUTF16toUTF8(aAppId);
rv = urlParser->ParseURL(appIdUrl.get(), aAppId.Length(),
&appIdSchemePos, &appIdSchemeLen,
&appIdAuthPos, &appIdAuthLen,
nullptr, nullptr); // ignore path
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
nsAutoCString appIdScheme(Substring(appIdUrl, appIdSchemePos, appIdSchemeLen));
nsAutoCString appIdAuth(Substring(appIdUrl, appIdAuthPos, appIdAuthLen));
// If the facetId (origin) is not HTTPS, reject
if (!facetScheme.LowerCaseEqualsLiteral("https")) {
return false;
}
// If the appId is empty or null, overwrite it with the facetId and accept
if (aAppId.IsEmpty() || aAppId.EqualsLiteral("null")) {
aAppId.Assign(mOrigin);
return true;
}
// if the appId URL is not HTTPS, reject.
if (!appIdScheme.LowerCaseEqualsLiteral("https")) {
return false;
}
// If the facetId and the appId auths match, accept
if (facetAuth == appIdAuth) {
return true;
}
nsAutoCString appIdTld;
nsAutoCString facetTld;
rv = tldService->GetBaseDomainFromHost(appIdAuth, 0, appIdTld);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
rv = tldService->GetBaseDomainFromHost(facetAuth, 0, facetTld);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
// If this AppID's registered domain matches the Facet's, accept
if (!facetTld.IsEmpty() && !appIdTld.IsEmpty() &&
(facetTld == appIdTld)) {
return true;
}
// TODO(Bug 1244959) Implement the remaining algorithm.
return false;
}
template <class CB, class Rsp>
void
SendError(CB& aCallback, ErrorCode aErrorCode)
{
Rsp response;
response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
ErrorResult rv;
aCallback.Call(response, rv);
NS_WARN_IF(rv.Failed());
// Useful exceptions already got reported.
rv.SuppressException();
}
void
U2F::Register(const nsAString& aAppId,
const Sequence<RegisterRequest>& aRegisterRequests,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FRegisterCallback& aCallback,
const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
ErrorResult& aRv)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
const bool softTokenEnabled =
Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
const bool usbTokenEnabled =
Preferences::GetBool(PREF_U2F_USBTOKEN_ENABLED);
nsAutoString appId(aAppId);
// Verify the global appId first.
if (!ValidAppID(appId)) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::BAD_REQUEST);
return;
}
for (size_t i = 0; i < aRegisteredKeys.Length(); ++i) {
RegisteredKey request(aRegisteredKeys[i]);
// Check for equired attributes
if (!(request.mKeyHandle.WasPassed() &&
request.mVersion.WasPassed())) {
continue;
}
// Verify the appId for this Registered Key, if set
if (request.mAppId.WasPassed() &&
!ValidAppID(request.mAppId.Value())) {
continue;
}
// Decode the key handle
CryptoBuffer keyHandle;
nsresult rv = keyHandle.FromJwkBase64(request.mKeyHandle.Value());
if (NS_WARN_IF(NS_FAILED(rv))) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::BAD_REQUEST);
return;
}
// We ignore mTransports, as it is intended to be used for sorting the
// available devices by preference, but is not an exclusion factor.
// Determine if the provided keyHandle is registered at any device. If so,
// then we'll return DEVICE_INELIGIBLE to signify we're already registered.
if (usbTokenEnabled &&
mUSBToken.IsCompatibleVersion(request.mVersion.Value()) &&
mUSBToken.IsRegistered(keyHandle)) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::DEVICE_INELIGIBLE);
return;
}
if (softTokenEnabled &&
mSoftToken.IsCompatibleVersion(request.mVersion.Value()) &&
mSoftToken.IsRegistered(keyHandle)) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::DEVICE_INELIGIBLE);
return;
}
}
// Search the requests in order for the first some token can fulfill
for (size_t i = 0; i < aRegisterRequests.Length(); ++i) {
RegisterRequest request(aRegisterRequests[i]);
// Check for equired attributes
if (!(request.mVersion.WasPassed() &&
request.mChallenge.WasPassed())) {
continue;
}
CryptoBuffer clientData;
nsresult rv = AssembleClientData(FinishEnrollment,
request.mChallenge.Value(),
clientData);
if (NS_WARN_IF(NS_FAILED(rv))) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
// Hash the AppID and the ClientData into the AppParam and ChallengeParam
SECStatus srv;
nsCString cAppId = NS_ConvertUTF16toUTF8(appId);
CryptoBuffer appParam;
CryptoBuffer challengeParam;
if (!appParam.SetLength(SHA256_LENGTH, fallible) ||
!challengeParam.SetLength(SHA256_LENGTH, fallible)) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
srv = PK11_HashBuf(SEC_OID_SHA256, appParam.Elements(),
reinterpret_cast<const uint8_t*>(cAppId.BeginReading()),
cAppId.Length());
if (srv != SECSuccess) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
srv = PK11_HashBuf(SEC_OID_SHA256, challengeParam.Elements(),
clientData.Elements(), clientData.Length());
if (srv != SECSuccess) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
// Get the registration data from the token
CryptoBuffer registrationData;
bool registerSuccess = false;
if (usbTokenEnabled &&
mUSBToken.IsCompatibleVersion(request.mVersion.Value())) {
rv = mUSBToken.Register(opt_aTimeoutSeconds, challengeParam,
appParam, registrationData);
if (NS_WARN_IF(NS_FAILED(rv))) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
registerSuccess = true;
}
if (!registerSuccess && softTokenEnabled &&
mSoftToken.IsCompatibleVersion(request.mVersion.Value())) {
rv = mSoftToken.Register(challengeParam, appParam, registrationData);
if (NS_WARN_IF(NS_FAILED(rv))) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
registerSuccess = true;
}
if (!registerSuccess) {
// Try another request
continue;
}
// Assemble a response object to return
nsString clientDataBase64, registrationDataBase64;
nsresult rvClientData =
clientData.ToJwkBase64(clientDataBase64);
nsresult rvRegistrationData =
registrationData.ToJwkBase64(registrationDataBase64);
if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
NS_WARN_IF(NS_FAILED(rvRegistrationData))) {
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
RegisterResponse response;
response.mClientData.Construct(clientDataBase64);
response.mRegistrationData.Construct(registrationDataBase64);
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
ErrorResult result;
aCallback.Call(response, result);
NS_WARN_IF(result.Failed());
// Useful exceptions already got reported.
result.SuppressException();
return;
}
// Nothing could satisfy
SendError<U2FRegisterCallback, RegisterResponse>(aCallback,
ErrorCode::BAD_REQUEST);
return;
}
void
U2F::Sign(const nsAString& aAppId,
const nsAString& aChallenge,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FSignCallback& aCallback,
const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
ErrorResult& aRv)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
const bool softTokenEnabled =
Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
const bool usbTokenEnabled =
Preferences::GetBool(PREF_U2F_USBTOKEN_ENABLED);
nsAutoString appId(aAppId);
// Verify the global appId first.
if (!ValidAppID(appId)) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::BAD_REQUEST);
return;
}
// Search the requests for one a token can fulfill
for (size_t i = 0; i < aRegisteredKeys.Length(); i += 1) {
RegisteredKey request(aRegisteredKeys[i]);
// Check for required attributes
if (!(request.mVersion.WasPassed() &&
request.mKeyHandle.WasPassed())) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
continue;
}
// Allow an individual RegisteredKey to assert a different AppID
nsAutoString regKeyAppId(appId);
if (request.mAppId.WasPassed()) {
regKeyAppId.Assign(request.mAppId.Value());
if (!ValidAppID(regKeyAppId)) {
continue;
}
}
// Assemble a clientData object
CryptoBuffer clientData;
nsresult rv = AssembleClientData(GetAssertion, aChallenge, clientData);
if (NS_WARN_IF(NS_FAILED(rv))) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
// Hash the AppID and the ClientData into the AppParam and ChallengeParam
SECStatus srv;
nsCString cAppId = NS_ConvertUTF16toUTF8(regKeyAppId);
CryptoBuffer appParam;
CryptoBuffer challengeParam;
if (!appParam.SetLength(SHA256_LENGTH, fallible) ||
!challengeParam.SetLength(SHA256_LENGTH, fallible)) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
srv = PK11_HashBuf(SEC_OID_SHA256, appParam.Elements(),
reinterpret_cast<const uint8_t*>(cAppId.BeginReading()),
cAppId.Length());
if (srv != SECSuccess) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
srv = PK11_HashBuf(SEC_OID_SHA256, challengeParam.Elements(),
clientData.Elements(), clientData.Length());
if (srv != SECSuccess) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
// Decode the key handle
CryptoBuffer keyHandle;
rv = keyHandle.FromJwkBase64(request.mKeyHandle.Value());
if (NS_WARN_IF(NS_FAILED(rv))) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
// Get the signature from the token
CryptoBuffer signatureData;
bool signSuccess = false;
// We ignore mTransports, as it is intended to be used for sorting the
// available devices by preference, but is not an exclusion factor.
if (usbTokenEnabled &&
mUSBToken.IsCompatibleVersion(request.mVersion.Value())) {
rv = mUSBToken.Sign(opt_aTimeoutSeconds, appParam, challengeParam,
keyHandle, signatureData);
if (NS_WARN_IF(NS_FAILED(rv))) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
signSuccess = true;
}
if (!signSuccess && softTokenEnabled &&
mSoftToken.IsCompatibleVersion(request.mVersion.Value())) {
rv = mSoftToken.Sign(appParam, challengeParam, keyHandle, signatureData);
if (NS_WARN_IF(NS_FAILED(rv))) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
signSuccess = true;
}
if (!signSuccess) {
// Try another request
continue;
}
// Assemble a response object to return
nsString clientDataBase64, signatureDataBase64;
nsresult rvClientData =
clientData.ToJwkBase64(clientDataBase64);
nsresult rvSignatureData =
signatureData.ToJwkBase64(signatureDataBase64);
if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
NS_WARN_IF(NS_FAILED(rvSignatureData))) {
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::OTHER_ERROR);
return;
}
SignResponse response;
response.mKeyHandle.Construct(request.mKeyHandle.Value());
response.mClientData.Construct(clientDataBase64);
response.mSignatureData.Construct(signatureDataBase64);
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
ErrorResult result;
aCallback.Call(response, result);
NS_WARN_IF(result.Failed());
// Useful exceptions already got reported.
result.SuppressException();
return;
}
// Nothing could satisfy
SendError<U2FSignCallback, SignResponse>(aCallback,
ErrorCode::DEVICE_INELIGIBLE);
return;
}
} // namespace dom
} // namespace mozilla

106
dom/u2f/U2F.h Normal file
Просмотреть файл

@ -0,0 +1,106 @@
/* -*- 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_U2F_h
#define mozilla_dom_U2F_h
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"
#include "NSSToken.h"
#include "USBToken.h"
namespace mozilla {
namespace dom {
struct RegisterRequest;
struct RegisteredKey;
class U2FRegisterCallback;
class U2FSignCallback;
} // namespace dom
} // namespace mozilla
namespace mozilla {
namespace dom {
class U2F final : public nsISupports,
public nsWrapperCache,
public nsNSSShutDownObject
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(U2F)
U2F();
nsPIDOMWindowInner*
GetParentObject() const
{
return mParent;
}
void
Init(nsPIDOMWindowInner* aParent, ErrorResult& aRv);
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void
Register(const nsAString& aAppId,
const Sequence<RegisterRequest>& aRegisterRequests,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FRegisterCallback& aCallback,
const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
ErrorResult& aRv);
void
Sign(const nsAString& aAppId,
const nsAString& aChallenge,
const Sequence<RegisteredKey>& aRegisteredKeys,
U2FSignCallback& aCallback,
const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
ErrorResult& aRv);
// No NSS resources to release.
virtual
void virtualDestroyNSSReference() override {};
private:
nsCOMPtr<nsPIDOMWindowInner> mParent;
nsString mOrigin;
NSSToken mSoftToken;
USBToken mUSBToken;
static const nsString FinishEnrollment;
static const nsString GetAssertion;
~U2F();
nsresult
AssembleClientData(const nsAString& aTyp,
const nsAString& aChallenge,
CryptoBuffer& aClientData) const;
// ValidAppID determines whether the supplied FIDO AppID is valid for
// the current FacetID, e.g., the current origin. If the supplied
// aAppId param is null or empty, it will be filled in per the algorithm.
// See https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-appid-and-facets.html
// for a description of the algorithm.
bool
ValidAppID(/* in/out */ nsString& aAppId) const;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_U2F_h

72
dom/u2f/USBToken.cpp Normal file
Просмотреть файл

@ -0,0 +1,72 @@
/* -*- 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 "USBToken.h"
namespace mozilla {
namespace dom {
USBToken::USBToken()
: mInitialized(false)
{}
USBToken::~USBToken()
{}
nsresult
USBToken::Init()
{
// This routine does nothing at present, but Bug 1245527 will
// integrate the upcoming USB HID service here, which will likely
// require an initialization upon load.
MOZ_ASSERT(!mInitialized);
if (mInitialized) {
return NS_OK;
}
mInitialized = true;
return NS_OK;
}
const nsString USBToken::mVersion = NS_LITERAL_STRING("U2F_V2");
bool
USBToken::IsCompatibleVersion(const nsString& aVersionParam) const
{
MOZ_ASSERT(mInitialized);
return mVersion == aVersionParam;
}
bool
USBToken::IsRegistered(const CryptoBuffer& aKeyHandle) const
{
MOZ_ASSERT(mInitialized);
return false;
}
nsresult
USBToken::Register(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
const CryptoBuffer& /* aChallengeParam */,
const CryptoBuffer& /* aApplicationParam */,
CryptoBuffer& aRegistrationData) const
{
MOZ_ASSERT(mInitialized);
return NS_ERROR_NOT_AVAILABLE;
}
nsresult
USBToken::Sign(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
const CryptoBuffer& aKeyHandle,
CryptoBuffer& aSignatureData) const
{
MOZ_ASSERT(mInitialized);
return NS_ERROR_NOT_AVAILABLE;
}
} // namespace dom
} // namespace mozilla

49
dom/u2f/USBToken.h Normal file
Просмотреть файл

@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_USBToken_h
#define mozilla_dom_USBToken_h
#include "mozilla/dom/CryptoBuffer.h"
namespace mozilla {
namespace dom {
// USBToken implements FIDO operations using a USB device.
class USBToken final
{
public:
USBToken();
~USBToken();
nsresult Init();
bool IsCompatibleVersion(const nsString& aVersionParam) const;
bool IsRegistered(const CryptoBuffer& aKeyHandle) const;
nsresult Register(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
CryptoBuffer& aRegistrationData) const;
nsresult Sign(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
const CryptoBuffer& aApplicationParam,
const CryptoBuffer& aChallengeParam,
const CryptoBuffer& aKeyHandle,
CryptoBuffer& aSignatureData) const;
private:
bool mInitialized;
static const nsString mVersion;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_USBToken_h

28
dom/u2f/moz.build Normal file
Просмотреть файл

@ -0,0 +1,28 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
EXPORTS.mozilla.dom += [
'NSSToken.h',
'U2F.h',
'USBToken.h',
]
UNIFIED_SOURCES += [
'NSSToken.cpp',
'U2F.cpp',
'USBToken.cpp',
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/base',
'/dom/crypto',
'/security/manager/ssl',
'/security/pkix/include',
]
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']

Просмотреть файл

@ -0,0 +1,8 @@
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
"ids": [
"https://fido.example.com"
]
}]
}

Просмотреть файл

@ -0,0 +1 @@
Content-Type: application/fido.trusted-apps+json

Просмотреть файл

@ -0,0 +1,6 @@
# This file isn't actually JSON, so it shouldn't successfully parse.
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
},{}]
}

Просмотреть файл

@ -0,0 +1 @@
Content-Type: application/fido.trusted-apps+json

Просмотреть файл

@ -0,0 +1,9 @@
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
"ids": [
"https://example.net",
"http://www.example.com"
]
}]
}

Просмотреть файл

@ -0,0 +1 @@
Content-Type: application/fido.trusted-apps+json

Просмотреть файл

@ -0,0 +1,8 @@
{
"trustedFacets" : [{
"version": { "major": 1, "minor" : 0 },
"ids": [
"https://fido.example.com"
]
}]
}

Просмотреть файл

@ -0,0 +1,19 @@
[DEFAULT]
support-files =
u2futil.js
test_frame_appid_facet.html
test_frame_register.html
test_frame_appid_facet_remoteload.html
test_frame_appid_facet_insecure.html
test_frame_appid_facet_subdomain.html
facet/facetList.txt
facet/facetList-good
facet/facetList-good^headers^
facet/facetList-no_overlap
facet/facetList-no_overlap^headers^
facet/facetList-invalid_format
facet/facetList-invalid_format^headers^
[test_util_methods.html]
[test_no_token.html]
[test_frame.html]

Просмотреть файл

@ -0,0 +1,67 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<div id="framediv">
<iframe id="testing_frame"></iframe>
</div>
<pre id="log"></pre>
<script class="testbody" type="text/javascript">
SpecialPowers.setBoolPref("security.webauth.u2f", true);
SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
var testList = [
"https://example.com/tests/dom/u2f/tests/test_frame_register.html",
"http://mochi.test:8888/tests/dom/u2f/tests/test_frame_appid_facet_insecure.html",
"https://example.com/tests/dom/u2f/tests/test_frame_appid_facet.html",
"https://example.com/tests/dom/u2f/tests/test_frame_appid_facet_remoteload.html",
"https://test1.example.com/tests/dom/u2f/tests/test_frame_appid_facet_subdomain.html"
];
function log(msg) {
document.getElementById("log").textContent += "\n" + msg;
}
function nextTest() {
if (testList.length < 1) {
SimpleTest.finish();
return;
}
document.getElementById('testing_frame').src = testList.shift();
}
// listen for a messages from the mixed content test harness
function receiveMessage(event) {
if ("test" in event.data) {
var summary = event.data.test + ": " + event.data.msg;
log(event.data.status + ": " + summary);
ok(event.data.status, summary);
} else if ("done" in event.data) {
nextTest();
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener("message", receiveMessage, false);
nextTest();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,69 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
SpecialPowers.setBoolPref("security.webauth.u2f", true);
SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
local_is(window.location.origin, "https://example.com", "Is loaded correctly");
var version = "U2F_V2";
var challenge = new Uint8Array(16);
u2f.register(null, [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "Null AppID should work.");
});
u2f.register("", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "Empty AppID should work.");
});
// Test: Correct TLD, but incorrect scheme
u2f.register("http://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "HTTP scheme is disallowed");
});
// Test: Correct TLD, and also HTTPS
u2f.register("https://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "HTTPS origin for example.com should work");
});
// Test: Dynamic origin
u2f.register(window.location.origin + "/otherAppId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "Direct window origin should work");
});
// eTLD+1 check
u2f.register("https://test1.example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "Subdomain AppID should work");
});
local_finished();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,58 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
SpecialPowers.setBoolPref("security.webauth.u2f", true);
SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
local_is(window.location.origin, "http://mochi.test:8888", "Is loaded correctly");
var version = "U2F_V2";
var challenge = new Uint8Array(16);
u2f.register(null, [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for null AppID");
});
u2f.register("", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for empty AppID");
});
u2f.register("http://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTP AppID");
});
u2f.register("https://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTPS AppID from HTTP origin");
});
u2f.register(window.location.origin + "/otherAppId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTP origin");
});
local_finished();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,60 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for Remote AppId Load behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
SpecialPowers.setBoolPref("security.webauth.u2f", true);
SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
var version = "U2F_V2";
var challenge = new Uint8Array(16);
local_is(window.location.origin, "https://example.com", "Is loaded correctly");
// TODO: Must support remote loads of AppID manifests first.
//
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList.txt", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 2, "Should not permit this AppId contentType");
// });
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetListMissing", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 2, "Should not permit with a missing AppID list");
// });
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-good", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 0, "The AppId should permit example.com");
// });
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-no_overlap", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 2, "Should not permit with a missing AppID list");
// });
// u2f.register("https://test1.example.com/dom/u2f/tests/facet/facetList-invalid_format", [{
// version: version,
// challenge: bytesToBase64UrlSafe(challenge),
// }], [], function(res){
// local_is(res.errorCode, 2, "Should not fail gracefully on invalid formatted facet lists");
// });
local_finished();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,38 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for AppID / FacetID behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
SpecialPowers.setBoolPref("security.webauth.u2f", true);
SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
var version = "U2F_V2";
var challenge = new Uint8Array(16);
local_is(window.location.origin, "https://test1.example.com", "Is loaded correctly");
// eTLD+1 check
u2f.register("https://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "AppID should work from a subdomain");
});
u2f.register("https://example.net/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_isnot(res.errorCode, 0, "AppID should not work from other domains");
});
local_finished();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,77 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<script src="u2futil.js"></script>
</head>
<body>
<p>Test for Register behavior for FIDO Universal Second Factor</p>
<script class="testbody" type="text/javascript">
"use strict";
SpecialPowers.setBoolPref("security.webauth.u2f", true);
SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
var version = "U2F_V2";
var challenge = new Uint8Array(16);
local_is(window.location.origin, "https://example.com", "Is loaded correctly");
// eTLD+1 check
u2f.register("https://example.com/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 0, "AppID should work from a subdomain");
});
u2f.register("https://example.net/appId", [{
version: version,
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 2, "AppID should not work from other domains");
});
u2f.register("", [], [], function(res){
local_is(res.errorCode, 2, "Empty register requests");
});
local_doesThrow(function(){
u2f.register("", null, [], null);
}, "Non-array register requests");
local_doesThrow(function(){
u2f.register("", [], null, null);
}, "Non-array sign requests");
local_doesThrow(function(){
u2f.register("", null, null, null);
}, "Non-array for both arguments");
u2f.register("", [{}], [], function(res){
local_is(res.errorCode, 2, "Empty request");
});
u2f.register("https://example.net/appId", [{
version: version,
}], [], function(res){
local_is(res.errorCode, 2, "Missing challenge");
});
u2f.register("https://example.net/appId", [{
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 2, "Missing version");
});
u2f.register("https://example.net/appId", [{
version: "a_version_00",
challenge: bytesToBase64UrlSafe(challenge),
}], [], function(res){
local_is(res.errorCode, 2, "Invalid version");
});
local_finished();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,38 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for FIDO Universal Second Factor No Token</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.setBoolPref("security.webauth.u2f", true);
SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", false);
SpecialPowers.setBoolPref("security.webauth.u2f.usbtoken", false);
var challenge = new Uint8Array(16);
window.crypto.getRandomValues(challenge);
var regRequest = {
version: "U2F_V2",
challenge: bytesToBase64UrlSafe(challenge),
};
u2f.register(window.location.origin, [regRequest], [], function (regResponse) {
isnot(regResponse.errorCode, 0, "The registration should be rejected.");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,57 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for Utility Methods for other FIDO Universal Second Factor tests</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/dom/u2f/tests/u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.setBoolPref("security.webauth.u2f", true);
SpecialPowers.setBoolPref("security.webauth.u2f.softtoken", true);
SpecialPowers.setBoolPref("security.webauth.u2f.usbtoken", false);
// Example from:
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html
//
// Run this example from the console to check that the u2futil methods work
var pubKey = hexDecode("04d368f1b665bade3c33a20f1e429c7750d5033660c019119d29aa4ba7abc04aa7c80a46bbe11ca8cb5674d74f31f8a903f6bad105fb6ab74aefef4db8b0025e1d");
var appId = "https://gstatic.com/securitykey/a/example.com";
var clientData = string2buffer('{"typ":"navigator.id.getAssertion","challenge":"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}');
var presenceAndCounter = hexDecode("0100000001");
var signature = hexDecode("304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f");
// Import the key
// Assemble the client data
// Verify
Promise.all([
importPublicKey(pubKey),
assembleSignedData(appId, presenceAndCounter, clientData)
])
.then(function(results) {
var importedKey = results[0];
var signedData = new Uint8Array(results[1]);
return verifySignature(importedKey, signedData, signature);
})
.then(function(verified) {
console.log("verified:", verified);
ok(true, "Utility methods work")
SimpleTest.finish();
})
.catch(function(err) {
console.log("error:", err);
ok(false, "Utility methods failed")
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

144
dom/u2f/tests/u2futil.js Normal file
Просмотреть файл

@ -0,0 +1,144 @@
function local_is(value, expected, message) {
if (value === expected) {
local_ok(true, message);
} else {
local_ok(false, message + " unexpectedly: " + value + " !== " + expected);
}
}
function local_isnot(value, expected, message) {
if (value !== expected) {
local_ok(true, message);
} else {
local_ok(false, message + " unexpectedly: " + value + " === " + expected);
}
}
function local_ok(expression, message) {
let body = {"test": this.location.pathname, "status":expression, "msg": message}
parent.postMessage(body, "http://mochi.test:8888");
}
function local_doesThrow(fn, name) {
var gotException = false;
try {
fn();
} catch (ex) { gotException = true; }
local_ok(gotException, name);
};
function local_finished() {
parent.postMessage({"done":true}, "http://mochi.test:8888");
}
function string2buffer(str) {
return (new Uint8Array(str.length)).map((x, i) => str.charCodeAt(i));
}
function buffer2string(buf) {
var str = "";
buf.map(x => str += String.fromCharCode(x));
return str;
}
function bytesToBase64(u8a){
var CHUNK_SZ = 0x8000;
var c = [];
for (var i = 0; i < u8a.length; i += CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
}
return window.btoa(c.join(""));
}
function base64ToBytes(b64encoded) {
return new Uint8Array(window.atob(b64encoded).split("").map(function(c) {
return c.charCodeAt(0);
}));
}
function bytesToBase64UrlSafe(buf) {
return bytesToBase64(buf)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
function base64ToBytesUrlSafe(str) {
if (str.length % 4 == 1) {
throw "Improper b64 string";
}
var b64 = str.replace(/\-/g, "+").replace(/\_/g, "/");
while (b64.length % 4 != 0) {
b64 += "=";
}
return base64ToBytes(b64);
}
function hexEncode(buf) {
return Array.from(buf)
.map(x => ("0"+x.toString(16)).substr(-2))
.join("");
}
function hexDecode(str) {
return new Uint8Array(str.match(/../g).map(x => parseInt(x, 16)));
}
function importPublicKey(keyBytes) {
if (keyBytes[0] != 0x04 || keyBytes.byteLength != 65) {
throw "Bad public key octet string";
}
var jwk = {
kty: "EC",
crv: "P-256",
x: bytesToBase64UrlSafe(keyBytes.slice(1, 33)),
y: bytesToBase64UrlSafe(keyBytes.slice(33))
};
return crypto.subtle.importKey("jwk", jwk, {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"])
}
function assembleSignedData(appId, presenceAndCounter, clientData) {
var appIdBuf = string2buffer(appId);
return Promise.all([
crypto.subtle.digest("SHA-256", appIdBuf),
crypto.subtle.digest("SHA-256", clientData)
])
.then(function(digests) {
var appParam = new Uint8Array(digests[0]);
var clientParam = new Uint8Array(digests[1]);
var signedData = new Uint8Array(32 + 1 + 4 + 32);
appParam.map((x, i) => signedData[0 + i] = x);
presenceAndCounter.map((x, i) => signedData[32 + i] = x);
clientParam.map((x, i) => signedData[37 + i] = x);
return signedData;
});
}
function verifySignature(key, data, derSig) {
if (derSig.byteLength < 70) {
console.log("bad sig: " + hexEncode(derSig))
throw "Invalid signature length: " + derSig.byteLength;
}
// Poor man's ASN.1 decode
// R and S are always 32 bytes. If ether has a DER
// length > 32, it's just zeros we can chop off.
var lenR = derSig[3];
var lenS = derSig[3 + lenR + 2];
var padR = lenR - 32;
var padS = lenS - 32;
var sig = new Uint8Array(64);
derSig.slice(4 + padR, 4 + lenR).map((x, i) => sig[i] = x);
derSig.slice(4 + lenR + 2 + padS, 4 + lenR + 2 + lenS).map(
(x, i) => sig[32 + i] = x
);
console.log("data: " + hexEncode(data));
console.log("der: " + hexEncode(derSig));
console.log("raw: " + hexEncode(sig));
var alg = {name: "ECDSA", hash: "SHA-256"};
return crypto.subtle.verify(alg, key, sig, data);
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше