Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2015-03-30 12:18:27 +02:00
commit 8fb4a8baee
116 changed files with 2317 additions and 292 deletions

View File

@ -323,7 +323,7 @@ pref("media.video-queue.default-size", 3);
// optimize images' memory usage
pref("image.decode-only-on-draw.enabled", true);
pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
pref("image.mem.allow_locking_in_content_processes", true);
// Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
// Almost everything that was factored into 'max_decoded_image_kb' is now stored
// in the surface cache. 1/8 of main memory is 32MB on a 256MB device, which is

View File

@ -199,6 +199,11 @@ Sanitizer.prototype = {
cookieMgr.removeAll();
}
// Clear deviceIds. Done asynchronously (returns before complete).
let mediaMgr = Components.classes["@mozilla.org/mediaManagerService;1"]
.getService(Ci.nsIMediaManagerService);
mediaMgr.sanitizeDeviceIds(this.range && this.range[0]);
// Clear plugin data.
const phInterface = Ci.nsIPluginHost;
const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;

View File

@ -2848,7 +2848,6 @@ let E10SUINotification = {
if (!Services.appinfo.inSafeMode &&
!Services.appinfo.accessibilityEnabled &&
!Services.appinfo.keyboardMayHaveIME &&
isHardwareAccelerated &&
e10sPromptShownCount < 5) {
Services.tm.mainThread.dispatch(() => {

View File

@ -0,0 +1,67 @@
/* 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/MediaDeviceInfo.h"
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/MediaManager.h"
#include "nsIScriptGlobalObject.h"
namespace mozilla {
namespace dom {
MediaDeviceInfo::MediaDeviceInfo(const nsAString& aDeviceId,
MediaDeviceKind aKind,
const nsAString& aLabel,
const nsAString& aGroupId)
: mKind(aKind)
, mDeviceId(aDeviceId)
, mLabel(aLabel)
, mGroupId(aGroupId) {}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(MediaDeviceInfo)
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaDeviceInfo)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaDeviceInfo)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaDeviceInfo)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
MediaDeviceInfo::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return MediaDeviceInfoBinding::Wrap(aCx, this, aGivenProto);
}
nsISupports* MediaDeviceInfo::GetParentObject()
{
return nullptr;
}
void MediaDeviceInfo::GetDeviceId(nsString& retval)
{
retval = mDeviceId;
}
MediaDeviceKind
MediaDeviceInfo::Kind()
{
return mKind;
}
void MediaDeviceInfo::GetGroupId(nsString& retval)
{
retval = mGroupId;
}
void MediaDeviceInfo::GetLabel(nsString& retval)
{
retval = mLabel;
}
MediaDeviceKind Kind();
void GetLabel(nsString& retval);
void GetGroupId(nsString& retval);
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,60 @@
/* 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_MediaDeviceInfo_h
#define mozilla_dom_MediaDeviceInfo_h
#include "mozilla/ErrorResult.h"
#include "nsISupportsImpl.h"
#include "mozilla/dom/BindingUtils.h"
#include "MediaDeviceInfoBinding.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
class Promise;
struct MediaStreamConstraints;
#define MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID \
{0x25091870, 0x84d6, 0x4acf, {0xaf, 0x97, 0x6e, 0xd5, 0x5b, 0xe0, 0x47, 0xb2}}
class MediaDeviceInfo final : public nsISupports, public nsWrapperCache
{
public:
explicit MediaDeviceInfo(const nsAString& aDeviceId,
MediaDeviceKind aKind,
const nsAString& aLabel,
const nsAString& aGroupId = nsString());
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaDeviceInfo)
NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID)
JSObject*
WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
nsISupports* GetParentObject();
void GetDeviceId(nsString& retval);
MediaDeviceKind Kind();
void GetLabel(nsString& retval);
void GetGroupId(nsString& retval);
private:
MediaDeviceKind mKind;
nsString mDeviceId;
nsString mLabel;
nsString mGroupId;
virtual ~MediaDeviceInfo() {}
};
NS_DEFINE_STATIC_IID_ACCESSOR(MediaDeviceInfo,
MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID)
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MediaDeviceInfo_h

View File

@ -9,6 +9,7 @@
#include "mozilla/MediaManager.h"
#include "nsIEventTarget.h"
#include "nsIScriptGlobalObject.h"
#include "nsIPermissionManager.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
@ -37,6 +38,109 @@ private:
nsRefPtr<Promise> mPromise;
};
class MediaDevices::EnumDevResolver : public nsIGetUserMediaDevicesSuccessCallback
{
static bool HasAPersistentPermission(uint64_t aWindowId)
{
nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
(nsGlobalWindow::GetInnerWindowWithId(aWindowId));
if (NS_WARN_IF(!window)) {
return false;
}
// Check if this site has persistent permissions.
nsresult rv;
nsCOMPtr<nsIPermissionManager> mgr =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false; // no permission manager no permissions!
}
uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
{
auto* principal = window->GetExtantDoc()->NodePrincipal();
rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
rv = mgr->TestExactPermissionFromPrincipal(principal, "camera", &video);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
}
return audio == nsIPermissionManager::ALLOW_ACTION ||
video == nsIPermissionManager::ALLOW_ACTION;
}
public:
NS_DECL_ISUPPORTS
EnumDevResolver(Promise* aPromise, uint64_t aWindowId)
: mPromise(aPromise), mWindowId(aWindowId) {}
NS_IMETHOD
OnSuccess(nsIVariant* aDevices) override
{
// Cribbed from MediaPermissionGonk.cpp
nsIID elementIID;
uint16_t elementType;
// Create array for nsIMediaDevice
nsTArray<nsCOMPtr<nsIMediaDevice>> devices;
// Contain the fumes
{
void* rawArray;
uint32_t arrayLen;
nsresult rv;
rv = aDevices->GetAsArray(&elementType, &elementIID, &arrayLen, &rawArray);
NS_ENSURE_SUCCESS(rv, rv);
if (elementType != nsIDataType::VTYPE_INTERFACE) {
NS_Free(rawArray);
return NS_ERROR_FAILURE;
}
nsISupports **supportsArray = reinterpret_cast<nsISupports **>(rawArray);
for (uint32_t i = 0; i < arrayLen; ++i) {
nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supportsArray[i]));
devices.AppendElement(device);
NS_IF_RELEASE(supportsArray[i]); // explicitly decrease refcount for rawptr
}
NS_Free(rawArray); // explicitly free memory from nsIVariant::GetAsArray
}
nsTArray<nsRefPtr<MediaDeviceInfo>> infos;
for (auto& device : devices) {
nsString type;
device->GetType(type);
bool isVideo = type.EqualsLiteral("video");
bool isAudio = type.EqualsLiteral("audio");
if (isVideo || isAudio) {
MediaDeviceKind kind = isVideo ?
MediaDeviceKind::Videoinput : MediaDeviceKind::Audioinput;
nsString id;
nsString name;
device->GetId(id);
// Include name only if page currently has a gUM stream active or
// persistent permissions (audio or video) have been granted
if (MediaManager::Get()->IsWindowActivelyCapturing(mWindowId) ||
HasAPersistentPermission(mWindowId) ||
Preferences::GetBool("media.navigator.permission.disabled", false)) {
device->GetName(name);
}
nsRefPtr<MediaDeviceInfo> info = new MediaDeviceInfo(id, kind, name);
infos.AppendElement(info);
}
}
mPromise->MaybeResolve(infos);
return NS_OK;
}
private:
virtual ~EnumDevResolver() {}
nsRefPtr<Promise> mPromise;
uint64_t mWindowId;
};
class MediaDevices::GumRejecter : public nsIDOMGetUserMediaErrorCallback
{
public:
@ -61,17 +165,17 @@ private:
};
NS_IMPL_ISUPPORTS(MediaDevices::GumResolver, nsIDOMGetUserMediaSuccessCallback)
NS_IMPL_ISUPPORTS(MediaDevices::EnumDevResolver, nsIGetUserMediaDevicesSuccessCallback)
NS_IMPL_ISUPPORTS(MediaDevices::GumRejecter, nsIDOMGetUserMediaErrorCallback)
already_AddRefed<Promise>
MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints,
ErrorResult &aRv)
{
ErrorResult rv;
nsPIDOMWindow* window = GetOwner();
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
nsRefPtr<Promise> p = Promise::Create(go, aRv);
NS_ENSURE_TRUE(!rv.Failed(), nullptr);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
nsRefPtr<GumResolver> resolver = new GumResolver(p);
nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
@ -81,6 +185,21 @@ MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints,
return p.forget();
}
already_AddRefed<Promise>
MediaDevices::EnumerateDevices(ErrorResult &aRv)
{
nsPIDOMWindow* window = GetOwner();
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
nsRefPtr<Promise> p = Promise::Create(go, aRv);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
nsRefPtr<EnumDevResolver> resolver = new EnumDevResolver(p, window->WindowID());
nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
aRv = MediaManager::Get()->EnumerateDevices(window, resolver, rejecter);
return p.forget();
}
NS_IMPL_ADDREF_INHERITED(MediaDevices, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(MediaDevices, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN(MediaDevices)

View File

@ -2,8 +2,8 @@
* 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 MediaDevices_h__
#define MediaDevices_h__
#ifndef mozilla_dom_MediaDevices_h
#define mozilla_dom_MediaDevices_h
#include "mozilla/ErrorResult.h"
#include "nsISupportsImpl.h"
@ -35,8 +35,12 @@ public:
already_AddRefed<Promise>
GetUserMedia(const MediaStreamConstraints& aConstraints, ErrorResult &aRv);
already_AddRefed<Promise>
EnumerateDevices(ErrorResult &aRv);
private:
class GumResolver;
class EnumDevResolver;
class GumRejecter;
virtual ~MediaDevices() {}
@ -48,4 +52,4 @@ NS_DEFINE_STATIC_IID_ACCESSOR(MediaDevices,
} // namespace dom
} // namespace mozilla
#endif // MediaDevices_h__
#endif // mozilla_dom_MediaDevices_h

View File

@ -25,6 +25,13 @@
#include "nsIInterfaceRequestorUtils.h"
#include "nsIIDNService.h"
#include "nsNetUtil.h"
#include "nsPrincipal.h"
#include "nsICryptoHash.h"
#include "nsICryptoHMAC.h"
#include "nsIKeyModule.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIInputStream.h"
#include "nsILineInputStream.h"
#include "mozilla/Types.h"
#include "mozilla/PeerIdentity.h"
#include "mozilla/dom/ContentChild.h"
@ -33,8 +40,11 @@
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/dom/GetUserMediaRequestBinding.h"
#include "mozilla/Preferences.h"
#include "mozilla/Base64.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/media/MediaChild.h"
#include "MediaTrackConstraints.h"
#include "VideoUtils.h"
#include "Latency.h"
// For PR_snprintf
@ -42,6 +52,10 @@
#include "nsJSUtils.h"
#include "nsGlobalWindow.h"
#include "nsIUUIDGenerator.h"
#include "nspr.h"
#include "nss.h"
#include "pk11pub.h"
/* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
#include "MediaEngineDefault.h"
@ -194,45 +208,61 @@ HostHasPermission(nsIURI &docURI)
return false;
}
ErrorCallbackRunnable::ErrorCallbackRunnable(
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
MediaMgrError& aError,
uint64_t aWindowID)
: mError(&aError)
, mWindowID(aWindowID)
, mManager(MediaManager::GetInstance())
/**
* Send an error back to content.
* Do this only on the main thread. The onSuccess callback is also passed here
* so it can be released correctly.
*/
template<class SuccessCallbackType>
class ErrorCallbackRunnable : public nsRunnable
{
mOnSuccess.swap(aOnSuccess);
mOnFailure.swap(aOnFailure);
}
public:
ErrorCallbackRunnable(
nsCOMPtr<SuccessCallbackType>& aOnSuccess,
nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
MediaMgrError& aError,
uint64_t aWindowID)
: mError(&aError)
, mWindowID(aWindowID)
, mManager(MediaManager::GetInstance())
{
mOnSuccess.swap(aOnSuccess);
mOnFailure.swap(aOnFailure);
}
ErrorCallbackRunnable::~ErrorCallbackRunnable()
{
MOZ_ASSERT(!mOnSuccess && !mOnFailure);
}
NS_IMETHODIMP
Run()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
NS_IMETHODIMP
ErrorCallbackRunnable::Run()
{
// Only run if the window is still active.
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
nsCOMPtr<SuccessCallbackType> onSuccess = mOnSuccess.forget();
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess = mOnSuccess.forget();
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
if (!(mManager->IsWindowStillActive(mWindowID))) {
// Only run if the window is still active.
if (!(mManager->IsWindowStillActive(mWindowID))) {
return NS_OK;
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
if (window) {
nsRefPtr<MediaStreamError> error = new MediaStreamError(window, *mError);
onFailure->OnError(error);
}
return NS_OK;
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
if (window) {
nsRefPtr<MediaStreamError> error = new MediaStreamError(window, *mError);
onFailure->OnError(error);
private:
~ErrorCallbackRunnable()
{
MOZ_ASSERT(!mOnSuccess && !mOnFailure);
}
return NS_OK;
}
nsCOMPtr<SuccessCallbackType> mOnSuccess;
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
nsRefPtr<MediaMgrError> mError;
uint64_t mWindowID;
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
};
/**
* Invoke the "onSuccess" callback in content. The callback will take a
@ -302,6 +332,50 @@ public:
mOnFailure.swap(aOnFailure);
}
nsresult
AnonymizeId(nsAString& aId, const nsACString& aOriginKey)
{
nsresult rv;
nsCOMPtr<nsIKeyObjectFactory> factory =
do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv);
if (NS_FAILED(rv)) {
return rv;
}
nsCString rawKey;
rv = Base64Decode(aOriginKey, rawKey);
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIKeyObject> key;
rv = factory->KeyFromString(nsIKeyObject::HMAC, rawKey, getter_AddRefs(key));
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsICryptoHMAC> hasher =
do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
rv = hasher->Init(nsICryptoHMAC::SHA256, key);
if (NS_FAILED(rv)) {
return rv;
}
NS_ConvertUTF16toUTF8 id(aId);
rv = hasher->Update(reinterpret_cast<const uint8_t*> (id.get()), id.Length());
if (NS_FAILED(rv)) {
return rv;
}
nsCString mac;
rv = hasher->Finish(true, mac);
if (NS_FAILED(rv)) {
return rv;
}
aId = NS_ConvertUTF8toUTF16(mac);
return NS_OK;
}
NS_IMETHOD
Run()
{
@ -315,7 +389,7 @@ public:
nsCOMPtr<nsIWritableVariant> devices =
do_CreateInstance("@mozilla.org/variant;1");
int32_t len = mDevices->Length();
size_t len = mDevices->Length();
if (len == 0) {
// XXX
// We should in the future return an empty array, and dynamically add
@ -331,8 +405,14 @@ public:
}
nsTArray<nsIMediaDevice*> tmp(len);
for (int32_t i = 0; i < len; i++) {
tmp.AppendElement(mDevices->ElementAt(i));
for (auto& device : *mDevices) {
if (!mOriginKey.IsEmpty()) {
nsString id;
device->GetId(id);
AnonymizeId(id, mOriginKey);
device->SetId(id);
}
tmp.AppendElement(device);
}
devices->SetAsArray(nsIDataType::VTYPE_INTERFACE,
@ -346,6 +426,7 @@ public:
return NS_OK;
}
nsCString mOriginKey;
private:
nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mOnSuccess;
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
@ -516,6 +597,12 @@ MediaDevice::GetId(nsAString& aID)
return NS_OK;
}
void
MediaDevice::SetId(const nsAString& aID)
{
mID.Assign(aID);
}
NS_IMETHODIMP
MediaDevice::GetFacingMode(nsAString& aFacingMode)
{
@ -726,6 +813,26 @@ public:
uint32_t mPlayoutDelay;
};
void
MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog)
{
MM_LOG(("%s , rv=%d", errorLog, rv));
NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
mOnTracksAvailableCallback.forget()));
nsString log;
log.AssignASCII(errorLog);
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
nsRefPtr<MediaMgrError> error = new MediaMgrError(
NS_LITERAL_STRING("InternalError"), log);
NS_DispatchToMainThread(
new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(onSuccess,
mOnFailure,
*error,
mWindowID));
}
/**
* Creates a MediaStream, attaches a listener and fires off a success callback
* to the DOM with the stream. We also pass in the error callback so it can
@ -1091,8 +1198,11 @@ public:
Fail(const nsAString& aName,
const nsAString& aMessage = EmptyString()) {
nsRefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage);
nsRefPtr<ErrorCallbackRunnable> runnable =
new ErrorCallbackRunnable(mOnSuccess, mOnFailure, *error, mWindowID);
nsRefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>> runnable =
new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(mOnSuccess,
mOnFailure,
*error,
mWindowID);
// These should be empty now
MOZ_ASSERT(!mOnSuccess);
MOZ_ASSERT(!mOnFailure);
@ -1105,7 +1215,7 @@ public:
void
Run()
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(mOnSuccess);
MOZ_ASSERT(mOnFailure);
@ -1309,11 +1419,30 @@ private:
};
#endif
class SanitizeDeviceIdsTask : public Task
{
public:
explicit SanitizeDeviceIdsTask(int64_t aSinceWhen)
: mSinceWhen(aSinceWhen) {}
void // NS_IMETHOD
Run()
{
MOZ_ASSERT(!NS_IsMainThread());
nsRefPtr<media::ChildPledge<bool>> p =
mozilla::media::SanitizeOriginKeys(mSinceWhen); // we fire and forget
}
private:
int64_t mSinceWhen;
};
/**
* Similar to GetUserMediaTask, but used for the chrome-only
* GetUserMediaDevices function. Enumerates a list of audio & video devices,
* wraps them up in nsIMediaDevice objects and returns it to the success
* callback.
*
* All code in this class runs on the MediaManager thread.
*/
class GetUserMediaDevicesTask : public Task
{
@ -1323,7 +1452,8 @@ public:
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aOnSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
uint64_t aWindowId, nsACString& aAudioLoopbackDev,
nsACString& aVideoLoopbackDev, bool aUseFakeDevices)
nsACString& aVideoLoopbackDev, bool aPrivileged, const nsACString& aOrigin,
bool aInPrivateBrowsing, bool aUseFakeDevices)
: mConstraints(aConstraints)
, mOnSuccess(aOnSuccess)
, mOnFailure(aOnFailure)
@ -1331,12 +1461,15 @@ public:
, mWindowId(aWindowId)
, mLoopbackAudioDevice(aAudioLoopbackDev)
, mLoopbackVideoDevice(aVideoLoopbackDev)
, mPrivileged(aPrivileged)
, mOrigin(aOrigin)
, mInPrivateBrowsing(aInPrivateBrowsing)
, mUseFakeDevices(aUseFakeDevices) {}
void // NS_IMETHOD
Run()
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
MOZ_ASSERT(!NS_IsMainThread());
nsRefPtr<MediaEngine> backend;
if (mConstraints.mFake || mUseFakeDevices)
@ -1346,28 +1479,48 @@ public:
typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
ScopedDeletePtr<SourceSet> final(new SourceSet);
ScopedDeletePtr<SourceSet> result(new SourceSet);
if (IsOn(mConstraints.mVideo)) {
nsTArray<nsRefPtr<VideoDevice>> s;
nsTArray<nsRefPtr<VideoDevice>> sources;
GetSources(backend, GetInvariant(mConstraints.mVideo),
&MediaEngine::EnumerateVideoDevices, s, mLoopbackVideoDevice.get());
for (uint32_t i = 0; i < s.Length(); i++) {
final->AppendElement(s[i]);
&MediaEngine::EnumerateVideoDevices, sources,
mLoopbackVideoDevice.get());
for (auto& source : sources) {
result->AppendElement(source);
}
}
if (IsOn(mConstraints.mAudio)) {
nsTArray<nsRefPtr<AudioDevice>> s;
nsTArray<nsRefPtr<AudioDevice>> sources;
GetSources(backend, GetInvariant(mConstraints.mAudio),
&MediaEngine::EnumerateAudioDevices, s, mLoopbackAudioDevice.get());
for (uint32_t i = 0; i < s.Length(); i++) {
final->AppendElement(s[i]);
&MediaEngine::EnumerateAudioDevices, sources,
mLoopbackAudioDevice.get());
for (auto& source : sources) {
result->AppendElement(source);
}
}
NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
mOnSuccess, mOnFailure,
final.forget()));
// DeviceSuccessCallbackRunnable should have taken these.
// In the case of failure with this newly allocated runnable, we
// intentionally leak the runnable, because we've pawned mOnSuccess and
// mOnFailure onto it which are main thread objects unsafe to release here.
DeviceSuccessCallbackRunnable* runnable =
new DeviceSuccessCallbackRunnable(mWindowId, mOnSuccess, mOnFailure,
result.forget());
if (mPrivileged) {
NS_DispatchToMainThread(runnable);
} else {
// Get persistent origin-unique uuid to anonymize deviceIds back on main.
//
// GetOriginKey is an async API that returns a pledge (as promise-like
// pattern). We use .Then() to pass in a lambda to run back on this
// thread once GetOriginKey resolves asynchronously . The "runnable"
// pointer is "captured" (passed by value) into the lambda.
nsRefPtr<media::ChildPledge<nsCString>> p =
media::GetOriginKey(mOrigin, mInPrivateBrowsing);
p->Then([runnable](nsCString result) mutable {
runnable->mOriginKey = result;
NS_DispatchToMainThread(runnable);
});
}
// One of the Runnables have taken these.
MOZ_ASSERT(!mOnSuccess && !mOnFailure);
}
@ -1383,6 +1536,9 @@ private:
// automated media tests only.
nsCString mLoopbackAudioDevice;
nsCString mLoopbackVideoDevice;
bool mPrivileged;
nsCString mOrigin;
bool mInPrivateBrowsing;
bool mUseFakeDevices;
};
@ -1411,6 +1567,16 @@ NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver)
/* static */ StaticRefPtr<MediaManager> MediaManager::sSingleton;
#ifdef DEBUG
/* static */ bool
MediaManager::IsInMediaThread()
{
return sSingleton?
(sSingleton->mMediaThread->thread_id() == PlatformThread::CurrentId()) :
false;
}
#endif
// NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
// thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
// from MediaManager thread.
@ -1577,16 +1743,8 @@ MediaManager::GetUserMedia(
#endif //MOZ_B2G
}
// Store the WindowID in a hash table and mark as active. The entry is removed
// when this window is closed or navigated away from.
uint64_t windowID = aWindow->WindowID();
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
StreamListeners* listeners = GetActiveWindows()->Get(windowID);
if (!listeners) {
listeners = new StreamListeners;
GetActiveWindows()->Put(windowID, listeners);
}
StreamListeners* listeners = AddWindowID(windowID);
// Create a disabled listener to act as a placeholder
GetUserMediaCallbackMediaStreamListener* listener =
@ -1801,7 +1959,8 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
const MediaStreamConstraints& aConstraints,
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnFailure,
uint64_t aInnerWindowID)
uint64_t aInnerWindowID,
bool aPrivileged)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
@ -1819,15 +1978,40 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
bool useFakeStreams =
Preferences::GetBool("media.navigator.streams.fake", false);
nsCString origin;
nsPrincipal::GetOriginForURI(aWindow->GetDocumentURI(),
getter_Copies(origin));
bool inPrivateBrowsing;
{
nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
}
MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
new GetUserMediaDevicesTask(
aConstraints, onSuccess.forget(), onFailure.forget(),
(aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
loopbackAudioDevice, loopbackVideoDevice, useFakeStreams));
loopbackAudioDevice, loopbackVideoDevice, aPrivileged, origin,
inPrivateBrowsing, useFakeStreams));
return NS_OK;
}
nsresult
MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow,
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnFailure)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
MediaStreamConstraints c;
c.mVideo.SetAsBoolean() = true;
c.mAudio.SetAsBoolean() = true;
AddWindowID(aWindow->WindowID());
return GetUserMediaDevices(aWindow, c, aOnSuccess, aOnFailure, 0, false);
}
MediaEngine*
MediaManager::GetBackend(uint64_t aWindowId)
{
@ -1896,6 +2080,22 @@ MediaManager::OnNavigation(uint64_t aWindowID)
}
}
StreamListeners*
MediaManager::AddWindowID(uint64_t aWindowId)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
// Store the WindowID in a hash table and mark as active. The entry is removed
// when this window is closed or navigated away from.
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
StreamListeners* listeners = GetActiveWindows()->Get(aWindowId);
if (!listeners) {
listeners = new StreamListeners;
GetActiveWindows()->Put(aWindowId, listeners);
}
return listeners;
}
void
MediaManager::RemoveWindowID(uint64_t aWindowId)
{
@ -2009,8 +2209,34 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
prefs->RemoveObserver("media.navigator.video.default_minfps", this);
}
// Close off any remaining active windows.
// Because mMediaThread is not an nsThread, we must dispatch to it so it can
// clean up BackgroundChild. Continue stopping thread once this is done.
class ShutdownTask : public Task
{
public:
explicit ShutdownTask(nsRunnable* aReply) : mReply(aReply) {}
private:
virtual void
Run()
{
MOZ_ASSERT(MediaManager::IsInMediaThread());
mozilla::ipc::BackgroundChild::CloseForCurrentThread();
NS_DispatchToMainThread(mReply);
}
nsRefPtr<nsRunnable> mReply;
};
// Post ShutdownTask to execute on mMediaThread and pass in a lambda
// callback to be executed back on this thread once it is done.
//
// The lambda callback "captures" the 'this' pointer for member access.
// This is safe since this is guaranteed to be here since sSingleton isn't
// cleared until the lambda function clears it.
MediaManager::GetMessageLoop()->PostTask(FROM_HERE, new ShutdownTask(
media::CallbackRunnable::New([this]() mutable {
// Close off any remaining active windows.
MutexAutoLock lock(mMutex);
GetActiveWindows()->Clear();
mActiveCallbacks.Clear();
@ -2022,8 +2248,8 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
mMediaThread->Stop();
}
mBackend = nullptr;
}
return NS_OK;
})));
return NS_OK;
} else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
@ -2259,6 +2485,17 @@ MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow, bool* aVideo,
return NS_OK;
}
NS_IMETHODIMP
MediaManager::SanitizeDeviceIds(int64_t aSinceWhen)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
LOG(("%s: sinceWhen = %llu", __FUNCTION__, aSinceWhen));
MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
new SanitizeDeviceIdsTask(aSinceWhen));
return NS_OK;
}
static void
StopScreensharingCallback(MediaManager *aThis,
uint64_t aWindowID,
@ -2345,6 +2582,24 @@ MediaManager::StopMediaStreams()
}
}
bool
MediaManager::IsWindowActivelyCapturing(uint64_t aWindowId)
{
nsCOMPtr<nsISupportsArray> array;
GetActiveMediaCaptureWindows(getter_AddRefs(array));
uint32_t len;
array->Count(&len);
for (uint32_t i = 0; i < len; i++) {
nsCOMPtr<nsISupports> window;
array->GetElementAt(i, getter_AddRefs(window));
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(window));
if (win && win->WindowID() == aWindowId) {
return true;
}
}
return false;
}
void
GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
uint32_t aEcho,

View File

@ -300,29 +300,6 @@ typedef enum {
class MediaManager;
class GetUserMediaTask;
/**
* Send an error back to content.
* Do this only on the main thread. The onSuccess callback is also passed here
* so it can be released correctly.
*/
class ErrorCallbackRunnable : public nsRunnable
{
public:
ErrorCallbackRunnable(
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
MediaMgrError& aError, uint64_t aWindowID);
NS_IMETHOD Run();
private:
~ErrorCallbackRunnable();
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
nsRefPtr<MediaMgrError> mError;
uint64_t mWindowID;
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
};
class ReleaseMediaOperationResource : public nsRunnable
{
public:
@ -369,20 +346,7 @@ public:
}
void
ReturnCallbackError(nsresult rv, const char* errorLog)
{
MM_LOG(("%s , rv=%d", errorLog, rv));
NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
mOnTracksAvailableCallback.forget()));
nsString log;
log.AssignASCII(errorLog);
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
nsRefPtr<MediaMgrError> error = new MediaMgrError(
NS_LITERAL_STRING("InternalError"), log);
NS_DispatchToMainThread(new ErrorCallbackRunnable(onSuccess, mOnFailure,
*error, mWindowID));
}
ReturnCallbackError(nsresult rv, const char* errorLog);
void
Run()
@ -503,6 +467,7 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMEDIADEVICE
void SetId(const nsAString& aID);
protected:
virtual ~MediaDevice() {}
explicit MediaDevice(MediaEngineSource* aSource);
@ -556,6 +521,9 @@ public:
static MediaManager* Get();
static MediaManager* GetIfExists();
static MessageLoop* GetMessageLoop();
#ifdef DEBUG
static bool IsInMediaThread();
#endif
static bool Exists()
{
@ -595,12 +563,21 @@ public:
const dom::MediaStreamConstraints& aConstraints,
nsIGetUserMediaDevicesSuccessCallback* onSuccess,
nsIDOMGetUserMediaErrorCallback* onError,
uint64_t aInnerWindowID = 0);
uint64_t aInnerWindowID = 0,
bool aPrivileged = true);
nsresult EnumerateDevices(nsPIDOMWindow* aWindow,
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnFailure);
nsresult EnumerateDevices(nsPIDOMWindow* aWindow, dom::Promise& aPromise);
void OnNavigation(uint64_t aWindowID);
bool IsWindowActivelyCapturing(uint64_t aWindowId);
MediaEnginePrefs mPrefs;
private:
StreamListeners* AddWindowID(uint64_t aWindowId);
WindowTable *GetActiveWindows() {
NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
return &mActiveWindows;
@ -628,6 +605,7 @@ private:
WindowTable mActiveWindows;
nsClassHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
// Always exists
nsAutoPtr<base::Thread> mMediaThread;

View File

@ -261,18 +261,16 @@ ExtractH264CodecDetails(const nsAString& aCodec,
}
nsresult
GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength)
GenerateRandomName(nsCString& aOutSalt, uint32_t aLength)
{
nsresult rv;
nsCOMPtr<nsIRandomGenerator> rg =
do_GetService("@mozilla.org/security/random-generator;1", &rv);
if (NS_FAILED(rv)) return rv;
// For each three bytes of random data we will get four bytes of
// ASCII. Request a bit more to be safe and truncate to the length
// we want at the end.
// For each three bytes of random data we will get four bytes of ASCII.
const uint32_t requiredBytesLength =
static_cast<uint32_t>((aLength + 1) / 4 * 3);
static_cast<uint32_t>((aLength + 3) / 4 * 3);
uint8_t* buffer;
rv = rg->GenerateRandomBytes(requiredBytesLength, &buffer);
@ -286,14 +284,19 @@ GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength)
buffer = nullptr;
if (NS_FAILED (rv)) return rv;
temp.Truncate(aLength);
aOutSalt = temp;
return NS_OK;
}
nsresult
GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength)
{
nsresult rv = GenerateRandomName(aOutSalt, aLength);
if (NS_FAILED(rv)) return rv;
// Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
// to replace illegal characters -- notably '/'
temp.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
aOutSalt = temp;
aOutSalt.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
return NS_OK;
}

View File

@ -261,7 +261,11 @@ ExtractH264CodecDetails(const nsAString& aCodecs,
int16_t& aLevel);
// Use a cryptographic quality PRNG to generate raw random bytes
// and convert that to a base64 string suitable for use as a file or URL
// and convert that to a base64 string.
nsresult
GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
// This version returns a string suitable for use as a file or URL
// path. This is based on code from nsExternalAppHandler::SetUpTempFile.
nsresult
GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);

View File

@ -140,7 +140,7 @@ nsresult
GMPAudioDecoderParent::Close()
{
LOGD(("%s: %p", __FUNCTION__, this));
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
// Consumer is done with us; we can shut down. No more callbacks should
// be made to mCallback. Note: do this before Shutdown()!
@ -160,7 +160,7 @@ nsresult
GMPAudioDecoderParent::Shutdown()
{
LOGD(("%s: %p", __FUNCTION__, this));
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
if (mShuttingDown) {
return NS_OK;

View File

@ -146,6 +146,7 @@ EXPORTS.mozilla.dom += [
'AudioTrack.h',
'AudioTrackList.h',
'GetUserMediaRequest.h',
'MediaDeviceInfo.h',
'MediaDevices.h',
'MediaStreamError.h',
'MediaStreamTrack.h',
@ -183,6 +184,7 @@ UNIFIED_SOURCES += [
'MediaDecoder.cpp',
'MediaDecoderReader.cpp',
'MediaDecoderStateMachine.cpp',
'MediaDeviceInfo.cpp',
'MediaDevices.cpp',
'MediaManager.cpp',
'MediaRecorder.cpp',

View File

@ -12,7 +12,7 @@ interface nsIDOMWindow;
#define MEDIAMANAGERSERVICE_CONTRACTID "@mozilla.org/mediaManagerService;1"
%}
[scriptable, builtinclass, uuid(9b10661f-77b3-47f7-a8de-ee58daaff5d2)]
[scriptable, builtinclass, uuid(24b23e01-33fd-401f-ba25-6e52658750b0)]
interface nsIMediaManagerService : nsISupports
{
/* return a array of inner windows that have active captures */
@ -22,4 +22,8 @@ interface nsIMediaManagerService : nsISupports
void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio,
[optional] out boolean aScreenShare, [optional] out boolean aWindowShare,
[optional] out boolean aAppShare, [optional] out boolean aBrowserShare);
/* Clear per-orgin list of persistent DeviceIds stored for enumerateDevices
sinceTime is milliseconds since 1 January 1970 00:00:00 UTC. 0 = clear all */
void sanitizeDeviceIds(in long long sinceWhen);
};

View File

@ -0,0 +1,174 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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 "MediaChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "nsGlobalWindow.h"
#include "mozilla/MediaManager.h"
#include "prlog.h"
#undef LOG
#if defined(PR_LOGGING)
PRLogModuleInfo *gMediaChildLog;
#define LOG(args) PR_LOG(gMediaChildLog, PR_LOG_DEBUG, args)
#else
#define LOG(args)
#endif
namespace mozilla {
namespace media {
static Child* sChild;
template<typename ValueType> void
ChildPledge<ValueType>::ActorCreated(PBackgroundChild* aActor)
{
if (!sChild) {
// Create PMedia by sending a message to the parent
sChild = static_cast<Child*>(aActor->SendPMediaConstructor());
}
Run(sChild);
}
template<typename ValueType> void
ChildPledge<ValueType>::ActorFailed()
{
Pledge<ValueType>::Reject(NS_ERROR_UNEXPECTED);
}
template<typename ValueType> NS_IMPL_ADDREF(ChildPledge<ValueType>)
template<typename ValueType> NS_IMPL_RELEASE(ChildPledge<ValueType>)
template<typename ValueType> NS_INTERFACE_MAP_BEGIN(ChildPledge<ValueType>)
NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
NS_INTERFACE_MAP_END
already_AddRefed<ChildPledge<nsCString>>
GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing)
{
class Pledge : public ChildPledge<nsCString>
{
public:
explicit Pledge(const nsCString& aOrigin, bool aPrivateBrowsing)
: mOrigin(aOrigin), mPrivateBrowsing(aPrivateBrowsing) {}
private:
~Pledge() {}
void Run(PMediaChild* aChild)
{
Child* child = static_cast<Child*>(aChild);
uint32_t id = child->AddRequestPledge(*this);
child->SendGetOriginKey(id, mOrigin, mPrivateBrowsing);
}
const nsCString mOrigin;
const bool mPrivateBrowsing;
};
nsRefPtr<ChildPledge<nsCString>> p = new Pledge(aOrigin, aPrivateBrowsing);
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
MOZ_RELEASE_ASSERT(ok);
return p.forget();
}
already_AddRefed<ChildPledge<bool>>
SanitizeOriginKeys(const uint64_t& aSinceWhen)
{
class Pledge : public ChildPledge<bool>
{
public:
explicit Pledge(const uint64_t& aSinceWhen) : mSinceWhen(aSinceWhen) {}
private:
void Run(PMediaChild* aMedia)
{
aMedia->SendSanitizeOriginKeys(mSinceWhen);
mValue = true;
LOG(("SanitizeOriginKeys since %llu", mSinceWhen));
Resolve();
}
const uint64_t mSinceWhen;
};
nsRefPtr<ChildPledge<bool>> p = new Pledge(aSinceWhen);
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
MOZ_RELEASE_ASSERT(ok);
return p.forget();
}
Child::Child()
{
#if defined(PR_LOGGING)
if (!gMediaChildLog) {
gMediaChildLog = PR_NewLogModule("MediaChild");
}
#endif
LOG(("media::Child: %p", this));
MOZ_COUNT_CTOR(Child);
}
Child::~Child()
{
LOG(("~media::Child: %p", this));
sChild = nullptr;
MOZ_COUNT_DTOR(Child);
}
uint32_t Child::sRequestCounter = 0;
uint32_t
Child::AddRequestPledge(ChildPledge<nsCString>& aPledge)
{
uint32_t id = ++sRequestCounter;
nsRefPtr<ChildPledge<nsCString>> ptr(&aPledge);
mRequestPledges.AppendElement(PledgeEntry(id, ptr));
return id;
}
already_AddRefed<ChildPledge<nsCString>>
Child::RemoveRequestPledge(uint32_t aRequestId)
{
for (PledgeEntry& entry : mRequestPledges) {
if (entry.first == aRequestId) {
nsRefPtr<ChildPledge<nsCString>> ref;
ref.swap(entry.second);
mRequestPledges.RemoveElement(entry);
return ref.forget();
}
}
MOZ_ASSERT_UNREACHABLE("Received response with no matching media::ChildPledge!");
return nullptr;
}
bool
Child::RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey)
{
nsRefPtr<ChildPledge<nsCString>> pledge = RemoveRequestPledge(aRequestId);
if (pledge) {
pledge->Resolve(aKey);
}
return true;
}
PMediaChild*
AllocPMediaChild()
{
Child* obj = new Child();
obj->AddRef();
return obj;
}
bool
DeallocPMediaChild(media::PMediaChild *aActor)
{
static_cast<Child*>(aActor)->Release();
return true;
}
}
}

View File

@ -0,0 +1,74 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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_MediaChild_h
#define mozilla_MediaChild_h
#include "mozilla/dom/ContentChild.h"
#include "mozilla/media/PMediaChild.h"
#include "mozilla/media/PMediaParent.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
#include "MediaUtils.h"
namespace mozilla {
namespace media {
// media::Child implements proxying to the chrome process for some media-related
// functions, for the moment just:
//
// GetOriginKey() - get a cookie-like persisted unique key for a given origin.
// SanitizeOriginKeys() - reset persisted unique keys.
// GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges
// (promise-like objects) with the future value. Use pledge.Then(func) to access.
class Child;
template<typename ValueType>
class ChildPledge : public Pledge<ValueType>,
public nsIIPCBackgroundChildCreateCallback
{
friend Child;
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
NS_DECL_ISUPPORTS
public:
explicit ChildPledge() {};
protected:
virtual ~ChildPledge() {}
virtual void Run(PMediaChild* aMedia) = 0;
};
already_AddRefed<ChildPledge<nsCString>>
GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing);
already_AddRefed<ChildPledge<bool>>
SanitizeOriginKeys(const uint64_t& aSinceWhen);
class Child : public PMediaChild
{
NS_INLINE_DECL_REFCOUNTING(Child)
public:
Child();
bool RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey);
uint32_t AddRequestPledge(ChildPledge<nsCString>& aPledge);
already_AddRefed<ChildPledge<nsCString>> RemoveRequestPledge(uint32_t aRequestId);
private:
virtual ~Child();
typedef std::pair<uint32_t,nsRefPtr<ChildPledge<nsCString>>> PledgeEntry;
static uint32_t sRequestCounter;
nsTArray<PledgeEntry> mRequestPledges;
};
PMediaChild* AllocPMediaChild();
bool DeallocPMediaChild(PMediaChild *aActor);
} // namespace media
} // namespace mozilla
#endif // mozilla_MediaChild_h

View File

@ -0,0 +1,456 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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 "MediaParent.h"
#include "mozilla/Base64.h"
#include <mozilla/StaticMutex.h>
#include "MediaUtils.h"
#include "MediaEngine.h"
#include "VideoUtils.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsILineInputStream.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsISupportsImpl.h"
#include "prlog.h"
#undef LOG
#if defined(PR_LOGGING)
PRLogModuleInfo *gMediaParentLog;
#define LOG(args) PR_LOG(gMediaParentLog, PR_LOG_DEBUG, args)
#else
#define LOG(args)
#endif
// A file in the profile dir is used to persist mOriginKeys used to anonymize
// deviceIds to be unique per origin, to avoid them being supercookies.
#define ORIGINKEYS_FILE "enumerate_devices.txt"
#define ORIGINKEYS_VERSION "1"
namespace mozilla {
namespace media {
static StaticMutex gMutex;
static ParentSingleton* sParentSingleton = nullptr;
class ParentSingleton : public nsISupports
{
NS_DECL_THREADSAFE_ISUPPORTS
class OriginKey
{
public:
static const size_t DecodedLength = 18;
OriginKey(const nsACString& aKey, int64_t aSecondsStamp)
: mKey(aKey)
, mSecondsStamp(aSecondsStamp) {}
nsCString mKey; // Base64 encoded.
int64_t mSecondsStamp;
};
class OriginKeysTable
{
public:
OriginKeysTable() {}
nsresult
GetOriginKey(const nsACString& aOrigin, nsCString& result)
{
OriginKey* key;
if (!mKeys.Get(aOrigin, &key)) {
nsCString salt; // Make a new one
nsresult rv = GenerateRandomName(salt, key->DecodedLength * 4 / 3);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
key = new OriginKey(salt, PR_Now() / PR_USEC_PER_SEC);
mKeys.Put(aOrigin, key);
}
result = key->mKey;
return NS_OK;
}
static PLDHashOperator
HashCleaner(const nsACString& aOrigin, nsAutoPtr<OriginKey>& aOriginKey,
void *aUserArg)
{
OriginKey* since = static_cast<OriginKey*>(aUserArg);
LOG((((aOriginKey->mSecondsStamp >= since->mSecondsStamp)?
"%s: REMOVE %lld >= %lld" :
"%s: KEEP %lld < %lld"),
__FUNCTION__, aOriginKey->mSecondsStamp, since->mSecondsStamp));
return (aOriginKey->mSecondsStamp >= since->mSecondsStamp)?
PL_DHASH_REMOVE : PL_DHASH_NEXT;
}
void Clear(int64_t aSinceWhen)
{
// Avoid int64_t* <-> void* casting offset
OriginKey since(nsCString(), aSinceWhen / PR_USEC_PER_SEC);
mKeys.Enumerate(HashCleaner, &since);
}
protected:
nsClassHashtable<nsCStringHashKey, OriginKey> mKeys;
};
class OriginKeysLoader : public OriginKeysTable
{
public:
OriginKeysLoader()
{
Load();
}
nsresult
GetOriginKey(const nsACString& aOrigin, nsCString& result)
{
auto before = mKeys.Count();
OriginKeysTable::GetOriginKey(aOrigin, result);
if (mKeys.Count() != before) {
Save();
}
return NS_OK;
}
already_AddRefed<nsIFile>
GetFile()
{
if (!mProfileDir) {
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(mProfileDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
}
nsCOMPtr<nsIFile> file;
nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
file->Append(NS_LITERAL_STRING(ORIGINKEYS_FILE));
return file.forget();
}
// Format of file is key secondsstamp origin (first line is version #):
//
// 1
// rOMAAbFujNwKyIpj4RJ3Wt5Q 1424733961 http://fiddle.jshell.net
// rOMAAbFujNwKyIpj4RJ3Wt5Q 1424734841 http://mozilla.github.io
// etc.
nsresult Read()
{
nsCOMPtr<nsIFile> file = GetFile();
if (NS_WARN_IF(!file)) {
return NS_ERROR_UNEXPECTED;
}
bool exists;
nsresult rv = file->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!exists) {
return NS_OK;
}
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsILineInputStream> i = do_QueryInterface(stream);
MOZ_ASSERT(i);
nsCString line;
bool hasMoreLines;
rv = i->ReadLine(line, &hasMoreLines);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!line.EqualsLiteral(ORIGINKEYS_VERSION)) {
// If version on disk is newer than we can understand then ignore it.
return NS_OK;
}
while (hasMoreLines) {
rv = i->ReadLine(line, &hasMoreLines);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Read key secondsstamp origin.
// Ignore any lines that don't fit format in the comment above exactly.
int32_t f = line.FindChar(' ');
if (f < 0) {
continue;
}
const nsACString& key = Substring(line, 0, f);
const nsACString& s = Substring(line, f+1);
f = s.FindChar(' ');
if (f < 0) {
continue;
}
int64_t secondsstamp = nsCString(Substring(s, 0, f)).ToInteger64(&rv);
if (NS_FAILED(rv)) {
continue;
}
const nsACString& origin = Substring(s, f+1);
// Validate key
if (key.Length() != OriginKey::DecodedLength) {
continue;
}
nsCString dummy;
rv = Base64Decode(key, dummy);
if (NS_FAILED(rv)) {
continue;
}
mKeys.Put(origin, new OriginKey(key, secondsstamp));
}
return NS_OK;
}
static PLDHashOperator
HashWriter(const nsACString& aOrigin, OriginKey* aOriginKey, void *aUserArg)
{
auto* stream = static_cast<nsIOutputStream *>(aUserArg);
nsCString buffer;
buffer.Append(aOriginKey->mKey);
buffer.Append(' ');
buffer.AppendInt(aOriginKey->mSecondsStamp);
buffer.Append(' ');
buffer.Append(aOrigin);
buffer.Append('\n');
uint32_t count;
nsresult rv = stream->Write(buffer.Data(), buffer.Length(), &count);
if (NS_WARN_IF(NS_FAILED(rv)) || count != buffer.Length()) {
return PL_DHASH_STOP;
}
return PL_DHASH_NEXT;
}
nsresult
Write()
{
nsCOMPtr<nsIFile> file = GetFile();
if (NS_WARN_IF(!file)) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIOutputStream> stream;
nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString buffer;
buffer.AppendLiteral(ORIGINKEYS_VERSION);
buffer.Append('\n');
uint32_t count;
rv = stream->Write(buffer.Data(), buffer.Length(), &count);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (count != buffer.Length()) {
return NS_ERROR_UNEXPECTED;
}
mKeys.EnumerateRead(HashWriter, stream.get());
nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
MOZ_ASSERT(safeStream);
rv = safeStream->Finish();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult Load()
{
nsresult rv = Read();
if (NS_WARN_IF(NS_FAILED(rv))) {
Delete();
}
return rv;
}
nsresult Save()
{
nsresult rv = Write();
if (NS_WARN_IF(NS_FAILED(rv))) {
NS_WARNING("Failed to write data for EnumerateDevices id-persistence.");
Delete();
}
return rv;
}
void Clear(int64_t aSinceWhen)
{
OriginKeysTable::Clear(aSinceWhen);
Delete();
Save();
}
nsresult Delete()
{
nsCOMPtr<nsIFile> file = GetFile();
if (NS_WARN_IF(!file)) {
return NS_ERROR_UNEXPECTED;
}
nsresult rv = file->Remove(false);
if (rv == NS_ERROR_FILE_NOT_FOUND) {
return NS_OK;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
private:
nsCOMPtr<nsIFile> mProfileDir;
};
private:
virtual ~ParentSingleton()
{
sParentSingleton = nullptr;
LOG((__FUNCTION__));
}
public:
static ParentSingleton* Get()
{
// Protect creation of singleton and access from multiple Background threads.
//
// Multiple Background threads happen because sanitize.js calls us from the
// chrome process and gets a thread separate from the one servicing ipc from
// the content process.
StaticMutexAutoLock lock(gMutex);
if (!sParentSingleton) {
sParentSingleton = new ParentSingleton();
}
return sParentSingleton;
}
OriginKeysLoader mOriginKeys;
OriginKeysTable mPrivateBrowsingOriginKeys;
};
NS_IMPL_ISUPPORTS0(ParentSingleton)
bool
Parent::RecvGetOriginKey(const uint32_t& aRequestId,
const nsCString& aOrigin,
const bool& aPrivateBrowsing)
{
// Hand over to stream-transport thread.
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(sts);
nsRefPtr<ParentSingleton> singleton(mSingleton);
nsRefPtr<PledgeRunnable<nsCString>> p = PledgeRunnable<nsCString>::New(
[singleton, aOrigin, aPrivateBrowsing](nsCString& aResult) {
if (aPrivateBrowsing) {
singleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, aResult);
} else {
singleton->mOriginKeys.GetOriginKey(aOrigin, aResult);
}
return NS_OK;
});
nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
nsRefPtr<media::Parent> keepAlive(this);
p->Then([this, keepAlive, aRequestId](const nsCString& aKey) mutable {
if (!mDestroyed) {
unused << SendGetOriginKeyResponse(aRequestId, aKey);
}
return NS_OK;
});
return true;
}
bool
Parent::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen)
{
// Hand over to stream-transport thread.
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(sts);
nsRefPtr<ParentSingleton> singleton(mSingleton);
nsRefPtr<PledgeRunnable<bool>> p = PledgeRunnable<bool>::New(
[singleton, aSinceWhen](bool) {
singleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
singleton->mOriginKeys.Clear(aSinceWhen);
return NS_OK;
});
nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return true;
}
void
Parent::ActorDestroy(ActorDestroyReason aWhy)
{
// No more IPC from here
mDestroyed = true;
LOG((__FUNCTION__));
}
Parent::Parent()
: mSingleton(ParentSingleton::Get())
, mDestroyed(false)
{
#if defined(PR_LOGGING)
if (!gMediaParentLog)
gMediaParentLog = PR_NewLogModule("MediaParent");
#endif
LOG(("media::Parent: %p", this));
MOZ_COUNT_CTOR(Parent);
}
Parent::~Parent()
{
LOG(("~media::Parent: %p", this));
MOZ_COUNT_DTOR(Parent);
}
PMediaParent*
AllocPMediaParent()
{
Parent* obj = new Parent();
obj->AddRef();
return obj;
}
bool
DeallocPMediaParent(media::PMediaParent *aActor)
{
static_cast<Parent*>(aActor)->Release();
return true;
}
}
}

View File

@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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_MediaParent_h
#define mozilla_MediaParent_h
#include "MediaChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/media/PMediaParent.h"
namespace mozilla {
namespace media {
// media::Parent implements the chrome-process side of ipc for media::Child APIs
class ParentSingleton;
class Parent : public PMediaParent
{
NS_INLINE_DECL_REFCOUNTING(Parent)
public:
virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
const nsCString& aOrigin,
const bool& aPrivateBrowsing) override;
virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
Parent();
private:
virtual ~Parent();
nsRefPtr<ParentSingleton> mSingleton;
bool mDestroyed;
};
PMediaParent* AllocPMediaParent();
bool DeallocPMediaParent(PMediaParent *aActor);
} // namespace media
} // namespace mozilla
#endif // mozilla_MediaParent_h

View File

@ -0,0 +1,13 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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 "MediaUtils.h"
namespace mozilla {
namespace media {
}
}

View File

@ -0,0 +1,197 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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_MediaUtils_h
#define mozilla_MediaUtils_h
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace media {
// A media::Pledge is a promise-like pattern for c++ that takes lambda functions.
//
// Asynchronous APIs that proxy to the chrome process and back, may return a
// pledge to allow callers to use pledge.Then() to specify a lambda function to
// invoke with the result once the asynchronous API resolves later back on the
// same thread.
//
// An advantage of this pattern is that lambdas allow "capturing" of local
// variables, much like closures in JavaScript.
template<typename ValueType>
class Pledge
{
// TODO: Remove workaround once mozilla allows std::function from <functional>
class FunctorsBase
{
public:
FunctorsBase() {}
virtual void Succeed(const ValueType& result) = 0;
virtual void Fail(nsresult rv) = 0;
virtual ~FunctorsBase() {};
};
public:
explicit Pledge() : mDone(false), mResult(NS_OK) {}
template<typename OnSuccessType>
void Then(OnSuccessType aOnSuccess)
{
Then(aOnSuccess, [](nsresult){});
}
template<typename OnSuccessType, typename OnFailureType>
void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
{
class F : public FunctorsBase
{
public:
F(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
: mOnSuccess(aOnSuccess), mOnFailure(aOnFailure) {}
void Succeed(const ValueType& result)
{
mOnSuccess(result);
}
void Fail(nsresult rv)
{
mOnFailure(rv);
};
OnSuccessType mOnSuccess;
OnFailureType mOnFailure;
};
mFunctors = new F(aOnSuccess, aOnFailure);
if (mDone) {
if (mResult == NS_OK) {
mFunctors->Succeed(mValue);
} else {
mFunctors->Fail(mResult);
}
}
}
protected:
void Resolve(const ValueType& aValue)
{
mValue = aValue;
Resolve();
}
void Resolve()
{
if (!mDone) {
mDone = true;
MOZ_ASSERT(mResult == NS_OK);
if (mFunctors) {
mFunctors->Succeed(mValue);
}
}
}
void Reject(nsresult rv)
{
if (!mDone) {
mDone = true;
mResult = rv;
if (mFunctors) {
mFunctors->Fail(mResult);
}
}
}
ValueType mValue;
protected:
bool mDone;
nsresult mResult;
private:
nsAutoPtr<FunctorsBase> mFunctors;
};
// General purpose runnable that also acts as a Pledge for the resulting value.
// Use PledgeRunnable<>::New() factory function to use with lambdas.
template<typename ValueType>
class PledgeRunnable : public Pledge<ValueType>, public nsRunnable
{
public:
template<typename OnRunType>
static PledgeRunnable<ValueType>*
New(OnRunType aOnRun)
{
class P : public PledgeRunnable<ValueType>
{
public:
explicit P(OnRunType& aOnRun)
: mOriginThread(NS_GetCurrentThread())
, mOnRun(aOnRun)
, mHasRun(false) {}
private:
virtual ~P() {}
NS_IMETHODIMP
Run()
{
if (!mHasRun) {
P::mResult = mOnRun(P::mValue);
mHasRun = true;
return mOriginThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool on;
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(mOriginThread->IsOnCurrentThread(&on)));
MOZ_RELEASE_ASSERT(on);
if (NS_SUCCEEDED(P::mResult)) {
P::Resolve();
} else {
P::Reject(P::mResult);
}
return NS_OK;
}
nsCOMPtr<nsIThread> mOriginThread;
OnRunType mOnRun;
bool mHasRun;
};
return new P(aOnRun);
}
protected:
virtual ~PledgeRunnable() {}
};
// General purpose runnable with an eye toward lambdas
namespace CallbackRunnable
{
template<typename OnRunType>
class Impl : public nsRunnable
{
public:
explicit Impl(OnRunType& aOnRun) : mOnRun(aOnRun) {}
private:
NS_IMETHODIMP
Run()
{
return mOnRun();
}
OnRunType mOnRun;
};
template<typename OnRunType>
Impl<OnRunType>*
New(OnRunType aOnRun)
{
return new Impl<OnRunType>(aOnRun);
}
}
}
}
#endif // mozilla_MediaUtils_h

View File

@ -0,0 +1,35 @@
/* 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 protocol PContent;
include protocol PBackground;
namespace mozilla {
namespace media {
protocol PMedia
{
manager PBackground;
parent:
/**
* Requests a persistent unique secret key for each origin.
* Has no expiry, but is cleared by age along with cookies.
* This is needed by mediaDevices.enumerateDevices() to produce persistent
* deviceIds that wont work cross-origin.
*/
GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing);
/**
* On clear cookies. Fire and forget.
*/
SanitizeOriginKeys(uint64_t aSinceWhen);
child:
GetOriginKeyResponse(uint32_t aRequestId, nsCString key);
__delete__();
};
} // namespace media
} // namespace mozilla

View File

@ -37,6 +37,22 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
]
]
EXPORTS.mozilla.media += ['MediaChild.h',
'MediaParent.h',
'MediaUtils.h'
]
UNIFIED_SOURCES += ['MediaChild.cpp',
'MediaParent.cpp',
'MediaUtils.cpp'
]
IPDL_SOURCES += [
'PMedia.ipdl',
]
# /dom/base needed for nsGlobalWindow.h in MediaChild.cpp
LOCAL_INCLUDES += [
'/dom/base',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -644,6 +644,8 @@ var interfaceNamesInGlobalScope =
"LocalMediaStream",
// IMPORTANT: Do not change this list without review from a DOM peer!
"Location",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MediaDeviceInfo",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MediaDevices",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -0,0 +1,22 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* http://dev.w3.org/2011/webrtc/editor/getusermedia.html
*/
enum MediaDeviceKind {
"audioinput",
"audiooutput",
"videoinput"
};
[Func="Navigator::HasUserMediaSupport"]
interface MediaDeviceInfo {
readonly attribute DOMString deviceId;
readonly attribute MediaDeviceKind kind;
readonly attribute DOMString label;
readonly attribute DOMString groupId;
};

View File

@ -12,12 +12,12 @@
[Func="Navigator::HasUserMediaSupport"]
interface MediaDevices : EventTarget {
// attribute EventHandler ondevicechange;
//
// void enumerateDevices (MediaDeviceInfoCallback resultCallback);
//
// attribute EventHandler ondevicechange;
// static Dictionary getSupportedConstraints (DOMString kind);
[Throws, Func="Navigator::HasUserMediaSupport"]
[Throws]
Promise<sequence<MediaDeviceInfo>> enumerateDevices();
[Throws]
Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints);
};

View File

@ -262,6 +262,7 @@ WEBIDL_FILES = [
'ListBoxObject.webidl',
'LocalMediaStream.webidl',
'Location.webidl',
'MediaDeviceInfo.webidl',
'MediaDevices.webidl',
'MediaElementAudioSourceNode.webidl',
'MediaError.webidl',

View File

@ -40,7 +40,6 @@ protected:
nsCOMPtr<nsIURI> mUrl;
nsCOMPtr<nsIURI> mOriginalURI;
int64_t mContentLength;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
nsCOMPtr<nsISupports> mOwner;

View File

@ -460,8 +460,8 @@ nsIconChannel::
NS_IMETHODIMP
nsIconChannel::GetContentLength(int64_t* aContentLength)
{
*aContentLength = mContentLength;
return NS_OK;
*aContentLength = 0;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP

View File

@ -708,8 +708,8 @@ nsIconChannel::
NS_IMETHODIMP
nsIconChannel::GetContentLength(int64_t* aContentLength)
{
*aContentLength = mContentLength;
return NS_OK;
*aContentLength = 0;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP

View File

@ -43,7 +43,6 @@ public:
protected:
nsCOMPtr<nsIURI> mUrl;
nsCOMPtr<nsIURI> mOriginalURI;
int64_t mContentLength;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
nsCOMPtr<nsISupports> mOwner;

View File

@ -121,8 +121,13 @@ SourceBuffer::Compact()
// consumer will ever have to wait.
mWaitingConsumers.Compact();
// If we have no more than one chunk, then we can't compact further.
if (mChunks.Length() < 2) {
// If we have no chunks, then there's nothing to compact.
if (mChunks.Length() < 1) {
return NS_OK;
}
// If we have one chunk, then we can compact if it has excess capacity.
if (mChunks.Length() == 1 && mChunks[0].Length() == mChunks[0].Capacity()) {
return NS_OK;
}

View File

@ -662,6 +662,13 @@ imgRequest::GetMultipart() const
return mIsMultiPartChannel;
}
bool
imgRequest::HadInsecureRedirect() const
{
MutexAutoLock lock(mMutex);
return mHadInsecureRedirect;
}
/** nsIRequestObserver methods **/
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
@ -1184,6 +1191,7 @@ imgRequest::OnRedirectVerifyCallback(nsresult result)
NS_FAILED(mCurrentURI->SchemeIs("chrome", &isChrome)) ||
NS_FAILED(NS_URIChainHasFlags(mCurrentURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) ||
(!isHttps && !isChrome && !schemeLocal)) {
MutexAutoLock lock(mMutex);
mHadInsecureRedirect = true;
}

View File

@ -116,7 +116,7 @@ public:
// Returns whether we went through an insecure (non-HTTPS) redirect at some
// point during loading. This does not consider the current URI.
bool HadInsecureRedirect() const { return mHadInsecureRedirect; }
bool HadInsecureRedirect() const;
// The CORS mode for which we loaded this image.
int32_t GetCORSMode() const { return mCORSMode; }

View File

@ -7,6 +7,7 @@
#include "ActorsChild.h" // IndexedDB
#include "BroadcastChannelChild.h"
#include "FileDescriptorSetChild.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/PBlobChild.h"
#include "mozilla/dom/cache/ActorUtils.h"
@ -281,6 +282,18 @@ BackgroundChildImpl::DeallocPCacheStreamControlChild(PCacheStreamControlChild* a
return true;
}
media::PMediaChild*
BackgroundChildImpl::AllocPMediaChild()
{
return media::AllocPMediaChild();
}
bool
BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
{
return media::DeallocPMediaChild(aActor);
}
} // namespace ipc
} // namespace mozilla

View File

@ -71,6 +71,12 @@ protected:
virtual bool
DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) override;
virtual PMediaChild*
AllocPMediaChild() override;
virtual bool
DeallocPMediaChild(PMediaChild* aActor) override;
virtual PVsyncChild*
AllocPVsyncChild() override;

View File

@ -6,6 +6,7 @@
#include "BroadcastChannelParent.h"
#include "FileDescriptorSetParent.h"
#include "mozilla/media/MediaParent.h"
#include "mozilla/AppProcessChecker.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/ContentParent.h"
@ -360,6 +361,18 @@ BackgroundParentImpl::DeallocPBroadcastChannelParent(
return true;
}
media::PMediaParent*
BackgroundParentImpl::AllocPMediaParent()
{
return media::AllocPMediaParent();
}
bool
BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor)
{
return media::DeallocPMediaParent(aActor);
}
namespace {
class RegisterServiceWorkerCallback final : public nsRunnable

View File

@ -84,6 +84,12 @@ protected:
virtual bool
DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
virtual PMediaParent*
AllocPMediaParent() override;
virtual bool
DeallocPMediaParent(PMediaParent* aActor) override;
virtual bool
RecvRegisterServiceWorker(const ServiceWorkerRegistrationData& aData)
override;

View File

@ -11,6 +11,7 @@ include protocol PCacheStorage;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
include protocol PVsync;
include protocol PMedia;
include DOMTypes;
include PBackgroundSharedTypes;
@ -34,6 +35,7 @@ sync protocol PBackground
manages PCacheStreamControl;
manages PFileDescriptorSet;
manages PVsync;
manages PMedia;
parent:
// Only called at startup during mochitests to check the basic infrastructure.
@ -42,6 +44,7 @@ parent:
PBackgroundIDBFactory(LoggingInfo loggingInfo);
PVsync();
PMedia();
PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel);

View File

@ -9088,8 +9088,6 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
mCurrentReflowRoot = target;
#endif
target->WillReflow(mPresContext);
// If the target frame is the root of the frame hierarchy, then
// use all the available space. If it's simply a `reflow root',
// then use the target frame's size as the available space.

View File

@ -46,9 +46,9 @@ associated with the frame, painting the frame, and handling events that
are passed in from the widget hierarchy.&nbsp; The nsIHTMLReflow interface
inherits from the nsIReflow interface and adds methods related to word
breaking and whitespace querying.&nbsp; The nsIReflow interface defines
the Reflow() method that initiates the reflow process along with the WillReflow()
and DidReflow() methods that get called before and after the reflow process
respectively.&nbsp; nsReflowState and nsReflowMetrics are parameters to
the Reflow() method that initiates the reflow process along with the
DidReflow() method that get calledafter the reflow process.&nbsp;
nsReflowState and nsReflowMetrics are parameters to
the templatized nsIReflow interface: the former is used to hold state during
reflow of a frame and the latter is used to return the frame's desired
size and alignment to the parent frame during the reflow process.

View File

@ -775,6 +775,7 @@ nsComboboxControlFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
// Constraints we try to satisfy:
// 1) Default width of button is the vertical scrollbar size

View File

@ -379,6 +379,7 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -180,10 +180,11 @@ nsHTMLButtonControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
void
nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -126,10 +126,10 @@ nsImageControlFrame::GetType() const
}
void
nsImageControlFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
nsImageControlFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
DO_GLOBAL_REFLOW_COUNT("nsImageControlFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -364,6 +364,7 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
return;
}
MarkInReflow();
/*
* Due to the fact that our intrinsic height depends on the heights of our
* kids, we end up having to do two-pass reflow, in general -- the first pass
@ -457,8 +458,6 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
computedHeight = state.ApplyMinMaxHeight(computedHeight);
state.SetComputedHeight(computedHeight);
nsHTMLScrollFrame::WillReflow(aPresContext);
// XXXbz to make the ascent really correct, we should add our
// mComputedPadding.top to it (and subtract it from descent). Need that
// because nsGfxScrollFrame just adds in the border....
@ -577,7 +576,6 @@ nsListControlFrame::ReflowAsDropdown(nsPresContext* aPresContext,
mLastDropdownComputedHeight = state.ComputedHeight();
nsHTMLScrollFrame::WillReflow(aPresContext);
nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
}

View File

@ -95,10 +95,11 @@ NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
void
nsMeterFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsMeterFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -106,6 +106,7 @@ nsNumberControlFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -100,10 +100,11 @@ nsProgressFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
void
nsProgressFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsProgressFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -279,6 +279,7 @@ nsRangeFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsRangeFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -489,6 +489,7 @@ nsTextControlFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -397,9 +397,6 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat
aContainingBlock.width,
aContainingBlock.height);
// Send the WillReflow() notification and position the frame
aKidFrame->WillReflow(aPresContext);
// Get the border values
const nsMargin& border = aReflowState.mStyleBorder->GetComputedBorder();

View File

@ -84,6 +84,7 @@ BRFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("BRFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
WritingMode wm = aReflowState.GetWritingMode();

View File

@ -1012,6 +1012,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
#ifdef DEBUG
@ -7003,7 +7004,6 @@ nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame,
nsHTMLReflowState reflowState(aState.mPresContext, rs,
aBulletFrame, availSize);
nsReflowStatus status;
aBulletFrame->WillReflow(aState.mPresContext);
aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status);
// Get the float available space using our saved state from before we

View File

@ -282,9 +282,6 @@ nsBlockReflowContext::ReflowBlock(const LogicalRect& aSpace,
mOuterReflowState.mBlockDelta + mBCoord - aLine->BStart();
}
// Let frame know that we are reflowing it
mFrame->WillReflow(mPresContext);
#ifdef DEBUG
mMetrics.ISize(mWritingMode) = nscoord(0xdeadbeef);
mMetrics.BSize(mWritingMode) = nscoord(0xdeadbeef);

View File

@ -623,6 +623,7 @@ nsBulletFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);

View File

@ -604,6 +604,7 @@ nsCanvasFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");

View File

@ -1014,6 +1014,7 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
// Don't support interruption in columns
nsPresContext::InterruptPreventer noInterrupts(aPresContext);

View File

@ -934,11 +934,6 @@ nsContainerFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
return result;
}
/**
* Invokes the WillReflow() function, positions the frame and its view (if
* requested), and then calls Reflow(). If the reflow succeeds and the child
* frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
*/
void
nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
nsPresContext* aPresContext,
@ -957,10 +952,7 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
"FinishReflowChild with unconstrained container width!");
}
// Send the WillReflow() notification, and position the child frame
// and its view if requested
aKidFrame->WillReflow(aPresContext);
// Position the child frame and its view if requested.
if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
aKidFrame->SetPosition(aWM, aPos, aContainerWidth);
}
@ -1002,10 +994,7 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
{
NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
// Send the WillReflow() notification, and position the child frame
// and its view if requested
aKidFrame->WillReflow(aPresContext);
// Position the child frame and its view if requested.
if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
aKidFrame->SetPosition(nsPoint(aX, aY));
}

View File

@ -231,9 +231,9 @@ public:
bool aShrinkWrap) override;
/**
* Invokes the WillReflow() function, positions the frame and its view (if
* requested), and then calls Reflow(). If the reflow succeeds and the child
* frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
* Positions aChildFrame and its view (if requested), and then calls Reflow().
* If the reflow status after reflowing the child is FULLY_COMPLETE then any
* next-in-flows are deleted using DeleteNextInFlowChild().
*
* Flags:
* NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
@ -241,7 +241,7 @@ public:
* NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
* case. Also implies NS_FRAME_NO_MOVE_VIEW
*/
void ReflowChild(nsIFrame* aKidFrame,
void ReflowChild(nsIFrame* aChildFrame,
nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,

View File

@ -164,6 +164,7 @@ nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aReflowStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);
@ -205,7 +206,6 @@ nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
ll.SetInFirstLetter(true);
ll.SetFirstLetterStyleOK(true);
kid->WillReflow(aPresContext);
kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);
ll.EndLineReflow();

View File

@ -3429,6 +3429,7 @@ nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,

View File

@ -4410,20 +4410,6 @@ nsFrame::ShrinkWidthToFit(nsRenderingContext *aRenderingContext,
return result;
}
void
nsFrame::WillReflow(nsPresContext* aPresContext)
{
#ifdef DEBUG_dbaron_off
// bug 81268
NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
"nsFrame::WillReflow: frame is already in reflow");
#endif
NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
("WillReflow: oldState=%x", mState));
mState |= NS_FRAME_IN_REFLOW;
}
void
nsFrame::DidReflow(nsPresContext* aPresContext,
const nsHTMLReflowState* aReflowState,
@ -4524,6 +4510,7 @@ nsFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsFrame");
aDesiredSize.ClearSize();
aStatus = NS_FRAME_COMPLETE;
@ -8644,7 +8631,6 @@ nsFrame::BoxReflow(nsBoxLayoutState& aState,
#endif
// place the child and reflow
WillReflow(aPresContext);
Reflow(aPresContext, aDesiredSize, reflowState, status);
@ -8961,7 +8947,7 @@ GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize,
void
nsFrame::Trace(const char* aMethod, bool aEnter)
{
if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
char tagbuf[40];
GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
@ -8971,7 +8957,7 @@ nsFrame::Trace(const char* aMethod, bool aEnter)
void
nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
{
if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
char tagbuf[40];
GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
PR_LogPrint("%s: %s %s, status=%scomplete%s",
@ -8984,7 +8970,7 @@ nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
void
nsFrame::TraceMsg(const char* aFormatString, ...)
{
if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
// Format arguments into a buffer
char argbuf[200];
va_list ap;

View File

@ -302,7 +302,6 @@ public:
nscoord ShrinkWidthToFit(nsRenderingContext *aRenderingContext,
nscoord aWidthInCB);
virtual void WillReflow(nsPresContext* aPresContext) override;
/**
* Calculates the size of this frame after reflowing (calling Reflow on, and
* updating the size and position of) its children, as necessary. The

View File

@ -855,6 +855,7 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
nsIPresShell *shell = aPresContext->PresShell();

View File

@ -802,6 +802,7 @@ nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -1180,6 +1180,7 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -251,6 +251,7 @@ nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,

View File

@ -1766,17 +1766,6 @@ public:
nscoord* aX,
nscoord* aXMost);
/**
* Pre-reflow hook. Before a frame is reflowed this method will be called.
* This call will always be invoked at least once before a subsequent Reflow
* and DidReflow call. It may be called more than once, In general you will
* receive on WillReflow notification before each Reflow request.
*
* XXX Is this really the semantics we want? Because we have the NS_FRAME_IN_REFLOW
* bit we can ensure we don't call it more than once...
*/
virtual void WillReflow(nsPresContext* aPresContext) = 0;
/**
* The frame is given an available size and asked for its desired
* size. This is the frame's opportunity to reflow its children.
@ -3021,6 +3010,14 @@ private:
}
protected:
void MarkInReflow() {
#ifdef DEBUG_dbaron_off
// bug 81268
NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), "frame is already in reflow");
#endif
mState |= NS_FRAME_IN_REFLOW;
}
nsFrameState mState;
// When there is an overflow area only slightly larger than mRect,

View File

@ -858,6 +858,7 @@ nsImageFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,

View File

@ -333,6 +333,7 @@ nsInlineFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsInlineFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
if (nullptr == aReflowState.mLineLayout) {
@ -1064,6 +1065,7 @@ nsFirstLineFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
if (nullptr == aReflowState.mLineLayout) {
return; // XXX does this happen? why?
}

View File

@ -57,6 +57,7 @@ nsLeafFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsLeafFrame");
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("enter nsLeafFrame::Reflow: aMaxSize=%d,%d",

View File

@ -931,10 +931,8 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
mLineBox->DisableResizeReflowOptimization();
}
// Let frame know that are reflowing it. Note that we don't bother
// positioning the frame yet, because we're probably going to end up
// moving it when we do the block-direction alignment
aFrame->WillReflow(mPresContext);
// Note that we don't bother positioning the frame yet, because we're probably
// going to end up moving it when we do the block-direction alignment.
// Adjust spacemanager coordinate system for the frame.
nsHTMLReflowMetrics metrics(lineWM);

View File

@ -26,6 +26,7 @@ nsPageContentFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
aStatus = NS_FRAME_COMPLETE; // initialize out parameter

View File

@ -55,6 +55,7 @@ nsPageFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsPageFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
aStatus = NS_FRAME_COMPLETE; // initialize out parameter

View File

@ -141,6 +141,7 @@ nsPlaceholderFrame::Reflow(nsPresContext* aPresContext,
}
#endif
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
aDesiredSize.ClearSize();

View File

@ -495,6 +495,7 @@ nsPluginFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsPluginFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);

View File

@ -426,6 +426,7 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
aStatus = NS_FRAME_COMPLETE;

View File

@ -134,6 +134,7 @@ nsRubyFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -122,6 +122,7 @@ nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

View File

@ -161,6 +161,7 @@ nsSimplePageSequenceFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(),
"A Page Sequence is only for real pages");
DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame");

View File

@ -758,6 +758,7 @@ nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,

View File

@ -8003,6 +8003,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsTextFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);

View File

@ -242,6 +242,7 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,

View File

@ -175,6 +175,7 @@ ViewportFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("ViewportFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow");

View File

@ -891,6 +891,8 @@ nsMathMLContainerFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
mPresentationData.flags &= ~NS_MATHML_ERROR;
aDesiredSize.Width() = aDesiredSize.Height() = 0;
aDesiredSize.SetBlockStartAscent(0);
aDesiredSize.mBoundingMetrics = nsBoundingMetrics();

View File

@ -115,13 +115,6 @@ public:
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus) override;
virtual void
WillReflow(nsPresContext* aPresContext) override
{
mPresentationData.flags &= ~NS_MATHML_ERROR;
nsContainerFrame::WillReflow(aPresContext);
}
virtual void DidReflow(nsPresContext* aPresContext,
const nsHTMLReflowState* aReflowState,
nsDidReflowStatus aStatus) override

View File

@ -137,6 +137,8 @@ nsMathMLSelectedFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
mPresentationData.flags &= ~NS_MATHML_ERROR;
aStatus = NS_FRAME_COMPLETE;
aDesiredSize.ClearSize();
aDesiredSize.SetBlockStartAscent(0);

View File

@ -126,6 +126,9 @@ nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
mPresentationData.flags &= ~NS_MATHML_ERROR;
// initializations needed for empty markup like <mtag></mtag>
aDesiredSize.ClearSize();
aDesiredSize.SetBlockStartAscent(0);

View File

@ -215,6 +215,8 @@ nsMathMLmfencedFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
mPresentationData.flags &= ~NS_MATHML_ERROR;
aDesiredSize.ClearSize();
aDesiredSize.SetBlockStartAscent(0);
aDesiredSize.mBoundingMetrics = nsBoundingMetrics();

View File

@ -308,6 +308,7 @@ nsMathMLmpaddedFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
mPresentationData.flags &= ~NS_MATHML_ERROR;
ProcessAttributes();
///////////////

View File

@ -164,8 +164,10 @@ nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
nsReflowStatus childStatus;
mPresentationData.flags &= ~NS_MATHML_ERROR;
aDesiredSize.ClearSize();
aDesiredSize.SetBlockStartAscent(0);

View File

@ -98,6 +98,8 @@ nsMathMLmspaceFrame::Reflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
MarkInReflow();
mPresentationData.flags &= ~NS_MATHML_ERROR;
ProcessAttributes(aPresContext);
mBoundingMetrics = nsBoundingMetrics();

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -220,6 +220,9 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 775227 # b2g(times out,
[test_transitions_dynamic_changes.html]
[test_transitions_bug537151.html]
[test_unclosed_parentheses.html]
[test_unicode_range_loading.html]
support-files = ../../reftests/fonts/markA.woff ../../reftests/fonts/markB.woff ../../reftests/fonts/markC.woff ../../reftests/fonts/markD.woff
skip-if = (toolkit == "gtk2") || (toolkit == "gtk3") # bug 1056479
[test_units_angle.html]
[test_units_frequency.html]
[test_units_length.html]

View File

@ -0,0 +1,366 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset=utf-8>
<title>unicode-range load tests using font loading api</title>
<link rel="author" title="John Daggett" href="mailto:jdaggett@mozilla.com">
<link rel="help" href="http://www.w3.org/TR/css-fonts-3/#unicode-range-desc" />
<link rel="help" href="http://dev.w3.org/csswg/css-font-loading/" />
<meta name="assert" content="unicode-range descriptor defines precisely which fonts should be loaded" />
<script type="text/javascript" src="/resources/testharness.js"></script>
<script type="text/javascript" src="/resources/testharnessreport.js"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="log"></div>
<pre id="display"></pre>
<style id="testfonts"></style>
<style id="teststyle"></style>
<div id="testcontent"></div>
<script>
const kSheetFonts = 1;
const kSheetStyles = 2;
const redSquDataURL = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' width='100%' height='100%'><rect fill='red' x='0' y='0' width='10' height='10'/></svg>";
var unicodeRangeTests = [
{ test: "simple load sanity check, unused fonts not loaded",
fonts: [{ family: "a", src: "markA", descriptors: { }, loaded: false}],
content: "AAA", style: { "font-family": "unused" } },
{ test: "simple load sanity check, font for a used character loaded",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true}],
content: "AAA" },
{ test: "simple load sanity check, font for an unused character not loaded",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: false}],
content: "BBB" },
{ test: "simple load sanity check, with two fonts only font for used character loaded A",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false}],
content: "AAA" },
{ test: "simple load sanity check, with two fonts only font for used character loaded B",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: false},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true}],
content: "BBB" },
{ test: "simple load sanity check, two fonts but neither supports characters used",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: false},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false}],
content: "CCC" },
{ test: "simple load sanity check, two fonts and both are used",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true}],
content: "ABC" },
{ test: "simple load sanity check, one with Han ranges",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+3???,u+5???,u+7???,u+8???" }, loaded: true},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false}],
content: "納豆嫌い" },
{ test: "simple load sanity check, two fonts with different styles A",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
{ family: "a", src: "markB", descriptors: { weight: "bold", unicodeRange: "u+42" }, loaded: false}],
content: "ABC" },
{ test: "simple load sanity check, two fonts with different styles B",
fonts: [{ family: "a", src: "markA", descriptors: { weight: "bold", unicodeRange: "u+41" }, loaded: false},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true}],
content: "ABC" },
{ test: "multiple fonts with overlapping ranges, all with default ranges, only last one supports character used",
fonts: [{ family: "a", src: "markC", descriptors: { }, loaded: true},
{ family: "a", src: "markA", descriptors: { }, loaded: true},
{ family: "a", src: "markB", descriptors: { }, loaded: true}],
content: "CCC" },
{ test: "multiple fonts with overlapping ranges, all with default ranges, first one supports character used",
fonts: [{ family: "a", src: "markB", descriptors: { }, loaded: false},
{ family: "a", src: "markA", descriptors: { }, loaded: false},
{ family: "a", src: "markC", descriptors: { }, loaded: true}],
content: "CCC" },
{ test: "multiple fonts with overlapping ranges, one with default value in the fallback position",
fonts: [{ family: "a", src: "markC", descriptors: { }, loaded: true},
{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true}],
content: "ABC" },
{ test: "multiple fonts with overlapping ranges, one with default value in the primary use position, fallback to one",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false},
{ family: "a", src: "markC", descriptors: { }, loaded: true}],
content: "AAA" },
{ test: "multiple fonts with overlapping ranges, one with default value in the primary use position, fallback to two",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true},
{ family: "a", src: "markC", descriptors: { }, loaded: true}],
content: "ABC" },
{ test: "multiple fonts with overlapping ranges, one with default value in the primary use position, no fallback",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: false},
{ family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false},
{ family: "a", src: "markC", descriptors: { }, loaded: true}],
content: "CCC" },
{ test: "metrics only case, ex-sized image, single font with space in range",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+0??" }, loaded: true}],
content: "<img style='width: 2ex' src=\"" + redSquDataURL + "\">" },
{ test: "metrics only case, ex-sized image, single font with space outside range",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+1??" }, loaded: false}],
content: "<img style='width: 2ex' src=\"" + redSquDataURL + "\">" },
{ test: "metrics only case, ch-sized image, single font with space in range",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+0??" }, loaded: true}],
content: "<img style='width: 2ch' src=\"" + redSquDataURL + "\">" },
{ test: "metrics only case, ch-sized image, single font with space outside range",
fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+1??" }, loaded: false}],
content: "<img style='width: 2ch' src=\"" + redSquDataURL + "\">" },
];
// map font loading descriptor names to @font-face rule descriptor names
var mapDescriptorNames = {
style: "font-style",
weight: "font-weight",
stretch: "font-stretch",
unicodeRange: "unicode-range",
variant: "font-variant",
featureSettings: "font-feature-settings"
};
var kBaseFontURL;
if ("SpecialPowers" in window) {
kBaseFontURL = "";
} else {
kBaseFontURL = "fonts/";
}
var mapFontURLs = {
markA: "url(" + kBaseFontURL + "markA.woff" + ")",
markB: "url(" + kBaseFontURL + "markB.woff" + ")",
markC: "url(" + kBaseFontURL + "markC.woff" + ")",
markD: "url(" + kBaseFontURL + "markD.woff" + ")",
/* twourl versions include a bogus url followed by a valid url */
markAtwourl: "url(" + kBaseFontURL + "bogus-markA.woff" + "), url(" + kBaseFontURL + "markA.woff" + ")",
markBtwourl: "url(" + kBaseFontURL + "bogus-markB.woff" + "), url(" + kBaseFontURL + "markB.woff" + ")",
markCtwourl: "url(" + kBaseFontURL + "bogus-markC.woff" + "), url(" + kBaseFontURL + "markC.woff" + ")",
markDtwourl: "url(" + kBaseFontURL + "bogus-markD.woff" + "), url(" + kBaseFontURL + "markD.woff" + ")",
/* localfont versions include a bogus local ref followed by a valid url */
markAlocalfirst: "local(bogus-markA), url(" + kBaseFontURL + "markA.woff" + ")",
markBlocalfirst: "local(bogus-markB), url(" + kBaseFontURL + "markB.woff" + ")",
markClocalfirst: "local(bogus-markC), url(" + kBaseFontURL + "markC.woff" + ")",
markDlocalfirst: "local(bogus-markD), url(" + kBaseFontURL + "markD.woff" + ")",
};
function familyName(name, i) {
return "test" + i + "-" + name;
}
function fontFaceRule(name, fontdata, ft) {
var desc = [];
desc.push("font-family: " + name);
var srckey = fontdata.src + ft;
desc.push("src: " + mapFontURLs[srckey]);
for (var d in fontdata.descriptors) {
desc.push(mapDescriptorNames[d] + ": " + fontdata.descriptors[d]);
}
return "@font-face { " + desc.join(";") + " }";
}
function clearRules(sheetIndex) {
var sheet = document.styleSheets[sheetIndex];
while(sheet.cssRules.length > 0) {
sheet.deleteRule(0);
}
}
function clearAllRulesAndFonts() {
clearRules(kSheetFonts);
clearRules(kSheetStyles);
document.fonts.clear();
}
function addStyleRulesAndText(testdata, i) {
// add style rules for testcontent
var sheet = document.styleSheets[kSheetStyles];
while(sheet.cssRules.length > 0) {
sheet.deleteRule(0);
}
var rule = [];
var family = familyName(testdata.fonts[0].family, i);
rule.push("#testcontent { font-family: " + family);
if ("style" in testdata) {
for (s in testdata.style) {
rule.push(s + ": " + testdata.style[s]);
}
}
rule.push("}");
sheet.insertRule(rule.join("; "), 0);
var content = document.getElementById("testcontent");
content.innerHTML = testdata.content;
content.offsetHeight;
}
// work arounds
function getFonts() {
if ("forEach" in document.fonts) {
return document.fonts;
}
return Array.from(document.fonts);
}
function getSize() {
if ("size" in document.fonts) {
return document.fonts.size;
}
return getFonts().length;
}
function getReady() {
if (typeof(document.fonts.ready) == "function") {
return document.fonts.ready();
}
return document.fonts.ready;
}
function setTimeoutPromise(aDelay) {
return new Promise(function(aResolve, aReject) {
setTimeout(aResolve, aDelay);
});
}
function addFontFaceRules(testdata, i, ft) {
var sheet = document.styleSheets[kSheetFonts];
var createdFonts = [];
testdata.fonts.forEach(function(f) {
var n = sheet.cssRules.length;
var fn = familyName(f.family, i);
sheet.insertRule(fontFaceRule(fn, f, ft), n);
var newfont;
var fonts = getFonts();
try {
fonts.forEach(function(font) { newfont = font; });
createdFonts.push({family: fn, data: f, font: newfont});
} catch (e) {
console.log(e);
}
});
return createdFonts;
}
function addDocumentFonts(testdata, i, ft) {
var createdFonts = [];
testdata.fonts.forEach(function(fd) {
var fn = familyName(fd.family, i);
var srckey = fd.src + ft;
var f = new FontFace(fn, mapFontURLs[srckey], fd.descriptors);
document.fonts.add(f);
createdFonts.push({family: fn, data: fd, font: f});
});
return createdFonts;
}
var q = Promise.resolve();
function runTests() {
function setupTests() {
setup({explicit_done: true});
}
function checkFontsBeforeLoad(name, testdata, fd) {
test(function() {
assert_equals(document.fonts.status, "loaded", "before initializing test, no fonts should be loading - found: " + document.fonts.status);
var size = getSize();
assert_equals(size, testdata.fonts.length,
"fonts where not added to the font set object");
var i = 0;
fonts = getFonts();
fonts.forEach(function(ff) {
assert_equals(ff.status, "unloaded", "added fonts should be in unloaded state");
});
}, name + " before load");
}
function checkFontsAfterLoad(name, testdata, fd, afterTimeout) {
test(function() {
assert_equals(document.fonts.status, "loaded", "after ready promise resolved, no fonts should be loading");
var i = 0;
fd.forEach(function(f) {
assert_true(f.font instanceof FontFace, "font needs to be an instance of FontFace object");
if (f.data.loaded) {
assert_equals(f.font.status, "loaded", "font not loaded - font " + i + " " + f.data.src + " "
+ JSON.stringify(f.data.descriptors) + " for content " + testdata.content);
} else {
assert_equals(f.font.status, "unloaded", "font loaded - font " + i + " " + f.data.src + " "
+ JSON.stringify(f.data.descriptors) + " for content " + testdata.content);
}
i++;
});
}, name + " after load" + (afterTimeout ? " and timeout" : ""));
}
function testFontLoads(testdata, i, name, fd) {
checkFontsBeforeLoad(name, testdata, fd);
addStyleRulesAndText(testdata, i);
var ready = getReady();
return ready.then(function() {
checkFontsAfterLoad(name, testdata, fd, false);
}).then(function() {
return setTimeoutPromise(0).then(function() {
checkFontsAfterLoad(name, testdata, fd, true);
});
}).then(function() {
var ar = getReady();
return ar.then(function() {
test(function() {
assert_equals(document.fonts.status, "loaded", "after ready promise fulfilled once, fontset should not be loading");
var fonts = getFonts();
fonts.forEach(function(f) {
assert_not_equals(f.status, "loading", "after ready promise fulfilled once, no font should be loading");
});
}, name + " test done check");
});
}).then(function() {
clearAllRulesAndFonts();
});
}
function testUnicodeRangeFontFace(testdata, i, ft) {
var name = "TEST " + i + " " + testdata.test + " (@font-face rules)" + (ft != "" ? " " + ft : ft);
var fd = addFontFaceRules(testdata, i, ft);
return testFontLoads(testdata, i, name, fd);
}
function testUnicodeRangeDocumentFonts(testdata, i, ft) {
var name = "TEST " + i + " " + testdata.test + " (document.fonts)" + (ft != "" ? " " + ft : ft);
var fd = addDocumentFonts(testdata, i, ft);
return testFontLoads(testdata, i, name, fd);
}
q = q.then(function() {
setupTests();
});
var fontTypes = ["", "twourl", "localfirst"];
unicodeRangeTests.forEach(function(testdata, i) {
fontTypes.forEach(function(ft) {
q = q.then(function() {
return testUnicodeRangeFontFace(testdata, i, ft);
}).then(function() {
return testUnicodeRangeDocumentFonts(testdata, i, ft);
});
});
});
q = q.then(function() {
done();
});
}
if ("fonts" in document) {
runTests();
} else {
test(function() {
assert_true(true, "CSS Font Loading API is not enabled.");
}, "CSS Font Loading API is not enabled");
}
</script>
</body>
</html>

View File

@ -5263,7 +5263,6 @@ SVGTextFrame::DoReflow()
"style system should ensure that :-moz-svg-text "
"does not get styled");
kid->WillReflow(presContext);
kid->Reflow(presContext, desiredSize, reflowState, status);
kid->DidReflow(presContext, &reflowState, nsDidReflowStatus::FINISHED);
kid->SetSize(wm, desiredSize.Size(wm));

View File

@ -519,6 +519,7 @@ void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType)
void
nsSVGForeignObjectFrame::DoReflow()
{
MarkInReflow();
// Skip reflow if we're zero-sized, unless this is our first reflow.
if (IsDisabled() &&
!(GetStateBits() & NS_FRAME_FIRST_REFLOW))

Some files were not shown because too many files have changed in this diff Show More